|
此版本仍在开发中,尚未被视为稳定版。为了获取最新的快照版本,请使用Spring AI 1.1.3! |
顾问API
Spring AI顾问API提供了一种灵活且强大的方式,用于在Spring应用程序中拦截、修改和增强AI驱动的交互。 通过使用顾问API,开发人员可以创建更复杂、可复用和维护的AI组件。
核心优势包括封装重复的生成式AI模式,将数据转换为从和发送到大型语言模型(LLMs),以及提供在各种模型和使用场景中的可访问性。
您可以使用 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() 方法注册通知器。
通知器(Advisors)也会参与可观测性栈,因此您可以查看与其执行相关的指标和追踪信息。
核心组件
该API包含用于非流式场景的CallAdvisor和CallAdvisorChain,以及用于流式场景的StreamAdvisor和StreamAdvisorChain。
它还包括用于表示未密封的Prompt请求的ChatClientRequest,以及用于聊天补全响应的ChatClientResponse。两者都持有一个advise-context以在顾问链中共享状态。

adviseCall() 和 adviseStream() 是关键的顾问方法,通常执行诸如检查未密封的提示数据、自定义和增强提示数据、调用顾问链中的下一个实体、选择性地阻止请求、检查聊天补全响应以及抛出异常以指示处理错误等操作。
此外,getOrder() 方法确定链中通知器的顺序,而 getName() 提供唯一的通知器名称。
由Spring AI框架创建的Advisor链,允许按照其getOrder()值顺序依次调用多个advisor。
数值越小,优先级越高,越先执行。
最后一个advisor会自动将请求发送给大语言模型(LLM)。
下图展示了通知器链(advisor chain)与聊天模型(Chat Model)之间的交互:

-
Spring AI 框架根据用户的
Prompt创建一个ChatClientRequest,以及一个空的顾问context对象。 -
链中的每个通知器都会处理请求,可能会对其进行修改。或者,它也可以选择不调用下一个实体来阻止该请求。在后一种情况下,该通知器负责填充响应。
-
由框架提供的最终通知器,将请求发送到
Chat Model。 -
聊天模型的响应随后会通过顾问链回传,并被转换为
ChatClientResponse。之后包含共享的顾问context实例。 -
每个顾问都可以处理或修改响应。
-
最终的
ChatClientResponse通过提取ChatCompletion返回给客户端。
Advisor Order
顾问在链中的执行顺序由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();
}
|
对于在输入和输出端都需要位于链首的用例:
|
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();
}
实现顾问
要创建一个顾问,实现CallAdvisor或StreamAdvisor(或两者都实现)。需要实现的关键方法是对于非流式顾问为nextCall(),对于流式顾问为nextStream()。
示例
我们将提供几个动手示例,以说明如何实现用于观察和增强用例的顾问。
日志顾问
我们可以实现一个简单的日志通知器,它会在调用链中的下一个通知器之前记录 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 | 您可以设置 order 值来控制执行顺序。较低的值会优先执行。 |
| 3 | The MessageAggregator is a utility class that aggregates the Flux responses into a single ChatClientResponse.
This can be useful for logging or other processing that observe the entire response rather than individual items in the stream.
Note that you can not alter the response in the MessageAggregator as it is a read-only operation. |
重新阅读(Re2)顾问
“重读提升大型语言模型的推理能力”一文介绍了一种名为“重读”(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) { (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 augments the user’s input query applying the Re-Reading technique. |
| 2 | 您可以设置 order 值来控制执行顺序。较低的值会优先执行。 |
Spring AI 内置顾问
Spring AI 框架提供了多个内置的顾问,以增强您的 AI 交互。以下是可用顾问的概览:
聊天记忆顾问
这些顾问在聊天记忆存储中管理对话历史:
-
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.
推理顾问
-
ReReadingAdvisor实现了一种用于LLM推理的重读策略,称为RE2,以增强输入阶段的理解。 基于文章:[重读提升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
-
-
StreamResponseMode(以前是ResponseAdvisor的一部分)已被移除。 -
在 1.0.0 版本中,这些接口已被替换:
-
CallAroundAdvisor→CallAdvisor,StreamAroundAdvisor→StreamAdvisor,CallAroundAdvisorChain→CallAdvisorChain以及StreamAroundAdvisorChain→StreamAdvisorChain。 -
AdvisedRequest→ChatClientRequest和AdivsedResponse→ChatClientResponse。
-