Spring AI 上手:给现有 Java 服务加一个能用的 AI 接口
写在前面:不用新起 Python 服务。一个 BOM、一段配置、一个 ChatClient、一个 Controller,在现有 Spring Boot 里跑出能上生产的 AI 接口,外带流式返回、结构化输出、会话记忆,和四个必踩的坑。
标签:Java / Spring AI / Spring Boot / 大模型入门 / SSE
如果你现在的项目里要加一个"AI 问答"或者"AI 审核"的能力,第一反应多半是:再起一个 Python 服务,Java 这边调 HTTP。
我建议别这么干。
结论先说
用 Spring AI,在现有 Spring Boot 服务里加四样东西------一个 BOM、一段配置、一个 ChatClient Bean、一个 Controller------就有一个能直接上生产的 /chat 接口。代码在下面,复制能跑。
版本前提:Spring AI 1.0.x + Spring Boot 3.3 + JDK 17。1.0 之后 starter 改过名(
spring-ai-starter-model-openai),网上大量 0.8 时代的文章坐标对不上,先核对版本再看别人的代码。
为什么不新起一个 Python 服务
模型调用本质上是一次 HTTP 请求加一次 JSON 解析。它不需要独占一个技术栈、一套部署、一套鉴权。
而你已有的这些能力,在新服务里全要重做一遍:
| 能力 | 现有 Java 服务 | 新起 Python 服务 |
|---|---|---|
| 鉴权 | 拦截器/网关现成 | 重写 |
| 限流 | Sentinel/网关现成 | 重写 |
| 日志与监控 | 现成 | 重写 |
| 发布流水线 | 现成 | 新建一套 |
真正需要 Python 的是训练、微调、本地推理------那部分本来就不在业务服务里。把"调模型"当成一个普通的外部依赖,事情会简单很多。
四步接上
第一步:依赖
pom.xml 里加 BOM 和 starter:
xml
<properties>
<spring-ai.version>1.0.1</spring-ai.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
第二步:配置
yaml
spring:
ai:
openai:
api-key: ${AI_API_KEY}
base-url: https://api.deepseek.com
chat:
options:
model: deepseek-chat
temperature: 0.7
这个 starter 吃的是 OpenAI 协议。国内绝大多数服务(DeepSeek、通义、Kimi,以及你自己用 vLLM 起的)都兼容这套协议,所以换厂商只改 base-url 和 model 两个值。这也是我不建议一上来就接七八个专用 SDK 的原因:多一份 SDK 就多一份升级负担。
api-key 从环境变量读,别写死在 yml 里。这条后面还会提。
第三步:ChatClient
java
@Configuration
class AiConfig {
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem("你是一名严谨的 Java 技术顾问。"
+ "回答先给结论,再给可运行的代码,"
+ "不确定的地方直接说不知道。")
.build();
}
}
defaultSystem 是性价比最高的一行配置:把角色和回答格式固定住,后面每个调用点都不用重复写。
第四步:接口
java
@RestController
@RequestMapping("/api/ai")
class ChatController {
private final ChatClient chatClient;
ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/chat")
String chat(@RequestParam String q) {
return chatClient.prompt()
.user(q)
.call()
.content();
}
}
到这儿,GET /api/ai/chat?q=你好 就能返回模型回答了。整件事的核心只有 prompt().user().call().content() 这一条链,其余都是 Spring 的常规装配。
流式:别让人干等八秒
非流式接口有个体验问题:模型生成三百字大概要五到十秒,这期间页面是空白的。改成 SSE,就能边生成边显示。
WebMVC 的项目不用引 webflux,用 SseEmitter 转接就行:
java
@GetMapping(value = "/chat/stream",
produces = MediaType.TEXT_EVENT_STREAM_VALUE)
SseEmitter stream(@RequestParam String q) {
SseEmitter emitter = new SseEmitter(0L);
chatClient.prompt().user(q).stream().content()
.subscribe(
chunk -> {
try {
emitter.send(chunk);
} catch (IOException e) {
emitter.completeWithError(e);
}
},
emitter::completeWithError,
emitter::complete);
return emitter;
}
0L 表示不设超时,改由网关和模型侧控制。生产上建议给个明确上限,比如 60 秒,否则慢连接会一直占着线程。
两个马上就能用的增强
要对象,不要字符串
解析模型返回的 JSON 是纯浪费------字段名一漂移,正则就崩。用 entity() 直接映射成对象:
java
record Review(String risk, List<String> suggestions) {}
Review r = chatClient.prompt()
.user("审查这段配置的风险:" + yamlText)
.call()
.entity(Review.class);
会话记忆 + 会话隔离
java
@Bean
ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
}
@Bean
ChatClient chatClient(ChatClient.Builder builder, ChatMemory memory) {
return builder
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(memory).build())
.build();
}
调用时传会话 id,多个用户才不会串话:
java
chatClient.prompt()
.user(q)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, userId))
.call()
.content();
maxMessages(20) 是个成本阀门,不是效果阀门。上下文越长越贵,而且到后面模型反而更容易迷糊------这一点比多数人以为的更重要。
上线前必须处理的四个坑
- key 只能来自环境变量或配置中心。不进 git,不进前端,不进日志,没有例外。
- 超时必须显式设。模型 P99 抖动是常态,连接超时 5 秒、读取超时 60 秒起步;重试只对幂等请求做,且要限次数。
- 成本上限靠
maxTokens和配额,不是靠 prompt 里写"请简短回答"。把每次调用的 token 消耗打进日志,一周后你会庆幸自己做了这件事。 - 日志脱敏。prompt 里大概率有用户隐私、内网地址、业务数据,落库前先问一句:这段能不能给运维看。
最后
这件事的技术门槛比大多数人想的低,真正花时间的是上面那四个坑------而它们跟 AI 其实无关,全是后端工程的老问题。这也是我写这个号的原因:AI 功能落地失败的案例,很少是被模型能力卡住的。
这个系列后面还会写 @Tool:怎么让模型直接调用你项目里的 Java 方法(比如查订单),以及为什么不能把整个 Service 直接丢给它。
有想看的选题,评论区说一声。
本文同步更新于公众号「Java与AI落地」。如果这篇帮你少踩一个坑,点个赞,或转给正在给系统接 AI 的同事。