一、Advisors简介
1.1 Advisors定义
Advisors 是在 AI 应用程序中处理请求和响应的拦截器。我们可以使用它们为提示流程设置额外的功能。例如,可以建立聊天历史、排除敏感词或为每个请求添加额外的上下文。
Spring AI的Advisor,本质上是一个拦截器,专门负责在AI模型的请求和响应流中“搞事情”——动态修改输入、增强输出,甚至中途拦截敏感请求。它就像给AI应用装了个“智能管家”,既能帮你处理重复性工作(比如记录聊天历史),又能让模型回答更精准(比如结合知识库检索)。
1.2 Advisors核心功能
-
拦截请求:在用户问题发送给AI模型前,Advisor可以修改问题或添加上下文(比如加一句“深呼吸,慢慢想”)。
-
增强响应:在模型返回答案后,Advisor可以格式化结果(比如将字符串转为JSON)或添加安全校验。
-
链式处理:多个Advisor像流水线一样串联,每个环节都能对数据动手脚。
适用场景:聊天记忆管理、敏感词过滤、知识库检索(RAG)、日志记录等。
1.3 附加或增强带有上下文数据的提示,让模型回答更精准
在调用带有用户文本的 AI 模型时,一个常见的模式是附加或增强带有上下文数据的提示,这种上下文数据可以是不同类型。常见类型包括:
您自己的数据
这是 AI 模型尚未训练过的数据。即使模型见过类似的数据,附加的上下文数据在生成响应时也会优先考虑。
对话历史
聊天模型的 API 是无状态的。如果您告诉 AI 模型您的名字,它不会在后续交互中记住它。必须随每个请求发送对话历史,以确保在生成响应时考虑先前的交互。
1.4 Advisor 接口
Advisors API提供了一种灵活而强大的方式来拦截、修改和增强 Spring 应用程序中的 AI 驱动交互。该功能的核心组件是
CallAroundAdvisor
接口。我们通过实现该接口来创建 Advisor 链,从而影响我们的请求或响应。我们会将提示(prompt)发送到一个聊天模型,该模型关联了一个 Advisor 链。在发送提示之前,链上的每个 Advisor 都会执行其before
操作。同样,在收到聊天模型的回复之前,每个 Advisor 都会调用自己的after
操作。
ChatClient 流畅 API 提供了 AdvisorSpec
接口用于配置 advisors。这个接口提供了添加参数、一次设置多个参数以及向链中添加一个或多个 advisors 的方法。
interface AdvisorSpec {
AdvisorSpec param(String k, Object v);
AdvisorSpec params(Map<String, Object> p);
AdvisorSpec advisors(Advisor... advisors);
AdvisorSpec advisors(List<Advisor> advisors);
}
advisors 添加到链中的顺序至关重要,因为它决定了它们的执行顺序。每个 advisor 都以某种方式修改提示或上下文,一个 advisor 所做的更改会传递给链中的下一个。
ChatClient.builder(chatModel)
.build()
.prompt()
.advisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
QuestionAnswerAdvisor.builder(vectorStore).build()
)
.user(userText)
.call()
.content();
在此配置中,MessageChatMemoryAdvisor
将首先执行,将对话历史添加到提示中。然后,QuestionAnswerAdvisor
将基于用户的问题和添加的对话历史执行其搜索,可能会提供更相关的结果。
二、增强器 API
Spring AI 增强器 API 提供了一种灵活而强大的方式来拦截、修改和增强 Spring 应用程序中的 AI 驱动交互。 通过利用增强器 API,开发者可以创建更复杂、可重用和可维护的 AI 组件。主要优势包括封装重复出现的生成式 AI 模式、转换发送到大型语言模型(LLM) 的数据,以及提供跨各种模型和用例的可移植性。
可以使用 ChatClient API
配置现有增强器,如下例所示:
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), // chat-memory 增强器
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()
方法注册增强器。
2.1 核心组件
API 包含用于非流式场景的 CallAroundAdvisor
和 CallAroundAdvisorChain
,以及用于流式场景的 StreamAroundAdvisor
和 StreamAroundAdvisorChain
。 它还包括 AdvisedRequest
用于表示未密封的 Prompt 请求,AdvisedResponse
用于 Chat Completion 响应。两者都包含一个 advise-context
用于在增强器链中共享状态。
2.2 Advisors API 类
nextAroundCall()
和 nextAroundStream()
是关键增强器方法,通常执行诸如检查未密封的 Prompt 数据、自定义和增强 Prompt 数据、调用增强器链中的下一个实体、可选地阻止请求、检查聊天完成响应以及抛出异常以指示处理错误等操作。
此外,getOrder()
方法确定增强器在链中的顺序,而 getName()
提供唯一的增强器名称。
由 Spring AI 框架创建的增强器链允许按 getOrder()
值排序的多个增强器顺序调用。 较低的值首先执行。 最后一个增强器(自动添加)将请求发送到 LLM。
以下流程图说明了增强器链与聊天模型之间的交互:
Advisors API 流程
2.3 API 概述
主要增强器接口位于包 org.springframework.ai.chat.client.advisor.api
中。以下是创建自己的增强器时会遇到的关键接口
public interface Advisor extends Ordered {
String getName();
}
同步和响应式增强器的两个子接口是:
public interface CallAroundAdvisor extends Advisor {
/**
* 环绕通知,包装 ChatModel#call(Prompt) 方法。
* @param advisedRequest 建议的请求
* @param chain 增强器链
* @return 响应
*/
AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);
}
public interface StreamAroundAdvisor extends Advisor {
/**
* 环绕通知,包装建议请求的调用。
* @param advisedRequest 建议的请求
* @param chain 要执行的增强器链
* @return 建议请求的结果
*/
Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);
}
要继续建议链,在您的建议实现中使用 CallAroundAdvisorChain
和 StreamAroundAdvisorChain
:
接口是:
public interface CallAroundAdvisorChain {
AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest);
}
public interface StreamAroundAdvisorChain {
Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest);
}
2.4 实现增强器
要创建增强器,实现
CallAroundAdvisor
或StreamAroundAdvisor
(或两者)。要实现的关键方法是用于非流式的nextAroundCall()
或用于流式增强器的nextAroundStream()
。
下面将提供一些实践示例来说明如何实现用于观察和增强用例的增强器。
(1)日志增强器
我们可以实现一个简单的日志增强器,在调用链中的下一个增强器之前记录 AdvisedRequest
,之后记录 AdvisedResponse
。 注意,增强器只观察请求和响应,不修改它们。 此实现同时支持非流式和流式场景。
public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
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 AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
logger.debug("BEFORE: {}", advisedRequest);
AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);
logger.debug("AFTER: {}", advisedResponse);
return advisedResponse;
}
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
logger.debug("BEFORE: {}", advisedRequest);
Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);
return new MessageAggregator().aggregateAdvisedResponse(advisedResponses,
advisedResponse -> logger.debug("AFTER: {}", advisedResponse)); // (3)
}
}
-
为增强器提供唯一名称。
-
您可以通过设置顺序值来控制执行顺序。较低的值首先执行。
-
MessageAggregator
是一个实用类,将 Flux 响应聚合为单个 AdvisedResponse。这对于记录或观察整个响应而不是流中的单个项目的其他处理很有用。注意,您不能在MessageAggregator
中修改响应,因为它是只读操作。
(2)重读 (Re2) 增强器
“Re-Reading Improves Reasoning in Large Language Models” 文章介绍了一种称为重读 (Re2) 的技术,可以提高大型语言模型的推理能力。 Re2 技术要求像这样增强输入提示:
{Input_Query}
Read the question again: {Input_Query}
实现一个将 Re2 技术应用于用户输入查询的增强器可以这样做:
public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
private AdvisedRequest before(AdvisedRequest advisedRequest) { // (1)
Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());
advisedUserParams.put("re2_input_query", advisedRequest.userText());
return AdvisedRequest.from(advisedRequest)
.userText("""
{re2_input_query}
Read the question again: {re2_input_query}
""")
.userParams(advisedUserParams)
.build();
}
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { // (2)
return chain.nextAroundCall(this.before(advisedRequest));
}
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) { // (3)
return chain.nextAroundStream(this.before(advisedRequest));
}
@Override
public int getOrder() { // (4)
return 0;
}
@Override
public String getName() { // (5)
return this.getClass().getSimpleName();
}
}
-
before
方法通过应用重读技术增强用户的输入查询。 -
aroundCall
方法拦截非流式请求并应用重读技术。 -
aroundStream
方法拦截流式请求并应用重读技术。 -
您可以通过设置顺序值来控制执行顺序。较低的值首先执行。
-
为增强器提供唯一名称。
三、Spring AI 内置增强器
Spring AI 框架提供了几个内置增强器来增强您的 AI 交互。以下是可用增强器的概述:
3.1 聊天内存增强器
这些增强器在聊天内存存储中管理对话历史:
MessageChatMemoryAdvisor
检索内存并将其作为消息集合添加到提示中。这种方法保持了对话历史的结构。注意,并非所有 AI 模型都支持这种方法。
PromptChatMemoryAdvisor
检索内存并将其合并到提示的系统文本中。
VectorStoreChatMemoryAdvisor
从 VectorStore 检索内存并将其添加到提示的系统文本中。此增强器对于从大型数据集中高效搜索和检索相关信息很有用。
3.2 QuestionAnswerAdvisor 问题回答增强器
此增强器使用向量存储来提供问答功能,实现 RAG(检索增强生成)模式。
3.3 SafeGuardAdvisor 内容安全增强器
一个简单的增强器,旨在防止模型生成有害或不适当的内容。
-
MessageChatMemoryAdvisor
-
功能:自动记录聊天历史,让AI记住“你刚才说了啥”。
-
坑点:不是所有模型都支持上下文记忆,用前先查文档。
2.QuestionAnswerAdvisor
-
功能:执行RAG(检索增强生成),从知识库中捞答案,让AI不再“一本正经地胡说八道”。
-
原理:用户提问时,先检索相似文本,拼接到提示词中。
3.SafeGuardAdvisor
-
功能:敏感词拦截器,遇到“颜色内容”直接打断施法,保护应用合规性
4.VectorStoreChatMemoryAdvisor
-
功能:将聊天记录存入向量数据库,实现长期记忆,但需小心
chat_memory_conversation_id
管理不当引发“数据海啸”。
四、流式与非流式
增强器流式与非流式流程
-
非流式增强器处理完整的请求和响应。
-
流式增强器将请求和响应作为连续流处理,使用响应式编程概念(例如,用于响应的 Flux)。
实现带有阻塞和非阻塞代码的流式增强器的示例:
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
return Mono.just(advisedRequest)
.publishOn(Schedulers.boundedElastic())
.map(request -> {
// 这可以由阻塞和非阻塞线程执行。
// 增强器在下一个部分之前
})
.flatMapMany(request -> chain.nextAroundStream(request))
.map(response -> {
// 增强器在下一个部分之后
});
}
注意事项:
专注责任
保持增强器专注于特定任务以获得更好的模块化。
共享状态
必要时使用
adviseContext
在增强器之间共享状态。支持两种模式
实现增强器的流式和非流式版本以获得最大灵活性。
考虑顺序
仔细考虑增强器在链中的顺序以确保正确的数据流。
参考链接:
Spring AI Advisor 指南 - spring 中文网
如何用Spring AI的Advisor,让AI应用像“瑞士军刀”一样灵活?
Spring AI SafeGuardAdvisor-CSDN博客