文章目录
- [1. 概述](#1. 概述)
- [2. 核心组件](#2. 核心组件)
-
- [2.1 Advisor](#2.1 Advisor)
- [2.2 CallAdvisor](#2.2 CallAdvisor)
- [2.3 StreamAdvisor](#2.3 StreamAdvisor)
- [2.4 AdvisorChain](#2.4 AdvisorChain)
- [2.5 ChatClientRequest](#2.5 ChatClientRequest)
- [2.6 ChatClientResponse](#2.6 ChatClientResponse)
- [3. 执行流程](#3. 执行流程)
- [4. 案例演示](#4. 案例演示)
-
- [4.1 请求、响应日志打印](#4.1 请求、响应日志打印)
-
- [4.1.1 日志增强器](#4.1.1 日志增强器)
- [4.1.2 配置](#4.1.2 配置)
- [4.1.3 测试](#4.1.3 测试)
- [4.2 自定义增强器](#4.2 自定义增强器)
-
- [4.2.1 Re‑Reading(Re2)](#4.2.1 Re‑Reading(Re2))
- [4.2.2 Re2Advisor](#4.2.2 Re2Advisor)
- [4.2.3 测试](#4.2.3 测试)
1. 概述
Advisors API 是 Spring AI 提供的核心扩展机制,本质是「AI 交互的拦截器 / 增强器」,它能在 ChatClient 与大模型(LLM)的交互全链路中(调用前 / 调用后)插入自定义逻辑,实现对请求 / 响应的拦截、修改、增强,最终让开发者无需重复编写通用 AI 逻辑,快速构建复杂、可复用、易维护的 AI 组件。
2. 核心组件
Advisors 相关接口位于包 org.springframework.ai.chat.client.advisor.api :

Advisors API 包含用于非流式场景的 CallAdvisor 与 CallAdvisorChain,以及用于流式场景的 StreamAdvisor 与 StreamAdvisorChain。同时还包含用于表示待封装提示词请求的 ChatClientRequest,以及用于承载对话补全响应的 ChatClientResponse。二者均持有一个增强上下文(advise-context),用于在增强器链中共享状态。

2.1 Advisor
Advisor 是所有增强器的顶层父接口,定义了增强器的最基础能力:
java
public interface Advisor extends Ordered {
String getName();
}
能力介绍:
- 继承
Ordered接口:表示所有增强器都具备 "排序能力",可通过排序规则(如优先级)决定多个增强器的执行顺序。 - 核心方法
getName():获取增强器的唯一名称,用于标识增强器、日志打印、异常排查。
Advisor 有两个子接口:
CallAdvisorStreamAdvisor
2.2 CallAdvisor
用于同步、非流式的对话增强,适用于不需要实时流式返回、对响应完整性要求高的场景。
接口定义:
java
// 同步(非流式)增强器
public interface CallAdvisor extends Advisor {
ChatClientResponse adviseCall(
ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain);
}
核心方法:
adviseCall(...)增强同步对话的核心方法,接收两个参数、返回增强后的响应:ChatClientRequest:输入参数,代表原始对话请求。CallAdvisorChain:输入参数,增强器链用于调用下一个增强器,若有多个增强器,通过该链条按优先级执行。ChatClientResponse:返回值,增强后的对话响应。
所以子接口和子类:

2.3 StreamAdvisor
用于响应式、流式的对话增强,适用于需要实时反馈、响应内容较长的场景。其方法能力和CallAdvisor 一致。
接口定义:
java
// 响应式(流式)增强器
public interface StreamAdvisor extends Advisor {
Flux<ChatClientResponse> adviseStream(
ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain);
}
所以子接口和子类:

2.4 AdvisorChain
AdvisorChain 是增强器执行链的核心载体,设计理念完全类比 Spring AOP 切面链、Servlet 过滤器链,负责按优先级串联执行所有增强器,并最终调用大模型接口。
在 ChatClient 同步调用的核心方法中,会先构建增强器链,再将请求交由链条处理:
java
@Override
public CallResponseSpec call() {
BaseAdvisorChain advisorChain = buildAdvisorChain();
return new DefaultCallResponseSpec(DefaultChatClientUtils.toChatClientRequest(this), advisorChain,
this.observationRegistry, this.chatClientObservationConvention);
}
增强器链的顶层父接口,提供观测注册能力:
java
public interface AdvisorChain {
default ObservationRegistry getObservationRegistry() {
return ObservationRegistry.NOOP;
}
}
public interface CallAdvisorChain extends AdvisorChain {
ChatClientResponse nextCall(ChatClientRequest chatClientRequest);
List<CallAdvisor> getCallAdvisors();
CallAdvisorChain copy(CallAdvisor after);
}
Spring AI 框架提供了多个内置增强器实现:

管理对话记忆存储中的对话历史:
MessageChatMemoryAdvisor:检索记忆并以消息集合形式添加到提示词(保留对话历史结构,部分AI模型不支持)。PromptChatMemoryAdvisor:检索记忆并整合到提示词的系统文本中。VectorStoreChatMemoryAdvisor:从向量库检索记忆并添加到提示词的系统文本中(适用于从海量数据中高效检索相关信息)。
问答增强器:
QuestionAnswerAdvisor:基于向量库实现问答能力,采用朴素RAG(检索增强生成)模式。RetrievalAugmentationAdvisor:基于org.springframework.ai.rag包的构建块,遵循模块化RAG架构,实现通用RAG流程。
推理增强器:
ReReadingAdvisor:实现RE2重读策略,提升大语言模型在输入阶段的理解能力。
内容安全增强器:SafeGuardAdvisor:简单的增强器,用于防止模型生成有害或不当内容。
2.5 ChatClientRequest
用 Java record 定义的不可变数据载体类,专门用于封装聊天客户端的请求数据,核心包含 prompt(提示词)和 context(增强上下文)两部分信息。
核心代码:
java
public record ChatClientRequest(Prompt prompt, Map<String, Object> context) {
public ChatClientRequest(Prompt prompt, Map<String, Object> context) {
Assert.notNull(prompt, "prompt cannot be null");
Assert.notNull(context, "context cannot be null");
Assert.noNullElements(context.keySet(), "context keys cannot be null");
this.prompt = prompt;
this.context = context;
}
public ChatClientRequest copy() {
return new ChatClientRequest(this.prompt.copy(), new HashMap(this.context));
}
public Builder mutate() {
return (new Builder()).prompt(this.prompt.copy()).context(new HashMap(this.context));
}
public static Builder builder() {
return new Builder();
}
public Prompt prompt() {
return this.prompt;
}
public Map<String, Object> context() {
return this.context;
}
//.......
}
提示:增强上下文用于在增强器链中共享状态。
2.6 ChatClientResponse
用 Java record 定义的不可变数据载体类,专门用于封装聊天请求的返回结果,核心包含 ChatResponse(核心响应内容)和 context(上下文信息)两部分信息。
核心代码:
java
public record ChatClientResponse(ChatResponse chatResponse, Map<String, Object> context) {
public ChatClientResponse(@Nullable ChatResponse chatResponse, Map<String, Object> context) {
Assert.notNull(context, "context cannot be null");
Assert.noNullElements(context.keySet(), "context keys cannot be null");
this.chatResponse = chatResponse;
this.context = context;
}
public ChatClientResponse copy() {
return new ChatClientResponse(this.chatResponse, new HashMap(this.context));
}
public Builder mutate() {
return (new Builder()).chatResponse(this.chatResponse).context(new HashMap(this.context));
}
public static Builder builder() {
return new Builder();
}
@Nullable
public ChatResponse chatResponse() {
return this.chatResponse;
}
public Map<String, Object> context() {
return this.context;
}
3. 执行流程
以下流程图展示了增强器链与聊天模型的交互逻辑:

流程相关说明:
Spring AI框架根据用户的Prompt创建ChatClientRequest,并初始化空的增强上下文对象。- 链中的每个增强器处理请求(可能修改请求),也可选择不调用下一个组件以阻塞请求(此时需由该增强器填充响应)。
- 框架提供的最终增强器将请求发送至聊天模型。
- 聊天模型的响应沿增强器链回传,并转换为包含共享增强上下文的
ChatClientResponse。 - 每个增强器可处理或修改响应。
- 提取
ChatCompletion后,最终的ChatClientResponse返回给客户端。
增强器在链中的执行顺序由 getOrder() 方法决定,核心规则:
- 值越小,执行优先级越高(先执行);值越大,优先级越低。
- 若多个增强器
order值相同,执行顺序不保证。
增强器链遵循栈式逻辑:
- 链中第一个增强器最先处理请求。
- 最后处理响应。
控制执行顺序的方式:
- 设置
order接近Ordered.HIGHEST_PRECEDENCE:确保增强器最先执行(请求处理先执行,响应处理最后执行); - 设置
order接近Ordered.LOWEST_PRECEDENCE:确保增强器最后执行(请求处理最后执行,响应处理最先执行);
Spring Ordered 接口语义参考:
java
public interface Ordered {
/** 最高优先级常量 */
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/** 最低优先级常量 */
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* 获取对象的顺序值
* <p>值越大,优先级越低;值越小,优先级越高(类似 Servlet 的 load-on-startup)
* <p>相同值会导致对象排序位置随机
* @return 顺序值
*/
int getOrder();
}
4. 案例演示
4.1 请求、响应日志打印
4.1.1 日志增强器
Spring AI 默认已经提供了一个日志打印实现,实现了 CallAdvisor, StreamAdvisor 接口,说明同时支持同步和流式场景:
java
public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor {
public static final Function<ChatClientRequest, String> DEFAULT_REQUEST_TO_STRING = ChatClientRequest::toString;
public static final Function<ChatResponse, String> DEFAULT_RESPONSE_TO_STRING = ModelOptionsUtils::toJsonStringPrettyPrinter;
private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);
private final Function<ChatClientRequest, String> requestToString;
private final Function<ChatResponse, String> responseToString;
private final int order;
public SimpleLoggerAdvisor() {
this(DEFAULT_REQUEST_TO_STRING, DEFAULT_RESPONSE_TO_STRING, 0);
}
public SimpleLoggerAdvisor(int order) {
this(DEFAULT_REQUEST_TO_STRING, DEFAULT_RESPONSE_TO_STRING, order);
}
public SimpleLoggerAdvisor(@Nullable Function<ChatClientRequest, String> requestToString,
@Nullable Function<ChatResponse, String> responseToString, int order) {
this.requestToString = requestToString != null ? requestToString : DEFAULT_REQUEST_TO_STRING;
this.responseToString = responseToString != null ? responseToString : DEFAULT_RESPONSE_TO_STRING;
this.order = order;
}
@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);
return new ChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses, this::logResponse);
}
protected void logRequest(ChatClientRequest request) {
logger.debug("request: {}", this.requestToString.apply(request));
}
protected void logResponse(ChatClientResponse chatClientResponse) {
logger.debug("response: {}", this.responseToString.apply(chatClientResponse.chatResponse()));
}
4.1.2 配置
SimpleLoggerAdvisor 使用的是 logger.debug 所以还需要配置以下日志级别:
yml
# 日志配置
logging:
level:
org.springframework.ai.chat.client.advisor: DEBUG
ChatClient 中配置增强器:
java
@Bean("zhiPuAiChatClient")
public ChatClient zhiPuAiChatClient(ZhiPuAiChatModel zhiPuAiChatModel) {
return ChatClient.builder(zhiPuAiChatModel)
.defaultAdvisors(
SimpleLoggerAdvisor.builder().build()
)
.build();
}
4.1.3 测试
运行测试类,并查看控制台:
java
@Test
public void zhiPuAiChatClientTest() {
String content = zhiPuAiChatClient
.prompt("今天周几")
.call()
.content();
System.out.println(content);
}

4.2 自定义增强器
创建增强器需实现 CallAdvisor 或 StreamAdvisor(或两者),核心步骤:
- 实现
nextCall()(非流式)或nextStream()(流式)方法。 - 为增强器提供唯一名称。
- 通过
order值控制执行顺序(值越小越先执行)。
Spring AI 还提供了 MessageAggregator 工具类,可将 Flux 响应聚合为单个 ChatClientResponse ,适用于观测完整响应的场景,仅只读操作,无法修改响应。
4.2.1 Re‑Reading(Re2)
Re2 是论文 《Re‑Reading Improves Reasoning in Large Language Models》 里提出的一个超级简单但非常有效的技巧,用来提升大模型的推理、思考、解题能力。
核心思想:让模型把问题 "再读一遍",推理正确率会明显变高。
原始输入:
java
1+1等于几?
Re2 之后的输入:
java
1+1等于几?
Read the question again: 1+1等于几?
4.2.2 Re2Advisor
实现 Re2Advisor 处理逻辑:
java
/**
* Re2(Re-Reading)顾问器:自动扩充用户Prompt,提升LLM推理能力
*/
public class Re2Advisor implements CallAdvisor, StreamAdvisor, Ordered {
// Re2提示词模板(核心:原问题 + 再读一遍原问题)
private static final String DEFAULT_RE2_TEMPLATE = """
{user_query}
Read the question again: {user_query}
""";
private final PromptTemplate re2Template;
// 执行顺序(数值越小,越先执行,保证Re2最先修改Prompt)
private int order = Ordered.HIGHEST_PRECEDENCE + 10;
// 无参构造器:使用默认模板
public Re2Advisor() {
this.re2Template = new PromptTemplate(DEFAULT_RE2_TEMPLATE);
}
// 自定义模板构造器(支持个性化Re2提示)
public Re2Advisor(String customTemplate) {
this.re2Template = new PromptTemplate(customTemplate);
}
@Override
public String getName() {
return "Re2Advisor"; // 唯一名称,便于监控/日志识别
}
@Override
public int getOrder() {
return this.order;
}
// 自定义执行顺序
public Re2Advisor withOrder(int order) {
this.order = order;
return this;
}
// ========== 非流式调用(同步) ==========
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain chain) {
// 1. 应用Re2规则,生成增强后的Prompt
ChatClientRequest enhancedRequest = enhancePromptWithRe2(chatClientRequest);
// 2. 调用链的下一个组件(最终调用LLM)
return chain.nextCall(enhancedRequest);
}
// ========== 流式调用(异步) ==========
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest request, StreamAdvisorChain chain) {
// 1. 应用Re2规则,生成增强后的Prompt
ChatClientRequest enhancedRequest = enhancePromptWithRe2(request);
// 2. 调用链的下一个组件,返回流式响应
return chain.nextStream(enhancedRequest);
}
// ========== 核心:Re2 Prompt增强逻辑 ==========
private ChatClientRequest enhancePromptWithRe2(ChatClientRequest originalRequest) {
// 1. 获取用户原始提问内容
String originalUserText = originalRequest.prompt().getUserMessage().getText();
// 2. 跳过空内容(防御性编程)
if (originalUserText == null || originalUserText.isBlank()) {
return originalRequest;
}
// 3. 应用Re2模板,生成增强后的提问内容
String enhancedUserText = re2Template.create(Map.of("user_query", originalUserText)).getContents();
// 4. 构建新的请求
return originalRequest.mutate()
.prompt(originalRequest.prompt().augmentUserMessage(enhancedUserText))
.build();
}
}
4.2.3 测试
运行测试类,并查看控制台:
java
@Test
public void zhiPuAiChatClientTest() {
String reasoningQuestion = "有3个苹果,小明吃了1个,妈妈又买了2个,现在一共有几个苹果?";
// 调用ChatClient,Re2Advisor 会自动增强 Prompt
String response = zhiPuAiChatClient.prompt()
.user(reasoningQuestion)
.call()
.content();
System.out.println(response);
}
