首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring AI教程:用写Spring Boot的方式,把大模型接入你的应用

Spring AI教程:用写Spring Boot的方式,把大模型接入你的应用

原创
作者头像
闪 学it
发布2026-08-31 14:50:22
发布2026-08-31 14:50:22
1200
举报

如果你是一个Java后端工程师,2026年的今天,你可能已经无数次被一个问题困扰:“我们系统要不要接入大模型?怎么接?”

打开GitHub,搜到的AI相关SDK大多是Python生态的——LangChain、LlamaIndex、Transformers——仿佛AI世界和Java世界之间隔着一道难以逾越的墙。那些习惯了Spring Boot的清爽、依赖注入的优雅、AOP的简洁的Java工程师,面对Python里那些动态类型的、函数式风格浓厚的AI框架,常常有种“客场作战”的局促感。

这正是Spring AI诞生的背景。它不是要重新发明轮子,而是要把大模型的能力,用Spring开发者最熟悉的那套方式,包装好、端到面前来。你可以像写JdbcTemplate一样写ChatClient,像用RestTemplate一样调用大模型API,像定义Repository一样定义向量存储。所有的复杂度,都被一层熟悉的Spring外壳包裹住了。

本文就带你快速上手Spring AI,看它如何用“几行代码”把大模型请进你的Spring Boot应用。


一、Spring AI的哲学:让“熟悉”成为核心竞争力

在开始写代码之前,先理解Spring AI的设计基调。它做了三件关键的事:

  1. 抽象隔离:定义了统一的ChatClientEmbeddingClientImageClient接口,底层可以切换OpenAI、Azure、Ollama、通义千问、智谱等不同提供商,切换时业务代码几乎零改动。
  2. 模板化提示词:用PromptTemplate把变量嵌入提示词,就像用MessageFormatString.format一样自然。
  3. 结构化输出:不满足于让模型返回一段文本,而是通过BeanOutputConverter直接把模型输出映射成Java对象。

有了这三层,Java工程师几乎不需要学习任何新范式——依赖注入、配置绑定、面向接口编程、自动配置——全都是Spring生态玩了十几年的老本行。


二、快速起手:5分钟跑通第一个对话

第一步,在pom.xml里引入Spring AI的starter。以OpenAI为例:

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    <version>1.0.0-M4</version>
</dependency>

然后在application.yml里配置好API密钥:

代码语言:javascript
复制
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4o
          temperature: 0.7

接下来,写一个最简的Controller,几行代码搞定一个对话接口:

代码语言:javascript
复制
@RestController
@RequestMapping("/ai")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

启动应用,访问 http://localhost:8080/ai/chat?message=你好,请用一句话介绍石家庄,你就会收到模型的流利回复。

注意看这里ChatClient的构建方式是通过Builder注入的——这是Spring AI刻意保持的Spring Boot风格。不需要手动实例化OpenAI客户端,不需要管理连接池,一切交给Spring的自动配置。


三、提示词模板:告别字符串拼接的野蛮时代

当你的提示词开始变长、包含动态变量时,如果还在用String.format+拼接,代码会迅速腐烂。Spring AI提供的PromptTemplate可以帮你优雅地管理这些模板。

先定义一个提示词模板文件,放在src/main/resources/prompts/目录下:

代码语言:javascript
复制
你是一个{role},用{style}的语气回答用户的问题。

用户的咨询内容是:
{userInput}

你的回答要求:
1. 专业且准确
2. 控制在{maxWords}字以内
3. 如果信息不足,请直接告知用户

然后在代码里使用它:

代码语言:javascript
复制
@GetMapping("/consult")
public String consult(
        @RequestParam String role,
        @RequestParam String style,
        @RequestParam String userInput) {
    
    PromptTemplate template = new PromptTemplate("""
            你是一个{role},用{style}的语气回答用户的问题。
            
            用户的咨询内容是:
            {userInput}
            
            你的回答要求:
            1. 专业且准确
            2. 控制在200字以内
            3. 如果信息不足,请直接告知用户
            """);
    
    Prompt prompt = template.create(Map.of(
            "role", role,
            "style", style,
            "userInput", userInput
    ));
    
    return chatClient.prompt(prompt).call().content();
}

PromptTemplate底层会负责任何特殊字符的转义,你只需要关心业务变量本身。这比在代码里层层拼接字符串要干净一个数量级。


四、结构化输出:让模型的回答“长”成对象

大模型最让人头疼的事情之一,是它返回的是非结构化的文本。你想让它返回JSON,它可能多给你一段解释文字;你想让它返回一个日期格式,它可能给你带上了星期几。

Spring AI的BeanOutputConverter就是来终结这种痛苦的。你只需要定义一个POJO,告诉模型“你必须按照这个格式返回”,框架会自动将模型的文本输出反序列化成Java对象:

代码语言:javascript
复制
public record WeatherInfo(String city, String date, double temperature, String condition) {}

