現(xiàn)多輪對(duì)話(huà)記憶:ChatMemory 與 Redis 持久化實(shí)戰(zhàn))
前言在構(gòu)建智能對(duì)話(huà)應(yīng)用時(shí)多輪對(duì)話(huà)的上下文記憶能力是剛需。Spring AI Alibaba 提供了一套優(yōu)雅的 ChatMemory 機(jī)制將對(duì)話(huà)歷史的管理、存儲(chǔ)和恢復(fù)過(guò)程封裝得極為簡(jiǎn)潔。本文將帶大家從零掌握如何用 Spring AI Alibaba 集成 ChatMemory涵蓋內(nèi)存存儲(chǔ)、Redis 持久化、流式 SSE 輸出等場(chǎng)景并給出可直接用于生產(chǎn)的配置方案。一、架構(gòu)流程與核心組件整個(gè)記憶鏈路如下前端請(qǐng)求 → ChatClient → MessageChatMemoryAdvisor記憶攔截器↓ 自動(dòng)讀取/寫(xiě)入會(huì)話(huà)歷史ChatMemory消息窗口策略滑動(dòng)窗口限制消息條數(shù)↓ 底層存儲(chǔ)ChatMemoryRepository存儲(chǔ)層內(nèi)存/Redis/MySQL↓ 底層調(diào)用DashScopeChatModel → DashScopeApi → 阿里靈積 DashScope 平臺(tái)1. ChatMemoryRepository 存儲(chǔ)倉(cāng)庫(kù)負(fù)責(zé)對(duì)話(huà)消息持久化存儲(chǔ)Spring AI Alibaba 內(nèi)置三種實(shí)現(xiàn)InMemoryChatMemoryRepository內(nèi)存存儲(chǔ)單機(jī)測(cè)試專(zhuān)用重啟丟失數(shù)據(jù)RedisChatMemoryRepositoryRedis 持久化集群/生產(chǎn)推薦會(huì)話(huà)自動(dòng)過(guò)期支持兩種構(gòu)建模式Jedis 直連、復(fù)用 Spring Redis 連接工廠JdbcChatMemoryRepository數(shù)據(jù)庫(kù)持久化長(zhǎng)期留存對(duì)話(huà)記錄2. ChatMemory 記憶策略層標(biāo)準(zhǔn)實(shí)現(xiàn)MessageWindowChatMemory滑動(dòng)窗口淘汰機(jī)制配置maxMessages限制最大保存消息條數(shù)自動(dòng)丟棄最早對(duì)話(huà)防止上下文 Token 超限、API 計(jì)費(fèi)暴漲。3. MessageChatMemoryAdvisor 記憶攔截器掛載在 ChatClient 的 Advisor 鏈路中請(qǐng)求前自動(dòng)根據(jù)conversationId加載歷史消息模型返回后自動(dòng)保存用戶(hù)/助手消息業(yè)務(wù)代碼無(wú)需手動(dòng)拼接歷史。4. ConversationId 會(huì)話(huà)唯一標(biāo)識(shí)不同用戶(hù)、不同聊天窗口分配獨(dú)立 ID實(shí)現(xiàn)會(huì)話(huà)隔離同一個(gè) ID 自動(dòng)復(fù)用歷史對(duì)話(huà)不同 ID 完全互不干擾。5. ChatModel vs ChatClient 記憶能力區(qū)分ChatModel底層原始接口無(wú)內(nèi)置記憶管理能力需要手動(dòng)拼接消息列表代碼冗余ChatClient上層封裝搭配 Advisor 一鍵集成 ChatMemory支持全局系統(tǒng)提示詞、消息自動(dòng)管理、流式、函數(shù)調(diào)用企業(yè)開(kāi)發(fā)首選。二、Maven 核心依賴(lài)與配置2.1 基礎(chǔ)依賴(lài)!-- 基礎(chǔ) DashScope 對(duì)話(huà)依賴(lài)必引 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-alibaba-starter-dashscope/artifactId/dependency內(nèi)存存儲(chǔ)無(wú)需額外依賴(lài)Redis 持久化需引入記憶擴(kuò)展包并根據(jù)連接模式補(bǔ)充對(duì)應(yīng)依賴(lài)!-- Redis記憶擴(kuò)展包兩種模式都必須引入 --dependencygroupIdcom.alibaba.cloud.ai/groupIdartifactIdspring-ai-alibaba-starter-memory-redis/artifactId/dependency!-- 方式AJedis直連模式需要該依賴(lài) --dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactId/dependency!-- 方式B復(fù)用Spring Redis連接工廠模式需要該依賴(lài) --!--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency--2.2 application.yml 基礎(chǔ)配置spring:ai:dashscope:api-key: sk-xxxxxxxxxxxxxxxxxxxxchat:options:model: qwen-turbotemperature: 0.7# Redis配置Jedis直連模式通過(guò)Value讀取連接池復(fù)用模式由SpringDataRedis自動(dòng)讀取data:redis:host: 127.0.0.1port: 6379password: database: 0三、方案1內(nèi)存版 ChatMemory本地測(cè)試首選內(nèi)存實(shí)現(xiàn)適用于開(kāi)發(fā)調(diào)試提供兩種寫(xiě)法極簡(jiǎn)自動(dòng)裝配與手動(dòng)配置定制窗口大小。3.1 方式A極簡(jiǎn)自動(dòng)裝配90% 測(cè)試場(chǎng)景零配置類(lèi)Spring AI Alibaba 自動(dòng)配置了InMemoryChatMemoryRepository我們只需通過(guò)Bean定義ChatMemory和ChatClient即可無(wú)需手動(dòng)創(chuàng)建倉(cāng)庫(kù)。配置類(lèi)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 {// 使用自動(dòng)裝配的 InMemoryChatMemoryRepositoryBeanpublic ChatMemory chatMemory(InMemoryChatMemoryRepository repository) {return MessageWindowChatMemory.builder().chatMemoryRepository(repository).maxMessages(10) // 最多保存10條消息5輪對(duì)話(huà).build();}Beanpublic ChatClient chatClient(ChatModel chatModel, ChatMemory chatMemory) {return ChatClient.builder(chatModel).defaultSystem(你是資深Java后端專(zhuān)家結(jié)合上下文簡(jiǎn)潔回答問(wèn)題).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();}}Controller 示例RestControllerRequestMapping(/ai/chat)public class MemoryChatController {Autowiredprivate ChatClient chatClient;Autowiredprivate ChatMemory chatMemory;/*** 同步多輪對(duì)話(huà)* param conversationId 會(huì)話(huà)ID前端生成UUID* param userMsg 用戶(hù)提問(wèn)*/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();}// 清空指定會(huì)話(huà)記憶DeleteMapping(/memory/clear)public String clearMemory(RequestParam String conversationId) {chatMemory.clear(conversationId);return 會(huì)話(huà)[ conversationId ]記憶已清空;}}3.2 方式B手動(dòng)配置類(lèi)自定義窗口上限推薦工程化寫(xiě)法若需完全掌控 Bean 創(chuàng)建過(guò)程可手動(dòng)聲明 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后端專(zhuān)家結(jié)合上下文簡(jiǎn)潔回答問(wèn)題).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();}}四、方案2Redis 持久化 ChatMemory生產(chǎn)環(huán)境集群推薦內(nèi)存存儲(chǔ)致命缺陷服務(wù)重啟、多實(shí)例集群時(shí)會(huì)話(huà)記憶丟失線(xiàn)上必須使用 Redis 持久化。RedisChatMemoryRepository提供兩套構(gòu)建方式4.1 方式AJedis 直連簡(jiǎn)單 Demo組件內(nèi)部自建 Jedis 連接不共享項(xiàng)目 Redis 連接池yml 配置通過(guò)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(你是通義千問(wèn)對(duì)話(huà)助手記住用戶(hù)全部歷史提問(wèn)).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();}}4.2 方式B復(fù)用 Spring Data Redis 連接工廠生產(chǎn)推薦項(xiàng)目已引入spring-boot-starter-data-redis時(shí)使用連接池、哨兵、集群配置統(tǒng)一管理。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(你是通義千問(wèn)對(duì)話(huà)助手記住用戶(hù)全部歷史提問(wèn)).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();}}Redis 記憶特性會(huì)話(huà)數(shù)據(jù)序列化存入 Redis服務(wù)重啟不丟失支持配置 Key 過(guò)期時(shí)間自動(dòng)清理長(zhǎng)期未使用會(huì)話(huà)微服務(wù)多實(shí)例集群共享 Redis多節(jié)點(diǎn)對(duì)話(huà)上下文統(tǒng)一。重要區(qū)分Jedis 直連模式內(nèi)部新建獨(dú)立 Jedis 連接不和業(yè)務(wù) Redis 共用連接池適合簡(jiǎn)單 DemoRedisConnectionFactory 模式復(fù)用項(xiàng)目已有 Redis 連接池適合正式業(yè)務(wù)系統(tǒng)。五、流式 SSE 接口實(shí)現(xiàn)帶記憶打字機(jī)效果結(jié)合 ChatMemory 實(shí)現(xiàn)帶歷史上下文的實(shí)時(shí)流式輸出基于 WebFlux Flux SSERestControllerRequestMapping(/ai/chat)public class StreamMemoryChatController {Autowiredprivate ChatClient chatClient;/*** 流式帶記憶對(duì)話(huà)SSE實(shí)時(shí)推送*/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();}}六、總結(jié)與選型建議開(kāi)發(fā)測(cè)試使用內(nèi)存版InMemoryChatMemoryRepository極簡(jiǎn)配置快速驗(yàn)證多輪對(duì)話(huà)邏輯。單機(jī)生產(chǎn)內(nèi)存版不持久化若可接受重啟丟失歷史也可用否則切換至 Redis 持久化。集群/微服務(wù)必須選擇Redis 持久化并推薦復(fù)用 Spring Data Redis 連接工廠實(shí)現(xiàn)連接池統(tǒng)一管理保證多實(shí)例會(huì)話(huà)共享。流式輸出只需在ChatClient基礎(chǔ)上調(diào)用.stream().content()即可獲得 SSE 流記憶配置無(wú)需額外改動(dòng)。Spring AI Alibaba 的 ChatMemory 體系將對(duì)話(huà)上下文管理復(fù)雜度降到最低配合 ChatClient 的 Advisor 機(jī)制讓我們能更專(zhuān)注于業(yè)務(wù)邏輯。希望本文能幫助大家快速落地多輪對(duì)話(huà)功能如有疑問(wèn)歡迎在評(píng)論區(qū)交流。