Spring AI Alibaba 拦截Advisor
Spring AI Alibaba 拦截Advisor
一、概述
1.1 什么是Advisor
Spring AI Alibaba的Advisor(顾问/拦截器)是专门针对AI交互场景设计的增强组件,其核心灵感来源于责任链模式和**面向切面编程(AOP)**思想,为AI应用提供了统一、可扩展的请求/响应拦截与增强能力。
Advisor本质上是AI交互的"中间件",在用户请求发送到大语言模型(LLM)之前和模型响应返回给用户之后,自动执行通用逻辑,让开发者专注于业务核心而非重复的横切关注点。
1.2 核心优势
- 代码复用:将对话记忆、敏感词过滤、日志记录等通用功能封装为可重用组件
- 统一治理:在单一位置实现跨所有AI调用的安全、监控、限流等能力
- 灵活扩展:支持自定义拦截逻辑,轻松集成第三方服务
- 可移植性:编写的Advisor可在不同模型和用例间无缝迁移
- 链式执行:支持多个Advisor按指定顺序协同工作
1.3 适用场景
- 对话历史管理
- 检索增强生成(RAG)
- 敏感内容过滤
- 请求/响应日志记录
- 性能监控与指标采集
- 限流与熔断
- 提示词动态注入
- 工具调用拦截与增强
二、核心原理剖析
2.1 执行流程:洋葱模型
Advisor采用经典的**洋葱模型(Onion Model)**执行方式,请求从外层到内层依次经过每个Advisor的前置处理,到达模型后,响应再从内层到外层依次经过每个Advisor的后置处理。
用户请求
↓
Advisor1 前置处理
↓
Advisor2 前置处理
↓
实际调用大模型
↓
Advisor2 后置处理
↓
Advisor1 后置处理
↓
返回给用户
关键特性:第一个处理请求的Advisor,最后一个处理响应。
2.2 接口体系
Spring AI Alibaba的Advisor接口体系设计清晰,主要分为同步和流式两大分支:
Advisor (顶层接口)
├─ CallAdvisor (同步调用拦截)
│ └─ ChatClientResponse adviseCall(ChatClientRequest, CallAdvisorChain)
├─ StreamAdvisor (流式调用拦截)
│ └─ Flux<ChatClientResponse> adviseStream(ChatClientRequest, StreamAdvisorChain)
└─ BaseAdvisor (抽象基类,提供通用实现)
核心方法说明:
getName():返回Advisor的唯一名称getOrder():返回执行优先级,值越小优先级越高adviseCall()/adviseStream():拦截方法,包含前置和后置处理逻辑chain.nextCall()/chain.nextStream():调用链中的下一个Advisor
2.3 上下文共享:AdvisorContext
AdvisorContext是一个Map<String, Object>对象,用于在整个Advisor链中传递和共享数据。通过它,多个Advisor可以协同工作,例如第一个Advisor计算的值可以被后续的Advisor使用。
使用方式:
// 在前置处理中设置上下文
request.context().put("userId", "12345");
// 在后置处理中获取上下文
String userId = (String) response.context().get("userId");
三、内置Advisor详解
Spring AI Alibaba提供了多种开箱即用的内置Advisor,覆盖了大部分常见的AI应用场景:
3.1 MessageChatMemoryAdvisor
功能:自动管理对话历史,将历史消息注入到每次请求中,实现多轮对话能力。
使用示例:
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory)
.conversationId("default-conversation")
.maxMessages(10) // 保留最近10条消息
.build()
)
.build();
3.2 QuestionAnswerAdvisor
功能:实现检索增强生成(RAG),自动从向量库中检索相关文档并注入到提示词中。
使用示例:
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
QuestionAnswerAdvisor.builder(vectorStore)
.topK(5) // 返回最相关的5个文档
.similarityThreshold(0.7) // 相似度阈值
.build()
)
.build();
3.3 SimpleLoggerAdvisor
功能:记录AI请求和响应的详细日志,便于调试和问题排查。
使用示例:
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
3.4 SafeGuardAdvisor
功能:敏感词过滤,检测请求和响应中的敏感内容并进行拦截或替换。
使用示例:
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
new SafeGuardAdvisor(List.of("敏感词1", "敏感词2"))
)
.build();
四、自定义Advisor开发
4.1 同步调用拦截器(CallAdvisor)
适用于chatClient.prompt().call()这种同步调用场景。
完整示例:自定义日志拦截器
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
import org.springframework.core.Ordered;
@Slf4j
public class CustomLoggerAdvisor implements CallAdvisor {
@Override
public String getName() {
return "CustomLoggerAdvisor";
}
@Override
public int getOrder() {
// 较低优先级,接近模型调用
return Ordered.LOWEST_PRECEDENCE - 100;
}
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
// 前置处理:记录请求信息
long startTime = System.currentTimeMillis();
log.info("=== AI 请求开始 ===");
log.info("用户ID: {}", request.context().get("userId"));
log.info("用户消息: {}", request.prompt().getUserMessage().getText());
// 记录系统提示词
request.prompt().getSystemMessage().ifPresent(
sysMsg -> log.info("系统提示词: {}", sysMsg.getText())
);
// 放行,调用下一个Advisor
ChatClientResponse response = chain.nextCall(request);
// 后置处理:记录响应信息
long endTime = System.currentTimeMillis();
log.info("=== AI 请求结束 ===");
log.info("响应耗时: {}ms", endTime - startTime);
log.info("响应内容: {}", response.getResult().getOutput().getText());
return response;
}
}
4.2 流式调用拦截器(StreamAdvisor)
适用于chatClient.prompt().stream()这种流式响应场景。
完整示例:流式日志拦截器
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisor;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisorChain;
import org.springframework.core.Ordered;
import reactor.core.publisher.Flux;
@Slf4j
public class StreamLoggerAdvisor implements StreamAdvisor {
@Override
public String getName() {
return "StreamLoggerAdvisor";
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 100;
}
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest request, StreamAdvisorChain chain) {
// 前置处理:记录请求信息
long startTime = System.currentTimeMillis();
log.info("=== 流式AI 请求开始 ===");
log.info("用户消息: {}", request.prompt().getUserMessage().getText());
// 放行,调用下一个Advisor
return chain.nextStream(request)
.doOnNext(response -> {
// 处理每个响应片段
if (response.getResult() != null && response.getResult().getOutput() != null) {
log.debug("收到响应片段: {}", response.getResult().getOutput().getText());
}
})
.doOnComplete(() -> {
// 流式响应完成后的处理
long endTime = System.currentTimeMillis();
log.info("=== 流式AI 请求结束 ===");
log.info("总耗时: {}ms", endTime - startTime);
})
.doOnError(error -> {
// 异常处理
log.error("流式AI请求失败", error);
});
}
}
4.3 同时支持同步和流式
为了让自定义Advisor同时支持同步和流式调用,可以同时实现两个接口:
@Slf4j
public class UniversalLoggerAdvisor implements CallAdvisor, StreamAdvisor {
@Override
public String getName() {
return "UniversalLoggerAdvisor";
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 100;
}
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
// 同步处理逻辑
return chain.nextCall(request);
}
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest request, StreamAdvisorChain chain) {
// 流式处理逻辑
return chain.nextStream(request);
}
}
4.4 工具调用拦截器(ToolInterceptor)
Spring AI Alibaba还提供了专门用于拦截工具调用的ToolInterceptor接口,适用于Agent场景。
完整示例:工具调用耗时统计拦截器
import lombok.extern.slf4j.Slf4j;
import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallHandler;
import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallRequest;
import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallResponse;
import com.alibaba.cloud.ai.graph.agent.interceptor.ToolInterceptor;
@Slf4j
public class ToolPerformanceInterceptor extends ToolInterceptor {
@Override
public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {
// 前置处理:记录开始时间
long startTime = System.currentTimeMillis();
log.info("工具调用开始: {}", request.getToolName());
log.info("工具参数: {}", request.getArguments());
try {
// 执行工具调用
ToolCallResponse response = handler.call(request);
// 后置处理:记录耗时和结果
long endTime = System.currentTimeMillis();
log.info("工具调用成功: {}", request.getToolName());
log.info("耗时: {}ms", endTime - startTime);
log.info("返回结果: {}", response.getResult());
return response;
} catch (Exception e) {
// 异常处理
long endTime = System.currentTimeMillis();
log.error("工具调用失败: {}, 耗时: {}ms",
request.getToolName(), endTime - startTime, e);
throw e;
}
}
}
五、最佳实践与常见误区
5.1 最佳实践
-
合理设置执行顺序
- 安全相关的Advisor(如敏感词过滤、限流)应设置最高优先级
- 日志记录Advisor应设置较低优先级,确保能记录完整的请求和响应
- 业务相关的Advisor(如动态提示词)应在安全检查之后执行
-
同时支持同步和流式
- 自定义Advisor尽量同时实现
CallAdvisor和StreamAdvisor接口 - 确保两种调用方式下的行为一致
- 自定义Advisor尽量同时实现
-
使用上下文传递数据
- 避免使用ThreadLocal传递数据,使用
AdvisorContext - 上下文数据应明确命名,避免冲突
- 避免使用ThreadLocal传递数据,使用
-
异常处理
- 在Advisor中捕获并处理异常,避免影响整个调用链
- 对于不可恢复的异常,返回友好的错误响应
-
性能优化
- 避免在Advisor中执行耗时操作
- 对于需要外部调用的逻辑,考虑使用异步处理
5.2 常见误区
-
忘记调用chain.next()
- 这是最常见的错误,会导致请求无法到达模型
- 确保在前置处理完成后调用
chain.nextCall()或chain.nextStream()
-
在after()中抛出异常
- 后置处理中抛出异常会导致响应丢失
- 建议只在后置处理中记录日志和采集指标
-
流式调用使用CallAdvisor
- 流式调用必须使用
StreamAdvisor - 否则会导致响应无法正常返回
- 流式调用必须使用
-
硬编码配置
- 避免在Advisor中硬编码敏感词、限流阈值等配置
- 使用配置文件或外部服务进行管理
-
过度使用Advisor
- 不要将业务逻辑放在Advisor中
- Advisor只应处理横切关注点
六、总结
Spring AI Alibaba的Advisor机制为AI应用开发提供了强大而灵活的增强能力。通过理解其洋葱模型执行原理和接口体系,开发者可以轻松实现各种通用功能的封装和复用。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)