Spring AI Advisors API 提供了一种灵活而强大的方式,用于在 Spring 应用程序中拦截、修改和增强 AI 驱动的交互 。通过利用 Advisors API,开发者可以创建更复杂、可复用且可维护的 AI 组件。 其核心优势包括:
- 封装重复的生成式 AI 模式 (如对话历史管理、敏感词过滤)
- 转换与大语言模型(LLM)交互的数据 (如动态附加上下文或格式化输入/输出)
- 实现跨模型和用例的可移植性 (通过统一接口适配不同 AI 服务)
核心组件
API 包含以下组件:
- 非流式场景 :CallAroundAdvisor 和 CallAroundAdvisorChain
- 流式场景 :StreamAroundAdvisor 和 StreamAroundAdvisorChain
- AdvisedRequest :表示未密封的提示(Prompt)请求,包含可修改的输入数据
- AdvisedResponse :封装聊天补全(Chat Completion)响应,包含生成结果和元数据
两者均通过 advise-context 在Advisor链中共享状态。
Advisors API 类
1、关键方法 nextAroundCall()(非流式)和 nextAroundStream()(流式)负责执行以下操作:
- 检查或修改未密封的提示数据
- 自定义或增强提示内容
- 调用顾问链中的下一个节点
- 可选择阻断请求(需自行填充响应)
- 处理异常并抛出错误
2、控制方法
- getOrder() :决定顾问链中的执行顺序(数值越小优先级越高)
- getName() :提供唯一标识符以区分不同顾问
顾问链(Advisor Chain)
由 Spring AI 框架创建,按 getOrder() 值升序执行。最后一个内置顾问负责将请求发送至大语言模型(LLM)。
1、请求阶段
- 框架将用户提示(Prompt)转换为 AdvisedRequest,并初始化空AdvisorContext。
- 每个顾问依次处理请求,可修改数据或阻断流程(需自行生成响应)。
- 最终顾问将请求发送至 LLM。
2、响应阶段
- LLM 的响应通过 AdvisedResponse 封装,携带 AdvisorContext。
- 顾问链反向处理响应,允许进一步修改或记录日志。
- 最终结果提取为 ChatCompletion 返回客户端。
核心机制
- 链式调用 :顾问按顺序执行,前一个顾问的输出作为下一个的输入。
- 上下文共享 :通过 AdvisorContext 在链中传递状态(如对话ID、用户偏好)。
- 流式支持 :StreamAroundAdvisor 处理响应式数据流(如逐字生成)。
顾问顺序(Advisor Order)
顾问链中的执行顺序由 getOrder() 方法决定,关键要点如下:
1、低值优先 getOrder() 返回值较低的顾问优先执行。例如:
- Ordered.HIGHEST_PRECEDENCE(值为 Integer.MIN_VALUE)表示最高优先级。
- Ordered.LOWEST_PRECEDENCE(值为 Integer.MAX_VALUE)表示最低优先级。
2、栈式链特性 顾问链的行为类似栈(后进先出):
- 请求阶段 :高优先级顾问(低值)先处理请求。
- 响应阶段 :高优先级顾问最后处理响应(类似栈的展开顺序)。
3、顺序控制策略
- 需同时在请求/响应阶段优先的场景 : 使用两个独立顾问,分别设置不同顺序值,并通过 AdvisorContext 共享状态。
- 示例 :
// 请求处理顾问(高优先级)
public class RequestAdvisor implements CallAroundAdvisor {
@Override
public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } // 优先处理请求
// ...
}
// 响应处理顾问(低优先级)
public class ResponseAdvisor implements CallAroundAdvisor {
@Override
public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } // 最后处理响应
// ...
}
4、相同顺序值的处理 若多个顾问的 getOrder() 值相同,执行顺序不保证确定性。 Spring Ordered 接口语义
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; // 最高优先级
int LOWEST_PRECEDENCE = Integer.MAX_VALUE; // 最低优先级
int getOrder(); // 值越低优先级越高,值越高优先级越低
}
优先级规则 :值越低的顾问越早处理请求,但越晚处理响应(栈特性)。
5、核心机制解释
- 请求处理顺序 :顾问链按 getOrder() 升序执行(低值→高值)。
- 响应处理顺序 :顾问链按 getOrder() 降序执行(高值→低值)。
- 矛盾表象 :栈式结构导致高优先级顾问在请求阶段先执行,在响应阶段最后执行。
API 概览
主要的顾问(Advisor)接口位于包 org.springframework.ai.chat.client.advisor.api 中。以下是创建自定义顾问时需了解的核心接口:
1、基础接口 Advisor
public interface Advisor extends Ordered {
String getName(); // 获取顾问的唯一名称
}
继承自 Ordered 接口,通过 getOrder() 控制执行顺序。
2、同步顾问接口 CallAroundAdvisor 用于拦截和增强同步请求(非流式):
public interface CallAroundAdvisor extends Advisor {
/**
* 包裹 {@link ChatModel#call(Prompt)} 方法的环绕通知。
* @param advisedRequest 封装的请求对象
* @param chain 顾问链
* @return 增强后的响应
*/
AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);
}
通过 CallAroundAdvisorChain 继续执行顾问链:
public interface CallAroundAdvisorChain {
AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest); // 调用链中下一个顾问
}
3、响应式顾问接口 StreamAroundAdvisor 用于拦截和增强流式请求(如异步响应):
public interface StreamAroundAdvisor extends Advisor {
/**
* 包裹流式请求的环绕通知。
* @param advisedRequest 封装的请求对象
* @param chain 顾问链
* @return 响应式数据流
*/
Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);
}
通过 StreamAroundAdvisorChain 继续执行流式顾问链:
public interface StreamAroundAdvisorChain {
Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest); // 调用链中下一个顾问
}
4、核心机制
- 责任链模式 :顾问按 getOrder() 值顺序执行,前一个顾问的输出作为下一个的输入。
- 同步与流式分离 :通过 CallAroundAdvisor 和 StreamAroundAdvisor 分别处理不同编程模型。
- 上下文传递 :AdvisedRequest 和 AdvisedResponse 携带共享状态(如对话ID、用户参数)。
实现顾问(Implementing an Advisor)
要创建顾问,需实现 CallAroundAdvisor(同步场景)或 StreamAroundAdvisor(流式场景),或两者同时实现。核心方法为:
- 同步场景 :实现 nextAroundCall()
- 流式场景 :实现 nextAroundStream()
示例
以下通过具体案例说明如何实现用于观测(如日志)和增强(如数据修改)的顾问。
日志记录顾问(Logging Advisor)
实现一个简单的日志顾问,在调用下一个顾问前后记录 AdvisedRequest 和 AdvisedResponse。此顾问仅观测数据,不修改内容,同时支持同步和流式场景
public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);
@Override
public String getName() {
return this.getClass().getSimpleName(); // 提供唯一名称 [[1]][[10]]
}
@Override
public int getOrder() {
return 0; // 控制执行顺序,值越低优先级越高 [[1]][[10]]
}
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
logger.debug("调用前 - 请求: {}", advisedRequest); // 记录请求
AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest); // 继续链式调用
logger.debug("调用后 - 响应: {}", advisedResponse); // 记录响应
return advisedResponse;
}
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
logger.debug("流式调用前 - 请求: {}", advisedRequest);
Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest); // 继续流式链
return new MessageAggregator().aggregateAdvisedResponse(advisedResponses,
advisedResponse -> logger.debug("流式调用后 - 响应: {}", advisedResponse));
}
}
1、名称与顺序
- getName():返回顾问的唯一标识(如类名)。
- getOrder():值越低越早执行(同步阶段优先,流式阶段最后处理响应)。
2、流式聚合工具 MessageAggregator 是一个实用工具类,用于将流式响应(Flux)聚合为单个 AdvisedResponse。
- 适用于需要观测完整响应的场景(如日志记录)。
- 不可修改响应 :聚合操作是只读的,仅用于观测。
重新阅读(Re2)顾问
根据《Re-Reading Improves Reasoning in Large Language Models》一文介绍,Re-Reading(Re2)技术 通过增强输入提示(Prompt)提升大语言模型的推理能力。Re2的核心思想是让模型在回答前重新阅读问题 ,从而减少因遗漏细节导致的错误。具体提示格式如下:
{Input_Query}
Read the question again: {Input_Query}
实现Re2顾问 以下顾问类通过修改用户输入,将Re2技术应用于请求流程:
public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
private AdvisedRequest before(AdvisedRequest advisedRequest) {
// 复制用户参数并添加Re2输入占位符
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}
""") // 应用Re2模板 [[7]][[9]]
.userParams(advisedUserParams)
.build();
}
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
return chain.nextAroundCall(this.before(advisedRequest)); // 同步场景调用链 [[1]][[3]]
}
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
return chain.nextAroundStream(this.before(advisedRequest)); // 流式场景调用链 [[3]][[10]]
}
@Override
public int getOrder() {
return 0; // 控制执行顺序,值越低优先级越高 [[1]][[10]]
}
@Override
public String getName() {
return this.getClass().getSimpleName(); // 顾问唯一名称 [[1]][[10]]
}
}
核心方法说明: 1、before() 方法
- 增强用户输入,插入Re2提示模板,引导模型重新阅读问题。
- 通过 userParams 传递占位符参数(如 {re2_input_query})。
2、aroundCall() 和 aroundStream()
- 同步场景 :拦截请求并应用Re2增强,然后继续调用后续顾问链。
- 流式场景 :对响应式流执行相同增强逻辑。
3、顺序控制
- 通过 getOrder() 设置执行优先级(值越低越早执行)。
Spring AI 内置顾问
Spring AI 框架提供了多种内置顾问以增强 AI 交互能力。以下是可用顾问的概述:
聊天记忆顾问(Chat Memory Advisors)
此类顾问通过聊天记忆存储管理对话历史:
MessageChatMemoryAdvisor 从记忆存储中检索历史记录,并作为消息集合附加到提示中。此方法保留对话历史的结构,但并非所有AI模型均支持此方式 。
PromptChatMemoryAdvisor 将记忆检索后合并到提示的系统文本(System Text)中,适用于需要全局上下文的场景。
VectorStoreChatMemoryAdvisor 从向量存储(Vector Store)中检索记忆并合并到系统文本中,适合从大规模数据集中高效搜索相关信息,支持检索增强生成(RAG)模式 。
问答顾问(Question Answering Advisor)
- QuestionAnswerAdvisor 利用向量存储实现问答能力,通过检索外部数据增强生成结果,是RAG(检索增强生成)模式 的核心组件。
内容安全顾问(Content Safety Advisor)
- SafeGuardAdvisor 用于过滤或阻止模型生成有害或不适当的内容,提供基础的内容安全防护。
核心机制与适用场景
对话历史管理 :MessageChatMemoryAdvisor 和 PromptChatMemoryAdvisor 分别通过消息集合或系统文本传递历史记录,需根据模型兼容性选择。
RAG 支持 :VectorStoreChatMemoryAdvisor 和 QuestionAnswerAdvisor 结合外部数据源,提升生成结果的准确性和相关性。
安全性增强 :SafeGuardAdvisor 可与其他顾问链式组合,确保输出内容符合规范。
流式与非流式顾问
流式与非流式顾问的流程差异
- 非流式顾问 :处理完整的请求与响应对象。
- 流式顾问 :基于响应式编程概念(如使用 Flux 处理响应流)处理连续的数据流。
流式顾问示例
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
return Mono.just(advisedRequest)
.publishOn(Schedulers.boundedElastic()) // 切换调度器以支持阻塞操作[[6]][[9]]
.map(request -> {
// 执行前置逻辑(可在阻塞或非阻塞线程中运行)
return request;
})
.flatMapMany(request -> chain.nextAroundStream(request)) // 继续流式调用链[[3]][[10]]
.map(response -> {
// 执行后置逻辑(如日志记录或响应修改)
return response;
});
}
最佳实践
保持顾问职责单一 每个顾问应专注于特定任务(如日志记录、内容过滤),以提升模块化和可维护性。
使用 adviseContext 共享状态 通过上下文对象在顾问间传递数据(如对话ID、用户参数)。
实现双模式支持 同时提供流式(StreamAroundAdvisor)和非流式(CallAroundAdvisor)实现,以适配不同场景。
谨慎设计顾问顺序 低 getOrder() 值的顾问优先处理请求,但最后处理响应(栈式执行顺序)。
核心机制
- 响应式编程 :流式顾问依赖 Flux 和 Mono 实现异步数据流处理,适合高并发场景。
- 线程管理 :通过 publishOn(Schedulers.boundedElastic()) 切换线程池,避免阻塞操作影响性能。