欢迎光临
我们一直在努力

Spring AI Alibaba 实现多轮对话记忆:ChatMemory 与 Redis 持久化实战

前言

在构建智能对话应用时,多轮对话的上下文记忆能力是刚需。Spring AI Alibaba 提供了一套优雅的 ChatMemory 机制,将对话历史的管理、存储和恢复过程封装得极为简洁。本文将带大家从零掌握如何用 Spring AI Alibaba 集成 ChatMemory,涵盖内存存储、Redis 持久化、流式 SSE 输出等场景,并给出可直接用于生产的配置方案。

一、架构流程与核心组件

整个记忆链路如下:

前端请求 → ChatClient → MessageChatMemoryAdvisor(记忆拦截器) ↓ 自动读取/写入会话历史ChatMemory(消息窗口策略:滑动窗口,限制消息条数) ↓ 底层存储ChatMemoryRepository(存储层:内存/Redis/MySQL) ↓ 底层调用DashScopeChatModel → DashScopeApi → 阿里灵积 DashScope 平台

1. ChatMemoryRepository 存储仓库

负责对话消息持久化存储,Spring AI Alibaba 内置三种实现:

  • InMemoryChatMemoryRepository:内存存储,单机测试专用,重启丢失数据
  • RedisChatMemoryRepository:Redis 持久化,集群/生产推荐,会话自动过期,支持两种构建模式:Jedis 直连、复用 Spring Redis 连接工厂
  • JdbcChatMemoryRepository:数据库持久化,长期留存对话记录

2. ChatMemory 记忆策略层

标准实现:MessageWindowChatMemory,滑动窗口淘汰机制,配置 maxMessages 限制最大保存消息条数,自动丢弃最早对话,防止上下文 Token 超限、API 计费暴涨。

3. MessageChatMemoryAdvisor 记忆拦截器

挂载在 ChatClient 的 Advisor 链路中,请求前自动根据 conversationId 加载历史消息,模型返回后自动保存用户/助手消息,业务代码无需手动拼接历史。

4. ConversationId 会话唯一标识

不同用户、不同聊天窗口分配独立 ID,实现会话隔离,同一个 ID 自动复用历史对话,不同 ID 完全互不干扰。

5. ChatModel vs ChatClient 记忆能力区分

  • ChatModel:底层原始接口,无内置记忆管理能力,需要手动拼接消息列表,代码冗余;
  • ChatClient:上层封装,搭配 Advisor 一键集成 ChatMemory,支持全局系统提示词、消息自动管理、流式、函数调用,企业开发首选。

二、Maven 核心依赖与配置

2.1 基础依赖

<!– 基础 DashScope 对话依赖(必引) –><dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId></dependency>

内存存储无需额外依赖;Redis 持久化需引入记忆扩展包,并根据连接模式补充对应依赖:

<!– Redis记忆扩展包,两种模式都必须引入 –><dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-memory-redis</artifactId></dependency><!– 方式A:Jedis直连模式,需要该依赖 –><dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId></dependency><!– 方式B:复用Spring Redis连接工厂模式,需要该依赖 –><!–<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>–>

2.2 application.yml 基础配置

spring: ai: dashscope: api-key: sk-xxxxxxxxxxxxxxxxxxxx chat: options: model: qwen-turbo temperature: 0.7 # Redis配置,Jedis直连模式通过@Value读取;连接池复用模式由SpringDataRedis自动读取 data: redis: host: 127.0.0.1 port: 6379 password: "" database: 0

三、方案1:内存版 ChatMemory(本地测试首选)

内存实现适用于开发调试,提供两种写法:极简自动装配与手动配置定制窗口大小。

3.1 方式A:极简自动装配(90% 测试场景,零配置类)

Spring AI Alibaba 自动配置了 InMemoryChatMemoryRepository,我们只需通过 @Bean 定义 ChatMemory 和 ChatClient 即可,无需手动创建仓库。

配置类:

import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;import org.springframework.ai.chat.memory.ChatMemory;import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;import org.springframework.ai.chat.memory.MessageWindowChatMemory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class AiMemoryConfig { // 使用自动装配的 InMemoryChatMemoryRepository @Bean public ChatMemory chatMemory(InMemoryChatMemoryRepository repository) { return MessageWindowChatMemory.builder() .chatMemoryRepository(repository) .maxMessages(10) // 最多保存10条消息(5轮对话) .build(); } @Bean public ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) { return ChatClient.builder(chatModel) .defaultSystem("你是资深Java后端专家,结合上下文简洁回答问题") .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); }}

Controller 示例:

@RestController@RequestMapping("/ai/chat")public class MemoryChatController { @Autowired private ChatClient chatClient; @Autowired private ChatMemory chatMemory; /** * 同步多轮对话 * @param conversationId 会话ID,前端生成UUID * @param userMsg 用户提问 */ @GetMapping("/sync") public String syncChat( @RequestParam String conversationId, @RequestParam String userMsg) { return chatClient.prompt() .user(userMsg) .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, conversationId)) .call() .content(); } // 清空指定会话记忆 @DeleteMapping("/memory/clear") public String clearMemory(@RequestParam String conversationId) { chatMemory.clear(conversationId); return "会话[" + conversationId + "]记忆已清空"; }}

