Spring AI 使用教程,4个案例(Java版)
一、啥玩意是 Spring AI 啊?
兄弟们,我先唠两句,最近是不是也看到一堆文章标题整得唬人得很:Spring Boot + AI 实现万物皆智能!,结果一进去,全是 hello world,连个像样的接口都不给你写一个。
我就纳闷了,整天嚷嚷要用 Spring AI,结果你问他到底干啥用的,他就一句“封装 OpenAI 的 API 啊”,那我直接 HttpClient 不香吗?
所以今天,我不整那套唬人的标题党,我就掏心窝子给你写几个Spring AI 真正落地能跑起来的 Java 案例,从业务视角出发,接地气,能跑,能看懂,能直接用的那种。
案例1:基于 Spring AI 实现 Chat 接口服务(整一个“本地闲聊API”)
这个小案例适合那些做后台服务的兄弟,一句话总结就是——咱用 Spring AI 封一层,让你项目里原地蹦出一个能闲聊的机器人接口,前端丢个问题,它能回你一句人话。
1.1 先上个依赖配置,别慌哥带你整
你先得搞清楚 Spring AI 目前推荐用 spring-ai-openai-spring-boot-starter
这个依赖,maven 里搞下这个:
<!-- Spring AI 核心 OpenAI Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.0</version>
</dependency>
然后你得去搞个 OpenAI 的 key,这里就不展开说了哈,注册一下,完了把 key 配进 application.yml 里:
spring:
ai:
openai:
api-key: sk-xxxxxxx你自己的key哈
对了对了,不配 key,你发啥它都装傻,就跟你媳妇吵架那种沉默状态。
1.2 Controller:整一个能对话的接口出来先
@RestController
@RequestMapping("/chat")
public class ChatController {
// 注入 OpenAI 的聊天服务
@Autowired
private OpenAiChatClient chatClient;
/**
* 处理用户发来的问题
* @param question 用户问题
* @return AI 的回答
*/
@PostMapping
public String ask(@RequestBody String question) {
// 构建聊天消息
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER, question);
ChatRequest request = new ChatRequest(List.of(chatMessage));
// 发请求给 OpenAI,得到返回
ChatResponse response = chatClient.call(request);
// 提取返回内容
return response.getResult().getOutput().getContent();
}
}
唠点人话:
这个接口说白了就一个意思:你 POST 个问题过来,它帮你丢给 OpenAI,再把结果兜回来送你面前。打工人跟 AI 之间的“传话筒”就是它了。
1.3 吐槽点 + 注意事项:
- 有些兄弟问我为啥不用 RestTemplate 去调 OpenAI,那你这不就回到 HttpClient 的原始社会了嘛,Spring AI 都帮你封好了你还自己写一遍?有病啊?
- 哥试过了,用 Spring AI 封装的 ChatClient,优雅得很,代码干净,还能统一拦截、限流啥的,适合 Spring Boot 项目天然接入。
- 哦对了,还有个坑:问题太长或者你模型配错了,会直接报错,要不是 400 要不是 token 超长,小心别吓一跳。
小结下这个案例
咱这第一枪就打个“能跑”的基础 API,核心就一句话:Spring AI + OpenAI 接口 = 自建对话机器人服务。
你整完这玩意,前端小哥都能直接 POST 问问题,秒变智能客服,这比你装个 ChatGPT 插件高级多了——这是真·后端打工人自建 AI 服务!
案例2:Spring AI 用 PromptTemplate 写个 AI 写作器(内容写手,谁用谁说好)
你说现在搞运营的、写公众号的,天天改标题、编文案,哪儿有时间自己一个字一个字扣?这不,AI 上线干活了!但你别用死命令让它“你给我生成段文案”,人家不鸟你。咱就得整模板、喂提示词,调教得乖乖的,它才好使。
咱用 Spring AI 里的 PromptTemplate,给它塞点模板,填点变量,它就给你生成又快又顺的内容,省心得一批。
2.1 YML 配置,老规矩得先整好
spring:
ai:
openai:
api-key: sk-xxxxxxx(你自己的key)
chat:
model: gpt-3.5-turbo # 这模型便宜又稳定,真香
这个不用多说了,API key 要整对,不然一调用就直接“翻白眼”,跟你老婆说话一样没回应。
2.2 Prompt 模板配置类:拿个“固定格式”搞个“变数输出”
@Configuration
public class AIPromptConfig {
@Bean
public PromptTemplate promptTemplate() {
// 构建一个 Prompt 模板,带上变量 {scene} 和 {keyword}
String template = """
你是一个专业的{scene}写手,请你根据下面的关键词生成一段吸引人的内容,要求通俗易懂,结构完整,语气轻松幽默。
关键词: {keyword}
""";
return new PromptTemplate(template);
}
}
解释下哈:
你瞅瞅这玩意儿,和 String.format 差不多,就是把 {}
里塞点动态变量,它就能搞个完整提示词扔给 GPT,输出就顺溜得很。
2.3 Controller:喂关键词,吐出一段话
@RestController
@RequestMapping("/ai/writer")
public class WriterController {
@Autowired
private PromptTemplate promptTemplate;
@Autowired
private OpenAiChatClient chatClient;
@PostMapping
public String generate(@RequestParam String scene, @RequestParam String keyword) {
// 给模板填充变量
promptTemplate.add("scene", scene);
promptTemplate.add("keyword", keyword);
// 把 prompt 给 ChatClient,生成内容
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER, promptTemplate.render());
ChatRequest request = new ChatRequest(List.of(chatMessage));
ChatResponse response = chatClient.call(request);
return response.getResult().getOutput().getContent();
}
}
说两句废话(但是真实的)
你别不当回事儿,这玩意一套起来,运营部都能拿着这个接口批量写公众号标题和文案。你想想,"scene=视频号介绍", "keyword=短视频涨粉",一发请求,AI 就给你来段马云口吻的爆款稿子——直接抄上去!
当然了,要真用在生产里,建议你 prompt 写详细点,别整那么抽象,不然它瞎编瞎造,跟听村头老李喝酒之后扯的故事似的,跑题还跑偏。
小结小结再收尾:
这第二个例子核心就一个词——提示词工程 Prompt Engineering。
谁写得好,谁就能让 AI 乖乖给你搬砖。这不就跟带实习生一样嘛,你说不清楚它干啥,它能给你干点正事儿才怪。Spring AI 的 PromptTemplate 就是那个“安排明白”的工具,定死模板、按需填词、自动生成,能搞。
第二个搞完了啊,下面一个更带劲:Spring AI + Embedding + 本地知识库问答(QA)系统,就是你把你们公司内部资料塞进 AI 里,让它能回答专属问题,懂吧?不是 ChatGPT 啥都能答,是只懂你公司事儿的那种小秘书!
案例3:Spring AI + 本地知识库问答系统(打造你自己的 AI 小秘书)
兄弟你听我说,这块技术一听 Embedding、向量搜索,好多人就犯怵,感觉贼高深。其实也没啥,咱通俗讲一下,你就明白:
- 你手上有一堆文档,pdf、md、txt 都行;
- 咱把它们分段切片;
- 再用 OpenAI 把每段文本转成向量(就是一堆浮点数);
- 存到数据库或本地向量库;
- 问题一来,把问题也转成向量;
- 拿这个向量去库里“找最接近的段落”;
- 然后再拼起来,扔给 OpenAI,说:“你根据这个内容回答一下这问题”。
这就叫RAG 架构(Retrieval Augmented Generation),简单说:先找,再答,找得准,答得就不跑偏。
3.1 上点依赖(这次咱用 Milvus + Spring AI)
<!-- Spring AI Core -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.0</version>
</dependency>
<!-- Milvus 向量数据库 Java SDK -->
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.3.3</version>
</dependency>
Milvus 是专门用来搞向量搜索的,就像 Elasticsearch 之于全文检索一样,用得舒服得很。
3.2 文本入库操作:把你那堆资料转成向量存起来
咱先整个初始化类,把文本转 embedding,然后塞 Milvus(当然你也可以先存在内存 VectorStore,后期再换)
@Service
public class DocIngestService {
@Autowired
private OpenAiEmbeddingClient embeddingClient;
@Autowired
private MilvusVectorStore vectorStore;
/**
* 加载文档 -> 切片 -> 转向量 -> 存库
*/
public void ingestDocument(String doc) {
// 简单模拟,真实场景中可以分句、加预处理
String[] paragraphs = doc.split("n");
for (String chunk : paragraphs) {
// 把每一段生成 embedding
EmbeddingRequest request = new EmbeddingRequest(List.of(chunk));
EmbeddingResponse response = embeddingClient.call(request);
List<Double> vector = response.getResults().get(0).getOutput();
// 存进向量库(用你的主键ID标记)
vectorStore.saveVector("your-id-" + chunk.hashCode(), vector, chunk);
}
}
}
3.3 QA 接口:你来提问,AI 来答
@RestController
@RequestMapping("/ai/qa")
public class QAController {
@Autowired
private MilvusVectorStore vectorStore;
@Autowired
private OpenAiChatClient chatClient;
@Autowired
private OpenAiEmbeddingClient embeddingClient;
@PostMapping
public String ask(@RequestBody String question) {
// 1. 把问题转成向量
EmbeddingRequest request = new EmbeddingRequest(List.of(question));
EmbeddingResponse response = embeddingClient.call(request);
List<Double> questionVector = response.getResults().get(0).getOutput();
// 2. 在向量库里查最接近的知识段
List<VectorSearchResult> topMatches = vectorStore.search(questionVector, 3);
// 3. 拼接这些内容作为上下文,喂给 AI
String context = topMatches.stream()
.map(VectorSearchResult::getText)
.collect(Collectors.joining("n"));
String prompt = "请根据以下内容回答问题:n" + context + "n问题是:" + question;
// 4. 调用 AI 回答
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER, prompt);
ChatRequest chatRequest = new ChatRequest(List.of(chatMessage));
ChatResponse chatResponse = chatClient.call(chatRequest);
return chatResponse.getResult().getOutput().getContent();
}
}
垃圾话警告(但真有用)
- Milvus 搭起来有点门槛哈,实在不行你先用内存型 VectorStore,等搞明白再换;
- 不要想着一大篇文档直接塞给 GPT 它就能懂,那不是 AI,那是玄学;
- 提问的时候 prompt 不要太“假大空”,越具体越准,它才答得越像你要的。
小结一哈:
这个场景说白了就是——你让 AI 只看你给的东西答题,别在外边乱搜乱编。适合做 FAQ、客服、知识中心啥的,搞好了,产品小妹问你问题,都能甩个接口过去让 AI 替你回答,省事儿!
这就是 RAG 的基本盘:Embedding + Vector Search + Prompt 构造。
案例4:Spring AI + 多轮对话上下文(打造有记忆力的聊天机器人)
这事儿其实不复杂,说白了:就是你和 AI 聊天时,它得“知道”你说了什么,它“回”了什么,下一轮再说的时候,还得把前面的对话一并带上,这就叫上下文管理。
不然你问它:“今儿咱吃啥?”它给你一堆推荐,你回一句“来点辣的”,它直接傻住了:你是谁?你刚才说了啥?哥我不认识你……
4.1 咱先整点依赖,不废话
你之前整的那个 spring-ai-openai-spring-boot-starter
还继续用,不用换。重点是逻辑处理得变下。
4.2 先搞个上下文会话容器
我们搞个简单的 Map 来存“每个用户”的会话历史,当然正式项目你该存 Redis 就 Redis,别太寒碜哈。
@Component
public class ConversationMemory {
// 模拟上下文存储,用 Map 存用户 -> 对话历史
private final Map<String, List<ChatMessage>> memoryMap = new ConcurrentHashMap<>();
/**
* 获取某个用户的对话记录
*/
public List<ChatMessage> getHistory(String userId) {
return memoryMap.getOrDefault(userId, new ArrayList<>());
}
/**
* 保存对话记录
*/
public void appendMessage(String userId, ChatMessage message) {
memoryMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(message);
}
/**
* 清除历史(比如超时或手动结束会话)
*/
public void clearHistory(String userId) {
memoryMap.remove(userId);
}
}
4.3 对话 Controller,带上下文的聊天接口
@RestController
@RequestMapping("/chat/with-memory")
public class MemoryChatController {
@Autowired
private OpenAiChatClient chatClient;
@Autowired
private ConversationMemory conversationMemory;
@PostMapping
public String chat(@RequestParam String userId, @RequestBody String question) {
List<ChatMessage> history = conversationMemory.getHistory(userId);
// 追加当前用户输入
ChatMessage userMsg = new ChatMessage(ChatMessageRole.USER, question);
history.add(userMsg);
// 创建请求
ChatRequest request = new ChatRequest(history);
ChatResponse response = chatClient.call(request);
// 获取 AI 的回复
ChatMessage aiReply = response.getResult().getOutput();
// 存储 AI 的回复
conversationMemory.appendMessage(userId, aiReply);
return aiReply.getContent();
}
}
开始碎嘴子啦,注意几点:
- 多轮对话必须带上历史记录,不然 AI 永远“失忆”;
- 历史别无限制累加,OpenAI 有 Token 限制,聊天记录太长它会飘;
- 实际项目建议设定:每轮最多记 5 条,或最多 2 分钟内的记录,不然内存飙升。
4.4 可选增强:用户主动结束会话
@DeleteMapping
public String clearSession(@RequestParam String userId) {
conversationMemory.clearHistory(userId);
return "会话已清空,老铁重新来聊吧";
}
别小看这玩意,有时候用户想换话题,不清空会话它还在回之前的老茬子,就像你跟对象吵架突然讲起上个月那事儿,能不爆炸么?
最后总结收尾(哥真走心)
今天这四个案例啊,不整虚的,句句都是我自己项目里踩坑爬出来的:
- 基础聊天接口:入门级,项目秒集成;
- Prompt 模板生成文案:玩提示词,产内容,AI 工具人;
- Embedding 问答系统:搞知识库 AI,企业 FAQ 杀器;
- 上下文聊天机器人:能记仇、会扯淡,才像个活人。
Spring AI 虽然现在版本还没那么成熟吧,但这个封装和生态,是真的适合 Javaer 用,比你自己裸写 HttpClient + JSON 搞那种 low 接法强太多。
你不整这个?那就继续写接口搬砖吧,外头已经一堆人在用 AI 提效了,别说哥没告诉你。