|
此版本仍在开发中,尚未被视为稳定版。如需最新的快照版本,请使用 Spring AI 1.1.3! |
Advisors API
Spring AI顾问API提供了一种灵活且强大的方式来拦截、修改和增强您Spring应用中的AI驱动交互。 通过利用顾问API,开发者可以创建更加复杂、可重用和易于维护的AI组件。
The key benefits include encapsulating recurring Generative AI patterns, transforming data sent to and from Large Language Models (LLMs), and providing portability across various models and use cases.
您可以通过ChatClient API配置现有的顾问,如下例所示:
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()方法注册顾问。
顾问也会参与可观测性栈中,因此您可以查看与它们执行相关的指标和跟踪信息。
核心组件
The API consists of CallAdvisor and CallAdvisorChain for non-streaming scenarios, and StreamAdvisor and StreamAdvisorChain for streaming scenarios.
It also includes ChatClientRequest to represent the unsealed Prompt request, ChatClientResponse for the Chat Completion response. Both hold an advise-context to share state across the advisor chain.
The adviseCall() 和 adviseStream() 是关键咨询方法,通常执行的操作包括检查未密封的提示数据、自定义和增强提示数据、调用顾问链中的下一个实体、可选地阻止请求、检查聊天补全响应以及抛出异常以指示处理错误。
在其中,getOrder()方法确定顾问链中的顺序,而getName()提供一个唯一的顾问名称。
Spring AI框架创建的顾问链允许按其getOrder()值顺序依次调用多个顾问。较低的值会首先执行。
最后一个顾问会被自动添加,并将请求发送给LLM。
以下流程图说明了顾问链与聊天模型之间的交互:
-
The Spring AI框架从用户的
Prompt创建一个ChatClientRequest,并同时生成一个空的顾问context对象。 -
每个链中的顾问都会处理请求,可能会对其进行修改。或者它可以选择阻止请求,而不调用下一个实体的方法。在后一种情况下,顾问有责任填写响应。
-
框架提供的最终顾问将请求发送到
Chat Model。 -
The Chat Model 的响应随后通过顾问链传递,并转换为
ChatClientResponse。之后包括共享顾问context实例。 -
每个顾问都可以处理或修改响应。
-
最终返回给客户端的是
ChatClientResponse,它是从ChatCompletion中提取出来的。
Advisor Order
The execution order of advisors in the chain is determined by the getOrder() method. Key points to understand:
-
订单较低的顾问优先执行。
-
advisor链运行作为堆栈:
-
链中的第一个顾问最先处理请求。
-
这是最后一个处理响应的。
-
-
控制执行顺序:
-
设置顺序接近
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();
}
|
对于需要在输入和输出链中优先处理的应用场景:
|
API概述
The main Advisor interfaces are located in the package org.springframework.ai.chat.client.advisor.api. Here are the key interfaces you’ll encounter when creating your own advisor:
public interface Advisor extends Ordered {
String getName();
}
同步和响应式顾问的两个子接口是
public interface CallAdvisor extends Advisor {
ChatClientResponse adviseCall(
ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain);
}
和
public interface StreamAdvisor extends Advisor {
Flux<ChatClientResponse> adviseStream(
ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain);
}
继续链式建议,请在您的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();
}
实现顾问
要创建一个顾问,实现CallAdvisor或StreamAdvisor(或者两者都实现)。需要实现的关键方法是nextCall()用于非流式顾问,或者nextStream()用于流式顾问。
<h1>示例</h1>
我们将提供几个实践示例,以说明如何实现顾问来观察并增强用例的实现。
日志顾问
我们可以实现一个简单的日志记录顾问,该顾问在调用链中的下一个顾问之前和之后分别记录ChatClientRequest和ChatClientResponse。
请注意,顾问仅观察请求和响应,并不对它们进行修改。
此实现支持非流式和流式两种场景。
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 | 为顾问提供一个唯一的名称。 |
| 2 | 可以设置顺序值来控制执行顺序。较小的值优先执行。 |
| 3 | MessageAggregator 是一个工具类,用于将 Flux 响应聚合为单一的 ChatClientResponse。这在需要记录整个响应或对流中的各个项目进行其他处理时非常有用。
注意,在 MessageAggregator 中你不能修改响应,因为它是一个只读操作。 |
重新阅读(Re2)顾问
《重读提高大规模语言模型的推理能力'》文章介绍了一种称为重读(Re2)的技术,该技术可以提升大型语言模型的推理能力。 Re2 技术要求像这样增强输入提示:
{Input_Query}
Read the question again: {Input_Query}
使用Spring AI框架针对用户输入查询实现应用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) { (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 | The before method 增强用户的输入查询,应用了重读技术。 |
| 2 | 可以设置顺序值来控制执行顺序。较小的值优先执行。 |
Spring AI内置顾问
Spring AI框架提供了几种内置的顾问以增强您的AI交互。以下是一些可用顾问的概述:
Chat Memory Advisors
这些顾问管理聊天历史记录在聊天记忆存储中:
-
MessageChatMemoryAdvisor从内存中检索信息,并将其作为消息集合添加到提示中。此方法保持了对话历史的结构。请注意,并非所有AI模型都支持此方法。
-
PromptChatMemoryAdvisor检索内存并将其融入提示系统的文本中。
-
VectorStoreChatMemoryAdvisor从VectorStore检索记忆,并将其添加到提示的系统文本中。此顾问有助于高效地从大型数据集中搜索和检索相关的信息。
问答顾问
-
QuestionAnswerAdvisor此顾问利用向量存储来提供问答能力,实现了简单的RAG(检索增强生成)模式。
-
RetrievalAugmentationAdvisorAdvisor that implements common 检索增强生成(RAG) flows using the building blocks defined in the `org.springframework.ai.rag` package and following the Modular RAG Architecture.
reasoning advisor
-
ReReadingAdvisor实现了一种名为RE2的重新阅读策略,用于增强LLM推理中的理解能力,在输入阶段提升理解。 根据文章:[重新阅读能改进LLM的推理能力](arxiv.org/pdf/2309.06275)。
流式与非流式
-
非流式顾问处理完整的请求和响应。
-
流式顾问会话处理请求和响应作为连续的流,使用响应式编程概念(例如,对于响应使用 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更改
顾问接口
-
在1.0 M2中,有单独的
RequestAdvisor和ResponseAdvisor接口。-
RequestAdvisor在调用ChatModel.call和ChatModel.stream方法之前被调用了。 -
ResponseAdvisor被调用后这些方法被调用了。
-
-
在1.0 M3 中,这些接口已被替换为:
-
CallAroundAdvisor -
StreamAroundAdvisor
-
-
来自Java开发中Spring AI框架文档网站的英文HTML内容如下: The
StreamResponseMode, previously part ofResponseAdvisor, has been removed. -
在1.0.0版本中,这些接口已被替换:
-
CallAroundAdvisor→CallAdvisor,StreamAroundAdvisor→StreamAdvisor,CallAroundAdvisorChain→CallAdvisorChainandStreamAroundAdvisorChain→StreamAdvisorChain. -
AdvisedRequest→ChatClientRequest和AdivsedResponse→ChatClientResponse。
-
上下文映射处理
-
在1.0 M2:
-
来自Java开发中Spring AI框架文档网站的HTML内容是: <div class="translation"> The context map was a separate method argument. </div> 请确认是否需要将上述文本翻译成中文,保持HTML格式不变。如果有其他具体要求或需要进一步处理,请告知。</div>
-
该地图是可变的,并沿着链传递。
-
-
在1.0 M3:
-
The context map is now part of the
AdvisedRequestandAdvisedResponserecords. -
该地图是不可变的。
-
要更新上下文,请使用
updateContext方法,该方法创建一个包含最新内容的新不可修改映射。
-