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

发布时间:2026/8/16 22:24:16
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内存存储单机测试专用重启丢失数据RedisChatMemoryRepositoryRedis 持久化集群/生产推荐会话自动过期支持两种构建模式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 对话依赖必引 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-alibaba-starter-dashscope/artifactId/dependency内存存储无需额外依赖Redis 持久化需引入记忆扩展包并根据连接模式补充对应依赖!-- Redis记忆扩展包两种模式都必须引入 --dependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-starter-memory-redis/artifactId/dependency!-- 方式AJedis直连模式需要该依赖 --dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactId/dependency!-- 方式B复用Spring Redis连接工厂模式需要该依赖 --!--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency--2.2 application.yml 基础配置spring:ai:dashscope:api-key: sk-xxxxxxxxxxxxxxxxxxxxchat:options:model: qwen-turbotemperature: 0.7# Redis配置Jedis直连模式通过Value读取连接池复用模式由SpringDataRedis自动读取data:redis:host: 127.0.0.1port: 6379password: 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 {// 使用自动装配的 InMemoryChatMemoryRepositoryBeanpublic ChatMemory chatMemory(InMemoryChatMemoryRepository repository) {return MessageWindowChatMemory.builder().chatMemoryRepository(repository).maxMessages(10) // 最多保存10条消息5轮对话.build();}Beanpublic ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) {return ChatClient.builder(chatModel).defaultSystem(你是资深Java后端专家结合上下文简洁回答问题).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();}}Controller 示例RestControllerRequestMapping(/ai/chat)public class MemoryChatController {Autowiredprivate ChatClient chatClient;Autowiredprivate 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 和 ChatMemoryConfigurationpublic class AiMemoryConfig {Beanpublic InMemoryChatMemoryRepository memoryRepository() {return new InMemoryChatMemoryRepository();}Beanpublic ChatMemory chatMemory(InMemoryChatMemoryRepository repository) {return MessageWindowChatMemory.builder().chatMemoryRepository(repository).maxMessages(20) // 自定义窗口大小.build();}Beanpublic ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) {return ChatClient.builder(chatModel).defaultSystem(你是资深Java后端专家结合上下文简洁回答问题).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();}}四、方案2Redis 持久化 ChatMemory生产环境集群推荐内存存储致命缺陷服务重启、多实例集群时会话记忆丢失线上必须使用 Redis 持久化。RedisChatMemoryRepository提供两套构建方式4.1 方式AJedis 直连简单 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;Beanpublic RedisChatMemoryRepository redisChatMemoryRepository() {return RedisChatMemoryRepository.builder().host(host).port(port).password(password).database(database).build();}Beanpublic ChatMemory chatMemory(RedisChatMemoryRepository redisChatMemoryRepository) {return MessageWindowChatMemory.builder().chatMemoryRepository(redisChatMemoryRepository).maxMessages(20).build();}Beanpublic 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 {Beanpublic RedisChatMemoryRepository redisChatMemoryRepository(RedisConnectionFactory redisConnectionFactory) {return RedisChatMemoryRepository.builder().redisConnectionFactory(redisConnectionFactory).build();}Beanpublic ChatMemory chatMemory(RedisChatMemoryRepository redisChatMemoryRepository) {return MessageWindowChatMemory.builder().chatMemoryRepository(redisChatMemoryRepository).maxMessages(20).build();}Beanpublic ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) {return ChatClient.builder(chatModel).defaultSystem(你是通义千问对话助手记住用户全部历史提问).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();}}Redis 记忆特性会话数据序列化存入 Redis服务重启不丢失支持配置 Key 过期时间自动清理长期未使用会话微服务多实例集群共享 Redis多节点对话上下文统一。重要区分Jedis 直连模式内部新建独立 Jedis 连接不和业务 Redis 共用连接池适合简单 DemoRedisConnectionFactory 模式复用项目已有 Redis 连接池适合正式业务系统。五、流式 SSE 接口实现带记忆打字机效果结合 ChatMemory 实现带历史上下文的实时流式输出基于 WebFlux Flux SSERestControllerRequestMapping(/ai/chat)public class StreamMemoryChatController {Autowiredprivate ChatClient chatClient;/*** 流式带记忆对话SSE实时推送*/GetMapping(value /stream, produces text/event-stream;charsetutf-8)public FluxString 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 机制让我们能更专注于业务逻辑。希望本文能帮助大家快速落地多轮对话功能如有疑问欢迎在评论区交流。