在上一篇文章中我们学习了Spring AI基于AOP的Advisor机制并实现了无侵入式的会话日志功能。本文将继续深入讲解Spring AI的另一个核心Advisor——MessageChatMemoryAdvisor手把手教你实现大模型的多轮会话记忆功能让你的AI助手能够记住之前的对话内容。同时我们会深入分析Spring AI的可插拔设计思想理解如何通过解耦实现不同存储方式的无缝切换。一、大模型的本质无状态的“一次性对话”很多人会误以为大模型天生具有记忆能力其实恰恰相反所有大模型本质上都是无状态的。每一次HTTP请求对大模型来说都是完全独立的它不会记住上一次请求的任何内容。要实现多轮对话唯一的方法就是把之前所有的聊天历史和当前用户的新问题一起打包成完整的消息列表发送给大模型。我们可以用原生Python代码直观地理解这个底层过程fromopenaiimportOpenAI# 1. 初始化OpenAI客户端DeepSeek完全兼容OpenAI接口规范clientOpenAI(api_key你的DeepSeek API Key,base_urlhttps://api.deepseek.com)# 2. 手动维护聊天历史消息列表这就是实现记忆的核心messages[{role:system,content:你是一个热心的AI助手你的名字叫小团团},{role:user,content:你好你是谁}]# 3. 每次请求都发送完整的历史消息列表responseclient.chat.completions.create(modeldeepseek-r1,temperature0.7,messagesmessages,# 核心大模型只能看到这个列表里的内容streamFalse)# 4. 必须手动把大模型的回复添加到历史中下一轮才能继续对话assistant_replyresponse.choices[0].message.content messages.append({role:assistant,content:assistant_reply})print(assistant_reply)可以看到原生实现需要我们手动维护messages列表每次对话都要重复执行“添加历史-发送请求-保存回复”的操作非常繁琐。而Spring AI通过Advisor机制帮我们把这个过程完全自动化了。二、大模型对话的三个核心角色在实现会话记忆之前我们需要先理解大模型对话中三个标准的消息角色所有的聊天历史都是由这三种角色的消息按时间顺序组成的角色描述示例system系统指令优先级最高用于给大模型设定全局角色、任务背景和回答规则你是一个乐于助人的编程助手你的名字叫小博客请以小博客的风格来回答用户的问题。user用户输入的问题也就是终端用户在聊天框中发送的内容你好你是谁assistant大模型生成的回答每一轮对话大模型都会生成一个assistant角色的消息你好呀我是小博客一个热心的智能助手有什么可以帮你的吗大模型就是通过解析这个按时间顺序排列的消息列表来理解上下文并生成连贯回答的。三、Spring AI会话记忆的核心设计可插拔的ChatMemory接口Spring AI没有把会话记忆的逻辑硬编码在框架中而是设计了一个高度抽象的ChatMemory接口实现了存储逻辑和业务逻辑的完全解耦。3.1 ChatMemory接口定义ChatMemory接口只定义了三个核心方法所有的会话存储实现都必须遵守这个契约/** * 会话存储抽象接口 * 所有存储实现内存、Redis、MongoDB等都必须实现这个接口 */publicinterfaceChatMemory{/** * 添加消息到指定会话的历史记录中 * param conversationId 会话唯一标识区分不同用户的不同对话 * param messages 要添加的消息列表 */voidadd(StringconversationId,ListMessagemessages);/** * 获取指定会话的最近N条历史消息 * param conversationId 会话唯一标识 * param lastN 要获取的最近消息条数避免上下文过长 * return 历史消息列表 */ListMessageget(StringconversationId,intlastN);/** * 清空指定会话的历史记录 * param conversationId 会话唯一标识 */voidclear(StringconversationId);}3.2 可插拔的存储实现基于这个接口Spring AI提供了多种开箱即用的存储实现同时也支持用户完全自定义InMemoryChatMemory默认实现基于JVM内存存储适合开发测试环境RedisChatMemory基于Redis存储适合生产环境分布式部署MongoDBChatMemory基于MongoDB存储适合需要持久化大量会话数据的场景自定义实现只需要实现ChatMemory接口并重写三个方法即可对接MySQL、Elasticsearch等任何存储系统3.3 基于Bean的解耦注入Spring AI通过依赖注入实现了存储实现的无缝切换。我们只需要将ChatMemory接口的实现类声明为Spring BeanChatClient会自动依赖这个接口而不依赖具体的实现类。这意味着当我们需要从内存存储切换到Redis存储时只需要修改ChatMemory Bean的返回值其他所有业务代码一行都不用改。这就是面向接口编程和解耦的强大之处。四、基于AOP的无侵入式会话记忆实现和上一篇的日志功能一样会话记忆功能也是通过MessageChatMemoryAdvisor这个环绕通知实现的。它会在大模型调用的前后自动完成历史消息的读取和写入业务代码完全不需要关心。MessageChatMemoryAdvisor的完整工作流程请求前置增强从ChatMemory中根据会话ID获取历史消息自动拼接到当前请求的消息列表中调用大模型将包含完整历史的请求发送给底层Chat Model请求后置增强将本次对话的用户问题和大模型回答一起添加到ChatMemory中返回结果将增强后的响应返回给用户整个过程对业务代码完全透明真正实现了无侵入式增强。五、多轮会话记忆实战步骤我们基于之前的项目只需要三步即可实现完整的多轮会话记忆功能。5.1 第一步声明ChatMemory Bean首先在配置类中声明ChatMemoryBean这里我们先使用默认的内存实现importorg.springframework.ai.chat.memory.ChatMemory;importorg.springframework.ai.chat.memory.InMemoryChatMemory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;ConfigurationpublicclassCommonConfiguration{/** * 声明会话存储Bean * 这里使用内存存储InMemoryChatMemory * 未来切换到Redis只需要把返回值改成RedisChatMemory即可 */BeanpublicChatMemorychatMemory(){returnnewInMemoryChatMemory();}}5.2 第二步配置MessageChatMemoryAdvisor将MessageChatMemoryAdvisor添加到ChatClient的默认Advisor列表中这样所有通过这个ChatClient发起的对话都会自动应用会话记忆功能importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;importorg.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;importorg.springframework.ai.ollama.OllamaChatModel;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;ConfigurationpublicclassCommonConfiguration{BeanpublicChatMemorychatMemory(){returnnewInMemoryChatMemory();}/** * 自定义ChatClient添加会话记忆和日志增强器 * param model 自动注入的大模型客户端 * param chatMemory 自动注入的会话存储依赖接口不依赖具体实现 * return 配置好的ChatClient实例 */BeanpublicChatClientchatClient(OllamaChatModelmodel,ChatMemorychatMemory){returnChatClient.builder(model).defaultSystem(你是一个热心、可爱的智能助手你的名字叫小博客请以小博客的身份和语气回答问题。).defaultAdvisors(newSimpleLoggerAdvisor(),// 日志增强器保留之前的功能newMessageChatMemoryAdvisor(chatMemory)// 会话记忆增强器).build();}}5.3 第三步接口层添加会话ID参数最后我们需要在Controller层接收前端传递的会话ID并将其传递给Advisor上下文。这里用到了Lambda表达式的写法我们可以用一个非常形象的比喻来理解它Lambda表达式的打印机比喻这个Lambda表达式a - a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)就像一个打印参数设置器。你可以把Advisor想象成一台已经组装好的打印机它内置了所有的打印逻辑读取历史、拼接消息、保存历史。而Lambda表达式就是你给打印机的参数指令“请用这个会话IDchatId来处理这次打印任务”。你不需要知道打印机内部怎么进纸、怎么喷墨只需要告诉它关键参数它就会自动完成所有工作。完整的Controller代码importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.http.MediaType;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importreactor.core.publisher.Flux;importstaticorg.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;RestControllerpublicclassChatController{privatefinalChatClientchatClient;// 构造器注入ChatClientSpring 4.3推荐方式publicChatController(ChatClientchatClient){this.chatClientchatClient;}/** * 支持多轮对话的流式接口 * param question 用户问题 * param chatId 会话唯一标识前端生成建议使用UUID * return 流式回答打字机效果 */GetMapping(value/chat/stream,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicFluxStringchatStream(RequestParamStringquestion,RequestParamStringchatId){returnchatClient.prompt().user(question)// 核心将会话ID传递给Advisor上下文// Lambda表达式作为参数设置器告诉MessageChatMemoryAdvisor使用哪个会话.advisors(a-a.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId)).stream().content();}}5.4 测试效果启动项目后我们可以通过以下步骤测试多轮对话第一次调用http://localhost:8080/chat/stream?question你好我叫小明chatId123456第二次调用http://localhost:8080/chat/stream?question我叫什么名字chatId123456大模型会正确回答“你叫小明”说明它已经记住了上一轮的对话内容。同时我们可以在控制台看到日志历史消息已经被自动拼接和保存了。六、扩展无缝切换到Redis持久化存储内存存储的缺点是服务重启后会话数据会丢失生产环境我们通常使用Redis进行持久化存储。基于Spring AI的解耦设计切换存储只需要修改一行代码。6.1 引入Redis依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency6.2 配置Redis连接在application.yml中添加Redis配置spring:redis:host:localhostport:6379password:your-redis-password# 没有密码可以不写database:06.3 创建ChatMemory的实现类RedisChatMemory/** * Redis 会话记忆实现类 * 必须 implements ChatMemory */publicclassRedisChatMemoryimplementsChatMemory{privatefinalRedisTemplateString,ObjectredisTemplate;// 构造注入RedisTemplatepublicRedisChatMemory(RedisTemplateString,ObjectredisTemplate){this.redisTemplateredisTemplate;}/** * 存储会话消息 */Overridepublicvoidadd(StringconversationId,ListMessagemessages){// Redis key spring-ai-chat-memory: conversationIdStringkeychat:memory:conversationId;// 把消息列表 rightPush 存入 Redis Listfor(Messagemessage:messages){redisTemplate.opsForList().rightPush(key,message);}}/** * 获取最近 N 条历史消息 */OverridepublicListMessageget(StringconversationId,intlastN){Stringkeychat:memory:conversationId;// 从Redis List获取最新N条returnredisTemplate.opsForList().range(key,-lastN,-1);}/** * 清空会话 */Overridepublicvoidclear(StringconversationId){redisTemplate.delete(chat:memory:conversationId);}}6.4 修改ChatMemory Bean只需要将InMemoryChatMemory改成RedisChatMemory即可importorg.springframework.ai.chat.memory.ChatMemory;importorg.springframework.ai.chat.memory.RedisChatMemory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;ConfigurationpublicclassCommonConfiguration{/** * 无缝切换到Redis存储 * 其他所有代码ChatClient配置、Controller一行都不用改 */BeanpublicChatMemorychatMemory(RedisTemplateString,ObjectredisTemplate){returnnewRedisChatMemory(redisTemplate);}// ChatClient的配置完全不变BeanpublicChatClientchatClient(OllamaChatModelmodel,ChatMemorychatMemory){returnChatClient.builder(model).defaultSystem(你是一个热心的智能助手你的名字叫小博客请以小博客的身份和语气回答问题。).defaultAdvisors(newSimpleLoggerAdvisor(),newMessageChatMemoryAdvisor(chatMemory)).build();}}就是这么简单这就是面向接口编程和依赖注入带来的巨大优势。同样的如果你想切换到MongoDB只需要引入MongoDB依赖然后返回MongoDBChatMemory即可。七、Spring AI会话记忆的核心优势无侵入式增强基于AOP实现业务代码完全不需要关心历史消息的维护可插拔设计通过ChatMemory接口实现存储逻辑的解耦无缝切换不同存储方式高度灵活支持自定义会话ID、自定义历史消息条数、自定义存储策略统一管理所有会话逻辑集中在Advisor中便于维护和扩展总结本文详细讲解了Spring AI多轮会话记忆的实现原理和实战步骤。我们首先理解了大模型无状态的本质然后学习了Spring AI基于ChatMemory接口和Advisor机制的设计思想通过三步实现了完整的多轮对话功能。Spring AI最值得我们学习的就是它的解耦设计思想通过面向接口编程和依赖注入让框架具有极高的扩展性和灵活性。参考资料[1] 黑马程序员. SpringAIDeepSeek大模型应用开发实战视频教程[EB/OL]. 传统Java项目AI化转型必学课程.本文根据黑马程序员SpringAIDeepSeek大模型应用开发实战视频教程传统Java项目AI化转型必学课程学习总结而得部分图片取自视频内容。非专业人士如有错误欢迎指出感谢理解
Spring AI快速入门(三):会话记忆功能
在上一篇文章中我们学习了Spring AI基于AOP的Advisor机制并实现了无侵入式的会话日志功能。本文将继续深入讲解Spring AI的另一个核心Advisor——MessageChatMemoryAdvisor手把手教你实现大模型的多轮会话记忆功能让你的AI助手能够记住之前的对话内容。同时我们会深入分析Spring AI的可插拔设计思想理解如何通过解耦实现不同存储方式的无缝切换。一、大模型的本质无状态的“一次性对话”很多人会误以为大模型天生具有记忆能力其实恰恰相反所有大模型本质上都是无状态的。每一次HTTP请求对大模型来说都是完全独立的它不会记住上一次请求的任何内容。要实现多轮对话唯一的方法就是把之前所有的聊天历史和当前用户的新问题一起打包成完整的消息列表发送给大模型。我们可以用原生Python代码直观地理解这个底层过程fromopenaiimportOpenAI# 1. 初始化OpenAI客户端DeepSeek完全兼容OpenAI接口规范clientOpenAI(api_key你的DeepSeek API Key,base_urlhttps://api.deepseek.com)# 2. 手动维护聊天历史消息列表这就是实现记忆的核心messages[{role:system,content:你是一个热心的AI助手你的名字叫小团团},{role:user,content:你好你是谁}]# 3. 每次请求都发送完整的历史消息列表responseclient.chat.completions.create(modeldeepseek-r1,temperature0.7,messagesmessages,# 核心大模型只能看到这个列表里的内容streamFalse)# 4. 必须手动把大模型的回复添加到历史中下一轮才能继续对话assistant_replyresponse.choices[0].message.content messages.append({role:assistant,content:assistant_reply})print(assistant_reply)可以看到原生实现需要我们手动维护messages列表每次对话都要重复执行“添加历史-发送请求-保存回复”的操作非常繁琐。而Spring AI通过Advisor机制帮我们把这个过程完全自动化了。二、大模型对话的三个核心角色在实现会话记忆之前我们需要先理解大模型对话中三个标准的消息角色所有的聊天历史都是由这三种角色的消息按时间顺序组成的角色描述示例system系统指令优先级最高用于给大模型设定全局角色、任务背景和回答规则你是一个乐于助人的编程助手你的名字叫小博客请以小博客的风格来回答用户的问题。user用户输入的问题也就是终端用户在聊天框中发送的内容你好你是谁assistant大模型生成的回答每一轮对话大模型都会生成一个assistant角色的消息你好呀我是小博客一个热心的智能助手有什么可以帮你的吗大模型就是通过解析这个按时间顺序排列的消息列表来理解上下文并生成连贯回答的。三、Spring AI会话记忆的核心设计可插拔的ChatMemory接口Spring AI没有把会话记忆的逻辑硬编码在框架中而是设计了一个高度抽象的ChatMemory接口实现了存储逻辑和业务逻辑的完全解耦。3.1 ChatMemory接口定义ChatMemory接口只定义了三个核心方法所有的会话存储实现都必须遵守这个契约/** * 会话存储抽象接口 * 所有存储实现内存、Redis、MongoDB等都必须实现这个接口 */publicinterfaceChatMemory{/** * 添加消息到指定会话的历史记录中 * param conversationId 会话唯一标识区分不同用户的不同对话 * param messages 要添加的消息列表 */voidadd(StringconversationId,ListMessagemessages);/** * 获取指定会话的最近N条历史消息 * param conversationId 会话唯一标识 * param lastN 要获取的最近消息条数避免上下文过长 * return 历史消息列表 */ListMessageget(StringconversationId,intlastN);/** * 清空指定会话的历史记录 * param conversationId 会话唯一标识 */voidclear(StringconversationId);}3.2 可插拔的存储实现基于这个接口Spring AI提供了多种开箱即用的存储实现同时也支持用户完全自定义InMemoryChatMemory默认实现基于JVM内存存储适合开发测试环境RedisChatMemory基于Redis存储适合生产环境分布式部署MongoDBChatMemory基于MongoDB存储适合需要持久化大量会话数据的场景自定义实现只需要实现ChatMemory接口并重写三个方法即可对接MySQL、Elasticsearch等任何存储系统3.3 基于Bean的解耦注入Spring AI通过依赖注入实现了存储实现的无缝切换。我们只需要将ChatMemory接口的实现类声明为Spring BeanChatClient会自动依赖这个接口而不依赖具体的实现类。这意味着当我们需要从内存存储切换到Redis存储时只需要修改ChatMemory Bean的返回值其他所有业务代码一行都不用改。这就是面向接口编程和解耦的强大之处。四、基于AOP的无侵入式会话记忆实现和上一篇的日志功能一样会话记忆功能也是通过MessageChatMemoryAdvisor这个环绕通知实现的。它会在大模型调用的前后自动完成历史消息的读取和写入业务代码完全不需要关心。MessageChatMemoryAdvisor的完整工作流程请求前置增强从ChatMemory中根据会话ID获取历史消息自动拼接到当前请求的消息列表中调用大模型将包含完整历史的请求发送给底层Chat Model请求后置增强将本次对话的用户问题和大模型回答一起添加到ChatMemory中返回结果将增强后的响应返回给用户整个过程对业务代码完全透明真正实现了无侵入式增强。五、多轮会话记忆实战步骤我们基于之前的项目只需要三步即可实现完整的多轮会话记忆功能。5.1 第一步声明ChatMemory Bean首先在配置类中声明ChatMemoryBean这里我们先使用默认的内存实现importorg.springframework.ai.chat.memory.ChatMemory;importorg.springframework.ai.chat.memory.InMemoryChatMemory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;ConfigurationpublicclassCommonConfiguration{/** * 声明会话存储Bean * 这里使用内存存储InMemoryChatMemory * 未来切换到Redis只需要把返回值改成RedisChatMemory即可 */BeanpublicChatMemorychatMemory(){returnnewInMemoryChatMemory();}}5.2 第二步配置MessageChatMemoryAdvisor将MessageChatMemoryAdvisor添加到ChatClient的默认Advisor列表中这样所有通过这个ChatClient发起的对话都会自动应用会话记忆功能importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;importorg.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;importorg.springframework.ai.ollama.OllamaChatModel;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;ConfigurationpublicclassCommonConfiguration{BeanpublicChatMemorychatMemory(){returnnewInMemoryChatMemory();}/** * 自定义ChatClient添加会话记忆和日志增强器 * param model 自动注入的大模型客户端 * param chatMemory 自动注入的会话存储依赖接口不依赖具体实现 * return 配置好的ChatClient实例 */BeanpublicChatClientchatClient(OllamaChatModelmodel,ChatMemorychatMemory){returnChatClient.builder(model).defaultSystem(你是一个热心、可爱的智能助手你的名字叫小博客请以小博客的身份和语气回答问题。).defaultAdvisors(newSimpleLoggerAdvisor(),// 日志增强器保留之前的功能newMessageChatMemoryAdvisor(chatMemory)// 会话记忆增强器).build();}}5.3 第三步接口层添加会话ID参数最后我们需要在Controller层接收前端传递的会话ID并将其传递给Advisor上下文。这里用到了Lambda表达式的写法我们可以用一个非常形象的比喻来理解它Lambda表达式的打印机比喻这个Lambda表达式a - a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)就像一个打印参数设置器。你可以把Advisor想象成一台已经组装好的打印机它内置了所有的打印逻辑读取历史、拼接消息、保存历史。而Lambda表达式就是你给打印机的参数指令“请用这个会话IDchatId来处理这次打印任务”。你不需要知道打印机内部怎么进纸、怎么喷墨只需要告诉它关键参数它就会自动完成所有工作。完整的Controller代码importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.http.MediaType;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importreactor.core.publisher.Flux;importstaticorg.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;RestControllerpublicclassChatController{privatefinalChatClientchatClient;// 构造器注入ChatClientSpring 4.3推荐方式publicChatController(ChatClientchatClient){this.chatClientchatClient;}/** * 支持多轮对话的流式接口 * param question 用户问题 * param chatId 会话唯一标识前端生成建议使用UUID * return 流式回答打字机效果 */GetMapping(value/chat/stream,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicFluxStringchatStream(RequestParamStringquestion,RequestParamStringchatId){returnchatClient.prompt().user(question)// 核心将会话ID传递给Advisor上下文// Lambda表达式作为参数设置器告诉MessageChatMemoryAdvisor使用哪个会话.advisors(a-a.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId)).stream().content();}}5.4 测试效果启动项目后我们可以通过以下步骤测试多轮对话第一次调用http://localhost:8080/chat/stream?question你好我叫小明chatId123456第二次调用http://localhost:8080/chat/stream?question我叫什么名字chatId123456大模型会正确回答“你叫小明”说明它已经记住了上一轮的对话内容。同时我们可以在控制台看到日志历史消息已经被自动拼接和保存了。六、扩展无缝切换到Redis持久化存储内存存储的缺点是服务重启后会话数据会丢失生产环境我们通常使用Redis进行持久化存储。基于Spring AI的解耦设计切换存储只需要修改一行代码。6.1 引入Redis依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency6.2 配置Redis连接在application.yml中添加Redis配置spring:redis:host:localhostport:6379password:your-redis-password# 没有密码可以不写database:06.3 创建ChatMemory的实现类RedisChatMemory/** * Redis 会话记忆实现类 * 必须 implements ChatMemory */publicclassRedisChatMemoryimplementsChatMemory{privatefinalRedisTemplateString,ObjectredisTemplate;// 构造注入RedisTemplatepublicRedisChatMemory(RedisTemplateString,ObjectredisTemplate){this.redisTemplateredisTemplate;}/** * 存储会话消息 */Overridepublicvoidadd(StringconversationId,ListMessagemessages){// Redis key spring-ai-chat-memory: conversationIdStringkeychat:memory:conversationId;// 把消息列表 rightPush 存入 Redis Listfor(Messagemessage:messages){redisTemplate.opsForList().rightPush(key,message);}}/** * 获取最近 N 条历史消息 */OverridepublicListMessageget(StringconversationId,intlastN){Stringkeychat:memory:conversationId;// 从Redis List获取最新N条returnredisTemplate.opsForList().range(key,-lastN,-1);}/** * 清空会话 */Overridepublicvoidclear(StringconversationId){redisTemplate.delete(chat:memory:conversationId);}}6.4 修改ChatMemory Bean只需要将InMemoryChatMemory改成RedisChatMemory即可importorg.springframework.ai.chat.memory.ChatMemory;importorg.springframework.ai.chat.memory.RedisChatMemory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;ConfigurationpublicclassCommonConfiguration{/** * 无缝切换到Redis存储 * 其他所有代码ChatClient配置、Controller一行都不用改 */BeanpublicChatMemorychatMemory(RedisTemplateString,ObjectredisTemplate){returnnewRedisChatMemory(redisTemplate);}// ChatClient的配置完全不变BeanpublicChatClientchatClient(OllamaChatModelmodel,ChatMemorychatMemory){returnChatClient.builder(model).defaultSystem(你是一个热心的智能助手你的名字叫小博客请以小博客的身份和语气回答问题。).defaultAdvisors(newSimpleLoggerAdvisor(),newMessageChatMemoryAdvisor(chatMemory)).build();}}就是这么简单这就是面向接口编程和依赖注入带来的巨大优势。同样的如果你想切换到MongoDB只需要引入MongoDB依赖然后返回MongoDBChatMemory即可。七、Spring AI会话记忆的核心优势无侵入式增强基于AOP实现业务代码完全不需要关心历史消息的维护可插拔设计通过ChatMemory接口实现存储逻辑的解耦无缝切换不同存储方式高度灵活支持自定义会话ID、自定义历史消息条数、自定义存储策略统一管理所有会话逻辑集中在Advisor中便于维护和扩展总结本文详细讲解了Spring AI多轮会话记忆的实现原理和实战步骤。我们首先理解了大模型无状态的本质然后学习了Spring AI基于ChatMemory接口和Advisor机制的设计思想通过三步实现了完整的多轮对话功能。Spring AI最值得我们学习的就是它的解耦设计思想通过面向接口编程和依赖注入让框架具有极高的扩展性和灵活性。参考资料[1] 黑马程序员. SpringAIDeepSeek大模型应用开发实战视频教程[EB/OL]. 传统Java项目AI化转型必学课程.本文根据黑马程序员SpringAIDeepSeek大模型应用开发实战视频教程传统Java项目AI化转型必学课程学习总结而得部分图片取自视频内容。非专业人士如有错误欢迎指出感谢理解