Appearance
Spring AI 的 ChatClient、Advisors、RAG 应该怎么理解
很多 Java 团队第一次接触 Spring AI 时,容易把它理解成:
- 一个“帮 Spring 调大模型”的 SDK
- 或者一个“直接做 Agent 平台”的框架
这两种理解都不够准确。
Spring AI 更适合被理解成:
- 把常见 AI 能力纳入 Spring 统一抽象的一层基础框架
先说结论
如果你现在想在 Spring Boot 应用里做下面这些事情:
- 调用聊天模型
- 做结构化输出
- 接入工具调用
- 做会话记忆和 RAG
- 把这些调用放进现有观测体系
Spring AI 是很自然的一层基础设施。
它最重要的价值,不是“花哨”,而是让这些能力以更像 Spring 的方式组织起来。
如果再往前走一步,更实用的结论通常是:
text
先用 Starter + ChatClient 把调用跑通,
再用 Advisors 接记忆、RAG 和日志,
最后再决定是否需要更上层的 Agent / Workflow 框架。一、先别急着看 Agent,先把 Spring AI 的落地层级想清楚
Spring AI 真正适合回答的问题,不是:
- “我能不能一上来就做一个复杂多智能体平台”
而更像:
- “我怎么把模型调用、结构化输出、RAG、Tool Calling 先稳稳接进现有 Spring Boot 应用”
一条更常见的落地层级通常是:
- 先选模型 Starter,把调用跑通
- 再用
ChatClient把业务入口组织好 - 再用
Advisors接记忆、检索、日志和调试 - 最后才考虑复杂编排是不是应该交给更上层框架
如果你把这个顺序反过来,最容易出现的结果就是:
- 项目复杂度长得比业务价值更快
二、先看 Starter:Spring AI 的第一步不是写 Prompt,而是把模型接进 Boot
Spring AI 的第一层价值,首先体现在 Starter 上。
对 Spring 团队来说,Starter 带来的不是“少写几行依赖”,而是:
- 自动配置更统一
- 模型 Bean 生命周期更清楚
- 配置项进入 Spring Boot 体系
这决定了你后面做本地调试、环境切换、灰度验证时会不会很乱。
一个更实用的使用原则是:
- 每次先只接一个主模型,把最小闭环跑通
- 不要一开始就同时接聊天模型、Embedding 模型、图片模型、语音模型
因为真实项目里最容易先稳定下来的,通常是:
- 一个聊天模型
- 一个 Embedding 模型
- 一个向量库
这三件事已经够你把第一版 RAG 服务跑起来了。
三、ChatClient:它是最常用的应用层入口
Spring AI 里最值得先建立直觉的一个点,就是 ChatClient。
它的角色更像:
- 面向业务代码的高层调用入口
而不是底层模型协议本身。
为什么这点重要?
因为真实项目里,大多数开发者关心的不是“模型 HTTP 请求长什么样”,而是:
- 怎么把 system / user message 组织起来
- 怎么接 Tool Calling
- 怎么把 RAG、Memory 这些横切能力加进去
- 怎么把结果转成结构化对象
ChatClient 的存在,就是为了把这件事做成更贴近 Spring 风格的调用体验。
一个更适合落地的组织方式通常是:
- Controller 不直接拼 Prompt
- Controller 调业务 Service
- 业务 Service 内部持有
ChatClient - Prompt 模板、结构化输出、Advisors 都收敛在这一层
这样做的好处是:
- HTTP 层不会和模型细节耦合
- 后面要换模型或接记忆、RAG 时,不用把控制器全改一遍
一个更像真实业务代码的例子
下面这段代码不追求覆盖所有细节,但很接近真实项目里“先把聊天能力包成业务服务”的写法:
java
@Service
public class KnowledgeAnswerService {
private final ChatClient chatClient;
public KnowledgeAnswerService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public AnswerResult answer(String question, String tenantId) {
return chatClient.prompt()
.system("""
你是企业知识库助手。
回答时优先使用检索结果,不确定时明确说不知道。
""")
.user(question)
.advisors(advisorSpec -> advisorSpec
.param("tenantId", tenantId))
.call()
.entity(AnswerResult.class);
}
}这里最重要的不是 API 写法本身,而是三件事:
ChatClient由 Spring 管- 业务参数通过 advisor context 往下传
- 返回值尽量直接收敛成结构化对象
四、Structured Output:别只停在“返回一段字符串”
很多团队接模型的第一版都会这样写:
- 调模型
- 拿到一段文本
- 自己再用正则或字符串处理拆字段
这在 PoC 阶段可以接受,但很快就会变脆。
Spring AI 在结构化输出上的价值,是让你更早进入:
- “模型返回的是一个业务对象”
而不是:
- “模型返回一段自由文本,后处理再赌一把”
这种能力特别适合:
- 摘要
- 分类
- 关键词抽取
- 表单生成
- 工单归类
- 审核结果判定
一个常见的误区是:
- 觉得结构化输出只是“把 JSON 解析成对象”
更重要的其实是:
- 你把返回结果的稳定性要求提前显性化了
这会直接改善后面:
- 接口契约
- 单元测试
- 失败重试
- 风险兜底
五、Advisors:它更像 AI 请求链上的增强器
很多人第一次看到 Advisors,会把它误会成某种“插件系统”。
更准确的理解方式是:
- 它像 AI 请求链路上的拦截和增强层
这一层特别适合承载重复模式,比如:
- 会话记忆
- RAG 检索增强
- 日志和调试
- 某些统一参数注入
这也是 Spring AI 很有工程味的一点:
- 不把所有逻辑都塞进一次 prompt 组装里
- 而是把横切模式拆成可组合、可复用的增强层
如果你已经熟悉 Spring 里 Filter、Interceptor、Advice 这一类思路,会很容易理解它的价值。
更实用的 Advisor 心法
真实项目里可以优先把 Advisors 分成三类:
1. 上下文类
比如:
- Chat Memory
- RAG
- 某类用户态信息注入
2. 调试类
比如:
- 请求日志
- 响应日志
- Prompt 调试
3. 控制类
比如:
- 统一限流标签
- 安全开关
- 某些租户级参数透传
这么拆的好处是:
- 你以后排问题时,知道是哪一层在改模型输入
一个更贴近实战的 Advisor 组合例子
java
var response = chatClient.prompt()
.user(question)
.advisors(
messageChatMemoryAdvisor,
questionAnswerAdvisor,
simpleLoggerAdvisor
)
.call()
.content();这种顺序通常要自己想清楚:
- 先补历史
- 再补检索
- 最后看日志
如果顺序乱了,你经常会出现:
- 以为是 RAG 没召回
- 实际是 Memory 先把上下文污染了
六、Chat Memory:别一上来就把所有历史都塞回模型
很多团队第一次接记忆,默认动作都是:
- 把过去所有对话都拼回去
这通常很快就会出问题:
- Token 飙升
- 延迟上升
- 历史噪音越来越多
- 关键问题被老上下文稀释
更稳的使用方式通常是:
- 短对话先用窗口式记忆
- 长对话再考虑摘要式记忆
- 严格区分“会话记忆”和“业务事实”
这句话很重要:
- 会话记忆适合保留这轮上下文
- 业务事实更适合进数据库、缓存或检索系统
不要让 Chat Memory 承担本来应该由业务存储承担的职责。
七、Spring AI 里的 RAG,不只是“查向量库”
很多团队说自己在做 RAG,实际做的只是:
- 把用户问题拿去查一下向量库
- 再把结果拼到 prompt 里
这当然能跑,但工程上往往不够稳。
Spring AI 在 RAG 上的价值,是把它往“可组合能力”方向推了一步。
你可以理解成:
- 模型调用是一层
- 检索增强是一层
- 对话记忆又是一层
这样做的好处是:
- RAG 不再只是一个散落在业务代码里的小技巧
- 而是能成为统一调用链的一部分
这对后面做:
- 文档问答
- 企业知识库检索
- 对话式运维助手
- 带上下文的业务 Copilot
都更容易长期维护。
实战里更常见的两种起步方式
1. 先从 QuestionAnswerAdvisor 起步
适合:
- 先把“检索一下再回答”跑通
- 业务复杂度还不高
它更像:
- 一个较轻的 RAG 入口
2. 复杂度上来后转向 RetrievalAugmentationAdvisor
适合:
- 需要更细控制检索链路
- 需要 Query Transform、Document Join、Contextualizer 这一类组件能力
这时你通常已经不再满足:
- “只要查出来几段文本塞进去就行”
而是开始关心:
- 查询改写
- 多路召回
- 文档压缩
- 注入方式
- 输出是否真的引用到召回内容
一个更贴近业务演进的判断
更稳的路线通常是:
- 第一版先用轻量 RAG 跑通场景
- 第二版开始补 metadata filter、chunk 策略和召回质量
- 第三版再进入更细粒度的 Retrieval Augmentation 组件编排
八、Tool Calling 是“让模型接动作”,不是让模型替你写业务
Spring AI 也提供 Tool Calling 能力。
但这里最容易出现一个误区:
- 以为只要把工具注册进去,模型就自动变成完整 Agent
实际上更稳的理解是:
- Tool Calling 负责把“调用外部动作”的能力标准化
- 但业务流程控制仍然需要你自己决定
这意味着 Spring AI 很适合做:
- 查天气
- 查库存
- 调内部接口
- 触发某类受控动作
但如果你已经进入:
- 多步骤规划
- 多角色协作
- 长生命周期状态控制
那你通常就会开始需要更上层的 Agent / Workflow 编排能力。
实战里最重要的一个边界
工具调用适合暴露:
- 幂等查询
- 有明确权限边界的动作
- 输入输出清晰的业务能力
不适合一上来就暴露:
- 大范围写操作
- 高风险删除操作
- 缺少审计的资金或权限动作
更保守的路线通常是:
- 先让模型只能“查”
- 再让模型在审批或二次确认后“改”
九、一个更像真实项目的最小分层
如果你准备在 Spring Boot 里长期用 Spring AI,一个更稳的最小分层通常是:
1. Controller 层
- 只处理 HTTP 输入输出
- 不直接拼 Prompt
2. AI Application Service 层
- 负责
ChatClient - 负责挂 Advisors
- 负责结构化输出
- 负责决定是不是接工具、记忆、RAG
3. Retrieval / Tool Adapter 层
- 封装向量库
- 封装业务工具
- 封装外部 API
4. Domain / Repository 层
- 放真正业务事实和持久化逻辑
这样分层的最大好处是:
- 以后你换模型、换 Embedding、换向量库、换 Prompt,不会一路改到 Controller 和 Domain
十、可观测性是 Spring AI 很容易被低估的一点
很多团队前面都把注意力放在:
- 支不支持模型
- 能不能接向量库
- 有没有 Tool Calling
但真正进入生产后,你会越来越在意:
- 这次调用到底慢在哪
- 哪个 Advisor 在放大延迟
- 哪次检索命中了什么文档
- Token 用量和异常分布是什么样
Spring AI 把可观测性纳入核心组件视角里,这对 Spring 团队是很重要的加分项。
因为它意味着:
- AI 调用不会天然成为一块不可见黑盒
更实用的落地建议
至少建议把下面几类信息纳入观测:
- 模型名
- Token 用量
- 调用耗时
- Advisor 链路
- 检索条数
- 失败类型
如果你后面要做成本治理、慢查询排查和回答质量回溯,这些信息都会用上。
十一、什么时候只用 Spring AI 就够了
下面这些场景,通常只用 Spring AI 就已经足够:
- 文档问答
- 智能摘要
- 文本分类和抽取
- 简单业务助手
- 带知识库的客服或内部问答
- 受控工具调用的业务 Copilot
这些问题的共同特点是:
- 核心难点还在“模型接入 + 检索增强 + 工具调用”
- 而不在“复杂编排系统”
十二、什么时候该往更上层框架看
如果你开始遇到下面这些问题:
- 需要多 Agent 协作
- 需要顺序、并行、循环、路由控制
- 需要更强的长流程状态管理
- 需要人机协同审批
- 需要可视化 Prompt / Agent 运维与评估
那你通常就会开始看:
- Spring AI Alibaba
- 或其他更偏 Agent / Workflow 的上层框架
十三、最常见的几个落坑点
1. Prompt、RAG、Memory 全堆在一个方法里
这会让调用链越来越不可维护。
2. 把 Chat Memory 当业务数据库
这会让上下文越来越脏,也让问题越来越难复现。
3. 一上来就追求复杂 Agent,而不是先把单 Agent 服务做稳
这会让复杂度和风险远早于价值出现。
4. 没有做结构化输出和观测,就直接进入生产
这样后面很难排查“模型到底是答错了,还是流程没接好”。
一句话总结
Spring AI 最适合被当成 Spring Boot 应用里的 AI 基础设施层来理解。
更稳的落地顺序通常是:
- Starter 先接通模型
ChatClient收敛业务入口Advisors接记忆、RAG 和日志- Tool Calling 只暴露受控动作
- 观测先于复杂编排
先把这条主线做稳,再决定是否继续往 Agent 编排层上走,通常更适合真实项目。