Spring AI 的 Advisors API 提供一套灵活、强大的机制,用于拦截、修改、增强 Spring 应用内 AI 交互流程。借助 Advisors API,开发者可以构建更加复杂、可复用、易维护的 AI 组件。
核心收益:
- 封装各类重复出现的生成式AI业务模式;
- 对发给大模型(LLM)、从大模型返回的数据做转换处理;
- 在不同模型、不同业务场景之间实现业务逻辑可移植。
可以通过 ChatClient API 配置已有的 Advisor 组件,示例代码:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
ChatMemory chatMemory = ... // 初始化对话存储器 VectorStore vectorStore = ... // 初始化向量存储 var chatClient = ChatClient.builder(chatModel) .defaultAdvisors( MessageChatMemoryAdvisor.builder(chatMemory).build(), // 对话记忆Advisor QuestionAnswerAdvisor.builder(vectorStore).build() // RAG检索问答Advisor ) .build(); var conversationId = "678"; String response = this.chatClient.prompt() // 运行时设置Advisor参数 .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId)) .user(userText) .call() .content(); |
最佳实践:建议在构建
ChatClient阶段,通过defaultAdvisors()注册全局默认Advisor。
Advisor 组件会接入可观测性体系,可以查看 Advisor 执行相关的指标与链路追踪信息。
核心组件 Core Components
API分为两套接口:
- 非流式场景:
CallAdvisor、CallAdvisorChain - 流式场景:
StreamAdvisor、StreamAdvisorChain
ChatClientRequest:封装待发送给模型的请求(未处理的Prompt)ChatClientResponse:封装大模型返回的对话响应对象>
两个对象内部都持有
advise‑context(Advisor上下文Map),用于在整条Advisor链之间共享状态。
类图定义如下:
adviseCall()(非流式)与 adviseStream()(流式)是Advisor的核心方法,通常可以完成这些工作:
- 读取原始Prompt数据;
- 定制、增强、改写Prompt内容;
- 调用Advisor链中下一个处理单元;
- 可选择拦截、终止本次请求;
- 读取大模型返回结果;
- 抛出异常代表处理失败。
此外 getOrder() 方法决定Advisor在链中的执行顺序;getName() 返回Advisor唯一名称。
Advisor链(Advisor Chain)由Spring AI框架自动创建,多个Advisor会按照 getOrder() 的数值顺序依次执行;框架会自动追加最后一个Advisor,由它真正把请求发送给LLM大模型。
执行流程说明
下图说明了 Advisor链和 ChatModel 的交互逻辑:
解释一下:
- Spring AI 框架把用户传入的
Prompt构建成ChatClientRequest,并初始化空的Advisor上下文; - 链中每一个Advisor处理请求,可以修改请求对象,也可以不调用下游直接拦截请求并自行填充响应返回;
- 链末尾由框架内置Advisor,把处理完成的请求提交给 ChatModel;
- 模型返回响应,沿着Advisor链逆向回传,封装为
ChatClientResponse,携带共享的Advisor上下文; - 每一个Advisor都可以读取、修改返回响应;
- 最终将处理完成的
ChatClientResponse返回给调用方。
执行模式是环绕通知(Around)模式:Advisor先处理请求向下传递,拿到模型结果后,再逆向处理响应向上返回。
Advisor 执行顺序 Advisor Order
Advisor链的执行顺序完全由 getOrder() 返回值控制,重要规则:
- order数值越小,优先级越高,越先执行请求阶段逻辑;
- Advisor链是栈式执行:
- 优先级最高的Advisor,第一个处理入站请求;
- 拿到模型返回结果后,最后一个处理出站响应;
- 想要最先处理请求:设置值接近
Ordered.HIGHEST_PRECEDENCE; - 想要最后处理请求:设置值接近
Ordered.LOWEST_PRECEDENCE; - order值越大,代表优先级越低;
- 多个Advisor拥有相同order,执行顺序不保证。
栈式执行带来一个容易踩坑点:同一个Advisor对象,请求阶段最先执行,响应阶段就会变成最后执行。 如果需要请求和响应都排在整个链条第一位:
- 拆分为两个独立Advisor实例;
- 设置不同order;
- 使用
advise‑context上下文对象跨实例共享状态。
下面是 Spring Ordered 接口的语义说明:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
public interface Ordered { /** 最高优先级,等价于 Integer.MIN_VALUE */ int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; /** 最低优先级,等价于 Integer.MAX_VALUE */ int LOWEST_PRECEDENCE = Integer.MAX_VALUE; /** * 获取排序值 * 数值越大优先级越低;order相同,执行顺序不确定 */ int getOrder(); } |
API 概览
所有接口包路径:org.springframework.ai.chat.client.advisor.api
顶层基础接口
|
1 2 3 |
public interface Advisor extends Ordered { String getName(); } |
非流式同步接口
|
1 2 3 4 |
public interface CallAdvisor extends Advisor { ChatClientResponse adviseCall( ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain); } |
流式响应式接口
|
1 2 3 4 |
public interface StreamAdvisor extends Advisor { Flux<ChatClientResponse> adviseStream( ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain); } |
Advisor链接口,用来调用下游:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public interface CallAdvisorChain extends AdvisorChain { // 调用链中下一个非流式Advisor ChatClientResponse nextCall(ChatClientRequest chatClientRequest); // 获取链上全部Advisor列表 List<CallAdvisor> getCallAdvisors(); } public interface StreamAdvisorChain extends AdvisorChain { // 调用链中下一个流式Advisor Flux<ChatClientResponse> nextStream(ChatClientRequest chatClientRequest); // 获取链上全部流式Advisor列表 List<StreamAdvisor> getStreamAdvisors(); } |
实现自定义Advisor
实现Advisor需要实现 CallAdvisor、StreamAdvisor(二者选一或同时实现); 非流式重写 adviseCall(),流式重写 adviseStream();调用 nextCall() / nextStream() 放行给下游Advisor链。
示例1:日志打印Advisor
我们可以实现一个简易日志 Advisor,在调用责任链中下一个 Advisor之前打印ChatClientRequest,在调用之后打印ChatClientResponse。 请注意:该 Advisor 仅做请求、响应的观测,不会修改其中内容。此实现同时支持非流式与流式调用场景。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor { private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class); @Override public String getName() { return this.getClass().getSimpleName(); } @Override public int getOrder() { return 0; } @Override public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) { logRequest(chatClientRequest); ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest); logResponse(chatClientResponse); return chatClientResponse; } @Override public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain) { logRequest(chatClientRequest); Flux<ChatClientResponse> chatClientResponses = streamAdvisorChain.nextStream(chatClientRequest); // MessageAggregator:把流式分片聚合为完整响应对象,用于日志打印;注意聚合后不能修改原始流数据 return new ChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses, this::logResponse); } private void logRequest(ChatClientRequest request) { logger.debug("request: {}", request); } private void logResponse(ChatClientResponse chatClientResponse) { logger.debug("response: {}", chatClientResponse); } } |
代码中的注意事项:
- 为该 Advisor 提供唯一名称。
- 可以通过设置
order值控制执行顺序:数值越小,越优先执行。 MessageAggregator是一个工具类,它将 Flux 流式响应聚合为单个ChatClientResponse对象。当需要打印日志,或是做其他需要**观测完整响应(而非流中一条条分片数据)**的处理时,该工具类十分有用。 注意:MessageAggregator内部只能做只读操作,不可以修改响应内容。
示例2:Re‑Reading(Re2)增强推理Advisor
Re‑Reading论文思想:把用户问题追加一遍,增强模型推理能力
Re2 需要一个类似下面这样的 Prompt 请求:
|
1 2 |
{Input_Query} Read the question again: {Input_Query} |
按照如下方式即可实现一个 Advisor,对用户输入的查询语句应用 Re2(二次重读) :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
public class ReReadingAdvisor implements BaseAdvisor { private static final String DEFAULT_RE2_ADVISE_TEMPLATE = """ {re2_input_query} Read the question again: {re2_input_query} """; private final String re2AdviseTemplate; private int order = 0; public ReReadingAdvisor() { this(DEFAULT_RE2_ADVISE_TEMPLATE); } public ReReadingAdvisor(String re2AdviseTemplate) { this.re2AdviseTemplate = re2AdviseTemplate; } @Override public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) { String augmentedUserText = PromptTemplate.builder() .template(this.re2AdviseTemplate) .variables(Map.of("re2_input_query", chatClientRequest.prompt().getUserMessage().getText())) .build() .render(); return chatClientRequest.mutate() .prompt(chatClientRequest.prompt().augmentUserMessage(augmentedUserText)) .build(); } @Override public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) { return chatClientResponse; } @Override public int getOrder() { return this.order; } public ReReadingAdvisor withOrder(int order) { this.order = order; return this; } } |
代码中的注意事项:
before方法运用**重读(Re‑Reading)**技术对用户输入的查询语句做增强处理。- 可以通过设置
order值来控制执行顺序:数值越小,执行优先级越高,会优先执行。
Spring AI 内置Advisor Built‑in Advisors
- ChatMemoryAdvisor 对话记忆Advisor
MessageChatMemoryAdvisor:读取历史对话消息,追加到Prompt消息列表。VectorStoreChatMemoryAdvisor:从向量库检索历史记忆,注入系统提示词,适合超长会话。
- QuestionAnswerAdvisor 问答检索Advisor
- 实现简易RAG:查询向量库,把检索结果填充给Prompt。
RetrievalAugmentationAdvisor:模块化RAG架构的标准实现。
- ReasoningAdvisor推理增强
ReReadingAdvisor:Re2重读策略,增强大模型推理。
- ToolCallingAdvisor 工具调用Advisor
ChatClient默认自动注册,负责执行工具调用循环:调用工具、把结果送回模型,反复迭代直到不需要继续调用工具。 标记接口
ToolAdvisor会防止重复注册该Advisor。
- SafeGuardAdvisor 内容安全Advisor 简单的护栏Advisor,拦截不安全输出。
流式与非流式 Streaming vs Non‑Streaming
- 非流式 CallAdvisor:处理完整请求对象、完整响应对象;
- 流式 StreamAdvisor:基于 Project Reactor
Flux处理分片流数据。
流式骨架代码模板:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Override public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain chain) { return Mono.just(chatClientRequest) .publishOn(Schedulers.boundedElastic()) .map(request -> { // 请求阶段前置处理逻辑 return request; }) .flatMapMany(request -> chain.nextStream(request)) .map(response -> { // 响应分片后置处理逻辑 return response; }); } |
最佳实践 Best Practices
- 单一职责:每个Advisor只做一件事,提高模块化、可复用;
- 状态共享优先使用
adviseContext,不要用实例成员变量存会话状态; - 同时实现
CallAdvisor+StreamAdvisor,保证流式/非流式行为一致; - 仔细设计
getOrder()排序值,控制请求、响应处理顺序。
参考文档
原文链接:Advisors API