1.Advisor简介
Spring AI 中的 Advisor 是一种核心机制,用于拦截和增强 AI 应用程序中的请求与响应流。其设计灵感来源于 Spring AOP(面向切面编程)中的切面(Aspect)概念,但专门针对 AI 交互场景进行了优化。
1.1.核心概念
- 定义与定位
Advisor 是 Spring AI 中负责动态干预聊天请求和响应流程的组件,通过链式结构(Chain of Responsibility 模式)串联多个处理单元。每个 Advisor 可以修改请求参数、增强数据、拦截敏感操作,甚至中断请求传递。
- 与AOP的关系
其功能类似于 Spring 的 AspectJ
,但更专注于 AI 交互场景。例如,通过 AroundAdvisor
接口实现请求前后的增强逻辑。
1.2.Advisord的核心特点
- 模块化封装
- 重复任务封装:将生成式 AI 的通用模式(如上下文记忆、敏感词过滤)抽象为可复用的组件。
- 数据转换:优化发送至语言模型(LLM)的输入数据格式,并处理返回的响应(如结构化输出转换)。
- 可移植性:同一 Advisor 可适配不同模型(如 OpenAI、HuggingFace)和用例,提升代码灵活性。
- 链式处理机制
多个 Advisor 按顺序执行,每个环节可修改请求或响应,并决定是否继续传递。某些 Advisor(如 SafeGuardAdvisor
)可能直接中断链式流程。
- 内置与扩展性
Spring AI 提供多种内置 Advisor,同时也支持开发者自定义扩展,满足个性化需求。
2.Advisor源码及核心原理
2.1.关键类与关系
-
核心接口
-
Ordered
定义getOrder()
方法,用于控制Advisor
的执行顺序(类似 Spring 的优先级机制)。小order先管请求、后管响应,大order反之;同值顺序随机。
-
Advisor
基础接口,定义getName()
方法标识 Advisor 名称。
-
-
具体 Advisor 实现
CallAroundAdvisor
同步调用增强器,通过aroundCall()
方法拦截请求并返回响应。StreamAroundAdvisor
流式调用增强器,通过aroundStream()
方法处理流式响应(返回Flux<AdvisedResponse>
)。
-
请求与响应对象
AdvisedRequest
包含可变的 Prompt 数据和上下文信息(adviseContext
)。AdvisedResponse
封装 Chat 响应和上下文信息。
-
调用链(Chain)类
CallAroundAdvisorChain
通过nextAroundCall()
方法传递请求,支持链式调用多个CallAroundAdvisor
。StreamAroundAdvisorChain
类似CallAroundAdvisorChain
,但用于流式场景的链式调用。
2.2.核心交互流程
-
请求初始化
Spring AI框架将用户的输入(Prompt
)转换为AdvisedRequest
,并创建一个空的共享上下文对象AdvisorContext
。 -
Advisor链处理请求
- 每个
Advisor
依次处理AdvisedRequest
,可以:- 修改请求内容(例如调整参数、添加元数据)。
- 直接拦截请求,阻止后续调用,并自行生成响应(
AdvisedResponse
)。
- 若未拦截,请求会传递到链中的下一个
Advisor
。
- 每个
-
调用Chat Model
最后一个框架内置的
Advisor
负责将处理后的请求发送给Chat Model(如GPT模型)。 -
响应逆向传递
- Chat Model生成的原始响应(
ChatResponse
)会转换为AdvisedResponse
,并携带共享的AdvisorContext
。 - 响应沿Advisor链反向传递,每个
Advisor
可对响应进行二次处理或修改(例如过滤敏感内容、格式化输出)。
- Chat Model生成的原始响应(
-
响应返回
最终的
AdvisedResponse
通过提取ChatCompletion
(响应核心内容)返回给客户端。
流程图如下:
flowchart TDsubgraph 请求阶段A[Client Prompt] --> B[生成 AdvisedRequest\n+ AdvisorContext]B --> C[Advisor1]C -->|修改请求| D[Advisor2]D -->|拦截请求| E[生成 AdvisedResponse]D -->|放行| F[调用 Chat Model]endsubgraph 响应阶段F --> G[ChatResponse → AdvisedResponse]G --> H[Advisor2 处理响应]H --> I[Advisor1 处理响应]I --> J[返回最终响应]endE --> JJ --> K[Client]
2.3.设计特点
- 责任链模式
通过AdvisorChain
实现多个 Advisor 的链式调用,支持动态扩展增强逻辑。 - 上下文传递
AdvisedRequest
和AdvisedResponse
中的adviseContext
允许在链式调用中共享数据。 - 同步与流式分离
通过CallAroundAdvisor
和StreamAroundAdvisor
区分处理普通请求和流式请求。
2.4.Avisor相关源码解析
Advisor 接口位于org.springframework.ai.chat.client.advisor.api
包中,是实现自定义Advisor的关键接口:
public interface Advisor extends Ordered {String getName();
}
同步和流式Advisor的两个子接口继承自Advisor:
- 同步的CallAroundAdvisor:
/*** Around advisor that wraps the ChatModel#call(Prompt) method.** @author Christian Tzolov* @author Dariusz Jedrzejczyk* @since 1.0.0*/public interface CallAroundAdvisor extends Advisor {/*** Around advice that wraps the ChatModel#call(Prompt) method.* @param advisedRequest the advised request* @param chain the advisor chain* @return the response*/AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);}
- 流式的StreamAroundAdvisor:
/*** Around advisor that runs around stream based requests.** @author Christian Tzolov* @author Dariusz Jedrzejczyk* @since 1.0.0*/
public interface StreamAroundAdvisor extends Advisor {/*** Around advice that wraps the invocation of the advised request.* @param advisedRequest the advised request* @param chain the chain of advisors to execute* @return the result of the advised request*/Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);}
要使用Advice链,需在你的Advice实现中使用CallAroundAdvisorChain
和StreamAroundAdvisorChain
:
public interface CallAroundAdvisorChain {AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest);}
public interface StreamAroundAdvisorChain {Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest);}
自定义Advisor就需要实现
CallAroundAdvisor
和StreamAroundAdvisor
中的最少一个。其中的关键方法是nextAroundCall()
或nextAroundStream()
。
3.Spring AI内置的Advisor
-
MessageChatMemoryAdvisor
作用:
责维护对话的上下文记忆,将用户的问题与模型的回答保存到内存中,确保多轮对话的连贯性。例如,在连续对话中,历史记录会被自动附加到新请求中,帮助模型理解上下文。
核心特点:
- 上下文增强:通过
messages
参数传递历史对话记录。 - 模型兼容性要求:仅适用于支持
messages
参数的模型(如 OpenAI 的 GPT 系列)。 - 动态扩展:每次对话自动更新内存中的历史记录。
- 上下文增强:通过
-
PromptChatMemoryAdvisor
作用:
与
MessageChatMemoryAdvisor
类似,但将上下文历史记录封装到systemPrompt
提示词中,而非直接依赖messages
参数。这使得无论模型是否支持messages
参数,都能实现上下文记忆。核心特点:
- 灵活性:通过修改系统提示词(
systemPrompt
)嵌入历史对话,兼容性更广。 - 无侵入式设计:不依赖模型底层接口,仅通过提示词工程实现上下文管理。
- 灵活性:通过修改系统提示词(
-
QuestionAnswerAdvisor
作用:
实现检索增强生成(RAG),通过查询向量数据库或知识库获取相关文本片段,并将其附加到用户问题后,提升回答的准确性和相关性。
核心特点:
- 知识库集成:支持自定义向量数据库(如:Milvus、Weaviate、Elasticsearch、FAISS)。
- 默认拒绝机制:若知识库无匹配内容,则拒绝回答用户问题,避免生成错误信息。
- 提示词优化:内置默认提示词模板,确保回答与检索内容强相关。
-
SafeGuardAdvisor
作用:
敏感词过滤与安全拦截,防止用户输入或模型输出包含违规内容。当检测到敏感词时,直接中断请求链,避免调用大模型处理。
核心特点:
- 实时拦截:基于预定义规则或动态词库进行校验。
- 链式中断:直接终止后续处理,降低资源消耗与安全风险。
-
VectorStoreChatMemoryAdvisor
作用:
将对话历史长期存储到向量数据库中,并在每次提问时检索相关历史记录,增强上下文提示的准确性和长期记忆能力。
核心特点:
- 向量化存储:利用向量数据库高效检索相似历史对话片段。
- 关键参数管理:依赖
chat_memory_conversation_id
标识用户会话,避免数据冗余。 - 数据清理机制:需定期清理旧数据,防止数据库膨胀。
-
SimpleLoggerAdvisor
作用:
记录请求与响应的日志信息,便于调试和监控 AI 交互流程。例如,可打印用户输入、模型输出及处理耗时。
核心特点:
- 轻量级工具:无需复杂配置,快速集成日志功能。
- 可扩展性:支持自定义日志格式与存储方式。
4.自定义Advisor实现
4.1.自定义日志Advisor
我们自定义一个日志Advisor,在调用链中的下一个顾问之前记录AdvisedRequest
,之后记录AdvisedResponse
。
此advisor只观察请求和响应,不做任何膝盖且同时支持非流和流场景。
public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);// 为Advisor提供一个唯一的名称@Overridepublic String getName() {return this.getClass().getSimpleName();}// 设置order值来控制执行顺序,值较小的将优先执行@Overridepublic int getOrder() {return 0;}@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {logger.debug("BEFORE: {}", advisedRequest);AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);logger.debug("AFTER: {}", advisedResponse);return advisedResponse;}@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {logger.debug("BEFORE: {}", advisedRequest);Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);// MessageAggregator是一个实用程序类,它将Flux响应聚合到单个AdvisedResponse中,// MessageAggregator为只读,不可更改其中的响应return new MessageAggregator().aggregateAdvisedResponse(advisedResponses,advisedResponse -> logger.debug("AFTER: {}", advisedResponse));}
}
4.2.Re2 Advisor
“重读提升大型语言模型的推理能力”一文介绍了一种名为重读(Re2)的技术,它可以提升大型语言模型的推理能力。Re2 技术需要像这样扩充输入提示:
{输入查询} 再次阅读问题:{Input_Query}
实现Re2技术的Advisor:
public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {// 应用Re2增强用户的输入查询private AdvisedRequest before(AdvisedRequest advisedRequest) {Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());advisedUserParams.put("re2_input_query", advisedRequest.userText());return AdvisedRequest.from(advisedRequest).userText("""{re2_input_query}Read the question again: {re2_input_query}""").userParams(advisedUserParams).build();}// 拦截非流式请求并应用Re2@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {return chain.nextAroundCall(this.before(advisedRequest));}// 拦截流试请求并应用Re2技术@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {return chain.nextAroundStream(this.before(advisedRequest));}@Overridepublic int getOrder() {return 0;}@Overridepublic String getName() {return this.getClass().getSimpleName();}
}
5.Advisor使用示例
我们以MessageChatMemoryAdvisor
为例做一个advisor的使用实例,主要在ChatClient.builder中使用defaultAdvisors()方法配置Advisor,此方法可配置多个Advisor。
Builder defaultAdvisors(Advisor... advisor);Builder defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer);Builder defaultAdvisors(List<Advisor> advisors);
示例主要有controller、service及config配置,相关的pom依赖主要使用spring-ai-starter-model-minimax
包等,具体的pom及minimax模型的配置请参考:Spring AI开发跃迁指南(第二章:极速上手——ChatClient20行代码构建人工智能应用)
5.1.示例代码
- ChatConfig
package com.common;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ChatConfig {@Beanpublic ChatMemory chatMemory() {return new InMemoryChatMemory(); // 简单内存实现}// 2. 创建 MessageChatMemoryAdvisor@Beanpublic MessageChatMemoryAdvisor chatMemoryAdvisor(ChatMemory chatMemory) {return new MessageChatMemoryAdvisor(chatMemory);}// 配置 ChatClient,绑定 Advisor@Beanpublic ChatClient chatClient(ChatModel chatModel, MessageChatMemoryAdvisor advisor) {return ChatClient.builder(chatModel).defaultAdvisors(advisor) // 添加 Advisor.build();}
}
- service
@Service
public class TestSpringAIServiceImpl {@Autowiredprivate ChatClient chatClient;@Overridepublic String generate(String message) {return chatClient.prompt().advisors(advisorSpec -> advisorSpec.param("chat_memory_conversation_id", "111").param("chat_memory_response_size", 100)).user(message).call().content();}
}
- controller
@RestController
public class TestSpringAIController {@Autowiredprivate TestSpringAIServiceImpl service; @GetMapping("/ai/generate")public String generate(@RequestParam(value = "message", defaultValue = "生成一个中国女演员及其电影作品") String message) {return service.generate(message);}
}
- 运行结果
-
第一次运行结果:
-
第二次运行结果:
-
执行结果分析
- 第一次对话:声明自己的名字为“张三”,
MessageChatMemoryAdvisor
会将此对话记录(用户输入和模型回复)保存到内存中。 - 第二次对话:用户提问“我的名字是什么?”,模型会根据
MessageChatMemoryAdvisor
提供的上下文历史(包含第一次对话)生成正确回答。
5.2.关键机制解释
-
内存管理
InMemoryChatMemory
默认以List<Message>
形式存储对话历史,每次调用chatClient.call()
时,MessageChatMemoryAdvisor
会自动将历史记录附加到当前请求中(通过messages
参数传递)。 -
上下文传递
模型(如 OpenAI GPT)接收到完整的messages
列表后,会自动解析历史对话,确保回答的连贯性。 -
会话隔离
若需区分不同用户的对话,需通过ChatMemory
的conversationId
参数管理会话标识:// 为不同用户分配唯一会话 ID ChatMemory memory = new InMemoryChatMemory("user-123");
若需自定义历史记录的条数或存储策略:
@Bean
public ChatMemory chatMemory() {InMemoryChatMemory memory = new InMemoryChatMemory();memory.setMaxHistorySize(10); // 限制最多保留 10 条历史记录return memory;
}
注意:
- 模型兼容性:确保底层模型(如 OpenAI、MiniMax)支持
messages
参数格式。- 内存泄漏风险:若使用
InMemoryChatMemory
,需在长期运行的应用中定期清理过期会话。