Advisors API
Spring AI 的 Advisors API 提供了一种灵活且强大的方式,用于在 Spring 应用中拦截、修改和增强 AI 驱动的交互。通过利用 Advisors API,开发者可以创建更复杂、可复用且易于维护的 AI 组件。
其主要优势包括封装常见的生成式 AI 模式、转换发送给和从大型语言模型(LLM)返回的数据,并提供跨不同模型和用例的可移植性。
你可以使用 ChatClient API 配置现有的顾问(advisors),如下示例所示:
ChatMemory chatMemory = ... // 初始化聊天记忆存储
VectorStore vectorStore = ... // 初始化向量存储
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), // 聊天记忆顾问
QuestionAnswerAdvisor.builder(vectorStore).build() // RAG 顾问
)
.build();
var conversationId = "678";
String response = this.chatClient.prompt()
// 在运行时设置顾问参数
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId))
.user(userText)
.call()
.content();
建议在构建阶段通过 builder 的 defaultAdvisors() 方法注册顾问。
顾问还参与可观测性(Observability)栈,因此你可以查看与其执行相关的指标和追踪信息。
了解更多:
核心组件
该 API 包含用于非流式场景的 CallAdvisor 和 CallAdvisorChain,以及用于流式场景的 StreamAdvisor 和 StreamAdvisorChain。它还包括用于表示未封装 Prompt 请求的 ChatClientRequest 和表示 Chat Completion 响应的 ChatClientResponse。两者都持有一个 advise-context,用于在顾问链中共享状态。

adviseCall() 和 adviseStream() 是顾问的关键方法,通常执行的操作包括:
- 检查未封装的 Prompt 数据
- 定制和增强 Prompt 数据
- 调用顾问链中的下一个实体
- 可选择阻塞请求
- 检查聊天完成响应
- 抛出异常以指示处理错误
此外,getOrder() 方法用于确定顾问在链中的执行顺序,getName() 提供顾问的唯一名称。
Spring AI 框架创建的顾问链允许按 getOrder() 值顺序依次调用多个顾问。值越小的顾问越先执行。链中最后一个顾问由框架自动添加,它会将请求发送给 LLM。
下图展示了顾问链(Advisor Chain)与聊天模型(Chat Model)之间的交互流程:

