Spring AI 核心架构全解析:从 ChatModel 到 Advisor Chain 的设计哲学

Spring AI 核心架构全解析:从 ChatModel 到 Advisor Chain 的设计哲学
PeiSpring AI 核心架构全解析:从 ChatModel 到 Advisor Chain 的设计哲学
系统提示词、记忆管理、工具调用、结构化输出——这些 AI Agent 的核心能力,在 Spring AI 中是如何统一编排的?答案藏在三个核心抽象里。
如果你读过本系列的前面几篇文章,应该已经对 AI Agent 的各个”器官”有了清晰的认识:ReAct 模式是它的大脑推理循环,Tool Use是它的双手,Memory是它的记忆系统,Prompt Engineering是它与人沟通的语言艺术。
但你有没有想过一个问题:这些能力是怎么被组装到一起的?谁负责把用户的输入变成 API 调用?谁负责在调用前后注入记忆、拦截敏感内容、格式化输出?
答案是 Spring AI 的核心架构——一个由 ChatModel、PromptTemplate 和 Advisor Chain 构成的精巧流水线。理解了这个架构,你才能真正掌控 Spring AI,而不是被它牵着鼻子走。
一、为什么需要理解架构,而不仅仅是 API?
很多开发者学 Spring AI 的方式是:翻文档 → 找到一个 ChatClient 的例子 → 跑通 → 以为自己会了。
这就像学开车只学了踩油门——你知道怎么让车往前跑,但不知道变速箱怎么工作、刹车系统怎么介入、ESP 在什么时候帮你稳住车身。一旦出了问题(比如响应格式不对、调用成本太高、链路延迟异常),你就完全抓瞎了。
Spring AI 的架构设计有一个核心思想:把 LLM 调用变成一次标准的 Spring 请求处理流程。这个流程里,ChatModel 是执行器,PromptTemplate 是模板引擎,Advisor Chain 是拦截器链——听起来是不是很像 Spring MVC 的 DispatcherServlet → HandlerMapping → HandlerAdapter → Interceptor ?
没错,Spring 团队就是故意这么设计的。他们希望 Java 开发者用已经熟悉的思维方式来构建 AI 应用。
二、ChatModel:一切调用的起点
2.1 ChatModel 接口的设计
先看核心接口定义:
1 | public interface ChatModel extends Model<Prompt, ChatResponse> { |
就这么简单的三个方法。但背后的设计考量远比看起来深刻。
为什么 call 接收 Prompt 而不是 String? 因为一次 LLM 调用不仅仅是发送一段文字。一个 Prompt 对象封装了:
- 一组
Message(系统消息、用户消息、助手消息、工具消息) - 一个
ChatOptions(模型名、温度、最大 token 等参数)
这就像 Spring MVC 里 HttpServletRequest 不仅仅是用户提交的表单数据,还包含了 headers、cookies、session 等上下文信息。
为什么 stream 返回 Flux<ChatResponse> 而不是 Flux<String>? 因为流式响应不仅仅是文字片段。每次 chunk 可能携带:token 使用量、模型元数据、甚至工具调用的增量信息。用 ChatResponse 包装,上层代码可以统一处理。
2.2 消息类型体系
Spring AI 的消息体系是理解一切的基础:
1 | public interface Content { |
每种消息类型对应 OpenAI API 里的一个 role 字段。这个映射关系是这样的:
| Spring AI | OpenAI API |
|---|---|
SystemMessage |
{"role": "system"} |
UserMessage |
{"role": "user"} |
AssistantMessage |
{"role": "assistant"} |
ToolResponseMessage |
{"role": "tool"} |
为什么要这么设计?因为不同厂商的 API 对消息角色的定义不完全一致。Spring AI 通过统一的消息类型体系,把底层差异屏蔽了。你写一套代码,换 OpenAI、换 Anthropic、换 Ollama,消息模型不用改。
2.3 一次调用的完整生命周期
让我用一个实际的代码片段来追踪一次调用的完整流程:
1 |
|
这短短几行代码,底层发生了什么?
- 构建阶段:
ChatClient.Builder把系统提示词和默认选项存起来 - 组装阶段:
.user(message)创建一个UserMessage,与系统提示词一起组装成Prompt对象 - 执行阶段:
.call()触发ChatModel.call(prompt) - 序列化阶段:Prompt 对象被序列化为 OpenAI 的 JSON 格式
- 网络阶段:通过
RestClient发送 HTTP 请求 - 反序列化阶段:响应 JSON 被解析为
ChatResponse - 提取阶段:
.content()从ChatResponse中提取文本
关键洞察:在这个流程中,步骤 3 和步骤 6 之间可以插入任意多个处理环节——这就是 Advisor Chain 的价值,我们后面详细讲。
2.4 源码剖析:OpenAiChatModel
看一个具体实现。OpenAiChatModel 是 Spring AI 对 OpenAI 的适配:
1 | public class OpenAiChatModel extends AbstractToolCallSupport implements ChatModel { |
注意第 4 步——如果模型返回了工具调用请求,OpenAiChatModel 会自动执行工具、把结果封装为 ToolResponseMessage,然后再次调用模型。这个”自动循环”就是 Tool Use 的核心机制,它被封装在 AbstractToolCallSupport 基类里,所有模型实现都可以复用。
三、PromptTemplate:不仅仅是字符串替换
3.1 超越简单的模板
很多人以为 PromptTemplate 就是 String.format() 的高级版——往模板里填变量就完事了。但实际上,Spring AI 的 PromptTemplate 承担了更重要的职责。
1 | PromptTemplate template = new PromptTemplate(""" |
这里 create() 方法返回的是一个完整的 Prompt 对象,而不是简单的字符串。它做了这些事情:
- 变量替换:把
{xxx}占位符替换成实际值 - 消息拆分:根据模板结构判断哪些是系统消息、哪些是用户消息
- 选项合并:如果模板中定义了
ChatOptions,会与调用时的选项合并 - 验证:检查所有必填变量是否都提供了
3.2 与 RAG 的深度集成
在 RAG 场景下,PromptTemplate 的价值更加明显。还记得我们在 RAG 完整指南 中讲过的检索增强生成流程吗?Spring AI 把检索结果注入到 Prompt 中的方式非常优雅:
1 | // RAG 场景下的 Prompt 构建 |
而 {context} 的填充,通常通过 Advisor Chain 自动完成——这就是为什么理解 Advisor Chain 如此重要。
3.3 与 LangChain4j 的对比
LangChain4j 的模板系统叫 PromptTemplate(名字一样,但设计思路不同):
1 | // LangChain4j 的方式 |
关键差异:
| 维度 | Spring AI | LangChain4j |
|---|---|---|
| 返回类型 | Prompt 对象(含消息列表+选项) |
String(纯文本) |
| 消息角色感知 | 支持(可在模板中标记 system/user) | 不支持(统一作为 user 消息) |
| 与框架集成 | 通过 Advisor Chain 自动注入 | 需要手动组装 ChatMessage |
| 类型安全 | 支持 ST(Structured Template)类型安全模板 | 纯字符串替换 |
Spring AI 的设计更贴近 Spring 的哲学——模板不是孤立的字符串处理工具,而是整个请求处理管线的一环。这也是为什么 Spring AI 的 PromptTemplate 可以和 Advisor Chain 无缝配合,而 LangChain4j 需要更多手动编排。
四、Advisor Chain:Spring AI 的灵魂
如果说 ChatModel 是发动机,PromptTemplate 是油门踏板,那 Advisor Chain 就是整辆车的控制系统——它决定了调用前做什么、调用后做什么、出错了怎么办。
4.1 什么是 Advisor?
1 |
|
每个 Advisor 拦截一次 LLM 调用,可以在调用前后做任何事情:
- 调用前:注入记忆、注入检索结果、压缩 prompt、添加安全过滤
- 调用后:格式化输出、提取结构化数据、记录日志、计算成本
这和 Spring MVC 的 HandlerInterceptor、Servlet 的 Filter 是完全一样的设计模式——责任链模式。
4.2 Advisor 的执行顺序
1 | ChatClient chatClient = builder |
执行时,请求像剥洋葱一样穿过每一层:
1 | 用户请求 |
注意方向:Advisors 按添加顺序从外到内执行(前处理),响应从内到外返回(后处理)。这和 Spring AOP 的 Around Advice 是一个道理。
4.3 内置 Advisor 详解
Spring AI 提供了几个开箱即用的 Advisor,每一个都对应一个核心场景:
MessageChatMemoryAdvisor——记忆注入
1 | // 创建记忆存储 |
内部实现的核心逻辑:
1 |
|
设计亮点:记忆注入是在 Advisor 层完成的,ChatModel 完全不知道记忆的存在。这意味着你可以轻松替换记忆策略(窗口记忆、摘要记忆、向量记忆),而不用改任何模型调用代码。
QuestionAnswerAdvisor——RAG 检索增强
1 | QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore) |
这个 Advisor 在 知识检索引擎 那篇文章中有详细讲解。它的核心流程:
- 拿到用户的原始问题
- 用问题去向量数据库检索相关文档
- 把检索到的文档拼接到 prompt 的
{context}占位符中 - 把增强后的 prompt 传给下一个 Advisor
关键设计:检索结果是通过 PromptTemplate 的变量注入的,而不是直接拼接字符串。这保证了模板的一致性和可维护性。
SafeGuardAdvisor——安全过滤
1 | SafeGuardAdvisor safeGuard = new SafeGuardAdvisor( |
简单但有效——在输入和输出两端检查是否有敏感内容。在 AI Agent 安全防线 那篇文章中我们详细讨论了 Prompt 注入防御,SafeGuardAdvisor 是第一道防线的实现。
4.4 自定义 Advisor 实战
理解了 Advisor 的机制后,你可以编写自己的 Advisor 来扩展任何能力。比如一个 Token 用量监控 Advisor:
1 |
|
这个 Advisor 挂在链的最外层,包裹整个调用过程,记录了输入 token、输出 token 和延迟。配合 Micrometer + Prometheus,你就能在 Grafana 里看到 AI 调用的实时监控面板——这正是 AI Agent 可观测性 中讲的方案的底层实现。
五、ChatClient:统一的门面
理解了上面三个核心组件后,ChatClient 就很好理解了——它是把这些组件组合在一起的 门面(Facade)。
5.1 Builder 模式的精妙设计
1 | ChatClient client = ChatClient.builder(chatModel) |
Builder 的每个方法都在做一件事:往调用管线的不同层注入配置。最终 build 出来的 ChatClient 就是一个预配置好的调用管线。
5.2 调用时的动态覆盖
1 | // 可以在每次调用时覆盖默认配置 |
为什么要有”默认+覆盖”的设计? 因为大多数场景下配置是固定的(系统提示词、模型选择、Advisor 链),但偶尔需要在特定请求中调整参数(比如不同的对话需要不同的 conversation ID)。
六、与 LangChain4j 架构的横向对比
作为 Java 生态的两大 AI 框架,Spring AI 和 LangChain4j 的架构设计哲学有本质差异:
| 维度 | Spring AI | LangChain4j |
|---|---|---|
| 核心模式 | Advisor Chain(责任链) | Chain(顺序调用) |
| 扩展机制 | Advisor(声明式,可插拔) | Chain 步骤(代码式,需修改) |
| 记忆管理 | Advisor 自动注入 | 需手动注入 ChatMemory |
| RAG 集成 | Advisor 自动检索+注入 | 需手动编排 EmbeddingStoreRetriever |
| 工具调用 | 模型层自动处理循环 | AiServices 代理层处理 |
| Spring 生态集成 | 原生(DI、AOP、Actuator) | 需要桥接(CDI 适配) |
| 流式支持 | Flux 原生集成 | 需要额外配置 |
| 学习曲线 | 熟悉 Spring 的人上手快 | 更接近 Python AI 框架的思路 |
怎么选? 如果你的团队是 Spring 技术栈,Spring AI 是更自然的选择——你不需要学一套新的编程范式,用已经熟悉的拦截器、模板引擎、依赖注入就能构建完整的 AI 应用。如果你的项目不依赖 Spring,或者你更喜欢 LangChain 的”链式调用”风格,LangChain4j 也很好。
关键不是哪个”更好”,而是理解它们的设计哲学差异:Spring AI 把 AI 调用当作一次 HTTP 请求处理,LangChain4j 把它当作一条数据处理流水线。
七、生产环境的最佳实践
7.1 Advisor 的顺序很重要
1 | // ❌ 错误顺序:记忆在最外层,导致安全过滤无法检查注入了记忆的 prompt |
经验法则:安全类 Advisor 放最外层,数据增强类(RAG、记忆)放中间,格式化类放最内层。
7.2 异常处理
1 |
|
7.3 成本控制
结合 Token Usage Advisor 和预算守卫:
1 |
|
这个思路在 评估与优化 那篇有更详细的讨论。
八、架构的边界与未来
8.1 当前的局限
Spring AI 的 Advisor Chain 设计虽然优雅,但也有局限:
- 编排能力有限:目前的 Advisor Chain 是线性的,不支持条件分支(”如果用户问的是代码问题,走代码专用模型;否则走通用模型”)。这种复杂编排需要自己实现。
- 多模型协作不直接:虽然可以用不同的 ChatModel 创建不同的 ChatClient,但没有内置的”模型路由”机制。
- 调试体验:链路中间环节的调试还不够方便,没有内置的 Advisor 可视化工具。
8.2 未来方向
Spring AI 正在快速演进,几个值得关注的方向:
- Agent 抽象:目前 Spring AI 更偏向”模型调用框架”,Agent 能力(自主规划、循环推理)还在演进中
- 多模态统一:图片、音频、视频的处理正在整合到统一的 Advisor 体系中
- 评估框架:内置的模型评估和 A/B 测试能力
- 与 Spring 生态的深度融合:Spring Batch 处理大批量 AI 任务、Spring Integration 处理事件驱动的 AI 流程
总结
Spring AI 的核心架构可以用一句话概括:用 Spring 开发者熟悉的方式,把 LLM 调用变成可管理、可扩展、可观测的企业级组件。
- ChatModel 是执行器——负责与 LLM 通信
- PromptTemplate 是模板引擎——负责构建结构化的提示词
- Advisor Chain 是拦截器链——负责在调用前后注入各种横切关注点
- ChatClient 是门面——负责把这些组件组装成一个易用的 API
理解了这个架构,你就不再是”调 API 的人”,而是”设计系统的人”。你可以根据业务需求选择合适的 Advisor 组合,编写自定义 Advisor 处理特殊场景,用监控 Advisor 掌握系统运行状况。
这就是从”会用框架”到”理解框架”的跨越。
相关阅读:
- 理解 AI Agent 的大脑:ReAct 模式——Agent 的推理循环
- AI Agent 的工具箱:Tool Use 与 Function Calling——Spring AI 如何处理工具调用
- AI Agent 的记忆系统——Memory Advisor 的底层存储
- 让 AI 学会”说人话”:Spring AI 结构化输出实战——结构化输出的 Advisor 实现
- AI Agent 的可观测性——Token 监控 Advisor 的完整方案
- RAG 检索增强生成完整指南——QuestionAnswerAdvisor 的理论基础









