Spring AI Advisors 深度解析与应用指南
摘要:本文深入解析了Spring AI Advisors的核心功能与应用场景,介绍了其在AI交互中的关键作用。Advisors通过拦截和增强AI请求/响应流程,实现内容过滤、上下文管理等功能,支持链式处理和自定义扩展。详细阐述了三种聊天记忆实现方式(Message/Prompt/VectorStore)及其适用场景,分析了短期与长期记忆的区别。文章还探讨了会话ID管理策略和安全组件SafeGuar
Spring AI Advisors 深度解析与应用指南
概述
Spring AI Advisors 提供了一种灵活且强大的方式,可以在 Spring 应用中轻松拦截、调整和增强基于AI的交互操作。通过使用Advisors,可以构建更复杂、可重用且易于维护的AI组件,从而提升应用的功能性和简化开发流程,使项目更加高效和整洁。
典型应用场景
- 内容安全过滤:在请求到达大模型前进行敏感词检测
- 上下文管理:自动维护对话历史记录
- 输出格式化:统一模型输出的结构和样式
- 性能优化:缓存常见请求的响应结果
- 日志记录:详细记录AI交互过程用于分析和审计
运行原理
下图展示了,在与大模型交互过程中,Advisors的执行流程:
[用户请求] → [请求包装] → [Advisor链预处理] → [模型调用] → [响应处理] → [最终响应]
关键流程解析
-
请求包装阶段(对应步骤①)
- 框架将用户输入的Prompt封装为AdvisedRequest对象
- 包含原始消息内容
- 包含请求元数据(如时间戳、用户ID等)
- 同时创建空的AdvisorContext上下文容器,用于链式传递处理状态
- 线程安全的共享数据存储
- 支持自定义属性的添加和读取
- 框架将用户输入的Prompt封装为AdvisedRequest对象
-
Advisor链预处理(对应步骤②)
- 多个Advisor按链式顺序处理请求(支持自定义执行顺序)
- 每个Advisor可以:
- 修改请求内容(如添加系统提示词)
request.getPrompt().addSystemMessage("你是一个专业客服,请用中文回答"); - 直接拦截请求并生成响应(实现内容审查/快速响应)
if(containsSensitiveWords(request)) { context.setResponse("您的请求包含敏感内容"); return; // 中断后续处理 } - 添加处理日志或性能监控信息
- 修改请求内容(如添加系统提示词)
-
模型调用阶段(对应步骤③)
- 框架内置的最终Advisor将标准化请求发送至大模型
- 自动处理不同模型API的差异
- 统一错误处理和重试机制
- 触发Chat Model完成核心推理
- 支持同步/异步调用模式
- 可配置超时和重试策略
- 框架内置的最终Advisor将标准化请求发送至大模型
-
响应处理阶段(对应步骤④-⑥)
- 模型输出通过AdvisorContext携带上下文原路返回
- 保留完整的处理链路信息
- 各Advisor可二次处理响应:
- 格式化输出结构(如JSON/XML转换)
- 添加解释性内容(如添加免责声明)
response += "\n\n注:以上内容由AI生成,仅供参考"; - 执行最终安全校验(如二次内容过滤)
- 记录响应日志和性能指标
- 模型输出通过AdvisorContext携带上下文原路返回
聊天记忆实现
通过前面课程的学习,我们已经知道,再与大模型交流时,大模型是无状态的,如果要想有状态,就需要把之前的对话内容一起发给大模型,这样才能变成有状态。
在Spring AI中,对聊天记录已经做了实现,不需要我们手动收集聊天记录再发送了,通过简单的配置即可实现。
记忆实现类型
目前的版本中有三种实现:
-
MessageChatMemoryAdvisor
- 将历史消息与当前用户消息合并,一起发给大模型
- 典型配置示例:
@Bean public ChatMemoryAdvisor chatMemoryAdvisor(ChatMemory chatMemory) { return new MessageChatMemoryAdvisor(chatMemory); } - 适用场景:大多数现代AI模型(如GPT-4)
-
PromptChatMemoryAdvisor
- 将历史消息与系统提示词合并,放到系统提示词中,发给大模型
- 典型配置示例:
@Bean public ChatMemoryAdvisor chatMemoryAdvisor(ChatMemory chatMemory) { return new PromptChatMemoryAdvisor(chatMemory); } - 适用场景:对上下文格式有特殊要求的模型
-
VectorStoreChatMemoryAdvisor
- 将消息存储到向量数据库中,以实现长期记忆功能
- 支持多种向量数据库:
- Pinecone
- Redis
- PostgreSQL
- 典型配置示例:
@Bean public VectorStore vectorStore() { return new PineconeVectorStore(...); } @Bean public ChatMemoryAdvisor chatMemoryAdvisor(VectorStore vectorStore) { return new VectorStoreChatMemoryAdvisor(vectorStore); }
记忆类型对比
📚 长期记忆和短期记忆的区别:
| 特性 | 长期记忆 | 短期记忆 |
|---|---|---|
| 来源 | 训练阶段学习的知识,存储在模型参数中(如事实、规则) | 推理时通过上下文(如对话历史)临时保留的信息 |
| 更新方式 | 通过模型重新训练 | 通过上下文窗口传递 |
| 容量限制 | 模型参数容量 | 上下文窗口长度(如GPT-4最多128k token) |
| 持久性 | 永久保存 | 会话结束后消失 |
| 典型应用 | 事实性知识、语言规则 | 对话上下文、临时指令 |
对于聊天记忆功能,更适合用短期记忆,也就是MessageChatMemoryAdvisor或PromptChatMemoryAdvisor。
实现选择指南
❓ 既然MessageChatMemoryAdvisor或PromptChatMemoryAdvisor这两种都能实现,我们该如何选择呢?
选择原则:
- 优先使用MessageChatMemoryAdvisor
- 更符合大多数模型的预期输入格式
- 保持原始对话结构完整性
- 模型不支持时选择PromptChatMemoryAdvisor
- 某些模型对系统提示词有特殊处理逻辑
- 需要将历史对话转化为"背景知识"的场景
技术验证方法:
// 测试模型是否支持消息合并
try {
model.call(mergedMessages);
return MessageChatMemoryAdvisor.class;
} catch (ModelNotSupportedException e) {
return PromptChatMemoryAdvisor.class;
}
原理深入分析
前面我们使用InMemoryChatMemory进行了会话数据的存储,那他底层如何存储的呢?下面我们看一下他的底层源码:
public class InMemoryChatMemory implements ChatMemory {
private final Map<String, List<Message>> chatHistory = new ConcurrentHashMap<>();
@Override
public void add(String key, Message message) {
chatHistory.computeIfAbsent(key, k -> new ArrayList<>()).add(message);
}
@Override
public List<Message> get(String key) {
return chatHistory.getOrDefault(key, Collections.emptyList());
}
}
📚 其中:
UserMessage是用户输入的内容AssistantMessage是大模型回复的内容SystemMessage是系统提示信息
会话ID管理
默认实现中,key的值是"default",这在实际业务中显然不合理。我们需要根据不同的对话设置对应的会话ID。
解决方案:
// 通过请求参数传递sessionId
@GetMapping("/chat")
public String chat(@RequestParam String sessionId, @RequestParam String message) {
chatMemory.add(sessionId, new UserMessage(message));
// ...处理逻辑
}
会话ID生成策略:
- 用户级会话:
user-{userId} - 设备级会话:
device-{deviceId} - 临时会话:UUID随机生成
- 业务会话:
order-{orderId}
最佳实践:
// 使用Filter自动管理会话ID
public class SessionFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest req = (HttpServletRequest) request;
String sessionId = req.getHeader("X-Session-ID");
if(sessionId == null) {
sessionId = "session-" + UUID.randomUUID();
}
RequestContextHolder.setSessionId(sessionId);
chain.doFilter(request, response);
}
}
安全组件详解
Spring AI提供了安全组件SafeGuardAdvisor,当用户输入的内容包含敏感词时,立即拦截请求,避免调用大型模型处理,节省计算资源并降低安全风险。
实现原理
public class SafeGuardAdvisor implements Advisor {
private final SensitiveWordFilter filter;
@Override
public void advise(AdvisedRequest request, AdvisorContext context) {
if(filter.containsSensitiveWords(request.getPrompt())) {
context.setResponse("请求包含敏感内容");
context.setIntercepted(true);
}
}
}
敏感词库配置
-
静态词库:
spring: ai: safeguard: words: - 暴力 - 色情 - 政治敏感词 -
动态词库(数据库或API):
@Bean public SensitiveWordFilter sensitiveWordFilter() { return new DatabaseSensitiveWordFilter(dataSource); } -
正则表达式匹配:
@Bean public SensitiveWordFilter sensitiveWordFilter() { List<Pattern> patterns = List.of( Pattern.compile("\\d{4}-\\d{4}-\\d{4}-\\d{4}") // 信用卡号 ); return new RegexSensitiveWordFilter(patterns); }
高级安全策略
-
语义分析:
- 使用小型AI模型预判请求意图
- 识别变体敏感词(拼音、谐音)
-
频率限制:
@Advisor(order = 0) public class RateLimitAdvisor implements Advisor { private final RateLimiter limiter = RateLimiter.create(10); // 10次/秒 @Override public void advise(AdvisedRequest request, AdvisorContext context) { if(!limiter.tryAcquire()) { context.setResponse("请求过于频繁"); context.setIntercepted(true); } } } -
用户信用体系:
public class CreditAdvisor implements Advisor { @Override public void advise(AdvisedRequest request, AdvisorContext context) { User user = getUser(request); if(user.getCreditScore() < 60) { context.setResponse("信用分不足,请充值"); context.setIntercepted(true); } } }
性能优化建议
-
Advisor排序:
@Advisor(order = Ordered.HIGHEST_PRECEDENCE) // 最先执行 public class SafeGuardAdvisor implements Advisor { // ... } -
缓存策略:
@Advisor @Cacheable("aiResponses") public class CacheAdvisor implements Advisor { @Override public void advise(AdvisedRequest request, AdvisorContext context) { // 缓存命中逻辑 } } -
异步处理:
@Async @Advisor public class AsyncLoggingAdvisor implements Advisor { @Override public void advise(AdvisedRequest request, AdvisorContext context) { // 异步记录日志 } }
通过合理配置和组合这些Advisor,可以构建出既安全又高效的AI应用系统。
更多推荐



所有评论(0)