- Spring AI 框架根据用户的
Prompt创建一个ChatClientRequest,并附带一个空的顾问上下文对象(advisorcontext)。 - 链中的每个顾问处理请求,可能对请求进行修改;也可以选择阻塞请求,即不调用下一个顾问。在这种情况下,该顾问负责填充响应。
- 框架提供的最终顾问将请求发送给 Chat 模型。
- Chat 模型的响应再通过顾问链传回,并转换为
ChatClientResponse,其中包含共享的顾问上下文(context)实例。 - 每个顾问都可以对响应进行处理或修改。
- 最终的
ChatClientResponse通过提取ChatCompletion返回给客户端。
顾问(advisor)顺序
顾问链中顾问的执行顺序由 getOrder() 方法决定。关键点如下:
- 顺序值较低的顾问先执行。
- 顾问链的工作方式类似栈:
- 链中的第一个顾问是最先处理请求的。
- 它也是处理响应时最后一个执行的。
- 控制执行顺序的方法:
- 将顺序值设为接近
Ordered.HIGHEST_PRECEDENCE,确保顾问在链中最先执行(请求处理时最先,响应处理时最后)。 - 将顺序值设为接近
Ordered.LOWEST_PRECEDENCE,确保顾问在链中最后执行(请求处理时最后,响应处理时最先)。
- 将顺序值设为接近
- 顺序值越高,优先级越低。
- 如果多个顾问顺序值相同,其执行顺序不保证。
- 最高优先级的顾问(最小顺序值)被加入栈顶。
- 它在请求处理时最先执行(栈展开)。
- 它在响应处理时最后执行(栈回收)。
提醒一下,以下是 Spring Ordered 接口的语义:
public interface Ordered {
/**
* Constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* Constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* Get the order value of this object.
* <p>Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* <p>Same order values will result in arbitrary sort positions for the
* affected objects.
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
*/
int getOrder();
}
- 为输入和输出分别使用不同的顾问。
- 设置不同的顺序值。
- 使用顾问上下文(advisor context)在它们之间共享状态。
API 概览
主要的顾问(Advisor)接口位于包 org.springframework.ai.chat.client.advisor.api 中。以下是创建自定义顾问时会用到的关键接口:
public interface Advisor extends Ordered {
String getName();
}
用于同步和响应式(Reactive)顾问的两个子接口分别是:
同步顾问接口:
public interface CallAdvisor extends Advisor {
ChatClientResponse adviseCall(
ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain);
}
响应式顾问接口:
public interface StreamAdvisor extends Advisor {
Flux<ChatClientResponse> adviseStream(
ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain);
}
在自定义顾问的实现中,如果需要继续调用顾问链,请使用 CallAdvisorChain 和 StreamAdvisorChain:
CallAdvisorChain 接口:
public interface CallAdvisorChain extends AdvisorChain {
/**
* 调用链中下一个 CallAdvisor 并传入请求。
*/
ChatClientResponse nextCall(ChatClientRequest chatClientRequest);
/**
* 返回在链创建时包含的所有 CallAdvisor 实例列表。
*/
List<CallAdvisor> getCallAdvisors();
}
StreamAdvisorChain 接口:
public interface StreamAdvisorChain extends AdvisorChain {
/**
* 调用链中下一个 StreamAdvisor 并传入请求。
*/
Flux<ChatClientResponse> nextStream(ChatClientRequest chatClientRequest);
/**
* 返回在链创建时包含的所有 StreamAdvisor 实例列表。
*/
List<StreamAdvisor> getStreamAdvisors();
}
实现顾问
要创建一个顾问(Advisor),你需要实现 CallAdvisor 或 StreamAdvisor(或两者皆实现)。需要重点实现的方法是:
- 对于 非流式 顾问:实现方法调用
nextCall() - 对于 流式 顾问:实现方法调用
nextStream()
示例
我们将通过几个实际示例来展示如何实现用于 “观测与增强” 场景的 Advisor。
日志记录(Logging)Advisor
下面示例实现了一个简单的日志 Advisor,它会在调用下一个 Advisor 之前记录 ChatClientRequest,并在返回后记录 ChatClientResponse。
注意:该 Advisor 仅用于 观察 请求和响应,不会对其进行修改。
此实现同时支持 非流式 和 流式 场景。
public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor {
private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);
@Override
public String getName() { // 1
return this.getClass().getSimpleName();
}
@Override
public int getOrder() { // 2
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);
return new ChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses, this::logResponse); // 3
}
private void logRequest(ChatClientRequest request) {
logger.debug("request: {}", request);
}
private void logResponse(ChatClientResponse chatClientResponse) {
logger.debug("response: {}", chatClientResponse);
}
}
要点说明:
getName():提供顾问的唯一名称。getOrder():通过设置顺序值控制执行顺序。值越小执行越早。ChatClientMessageAggregator是一个工具类,用于将流式响应聚合为单个ChatClientResponse,主要用于日志记录或只读取不修改响应的场景(注意:响应在聚合器中是只读的,不允许修改)。
Re-Reading(Re2)Advisor
论文 《Re-Reading Improves Reasoning in Large Language Models》 提出一种技术 —— Re-Reading(Re2),可以提升大模型的推理能力。Re2 技术需要将输入 Prompt 增强为如下格式:
{Input_Query}
Read the question again: {Input_Query}
实现一个应用 Re2 技术的 Advisor 示例:
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) { // 1
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() { // 2
return this.order;
}
public ReReadingAdvisor withOrder(int order) {
this.order = order;
return this;
}
}
要点说明:
before()方法增强用户输入,应用 Re2 Prompt 技术;- 可通过
order设置优先级,值越小执行越早;
Spring AI 内置 Advisors
Spring AI 框架提供多种内置 Advisor,用来增强 AI 的交互体验。
Chat Memory Advisors(聊天记忆顾问)
用于管理对话历史:
- MessageChatMemoryAdvisor:从 Chat Memory 中读取历史,并以消息集合的形式加入 Prompt,此方法能够保持会话历史的结构。注意,并非所有 AI 模型都支持这种方式。
- PromptChatMemoryAdvisor:将历史内容写入 Prompt 的 system 文本中
- VectorStoreChatMemoryAdvisor:从 VectorStore 中检索历史,并写入 Prompt 的 system 文本。该助手对于高效搜索和检索大型数据集中相关的信息非常有用。
问答(RAG)类 Advisors
- QuestionAnswerAdvisor:该助手使用向量存储来提供问答能力,实现了朴素的 RAG(检索增强生成)模式。
- RetrievalAugmentationAdvisor:该助手使用
org.springframework.ai.rag包中定义的构建模块,并遵循模块化 RAG 架构,来实现常见的检索增强生成(RAG)流程。
推理增强 Advisor
- ReReadingAdvisor
- 基于 Re2 技术,在输入阶段增强 LLM 推理能力
- 来源论文:Re-Reading Improves Reasoning in LLMs.
内容安全 Advisor
- SafeGuardAdvisor
- 用于阻止模型生成有害或不当内容的简单拦截顾问
流式与非流式