@GetMapping("/weather")
public WeatherInfo getWeather(@RequestParam String city) {
    String promptText = """
            请查询{city}今天的天气。
            
            严格按照以下JSON格式返回,不要添加任何额外文字:
            {"city": "城市名", "date": "日期", "temperature": 温度数值, "condition": "天气状况"}
            """;
    
    // 创建输出转换器,指定目标类型
    BeanOutputConverter<WeatherInfo> converter = 
            new BeanOutputConverter<>(WeatherInfo.class);
    
    String userPrompt = promptText.replace("{city}", city);
    
    // 在提示词中追加格式要求
    String fullPrompt = userPrompt + "\n\n" + converter.getFormat();
    
    String response = chatClient.prompt()
            .user(fullPrompt)
            .call()
            .content();
    
    // 直接从响应字符串解析成对象
    return converter.convert(response);
}

这里最巧妙的设计是:converter.getFormat()会自动生成类似“请输出符合以下JSON Schema的JSON”的指令,并告知模型字段的类型和约束。你甚至不需要手写JSON Schema——从Java Record的结构反射生成

这意味着,如果你的WeatherInfo里有一个LocalDateTime字段,框架会自动要求模型按照ISO-8601格式输出日期字符串,并在反序列化时帮你转换成LocalDateTime对象。全程类型安全,没有JSONObject.getXXX那种手动强转的隐患。


五、函数调用:让模型主动“问”你要数据

这是Spring AI最吸引人的能力之一。在此之前,如果你想让模型知道“今天的实时天气”,你必须在调用模型之前先把天气查好,塞进提示词里。现在你可以getWeather这个函数直接注册给模型,当模型觉得需要这个信息时,它会主动调用它。

代码语言:javascript
复制
@Component
public class WeatherTools {

    @Tool(description = "获取指定城市的实时天气信息")
    public WeatherInfo getWeather(String city) {
        // 这里调用真实的天气API
        return weatherService.fetchByCity(city);
    }
}

然后在构建ChatClient时注册这个工具:

代码语言:javascript
复制
@Bean
public ChatClient chatClient(ChatClient.Builder builder, WeatherTools weatherTools) {
    return builder
            .defaultTools(weatherTools.getWeather())
            .build();
}

当用户问“石家庄今天热吗”,模型会判断:我需要调用getWeather这个工具,传入参数city=石家庄。框架自动执行这个方法,把返回的WeatherInfo塞回给模型,模型再基于真实数据生成最终回复。整个过程对调用方来说是透明的——它看到的只是一个返回了气温描述的自然语言回答。

这种模式,让大模型从“会聊天的百科全书”进化成了“能主动调配后端服务的智能路由”。你写的每一个@Tool标注的方法,都像是给大模型装上了一只“可伸出去的手”。


六、向量存储与RAG:让模型记住你的私有数据

企业级应用里最尴尬的场景是:模型虽然博学,但它不认识你公司的内部文档、产品手册、售后记录。直接问它“我们公司的退货政策是什么”,它大概率会编一个。

Spring AI统一了向量存储的抽象接口,无论你用的是Redis、Elasticsearch还是PgVector,代码层都是同一套API:

代码语言:javascript
复制
@Service
public class DocumentService {

    private final VectorStore vectorStore;
    private final ChatClient chatClient;

    public void indexDocument(String content) {
        // 将文档分块并向量化存入
        List<Document> chunks = splitIntoChunks(content);
        vectorStore.add(chunks);
    }

    public String askFromKnowledge(String question) {
        // 检索最相关的3个文档片段
        List<Document> relevantDocs = vectorStore.similaritySearch(
                SearchRequest.builder()
                        .query(question)
                        .topK(3)
                        .build()
        );
        
        // 构建RAG提示词
        String context = relevantDocs.stream()
                .map(Document::getContent)
                .collect(Collectors.joining("\n---\n"));
        
        return chatClient.prompt()
                .user("""
                        请基于以下参考资料回答用户的问题。
                        如果参考资料中没有相关信息,请直接说"找不到相关答案"。
                        
                        参考资料:
                        {context}
                        
                        用户问题:{question}
                        """.replace("{context}", context)
                        .replace("{question}", question))
                .call()
                .content();
    }
}

VectorStore的接口极其稳定,底层换存储引擎时,只有配置变更,业务代码纹丝不动。这是Spring生态最擅长的事——用统一的抽象层,屏蔽底层实现差异


写在最后

Spring AI目前还处于快速迭代期,API会有变动,版本号还在M里程碑阶段。但它传递的信号已经非常清晰:Java生态正在以自己最擅长的方式,拥抱大模型时代

Python的AI框架胜在灵活和先锋,但Spring AI胜在规范、稳定、可维护。对于那些已经用Spring Boot构建了复杂业务系统的团队而言,接入Spring AI意味着几十万行存量代码可以继续丝滑运行,AI能力只是一个附加的、可插拔的模块,而非一次伤筋动骨的重构。

在Spring AI的世界里,你写出来的代码依然是你熟悉的那个味道——Controller、Service、Repository的分层依然存在,@Autowired依然在用,单元测试依然可以Mock掉ChatClient。唯一的变化是:你调用的不再是一个数据库或一个REST接口,而是一个可能改变人类工作方式的智能引擎。

而这一切的开始,只需要那几行代码。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、Spring AI的哲学:让“熟悉”成为核心竞争力
  • 二、快速起手:5分钟跑通第一个对话
  • 三、提示词模板:告别字符串拼接的野蛮时代
  • 四、结构化输出:让模型的回答“长”成对象
  • 五、函数调用:让模型主动“问”你要数据
  • 六、向量存储与RAG:让模型记住你的私有数据
  • 写在最后
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档