· 王思腾 · 大模型工程 · 10 分钟阅读
Spring AI 介绍与实践:用 Java 生态的方式接入大模型
Java 团队想接大模型,不必硬套 Python 的 LangChain。Spring AI 把 ChatClient、Prompt 模板、结构化输出、RAG(向量库)、函数调用、对话记忆这些能力做成了 Spring 风格的抽象和 Starter,配几行 yml、写几行链式调用就能跑。这篇从核心抽象讲起,带你用 Spring Boot 一步步实现聊天、结构化映射、RAG 知识库和工具调用,并聊聊生产里要注意的事。

大模型应用这两年,Python 生态(LangChain、LlamaIndex)声量最大,但大量企业的后端是 Java、是 Spring Boot。为了接个 AI 能力再维护一套 Python 服务,成本不低。Spring AI 就是来填这个坑的:它是 Spring 官方的 AI 应用框架,理念和 Spring 一贯的风格一致——提供统一抽象 + 自动装配 + Starter,让你用熟悉的 @Bean、依赖注入、链式 API 接大模型,底层模型可替换。
这篇从核心概念讲起,写几个能跑的实践:聊天、结构化输出、RAG 知识库、函数调用,最后说生产注意事项。
一、Spring AI 是什么
一句话:Spring AI 是 Java 版的“大模型应用脚手架”,对标 LangChain,但深度贴合 Spring 生态。
- 统一模型抽象:
ChatModel、EmbeddingModel一套接口,底层可切换 OpenAI、Anthropic、Azure OpenAI、Ollama(本地)、通义/智谱等,换模型只改配置; - 高层 API:
ChatClient用流式链式调用完成对话、RAG、工具调用; - RAG 全套:文档读取(ETL)、切分、向量化、
VectorStore抽象(PGVector、Redis、Milvus、Chroma 等都有实现); - 结构化输出:直接把模型返回映射成 Java 对象;
- 函数调用(Function Calling):把 Spring Bean 方法暴露给模型当工具;
- Advisor(顾问)机制:把对话记忆、RAG 检索这类横切逻辑像拦截器一样织入;
- 可观测性:原生接 Micrometer,token、延迟、调用链直接进 Actuator/Prometheus。
二、核心抽象速览
flowchart TB
App["你的业务代码"] --> CC["ChatClient(高层门面)"]
CC --> Adv["Advisors(横切:记忆 / RAG / 日志)"]
Adv --> CM["ChatModel(统一模型接口)"]
CM --> Prov["具体实现<br/>OpenAI / Ollama / 通义 ..."]
CC --> VS["VectorStore(向量库抽象)"]
CC --> EM["EmbeddingModel(向量化)"]
CC --> Fn["Function Calling(工具/Bean)"]- ChatModel / EmbeddingModel:最底层的可移植抽象,对应“聊天模型”和“向量模型”;
- ChatClient:日常用得最多的门面 API,提供 fluent 链式调用;
- Prompt / Message:消息抽象(SystemMessage、UserMessage、AssistantMessage);
- VectorStore:向量库统一接口,
add()写入、similaritySearch()检索; - Advisor:请求/响应的拦截增强层,记忆和 RAG 都靠它织入,不用污染业务代码。
三、快速上手:一个聊天接口
引入 Starter(以 OpenAI 兼容接口为例,用国内模型换 base-url 即可):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>配置 application.yml:
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.deepseek.com # 指向任何 OpenAI 兼容网关
chat:
options:
model: deepseek-chat
temperature: 0.3写一个聊天接口:
@RestController
class ChatController {
private final ChatClient chatClient;
// 自动装配的 Builder 已带上全局默认配置
ChatController(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("你是一个严谨的技术助手,回答简洁。")
.build();
}
@GetMapping("/chat")
String chat(@RequestParam String q) {
return chatClient.prompt()
.user(q)
.call()
.content(); // 直接拿文本结果
}
}要流式输出(打字机效果),把 .call() 换成 .stream().content() 返回 Flux<String> 即可。
四、结构化输出:直接映射成 Java 对象
让模型返回可被程序消费的结果,是生产里的高频需求。Spring AI 可以直接把响应映射成一个 record/类:
record Sentiment(String sentiment, int score, String reason) {}
Sentiment result = chatClient.prompt()
.user("分析这条评价的情感倾向:「用了三天就坏了,客服还不理人」")
.call()
.entity(Sentiment.class); // 自动得到结构化对象框架会在底层用函数调用/JSON Schema 约束模型输出并反序列化,比自己拼 JSON、写正则稳得多。这对应 Prompt 工程里讲的“结构化输出”技巧,Spring 把它做成了一行 API。
五、RAG:几行代码搭知识库问答
RAG 的离线建库 + 在线检索,Spring AI 都有现成组件。整体流程和原理篇讲的一致:
flowchart TB
subgraph Off["离线"]
Doc["文档 PDF/TXT/MD"] --> Reader["DocumentReader 读取"]
Reader --> Split["TokenTextSplitter 切分"]
Split --> Embed["EmbeddingModel 向量化"]
Embed --> VS[("VectorStore")]
end
subgraph On["在线"]
Q["用户提问"] --> QA["QuestionAnswerAdvisor<br/>自动检索 + 拼上下文"]
QA --> CC["ChatClient"] --> Ans["带依据的回答"]
end
VS --> QA离线:文档入库
@Service
class IngestService {
private final VectorStore vectorStore;
IngestService(VectorStore vectorStore) { this.vectorStore = vectorStore; }
void ingest(Resource pdf) {
var docs = new PagePdfDocumentReader(pdf).get(); // 读 PDF
var split = new TokenTextSplitter().apply(docs); // 切分
vectorStore.add(split); // 内部自动 embedding 后写入
}
}向量库选 PGVector 就引入对应 starter 并配数据源,VectorStore Bean 自动创建,业务代码不关心底层是 Postgres 还是 Milvus。
在线:用 Advisor 自动做 RAG
String answer = chatClient.prompt()
.user(q)
.advisors(new QuestionAnswerAdvisor(vectorStore)) // 自动检索 Top-K 并注入上下文
.call()
.content();QuestionAnswerAdvisor 把“检索相关片段 → 拼进 Prompt”这步全包了。想控制 topK、相似度阈值、检索到的元数据过滤,可以传 SearchRequest。生产上别忘了 RAG 实战篇强调的几件事:切分参数、混合检索、Rerank、相似度兜底(让模型在没找到时说“不知道”)。
六、函数调用:让模型调用你的 Java 方法
模型本身不能查订单、不能发消息,函数调用让它“长出手脚”。在 Spring AI 里,一个工具就是一个带描述的 Bean:
@Configuration
class Tools {
@Bean
@Description("根据订单号查询订单物流状态")
Function<OrderRequest, OrderStatus> orderStatus() {
return req -> orderService.queryStatus(req.orderId());
}
record OrderRequest(String orderId) {}
record OrderStatus(String orderId, String status, String eta) {}
}调用时声明可用函数,模型会自己决定何时、用什么参数调用:
String reply = chatClient.prompt()
.user("帮我查一下订单 A20260409 到哪了")
.functions("orderStatus") // 注册给模型的工具名(Bean 方法名)
.call()
.content();模型识别出需要查订单 → 生成 OrderRequest 参数 → Spring 执行你的 Bean → 把结果回喂模型 → 模型组织成自然语言。参数的 schema、调用编排框架都处理了,你只写业务逻辑。
七、对话记忆:多轮上下文
默认每次调用是无状态的,多轮对话靠 ChatMemory + Advisor 织入:
@Bean
ChatMemory chatMemory() {
return new MessageWindowChatMemory(); // 窗口式记忆,也可用 JdbcChatMemory 持久化
}
// 请求时带上会话 id,并加记忆 Advisor
chatClient.prompt()
.user("我刚才问的那个问题再展开讲讲")
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, sessionId))
.advisors(new MessageChatMemoryAdvisor(chatMemory))
.call().content();框架按会话 id 自动加载历史消息塞进上下文、并把本轮对话存回。生产上要注意窗口长度(太长既费 token 又超上下文),通常配合摘要压缩,这点和 Agent 工程篇讲的记忆管理一致。
八、生产实践要注意的事
- 模型可移植但别过度依赖:统一抽象很好,但各厂商的高级特性(推理模式、特殊参数)抽象层未必暴露,必要时下沉到具体模型 API;
- 成本与超时:为每次调用设超时、重试和 token 上限;接入成本优化篇讲的模型分级路由——简单意图走小模型,难例再升级;
- 可观测性:开启 Spring AI 的 observation,把 token 消耗、延迟、错误率接进 Actuator + Prometheus,否则上量后账单和故障都是黑盒;
- RAG 数据更新:文档变更要能增量重建向量,别只建一次就不管;
- 函数调用的安全:模型能调你的方法,就要做权限校验和参数校验,高危操作(退款、删除)加人工确认,别让模型直接触发;
- 版本:Spring AI 迭代较快,1.0 正式版后 API 趋于稳定,升级时留意包名和 starter 命名变化。
小结
- Spring AI 是 Spring 官方的大模型应用框架,用统一抽象 + Starter 把聊天、Embedding、向量库、函数调用、记忆做成可替换组件,Java 团队无需另起 Python 服务;
- 日常开发围绕 ChatClient 的链式 API:
.prompt().user().call().content(),流式用.stream(); - 结构化输出用
.entity(Class),RAG 用VectorStore+QuestionAnswerAdvisor,工具调用用@Bean Function+.functions(),多轮记忆用ChatMemory+MessageChatMemoryAdvisor; - 换模型只改 yml,业务代码不动;
- 生产重点仍是那几件通用的事:超时/重试/成本控制、可观测、RAG 数据更新、函数调用的权限与人工卡点。
对 Java 后端来说,Spring AI 最大的价值是让接大模型这件事回到了熟悉的节奏:加依赖、写配置、注入 Bean、调方法。模型在变,但你的工程体系和团队技能栈不用推倒重来。
本文由 AI 辅助创作,所有内容仅供交流与参考。