- 非流式(Non-streaming)顾问处理完整的请求与响应。
- 流式(Streaming)顾问则以连续数据流的方式处理请求与响应,使用响应式编程概念(例如用 Flux 处理响应)。
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain chain) {
return Mono.just(chatClientRequest)
.publishOn(Schedulers.boundedElastic())
.map(request -> {
// 此处的逻辑可由阻塞线程或非阻塞线程执行。
// 在进入下一个链路前执行的顾问逻辑(before next)
})
.flatMapMany(request -> chain.nextStream(request))
.map(response -> {
// 在下一个链路执行完成后处理的顾问逻辑(after next)
});
}
最佳实践
- 将顾问(Advisor)聚焦于明确的单一职责,以获得更好的模块化效果。
- 在需要时使用
adviseContext在多个顾问之间共享状态。 - 同时实现顾问的流式与非流式版本,以获得最大的灵活性。
- 仔细规划顾问在链中的执行顺序,以确保数据流转正确无误。
重大 API 变更
顾问接口
- 在 1.0 M2 中,分别存在
RequestAdvisor和ResponseAdvisor接口。RequestAdvisor会在调用ChatModel.call和ChatModel.stream方法之前执行。ResponseAdvisor则在这些方法调用之后执行。
- 在 1.0 M3 中,这些接口被替换为:
CallAroundAdvisorStreamAroundAdvisor
- 此前属于
ResponseAdvisor的StreamResponseMode已被移除。 - 在 1.0.0 版本中,这些接口再次进行了调整:
CallAroundAdvisor→CallAdvisor,StreamAroundAdvisor→StreamAdvisor,CallAroundAdvisorChain→CallAdvisorChain和StreamAroundAdvisorChain→StreamAdvisorChainAdvisedRequest→ChatClientRequest和AdvisedResponse→ChatClientResponse
上下文 Map 处理
- 在 1.0 M2 中:
- Context Map 是一个独立的方法参数。
- 该 Map 是可变的,并在链路中传递。
- 在 1.0 M3 中:
- Context Map 现在被合并进了
AdvisedRequest和AdvisedResponse记录中。 - 该 Map 变为不可变。
- 若需更新 Context,请使用
updateContext方法,它会创建一个包含更新内容的全新不可修改 Map。
- Context Map 现在被合并进了