3.2 方式B:手动配置类(自定义窗口上限,推荐工程化写法)

若需完全掌控 Bean 创建过程,可手动声明 Repository 和 ChatMemory:

@Configurationpublic class AiMemoryConfig { @Bean public InMemoryChatMemoryRepository memoryRepository() { return new InMemoryChatMemoryRepository(); } @Bean public ChatMemory chatMemory(InMemoryChatMemoryRepository repository) { return MessageWindowChatMemory.builder() .chatMemoryRepository(repository) .maxMessages(20) // 自定义窗口大小 .build(); } @Bean public ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) { return ChatClient.builder(chatModel) .defaultSystem("你是资深Java后端专家,结合上下文简洁回答问题") .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); }}

四、方案2:Redis 持久化 ChatMemory(生产环境集群推荐)

内存存储致命缺陷:服务重启、多实例集群时会话记忆丢失,线上必须使用 Redis 持久化。RedisChatMemoryRepository 提供两套构建方式:

4.1 方式A:Jedis 直连(简单 Demo)

组件内部自建 Jedis 连接,不共享项目 Redis 连接池,yml 配置通过 @Value 读取。

@Configurationpublic class RedisMemoryConfig { @Value("${spring.data.redis.host}") private String host; @Value("${spring.data.redis.port}") private int port; @Value("${spring.data.redis.password:}") private String password; @Value("${spring.data.redis.database:0}") private int database; @Bean public RedisChatMemoryRepository redisChatMemoryRepository() { return RedisChatMemoryRepository.builder() .host(host) .port(port) .password(password) .database(database) .build(); } @Bean public ChatMemory chatMemory(RedisChatMemoryRepository redisChatMemoryRepository) { return MessageWindowChatMemory.builder() .chatMemoryRepository(redisChatMemoryRepository) .maxMessages(20) .build(); } @Bean public ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) { return ChatClient.builder(chatModel) .defaultSystem("你是通义千问对话助手,记住用户全部历史提问") .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); }}

4.2 方式B:复用 Spring Data Redis 连接工厂(生产推荐)

项目已引入 spring-boot-starter-data-redis 时使用,连接池、哨兵、集群配置统一管理。

@Configurationpublic class RedisMemoryConfig { @Bean public RedisChatMemoryRepository redisChatMemoryRepository(RedisConnectionFactory redisConnectionFactory) { return RedisChatMemoryRepository.builder() .redisConnectionFactory(redisConnectionFactory) .build(); } @Bean public ChatMemory chatMemory(RedisChatMemoryRepository redisChatMemoryRepository) { return MessageWindowChatMemory.builder() .chatMemoryRepository(redisChatMemoryRepository) .maxMessages(20) .build(); } @Bean public ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) { return ChatClient.builder(chatModel) .defaultSystem("你是通义千问对话助手,记住用户全部历史提问") .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); }}

Redis 记忆特性:

  • 会话数据序列化存入 Redis,服务重启不丢失;
  • 支持配置 Key 过期时间,自动清理长期未使用会话;
  • 微服务多实例集群共享 Redis,多节点对话上下文统一。

重要区分:

  • Jedis 直连模式:内部新建独立 Jedis 连接,不和业务 Redis 共用连接池,适合简单 Demo;
  • RedisConnectionFactory 模式:复用项目已有 Redis 连接池,适合正式业务系统。

五、流式 SSE 接口实现带记忆打字机效果

结合 ChatMemory 实现带历史上下文的实时流式输出,基于 WebFlux Flux + SSE:

@RestController@RequestMapping("/ai/chat")public class StreamMemoryChatController { @Autowired private ChatClient chatClient; /** * 流式带记忆对话,SSE实时推送 */ @GetMapping(value = "/stream", produces = "text/event-stream;charset=utf-8") public Flux<String> streamChatWithMemory( @RequestParam String conversationId, @RequestParam String userMsg) { return chatClient.prompt() .user(userMsg) .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, conversationId)) .stream() .content(); }}

六、总结与选型建议

  • 开发测试:使用内存版 InMemoryChatMemoryRepository,极简配置,快速验证多轮对话逻辑。
  • 单机生产:内存版不持久化,若可接受重启丢失历史,也可用;否则切换至 Redis 持久化。
  • 集群/微服务:必须选择 Redis 持久化,并推荐复用 Spring Data Redis 连接工厂,实现连接池统一管理,保证多实例会话共享。
  • 流式输出:只需在 ChatClient 基础上调用 .stream().content() 即可获得 SSE 流,记忆配置无需额外改动。
  • Spring AI Alibaba 的 ChatMemory 体系将对话上下文管理复杂度降到最低,配合 ChatClient 的 Advisor 机制,让我们能更专注于业务逻辑。希望本文能帮助大家快速落地多轮对话功能,如有疑问欢迎在评论区交流。

    赞(0)
    未经允许不得转载:171主机测评 » Spring AI Alibaba 实现多轮对话记忆:ChatMemory 与 Redis 持久化实战
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址