前言
在构建智能对话应用时,多轮对话的上下文记忆能力是刚需。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(); }}
六、总结与选型建议
Spring AI Alibaba 的 ChatMemory 体系将对话上下文管理复杂度降到最低,配合 ChatClient 的 Advisor 机制,让我们能更专注于业务逻辑。希望本文能帮助大家快速落地多轮对话功能,如有疑问欢迎在评论区交流。






