Advisors API
Spring AI Advisors API 提供了一种灵活而强大的方式来拦截、修改和增强 Spring 应用中的 AI 驱动交互。通过利用 Advisors API,开发者可以创建更复杂、可重用和可维护的 AI 组件。
其主要优势包括封装重复的生成式 AI 模式、转换发送到大型语言模型 (LLM) 和从其接收的数据,以及在各种模型和用例之间提供可移植性。
您可以使用ChatClient API配置现有 Advisor,示例如下
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() 方法注册 Advisor。
Advisors 也参与可观察性堆栈,因此您可以查看与其执行相关的指标和追踪。
核心组件
该 API 包含用于非流式场景的 CallAdvisor 和 CallAdvisorChain,以及用于流式场景的 StreamAdvisor 和 StreamAdvisorChain。它还包括用于表示未封装的 Prompt 请求的 ChatClientRequest,以及用于聊天完成响应的 ChatClientResponse。两者都包含一个 advise-context 以在 Advisor 链中共享状态。
adviseCall() 和 adviseStream() 是关键的 Advisor 方法,通常执行诸如检查未封装的 Prompt 数据、自定义和增强 Prompt 数据、调用 Advisor 链中的下一个实体、可选地阻止请求、检查聊天完成响应以及抛出异常以指示处理错误等操作。
此外,getOrder() 方法确定 Advisor 在链中的顺序,而 getName() 提供唯一的 Advisor 名称。
由 Spring AI 框架创建的 Advisor 链允许按其 getOrder() 值排序的多个 Advisor 顺序调用。值越低,执行越早。自动添加的最后一个 Advisor 将请求发送到 LLM。
以下流程图说明了 Advisor 链和聊天模型之间的交互
-
Spring AI 框架从用户的
Prompt以及一个空的 Advisorcontext对象创建ChatClientRequest。 -
链中的每个 Advisor 处理请求,并可能修改它。或者,它可以选择通过不调用下一个实体来阻止请求。在后一种情况下,Advisor 负责填充响应。
-
由框架提供的最后一个 Advisor 将请求发送到
Chat Model。 -
然后,聊天模型的响应通过 Advisor 链传递回并转换为
ChatClientResponse。稍后包括共享的 Advisorcontext实例。 -
每个 Advisor 都可以处理或修改响应。
-
通过提取
ChatCompletion,最终的ChatClientResponse返回给客户端。
Advisor 顺序
Advisor 在链中的执行顺序由 getOrder() 方法确定。需要理解的关键点是
-
订单值较低的 Advisor 首先执行。
-
Advisor 链作为一个堆栈运行
-
链中的第一个 Advisor 是第一个处理请求的。
-
它也是最后一个处理响应的。
-
-
要控制执行顺序
-
将顺序设置为接近
Ordered.HIGHEST_PRECEDENCE,以确保 Advisor 在链中首先执行(首先处理请求,最后处理响应)。 -
将顺序设置为接近
Ordered.LOWEST_PRECEDENCE,以确保 Advisor 在链中最后执行(最后处理请求,首先处理响应)。
-
-
较高的值表示较低的优先级。
-
如果多个 Advisor 具有相同的顺序值,则其执行顺序不确定。
|
顺序与执行序列之间的看似矛盾是由于 Advisor 链的堆栈式性质
|
提醒一下,以下是 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 链,请在 Advice 实现中使用 CallAdvisorChain 和 StreamAdvisorChain
接口是
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(或两者)。要实现的关键方法是非流式 Advisor 的 nextCall() 或流式 Advisor 的 nextStream()。
示例
我们将提供一些实践示例,以说明如何实现用于观察和增强用例的 Advisor。
日志 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);
}
}
| 1 | 为 Advisor 提供一个唯一的名称。 |
| 2 | 您可以通过设置顺序值来控制执行顺序。值越低,执行越早。 |
| 3 | MessageAggregator 是一个实用程序类,它将 Flux 响应聚合到一个 ChatClientResponse 中。这对于记录或观察整个响应而不是流中单个项目的其他处理可能很有用。请注意,您不能在 MessageAggregator 中更改响应,因为它是一个只读操作。 |
重读 (Re2) Advisor
《重读提高大型语言模型的推理能力》文章介绍了一种名为重读 (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;
}
}
| 1 | before 方法使用重读技术增强用户输入查询。 |
| 2 | 您可以通过设置顺序值来控制执行顺序。值越低,执行越早。 |
Spring AI 内置 Advisor
Spring AI 框架提供了几个内置 Advisor 来增强您的 AI 交互。以下是可用 Advisor 的概述
聊天记忆 Advisor
这些 Advisor 在聊天记忆存储中管理对话历史记录
-
MessageChatMemoryAdvisor检索记忆并将其作为消息集合添加到 Prompt 中。此方法维护对话历史的结构。请注意,并非所有 AI 模型都支持此方法。
-
PromptChatMemoryAdvisor检索记忆并将其合并到 Prompt 的系统文本中。
-
VectorStoreChatMemoryAdvisor从 VectorStore 检索记忆并将其添加到 Prompt 的系统文本中。此 Advisor 对于高效搜索和检索大型数据集中的相关信息非常有用。
问答 Advisor
-
QuestionAnswerAdvisor此 Advisor 使用向量存储提供问答功能,实现 Naive RAG(检索增强生成)模式。
-
RetrievalAugmentationAdvisorAdvisor that implements common Retrieval Augmented Generation (RAG) flows using the building blocks defined in the `org.springframework.ai.rag` package and following the Modular RAG Architecture.
推理 Advisor
-
ReReadingAdvisor实现了 LLM 推理的重读策略,称为 RE2,以增强输入阶段的理解。基于文章:[重读提高 LLM 中的推理能力](arxiv.org/pdf/2309.06275)。
流式与非流式
-
非流式 Advisor 处理完整的请求和响应。
-
流式 Advisor 使用响应式编程概念(例如,用于响应的 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
});
}
API 破坏性更改
Advisor 接口
-
在 1.0 M2 中,有单独的
RequestAdvisor和ResponseAdvisor接口。-
RequestAdvisor在ChatModel.call和ChatModel.stream方法之前被调用。 -
ResponseAdvisor在这些方法之后被调用。
-
-
在 1.0 M3 中,这些接口已被以下接口替换
-
CallAroundAdvisor -
StreamAroundAdvisor
-
-
以前作为
ResponseAdvisor一部分的StreamResponseMode已被删除。 -
在 1.0.0 中,这些接口已被替换
-
CallAroundAdvisor→CallAdvisor,StreamAroundAdvisor→StreamAdvisor,CallAroundAdvisorChain→CallAdvisorChain和StreamAroundAdvisorChain→StreamAdvisorChain。 -
AdvisedRequest→ChatClientRequest和AdivsedResponse→ChatClientResponse。
-