Advisors API
原文:https://docs.spring.io/spring-ai/reference/api/advisors.html
Spring AI Advisors API 提供了一种灵活而强大的方法来拦截、修改和增强 Spring 应用程序中的 AI 驱动交互。通过利用 Advisors API,开发人员可以创建更复杂、可重用且更易于维护的 AI 组件。
主要优势包括封装重复的生成式 AI 模式、转换发送到和来自大型语言模型 (LLM) 的数据,以及提供跨各种模型和用例的可移植性。
您可以使用 ChatClient API 配置现有的 Advisors ,如以下示例所示:
ChatMemory chatMemory = ... // Initialize your chat memory store
VectorStore vectorStore = ... // Initialize your vector store
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), // chat-memory advisor
QuestionAnswerAdvisor.builder(vectorStore).build() // RAG advisor
)
.build();
var conversationId = "678";
String response = this.chatClient.prompt()
// Set advisor parameters at runtime
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId))
.user(userText)
.call()
.content();
建议在构建时使用构建器的 defaultAdvisors()
方法注册通知(Advice)。
核心组件
该 API 由 CallAdvisor
和 CallAdvisorChain
(适用于非流式场景),以及 StreamAdvisor
和StreamAdvisorChain
( 适用于流式场景)组成。它还包括 ChatClientRequest,用于表示未密封的 Prompt 请求,以及 ,ChatClientResponse 用于表示聊天完成响应。两者都持有一个 ,advise-context 用于在顾问链中共享状态。
adviseCall()
和 adviseStream()
是通知(Advice)的 关键方法,通常执行诸如检查未密封的提示数据、自定义和扩充提示数据、调用顾问链中的下一个实体、可选地阻止请求、检查聊天完成响应以及抛出异常以指示处理错误等操作。
此外,getOrder()
方法确定链中顾问(Advisor)的顺序,同时 getName()
提供唯一的顾问(Advisor)名称。
Spring AI 框架创建的Advisor Chain
允许按 getOrder()
值的顺序调用多个 Advisor 。值较低的 Advisor 优先执行。最后一个 Advisor 自动添加,并将请求发送到 LLM。
以下流程图说明了顾问(Advisor)链和聊天模型之间的交互:
- Spring AI 框架创建一个ChatClientRequest来自用户的Prompt空顾问context对象。
- 链中的每个顾问都会处理请求,并可能对其进行修改。或者,它可以选择阻止请求,不调用下一个实体。在后一种情况下,顾问(Advisor)负责填写响应。
- 框架提供的最终顾问将请求发送给Chat Model。
- 然后,聊天模型的响应通过顾问链传回并转换为ChatClientResponse。稍后包含共享顾问context实例。
- 每个顾问都可以处理或修改回复。
- 最后ChatClientResponse通过提取返回给客户端ChatCompletion。
Advisor 顺序
链中 顾问(Advisor) 的执行顺序由 getOrder() 方法决定。需要理解的关键点:
- 具有较低顺序值的 Advisor 将首先执行。
- Advisor Chain 以堆栈的形式运行:
- 链中的第一位 Advisor 是第一个处理请求的。
- 它也是最后处理响应的。
- 控制执行顺序:
- 将顺序设置Ordered.HIGHEST_PRECEDENCE为确保Advisor在链中首先执行(第一个用于请求处理,最后一个用于响应处理)。
- 将顺序设置Ordered.LOWEST_PRECEDENCE为确保Advisor在链中最后执行(请求处理最后执行,响应处理首先执行)。
- 值越高,优先级越低。
- 如果多个Advisor具有相同的Order值,则无法保证它们的执行顺序。
提醒一下,以下是 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();
}
API 概述
主要的 Advisor 接口位于org.springframework.ai.chat.client.advisor.api
包中。以下是创建自己的 Advisor 时会遇到的关键接口:
public interface Advisor extends Ordered {
String getName();
}
同步和响应式Advisor的两个子接口是
public interface CallAdvisor extends Advisor {
ChatClientResponse adviseCall(
ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain);
}
和
public interface StreamAdvisor extends Advisor {
Flux<ChatClientResponse> adviseStream(
ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain);
}
继续看 Advice Chain,在你的实现中使用 CallAdvisorChain
在 StreamAdvisorChainAdvice
:
接口是
public interface CallAdvisorChain extends AdvisorChain {
/**
* Invokes the next {@link CallAdvisor} in the {@link CallAdvisorChain} with the given
* request.
*/
ChatClientResponse nextCall(ChatClientRequest chatClientRequest);
/**
* Returns the list of all the {@link CallAdvisor} instances included in this chain at
* the time of its creation.
*/
List<CallAdvisor> getCallAdvisors();
}
和
public interface StreamAdvisorChain extends AdvisorChain {
/**
* Invokes the next {@link StreamAdvisor} in the {@link StreamAdvisorChain} with the
* given request.
*/
Flux<ChatClientResponse> nextStream(ChatClientRequest chatClientRequest);
/**
* Returns the list of all the {@link StreamAdvisor} instances included in this chain
* at the time of its creation.
*/
List<StreamAdvisor> getStreamAdvisors();
}
实现一个顾问(Advisor)
要创建顾问(Advisor)程序,请实现 CallAdvisor、StreamAdvisor(其中之一或两者)。关键的实现方法取决于nextCall() 非流式顾问程序或 nextStream() 流式顾问程序。
示例
我们将提供一些实际的例子来说明如何实现顾问来观察和扩展用例。
日志顾问(Advisor)
我们可以实现一个简单的日志顾问,用于记录调用链中下一个顾问的 ChatClientRequest 前后情况ChatClientResponse。请注意,该顾问仅观察请求和响应,而不会对其进行修改。此实现支持非流式和流式场景。
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);
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);
}
}
- 为顾问提供一个唯一的名称。
- 您可以通过设置 order 值来控制执行顺序。值较低的将优先执行。
- 是MessageAggregator一个实用程序类,它将 Flux 响应聚合成单个 ChatClientResponse。这对于日志记录或其他需要观察整个响应(而非流中的单个项目)的处理非常有用。请注意,MessageAggregator由于这是只读操作,因此您无法更改其中的响应。
重读(Re2)顾问(Advisor)
“重读提升大型语言模型的推理能力”一文介绍了一种名为重读(Re2)的技术,它可以提升大型语言模型的推理能力。Re2 技术需要像这样扩充输入提示:
{Input_Query}
Read the question again: {Input_Query}
实现将 Re2 技术应用于用户输入查询的顾问可以如下完成:
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;
}
}
Spring AI 内置顾问(Advisor)
Spring AI 框架提供了多个内置顾问(Advisor),以增强您的 AI 交互。以下是可用顾问(Advisor)的概述:
问答顾问(Advisor)
这些顾问在聊天记忆库中管理对话历史记录:
MessageChatMemoryAdvisor
检索记忆并将其作为消息集合添加到提示中。此方法可以维护对话历史记录的结构。请注意,并非所有 AI 模型都支持此方法。PromptChatMemoryAdvisor
检索内存并将其合并到提示的系统文本中。VectorStoreChatMemoryAdvisor
从 VectorStore 检索内存并将其添加到提示的系统文本中。此建议器有助于高效地从大型数据集中搜索和检索相关信息。
问答顾问(Advisor)
QuestionAnswerAdvisor
该顾问使用向量存储来提供问答功能,实现了 Naive RAG(检索增强生成)模式。RetrievalAugmentationAdvisor
顾问使用org.springframework.ai.rag
包中定义的构建块并遵循模块化 RAG 架构来实现常见的检索增强生成 (RAG) 流程。
推理顾问
- ReReadingAdvisor
实施一种用于大语言模型 (LLM) 推理的重读策略 (RE2),以增强输入阶段的理解。基于文章:重读提升法大语言模型 (LLM) 的推理能力。
内容安全顾问
- SafeGuardAdvisor
一个简单的顾问,旨在防止模型生成有害或不适当的内容。
流式传输 vs. 非流式传输
- 非流式顾问处理完整的请求和响应。
- 流顾问使用反应式编程概念(例如,用于响应的 Flux)将请求和响应作为连续流进行处理。
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain chain) {
return Mono.just(chatClientRequest)
.publishOn(Schedulers.boundedElastic())
.map(request -> {
// This can be executed by blocking and non-blocking Threads.
// Advisor before next section
})
.flatMapMany(request -> chain.nextStream(request))
.map(response -> {
// Advisor after next section
});
}
最佳实践
- 让顾问专注于特定任务,以实现更好的模块化。
- 必要时使用adviseContext在顾问之间共享状态。
- 实施顾问的流式和非流式版本,以实现最大的灵活性。
- 仔细考虑链中顾问的顺序,以确保正确的数据流。
最后编辑:Jeebiz 更新时间:2025-08-08 00:47