Spring AI Advisors 深度解析与应用指南

概述

Spring AI Advisors 提供了一种灵活且强大的方式,可以在 Spring 应用中轻松拦截、调整和增强基于AI的交互操作。通过使用Advisors,可以构建更复杂、可重用且易于维护的AI组件,从而提升应用的功能性和简化开发流程,使项目更加高效和整洁。

典型应用场景

  1. 内容安全过滤:在请求到达大模型前进行敏感词检测
  2. 上下文管理:自动维护对话历史记录
  3. 输出格式化:统一模型输出的结构和样式
  4. 性能优化:缓存常见请求的响应结果
  5. 日志记录:详细记录AI交互过程用于分析和审计

运行原理

下图展示了,在与大模型交互过程中,Advisors的执行流程:

[用户请求] → [请求包装] → [Advisor链预处理] → [模型调用] → [响应处理] → [最终响应]

关键流程解析

  1. 请求包装阶段(对应步骤①)

    • 框架将用户输入的Prompt封装为AdvisedRequest对象
      • 包含原始消息内容
      • 包含请求元数据(如时间戳、用户ID等)
    • 同时创建空的AdvisorContext上下文容器,用于链式传递处理状态
      • 线程安全的共享数据存储
      • 支持自定义属性的添加和读取
  2. Advisor链预处理(对应步骤②)

    • 多个Advisor按链式顺序处理请求(支持自定义执行顺序)
    • 每个Advisor可以:
      • 修改请求内容(如添加系统提示词)
        request.getPrompt().addSystemMessage("你是一个专业客服,请用中文回答");
        
      • 直接拦截请求并生成响应(实现内容审查/快速响应)
        if(containsSensitiveWords(request)) {
            context.setResponse("您的请求包含敏感内容");
            return; // 中断后续处理
        }
        
      • 添加处理日志或性能监控信息
  3. 模型调用阶段(对应步骤③)

    • 框架内置的最终Advisor将标准化请求发送至大模型
      • 自动处理不同模型API的差异
      • 统一错误处理和重试机制
    • 触发Chat Model完成核心推理
      • 支持同步/异步调用模式
      • 可配置超时和重试策略
  4. 响应处理阶段(对应步骤④-⑥)

    • 模型输出通过AdvisorContext携带上下文原路返回
      • 保留完整的处理链路信息
    • 各Advisor可二次处理响应:
      • 格式化输出结构(如JSON/XML转换)
      • 添加解释性内容(如添加免责声明)
        response += "\n\n注:以上内容由AI生成,仅供参考";
        
      • 执行最终安全校验(如二次内容过滤)
      • 记录响应日志和性能指标

聊天记忆实现

通过前面课程的学习,我们已经知道,再与大模型交流时,大模型是无状态的,如果要想有状态,就需要把之前的对话内容一起发给大模型,这样才能变成有状态。

在Spring AI中,对聊天记录已经做了实现,不需要我们手动收集聊天记录再发送了,通过简单的配置即可实现。

记忆实现类型

目前的版本中有三种实现:

  1. MessageChatMemoryAdvisor

    • 将历史消息与当前用户消息合并,一起发给大模型
    • 典型配置示例:
      @Bean
      public ChatMemoryAdvisor chatMemoryAdvisor(ChatMemory chatMemory) {
          return new MessageChatMemoryAdvisor(chatMemory);
      }
      
    • 适用场景:大多数现代AI模型(如GPT-4)
  2. PromptChatMemoryAdvisor

    • 将历史消息与系统提示词合并,放到系统提示词中,发给大模型
    • 典型配置示例:
      @Bean
      public ChatMemoryAdvisor chatMemoryAdvisor(ChatMemory chatMemory) {
          return new PromptChatMemoryAdvisor(chatMemory);
      }
      
    • 适用场景:对上下文格式有特殊要求的模型
  3. 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这两种都能实现,我们该如何选择呢?

选择原则:

  1. 优先使用MessageChatMemoryAdvisor
    • 更符合大多数模型的预期输入格式
    • 保持原始对话结构完整性
  2. 模型不支持时选择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生成策略:

  1. 用户级会话user-{userId}
  2. 设备级会话device-{deviceId}
  3. 临时会话:UUID随机生成
  4. 业务会话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);
        }
    }
}

敏感词库配置

  1. 静态词库

    spring:
      ai:
        safeguard:
          words:
            - 暴力
            - 色情
            - 政治敏感词
    
  2. 动态词库(数据库或API):

    @Bean
    public SensitiveWordFilter sensitiveWordFilter() {
        return new DatabaseSensitiveWordFilter(dataSource);
    }
    
  3. 正则表达式匹配

    @Bean
    public SensitiveWordFilter sensitiveWordFilter() {
        List<Pattern> patterns = List.of(
            Pattern.compile("\\d{4}-\\d{4}-\\d{4}-\\d{4}") // 信用卡号
        );
        return new RegexSensitiveWordFilter(patterns);
    }
    

高级安全策略

  1. 语义分析

    • 使用小型AI模型预判请求意图
    • 识别变体敏感词(拼音、谐音)
  2. 频率限制

    @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);
            }
        }
    }
    
  3. 用户信用体系

    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);
            }
        }
    }
    

性能优化建议

  1. Advisor排序

    @Advisor(order = Ordered.HIGHEST_PRECEDENCE) // 最先执行
    public class SafeGuardAdvisor implements Advisor {
        // ...
    }
    
  2. 缓存策略

    @Advisor
    @Cacheable("aiResponses")
    public class CacheAdvisor implements Advisor {
        @Override
        public void advise(AdvisedRequest request, AdvisorContext context) {
            // 缓存命中逻辑
        }
    }
    
  3. 异步处理

    @Async
    @Advisor
    public class AsyncLoggingAdvisor implements Advisor {
        @Override
        public void advise(AdvisedRequest request, AdvisorContext context) {
            // 异步记录日志
        }
    }
    

通过合理配置和组合这些Advisor,可以构建出既安全又高效的AI应用系统。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