发布时间:2026年4月10日
各位开发者,当你在使用Spring AI构建应用时,是否遇到过这样的困境:每个AI调用都需要手动拼接对话历史、添加RAG检索上下文、记录日志、过滤敏感词?这些与核心业务无关却又不得不写的重复代码散落在各个方法中,让代码变得臃肿难维护。这正是Spring AI Advisors(顾问/拦截器) 要解决的核心问题。作为Spring AI生态中不可或缺的组件,AI城市旅游助手在智能客服、实时推荐等场景中,正是借助Advisors实现了对话记忆和知识库增强等功能。今天,我们就从零开始,彻底搞懂Spring AI Advisors的设计思想、工作原理和落地实践。

一、痛点切入:为什么需要Advisors?
让我们先看一个典型的AI调用场景。假设你正在开发一个智能客服系统,每个用户请求都需要做以下处理:

加载该用户的对话历史,让AI理解上下文
从向量数据库中检索相关文档,做RAG增强
记录请求和响应的日志,用于监控调试
过滤用户输入中的敏感词
统计本次调用的token消耗和成本
传统实现方式(无Advisors) :
@Service public class CustomerService { @Autowired private ChatClient chatClient; @Autowired private ChatMemoryService memoryService; @Autowired private VectorStore vectorStore; public String askQuestion(String userId, String question) { // 痛点1:每个方法都要写一遍记忆加载 List<Message> history = memoryService.loadHistory(userId); // 痛点2:每个方法都要写一遍RAG检索 List<Document> docs = vectorStore.similaritySearch(question); String context = buildRagContext(docs); // 痛点3:敏感词过滤逻辑散落各处 if (containsSensitiveWords(question)) { return "您的输入包含敏感词"; } // 痛点4:日志记录代码冗余 log.info("用户{}提问: {}", userId, question); long startTime = System.currentTimeMillis(); String response = chatClient.prompt() .system("基于以下上下文回答问题:" + context) .user(question) .call() .content(); log.info("回答耗时: {}ms, token消耗: {}", System.currentTimeMillis() - startTime, getTokenUsage()); memoryService.saveHistory(userId, question, response); return response; } }
痛点分析:
关注点耦合:记忆管理、RAG检索、日志监控等非业务逻辑与核心问答逻辑紧密耦合
代码重复:同样的记忆加载、日志记录逻辑在每个AI调用方法中都要重复书写
难以扩展:新增一个需求(如添加限流功能),需要修改所有涉及AI调用的方法
维护成本高:某个公共逻辑的修改(如记忆存储方式变更)将影响数十处代码
这正是 Spring AI Advisors 的设计初衷——将AI交互中的横切关注点(cross-cutting concerns)抽离成独立的、可复用的拦截组件,实现关注点分离和代码复用。
二、核心概念:什么是Advisor?
标准定义
Advisor(顾问/拦截器) 是Spring AI框架中用于拦截、修改和增强AI交互请求与响应流程的模块化组件-5。它以AOP(Aspect-Oriented Programming,面向切面编程)风格工作,允许开发者在AI调用生命周期的关键节点插入自定义逻辑,而无需修改核心业务代码-1。
关键词拆解
| 关键词 | 解释 |
|---|---|
| 拦截(Intercept) | 在请求发送给LLM之前和响应返回给客户端之后介入 |
| 修改(Modify) | 可以对用户输入进行增强(如添加对话历史),也可以对模型输出进行处理(如脱敏) |
| 增强(Enhance) | 为AI调用附加额外能力,如RAG、记忆、日志、护栏等 |
| 模块化(Modular) | 每个Advisor独立封装一种能力,可以自由组合 |
| 可复用(Reusable) | 编写一次,在任何ChatClient调用中均可使用 |
生活化类比
想象你去一家高级餐厅点餐。你向服务员说出你的需求,但服务员并不是直接把它交给厨师,而是经过层层处理:
备忘录顾问:先翻看你的用餐记录(对话历史)
推荐顾问:根据你的口味偏好,在点单上添加推荐菜品(RAG增强)
安全检查顾问:检查是否有过敏食材(敏感词过滤)
记录顾问:记录下点餐内容和时间(日志监控)
每一层处理都是独立的、可插拔的。如果你换一家餐厅,这些“顾问”依然可以复用。这正是Spring AI Advisors的设计哲学——让AI调用像搭积木一样灵活。
核心价值
封装常见的生成式AI模式(如RAG、对话记忆、结构化输出)为可重用单元-15
非侵入式增强:无需修改核心业务代码即可满足企业级复杂需求-1
跨模型可移植性:同一套Advisor可在不同LLM供应商间复用-12
三、关联概念:从Spring AOP理解Advisors
对于熟悉Spring生态的开发者来说,Advisors的概念并不陌生——它本质上是将AOP(面向切面编程) 理念迁移到了AI交互场景中-45。
AOP经典术语 vs Spring AI Advisors映射
| AOP概念 | 在Spring AI Advisors中的对应 |
|---|---|
| Join Point(连接点) | 执行LLM调用的时刻(如调用 chatClient.call())-4 |
| Advice(增强逻辑) | Advisor中实现的拦截逻辑(before/after/around)-4 |
| Pointcut(切点) | 哪些ChatClient调用应用该Advisor(通过客户端配置指定)-4 |
| Weaving(织入) | 在运行时将Advisor附加到AI客户端的过程-4 |
一句话理解
AOP为方法调用提供横切能力,Spring AI Advisors为LLM调用提供横切能力——思想一致,只是拦截的对象从“Java方法”变成了“AI模型调用”。
在传统的Spring AOP中,我们写一个 @Around 切面来给方法添加日志或事务;在Spring AI中,我们实现一个Advisor来给LLM调用添加记忆或RAG-4。
概念关系总结
| 维度 | Spring AOP | Spring AI Advisors |
|---|---|---|
| 拦截目标 | Java方法调用 | LLM API调用 |
| 实现方式 | @Aspect + @Around | 实现CallAdvisor接口 |
| 链式执行 | 切面顺序(@Order) | 顾问顺序(getOrder()) |
| 核心接口 | ProceedingJoinPoint | CallAdvisorChain |
| 底层依赖 | 动态代理、CGLIB | 责任链模式 + 动态代理 |
四、代码示例:从零搭建Advisors
4.1 环境准备
在 pom.xml 中添加Spring AI依赖:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-chroma</artifactId> </dependency>
4.2 基础用法:添加内置Advisor
Spring AI提供了多个开箱即用的内置Advisor,最常用的是对话记忆Advisor和RAG问答Advisor-8-30。
@Configuration public class AiConfig { @Bean public ChatClient chatClient(ChatModel chatModel, VectorStore vectorStore, ChatMemory chatMemory) { return ChatClient.builder(chatModel) // 注册默认Advisor:所有调用都会自动应用 .defaultAdvisors( // Advisor 1: 对话记忆 —— 自动维护多轮对话历史 MessageChatMemoryAdvisor.builder(chatMemory).build(), // Advisor 2: RAG增强 —— 自动从向量库检索相关文档并注入Prompt QuestionAnswerAdvisor.builder(vectorStore).build() ) .build(); } }
4.3 运行时动态配置Advisor
有些场景需要按请求动态调整Advisor行为(如不同会话使用不同的对话ID):
@Service public class ChatService { private final ChatClient chatClient; public String chatWithMemory(String conversationId, String userInput) { return chatClient.prompt() // 运行时指定Advisor并传入参数 .advisors(advisor -> advisor .param(ChatMemory.CONVERSATION_ID, conversationId)) .user(userInput) .call() .content(); } }
4.4 自定义Advisor:实现日志拦截器
如果你需要实现自定义逻辑(如请求/响应日志记录、耗时统计),可以实现 CallAdvisor 接口:
@Component public class LoggingAdvisor implements CallAdvisor { // 控制执行顺序:数值越小优先级越高 @Override public int getOrder() { return 100; } @Override public String getName() { return "loggingAdvisor"; } @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { // 【前置处理】请求到达LLM之前 long startTime = System.currentTimeMillis(); String userText = request.userText(); log.info("📨 用户请求: {}", truncate(userText, 200)); // 调用下一个Advisor(最终会调用LLM) ChatClientResponse response = chain.nextCall(request); // 【后置处理】LLM返回之后 long elapsed = System.currentTimeMillis() - startTime; String content = response.content(); log.info("✅ 响应完成, 耗时: {}ms, 内容长度: {}字符", elapsed, content.length()); return response; } }
4.5 对比效果:使用前后代码简化
| 对比维度 | 无Advisors | 使用Advisors |
|---|---|---|
| 业务代码行数 | ~30行(含记忆加载、RAG检索、日志、过滤) | 5行(仅核心问答逻辑) |
| 新增功能成本 | 修改所有AI调用方法 | 编写一个Advisor + 注册到Builder |
| 代码复用性 | 各方法重复实现 | 一处编写,全局复用 |
| 可测试性 | 需要mock多个依赖 | Advisor可独立单元测试 |
// 使用Advisors后的清爽代码 @Service public class CleanChatService { private final ChatClient chatClient; public String ask(String userId, String question) { // 仅保留核心业务逻辑!记忆、RAG、日志都交给Advisor return chatClient.prompt() .advisors(advisor -> advisor.param("userId", userId)) .user(question) .call() .content(); } }
五、底层原理:Advisor链如何工作?
5.1 执行流程
Advisor链的核心机制是 责任链模式(Chain of Responsibility) ,执行流程如下-11-15:
请求封装:Spring AI框架从用户Prompt创建
AdvisedRequest对象,并创建一个空的AdvisorContext用于跨Advisor共享状态链式处理(请求) :各Advisor按
getOrder()值从小到大顺序执行,每个Advisor可以对请求进行修改或决定是否阻断LLM调用:最后一个Advisor将请求发送给LLM
链式处理(响应) :LLM响应沿着链条返回,各Advisor依次处理后置逻辑
返回客户端:最终响应返回给调用方
用户请求 → AdvisorA(前置) → AdvisorB(前置) → LLM调用 → AdvisorB(后置) → AdvisorA(后置) → 返回用户⚠️ 关键注意:由于链条是堆栈结构,第一个处理请求的Advisor,是最后一个处理响应的-11。这在设计依赖响应结果的逻辑时需要特别注意。
5.2 递归Advisor(Recursive Advisor)
Spring AI从 1.1.0-M4 版本开始引入递归Advisor,它允许链条多次循环执行,支持更复杂的迭代式工作流-30:
工具调用循环:依次执行多个工具,每个工具的输出作为下一个决策的输入
输出验证:验证结构化响应,验证失败时重新调用LLM
重试逻辑:根据响应质量或外部条件优化请求
Agent循环:构建自主Agent,通过分析结果和决定下一步行动来迭代执行任务直到目标达成
传统单次Advisor链无法处理这类场景,而递归Advisor通过创建下游Advisor的子链并重复调用,实现了可控的迭代处理。
5.3 底层技术支撑
Spring AI Advisors的底层实现依赖于以下核心技术:
| 技术 | 作用 |
|---|---|
| 责任链模式 | 实现Advisor的串联调用和传递逻辑 |
| 动态代理 | 在运行时将Advisor链织入ChatClient调用 |
| Spring的Ordered接口 | 通过 getOrder() 控制执行顺序 |
| Builder模式 | 提供流畅的API配置Advisor |
| 不可变对象模式 | AdvisedRequest 采用Record实现,确保链式传递的安全性-5 |
六、高频面试题与参考答案
Q1:什么是Spring AI Advisors?它的核心作用是什么?
参考答案:
Spring AI Advisors是Spring AI框架中用于拦截、修改和增强AI交互请求与响应流程的模块化组件。它的核心作用包括三个方面:
关注点分离:将对话记忆、RAG检索、日志监控等横切关注点从业务逻辑中抽离
代码复用:封装常见的生成式AI模式为可重用单元,一处编写全局使用
非侵入式增强:通过责任链模式在AI调用前后插入自定义逻辑,无需修改核心业务代码-4-5
踩分点:拦截/增强、横切关注点、模块化复用、责任链模式。
Q2:Advisors的执行顺序是如何控制的?请求处理和响应处理的顺序有什么关系?
参考答案:
执行顺序通过 getOrder() 方法控制,数值越小,优先级越高,越先执行请求处理。由于Advisor链采用堆栈结构:
第一个处理请求的Advisor,是最后一个处理响应的
最后一个处理请求的Advisor(最接近LLM的),是第一个处理响应的
前置逻辑(如日志记录)适合放在低Order值Advisor中,后置逻辑(如响应脱敏)适合放在高Order值Advisor中-11-15。
踩分点:getOrder() 控制顺序、堆栈结构、请求与响应处理顺序相反。
Q3:Spring AI Advisors和Spring AOP有什么联系和区别?
参考答案:
联系:Spring AI Advisors借鉴了Spring AOP的设计理念,两者都用于处理横切关注点。
区别:
AOP拦截的是Java方法调用,Advisors拦截的是LLM API调用
AOP通过
@Around注解和ProceedingJoinPoint实现,Advisors通过实现CallAdvisor接口和CallAdvisorChain实现Advisors天然支持链式组合和RAG、对话记忆等AI领域特定能力
可以理解为:AOP是为Java方法调用提供横切能力,Advisors是为LLM调用提供横切能力-4-45。
踩分点:思想相同(横切关注点)、对象不同(方法调用 vs LLM调用)、AI领域特有封装。
Q4:自定义Advisor需要实现哪些方法?各有什么作用?
参考答案:
自定义Advisor需要实现 CallAdvisor 接口,核心方法包括:
getOrder():返回执行顺序,数值越小优先级越高getName():返回Advisor唯一标识,用于日志和调试adviseCall(ChatClientRequest request, CallAdvisorChain chain):核心拦截方法,包含前置逻辑(请求处理)、调用chain.nextCall(request)传递调用、后置逻辑(响应处理)
如果需要支持流式场景,还需实现 StreamAdvisor 接口及对应的 adviseStream 方法-30-2。
踩分点:getOrder()(顺序)、getName()(标识)、adviseCall(核心逻辑+调用next)、流式需额外实现。
Q5:什么是递归Advisor(Recursive Advisor)?适用于哪些场景?
参考答案:
递归Advisor是Spring AI 1.1.0-M4引入的新特性,允许Advisor链多次循环执行,而不是传统的一次性单次执行。它通过创建下游Advisor的子链并重复调用来实现迭代处理。
适用场景包括:
工具调用循环:依次执行多个工具,每个工具的输出作为下一个决策的输入
输出验证:验证结构化响应,失败时重新调用LLM并附带错误反馈
Agent循环:构建自主Agent,通过分析结果和决定下一步行动来迭代执行任务-30
踩分点:多次循环执行、工具调用循环、输出验证、Agent循环。
七、总结
本文全面解析了Spring AI Advisors,我们从四个维度逐步深入:
| 维度 | 核心要点 |
|---|---|
| 是什么 | 拦截/增强AI交互的模块化组件,采用AOP思想处理横切关注点 |
| 为什么需要 | 解决传统开发中关注点耦合、代码重复、难以扩展的痛点 |
| 怎么用 | 内置Advisor开箱即用,自定义Advisor实现 CallAdvisor 接口 |
| 底层原理 | 责任链模式 + 动态代理,支持递归执行复杂迭代逻辑 |
重点与易错点:
✅ 关注点分离:核心业务逻辑只关注问答本身,横切能力交给Advisor
✅ 执行顺序:
getOrder()值越小越先执行请求处理;堆栈结构下请求和响应处理顺序相反✅ 递归能力:从1.1.0-M4版本开始,支持多次循环执行,实现Agent和工具调用循环
✅ 两种Advisor:非流式(
CallAdvisor)和流式(StreamAdvisor)需分别实现
📌 预告
下一篇我们将深入 Spring AI中的Function Calling机制,讲解如何让AI模型动态调用外部工具和API,实现真正的Agent能力。敬请期待!
📢 本文为【AI城市旅游助手】技术系列第N篇,欢迎点赞、收藏、转发,一起探索Java + AI的技术前沿!