JAVA25+Spring Boot 4.1.0 +Spring AI 2.0.1 调用模型之长记忆

发布时间:2026/10/30 6:51:14

JAVA25+Spring Boot 4.1.0 +Spring AI 2.0.1 调用模型之长记忆 前置文章JAVA25 Spring Boot 4.1.0 Spring AI 2.0.1 调用 DeepSeek 模型前言大型语言模型LLM本身是无状态的也就是说它不会保留此前交互过的信息。当我们需要在多次交互之间维持上下文或状态时这便成了一个明显的限制。为此Spring AI 提供了聊天记忆Chat Memory功能让我们可以在与 LLM 的多轮交互中存储和检索信息。借助 ChatMemory 这一抽象层我们可以实现多种类型的记忆存储以适配不同的使用场景。本文在前置文章的基础上进一步扩展讲解如何在项目中调用 Chat Memory 功能。按照 Spring AI 2.0.1 中记忆存储方式的不同本篇把最常用的方式归纳为四类第一类是内存存储消息保存在内存中第二类是缓存存储消息保存在 Redis 缓存中第三类是 JDBC 存储消息保存到项目所连接的结构化数据库第四类是向量存储消息保存到项目配置的向量数据库。实际使用时可以根据业务需要选择合适的方式。当然Spring AI 2.0.1 的记忆存储并不止本篇介绍的这几种更多用法可以查阅 Spring AI 2.0.1 官网 API Chat Memory。一、内存存储内存存储无需引入任何第三方依赖开箱即可使用。1、修改前置文章中访问模型请求参数对象AiChatRequest/** * description: 业务接口请求参数对象 * author: LIHAO * createDate: 2026-10-27 14:48:25 */BuilderSchema(title业务接口请求参数对象)publicrecordAiChatRequest(NotBlank(message用户消息不能为空)Schema(description用户消息,requiredModeSchema.RequiredMode.REQUIRED)Stringmessage,Schema(description对话标识 可为空由业务传递保存记忆和保存链路日志时使用)StringconversationId,Schema(description是否启用jdbc长记忆保存启用后会保存问题到jdbc数据库并时带上上下文访问模型)BooleanisAddJDBCMemoryAdvisor,Schema(description是否启用向量长记忆保存启用后会保存问题到向量数据库并带上问题相关的上下文访问模型)BooleanisAddVectorMemoryAdvisor)implementsSerializable{SerialprivatestaticfinallongserialVersionUID1L;}2、修改前置文章中 Service修改有参构造方法//记忆存储对象privatefinalChatMemorychatMemory;/** * 构造函数通过依赖注入获取 ChatClient.Builder。 * * Spring AI 的自动配置会创建 DeepSeekChatClient 实例 * 并通过 Builder 模式提供给我们。 * */publicAiChatService(Qualifier(deepSeekChatModel)ChatModeldeepSeekChatModel,Qualifier(openAiChatModel)ChatModelopenAiChatModel,Qualifier(anthropicChatModel)ChatModelanthropicChatModel,ChatMemorychatMemory){this.chatMemorychatMemory;// 1. 使用 builder 配置默认系统提示词// defaultSystem()设置系统级提示词在所有对话中生效// build()最终创建 ChatClient 实例this.chatClientChatClient.builder(deepSeekChatModel).defaultSystem(请用中文回复).build();}注意ChatMemory 是 Spring AI 自动配置好的一个 ChatMemory Bean可以直接在应用程序中使用。默认情况下它使用内存存储库 InMemoryChatMemoryRepository 来保存消息并通过 MessageWindowChatMemory 实现来管理对话历史记录。如果已经配置了其他存储库例如 Cassandra、Redis、JDBC 或 Neo4jSpring AI 的 ChatMemory 就会改用该存储库。添加存储记忆顾问/** * description: 添加记忆顾问包含结构数据库和向量数据库 * author: LIHAO * createDate: 2026-10-29 11:06:03 */privatevoidaddMemoryAdvisor(AiChatRequestrequest,ChatClient.ChatClientRequestSpecchatClientRequestSpec){if(ObjectUtils.isNotEmpty(request.isAddJDBCMemoryAdvisor())request.isAddJDBCMemoryAdvisor()||ObjectUtils.isNotEmpty(request.isAddVectorMemoryAdvisor())request.isAddVectorMemoryAdvisor()){//会话标识不能为空由业务传递StringconversationIdrequest.conversationId();//判断值Assert.notNull(conversationId,SysAiChatServiceImpl#addMemoryAdvisor添加记忆顾问参数conversationId不能为空);//判断是否添加jdbc存储if(ObjectUtils.isNotEmpty(request.isAddJDBCMemoryAdvisor())request.isAddJDBCMemoryAdvisor()){//判断长时记忆顾问是否为空if(ObjectUtils.isNotEmpty(chatMemory)){//添加长时记忆顾问顾问//添加长时记忆顾问顾问chatClientRequestSpec.advisors(MessageChatMemoryAdvisor.builder(chatMemory).order(1).build());}}//判断是否添加向量存储if(ObjectUtils.isNotEmpty(request.isAddVectorMemoryAdvisor())request.isAddVectorMemoryAdvisor()){//判断向量长时记忆顾问是否为空}//传递 conversationIdif(StringUtils.isNotEmpty(conversationId)){chatClientRequestSpec.advisors(advisor-advisor.param(ChatMemory.CONVERSATION_ID,conversationId));//配置记忆顾问对应业务参数配置默认顾问调用时必须传值给顾问否则会报错);}}}注意conversationId 是业务侧传入的自定义值代表一组对话的 ID。不同组对话的 conversationId 可以不同但同一组对话的 conversationId 必须保持一致否则就无法找到对应的历史记录。所有调用模型方法添加addMemoryAdvisor方法调用/** * description: 单次模型访问一次性返回文本结果 * author: LIHAO * createDate: 2026-10-27 15:05:11 * param: [request] * return: java.lang.String */publicStringgenerateText(AiChatRequestrequest){Assert.notNull(request,SysAiChatService#generateText参数request不能为空);//添加文件信息并返回用户消息Stringmessagerequest.message();//创建 配置单次请求的参数对象ChatClient.ChatClientRequestSpecchatClientRequestSpecchatClient.prompt().user(message);//添加记忆顾问addMemoryAdvisor(request,chatClientRequestSpec);//访问模型并返回returnchatClientRequestSpec.call().content();}/** * description: 单次模型访问一次性返回实体对象结果 * author: LIHAO * createDate: 2026-10-27 16:20:00 */publicActorsFilmsgenerateEntity(AiChatRequestrequest){Assert.notNull(request,SysAiChatService#generateEntity参数request不能为空);//添加文件信息并返回用户消息Stringmessagerequest.message();//创建 配置单次请求的参数对象ChatClient.ChatClientRequestSpecchatClientRequestSpecchatClient.prompt().user(message);//添加记忆顾问addMemoryAdvisor(request,chatClientRequestSpec);//访问模型并返回returnchatClientRequestSpec.call()// 调用大模型// 指定目标类型并开启自修正.entity(ActorsFilms.class,spec-spec.validateSchema()//验证模型返回数据格式是否符合实体对象类型);}/** * description: 单次模型访问流式返回文本结果 * author: LIHAO * createDate: 2026-10-27 16:25:11 * param: [request] * return: reactor.core.publisher.Fluxjava.lang.String */publicFluxStringstreamText(AiChatRequestrequest){Assert.notNull(request,SysAiChatService#streamText参数request不能为空);//添加文件信息并返回用户消息Stringmessagerequest.message();//创建 配置单次请求的参数对象ChatClient.ChatClientRequestSpecchatClientRequestSpecchatClient.prompt().user(message);//添加记忆顾问addMemoryAdvisor(request,chatClientRequestSpec);//访问returnchatClientRequestSpec.stream().content();}/** * description: 单次模型访问流式返回结构化数据对象结果 * author: LIHAO * createDate: 2026-10-27 16:58:14 * param: [request] * return: reactor.core.publisher.Fluxcom.lh.dto.StreamEvent */publicFluxStreamEventstreamEvents(AiChatRequestrequest){Assert.notNull(request,SysAiChatService#streamText参数request不能为空);//添加文件信息并返回用户消息Stringmessagerequest.message();//创建 配置单次请求的参数对象ChatClient.ChatClientRequestSpecchatClientRequestSpecchatClient.prompt().user(message);//添加记忆顾问addMemoryAdvisor(request,chatClientRequestSpec);//AtomicReference 多线程原子性toEvents 跑在 concatMap 的 mapper 里和 supplier 很可能不在同一个线程AtomicReferenceStringfinishReasonnewAtomicReference();AtomicReferenceMapString,ObjectusagenewAtomicReference();returnchatClientRequestSpec.stream()// .chatResponse() 拿到完整 ChatResponse可取 finishReason / usage / metadata.chatResponse()//流每次返回数据触发.concatMap(resp-toEvents(resp,finishReason,usage))// concatWith 拦截的是主流的 onComplete事件不让它提前传给下游// 待上游完成后再订阅第二个流把 finish 帧追加到尾部// 等第二个流也完成后才把 onComplete 传给下游。.concatWith(// Mono.fromSupplier惰性求值 —— concatWith 等上游 complete 之后才订阅第二个源Mono.fromSupplier(()-StreamEvent.finish(finishReason.get()null?stop:finishReason.get(),// 拿不到就兜底 stop别让前端猜usage.get())));}二、Redis 储存博主使用的 Redis 8.8.0服务器上安装的 Redis 版本最好在 6.0 及以上否则项目启动连接低版本 Redis 会报错。1、Maven 引入!-- redis记忆储存 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-chat-memory-repository-redis/artifactId/dependency2、添加 application.yml 配置# 配置文件# 生产环境配置文件server:# 启动端口port:8001# Spring 配置spring:# 应用程序名称application:name:knowledge-base-agent# Spring AI 配置 ai:# DeepSeek 配置 deepseek:# API Key从环境变量读取安全也可直接填写api-key:自己账号deepseek的key# DeepSeek 官方 API 地址国内可正常访问base-url:https://api.deepseek.comchat:# 推荐使用最新模型model:deepseek-v4-flash# 温度0~2值越高输出越随机0.7 平衡创意与准确性temperature:0.7# 最大输出 Token 数可根据需要调整max-tokens:4096# 思考模式thinking:type:disabled# 关键配置禁用思考模式 (enabled:开启思考模式/disabled:禁用思考模式)options:timeout:5000# 5 秒超时# OpenAI 配置 openai:# API Key从环境变量读取安全也可直接填写api-key:自己账号deepseek的key# DeepSeek 官方 API 地址base-url:https://api.deepseek.comchat:# 推荐使用的模型model:deepseek-v4-flash# 温度0~2值越高输出越随机0.7 平衡创意与准确性temperature:0.7# 最大输出 Token 数可根据需要调整max-tokens:4096#配置模型关闭思考模式extra-body下的数据会合并到参数bodyextra-body:thinking:type:disabledoptions:timeout:50000# 5 秒超时# Anthropic 配置 anthropic:# API Key从环境变量读取安全也可直接填写api-key:自己账号deepseek的key# DeepSeek 官方 API 地址base-url:https://api.deepseek.com/anthropicchat:# 推荐使用的模型model:deepseek-v4-flash# 温度0~2值越高输出越随机0.7 平衡创意与准确性temperature:0.7# 最大输出 Token 数可根据需要调整max-tokens:4096# Chat 配置 chat:# 记忆存储配置 memory:repository:# redis记忆储存配置 redis:host:localhostport:6379# knife4j配置(在线接口测试)knife4j:# 开启增强配置enable:true# 开启Swagger的Basic认证功能,默认是falsebasic:enable:false# Basic认证用户名username:admin# Basic认证密码password:123# 开启生产环境屏蔽(当我们部署系统到生产系统,为了接口安全,需要屏蔽所有Swagger的相关资源)production:false3、修改前置文章中 Service这里的修改内容与「一、内存存储」中「1、修改前置文章中 Service」完全一致无需再做改动。原因在于ChatMemory 是 Spring AI 自动配置好的一个 ChatMemory Bean可以直接在应用程序中注入使用。默认情况下它采用内存存储库来保存消息一旦配置了其他存储库例如 Cassandra、Redis、JDBC 或 Neo4jSpring AI 的 ChatMemory 便会自动切换到该存储库。三、JDBC储存博主项目数据库使用的是 MySQL 5.7 MyBatis-Plus要先确保项目已经可以正常连接数据库且可以正常操作业务数据。注意JDBC 储存和 Redis 储存只能二选一不能同时存在。如果 Maven 同时引入Spring AI 创建 ChatMemory Bean会覆盖掉前一个配置。1、Maven 引入!-- jdbc记忆储存 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-chat-memory-repository-jdbc/artifactId/dependency2、添加 application.yml 配置# 配置文件# 生产环境配置文件server:# 启动端口port:8001# Spring 配置spring:# 应用程序名称application:name:knowledge-base-agent# Spring AI 配置 ai:# DeepSeek 配置 deepseek:# API Key从环境变量读取安全也可直接填写api-key:自己账号deepseek的key# DeepSeek 官方 API 地址国内可正常访问base-url:https://api.deepseek.comchat:# 推荐使用最新模型model:deepseek-v4-flash# 温度0~2值越高输出越随机0.7 平衡创意与准确性temperature:0.7# 最大输出 Token 数可根据需要调整max-tokens:4096# 思考模式thinking:type:disabled# 关键配置禁用思考模式 (enabled:开启思考模式/disabled:禁用思考模式)options:timeout:5000# 5 秒超时# OpenAI 配置 openai:# API Key从环境变量读取安全也可直接填写api-key:自己账号deepseek的key# DeepSeek 官方 API 地址base-url:https://api.deepseek.comchat:# 推荐使用的模型model:deepseek-v4-flash# 温度0~2值越高输出越随机0.7 平衡创意与准确性temperature:0.7# 最大输出 Token 数可根据需要调整max-tokens:4096#配置模型关闭思考模式extra-body下的数据会合并到参数bodyextra-body:thinking:type:disabledoptions:timeout:50000# 5 秒超时# Anthropic 配置 anthropic:# API Key从环境变量读取安全也可直接填写api-key:自己账号deepseek的key# DeepSeek 官方 API 地址base-url:https://api.deepseek.com/anthropicchat:# 推荐使用的模型model:deepseek-v4-flash# 温度0~2值越高输出越随机0.7 平衡创意与准确性temperature:0.7# 最大输出 Token 数可根据需要调整max-tokens:4096# Chat 配置 chat:# 记忆存储配置 memory:repository:# jdbc记忆储存配置 jdbc:initialize-schema:always# 开发时自动建表# knife4j配置(在线接口测试)knife4j:# 开启增强配置enable:true# 开启Swagger的Basic认证功能,默认是falsebasic:enable:false# Basic认证用户名username:admin# Basic认证密码password:123# 开启生产环境屏蔽(当我们部署系统到生产系统,为了接口安全,需要屏蔽所有Swagger的相关资源)production:false注意写好jdbc配置启动项目时会自动创建spring_ai_chat_memory的表来保存记录。3、修改前置文章中 Service这里的修改内容与「一、内存存储」中「1、修改前置文章中 Service」完全一致无需再做改动。原因在于ChatMemory 是 Spring AI 自动配置好的一个 ChatMemory Bean可以直接在应用程序中注入使用。默认情况下它采用内存存储库来保存消息一旦配置了其他存储库例如 Cassandra、Redis、JDBC 或 Neo4jSpring AI 的 ChatMemory 便会自动切换到该存储库。四、向量存储向量存储与前三类不同可以与前三类同时存在具体操作会在后面引入向量数据时再详细说明。五、接口测试启动项目访问 http://localhost:8001/doc.html六、总结本文围绕 Spring AI 2.0.1 的聊天记忆Chat Memory能力从前置文章的模型调用代码出发依次演示了内存、Redis、JDBC 三种存储方式的接入过程并对向量存储做了简要说明。整体来看Spring AI 把「记忆」这件事抽象得非常干净业务代码只需要注入一个ChatMemoryBean再通过MessageChatMemoryAdvisor把conversationId传进去剩下的消息存取、窗口裁剪都由框架完成切换存储介质时几乎不用改动业务逻辑。几种存储方式的选型可以参考下表存储方式依赖适用场景注意事项内存存储无需额外依赖本地开发、单机演示、临时会话应用重启后记忆丢失多实例之间不共享Redis 存储spring-ai-starter-model-chat-memory-repository-redis多实例部署、需要跨节点共享会话Redis 版本建议 6.0否则启动连接会报错JDBC 存储spring-ai-starter-model-chat-memory-repository-jdbc已有关系型数据库、希望记忆持久化可查与 Redis 存储互斥同时引入会被覆盖initialize-schema: always会自动建表向量存储向量数据库相关依赖需要语义检索、长期知识沉淀可与前三类共存属于另一条能力线几个实践中的要点值得再强调一次conversationId是记忆的钥匙。同一组对话必须始终传同一个值否则模型找不到历史记录表现为「记忆失效」。建议由业务侧统一生成并持久化例如「用户 ID 会话 ID」的组合。存储实现只能选一个向量存储除外。内存、Redis、JDBC 三者最终都会产出同一个ChatMemoryBean同时引入时后加载的配置会覆盖前一个排查问题时容易误判建议在 Maven 依赖层面就做好隔离。记忆不是无限的。默认的MessageWindowChatMemory只保留最近若干条消息超出窗口的会被丢弃。如果业务对上下文长度有更高要求可以自定义ChatMemory实现或结合向量存储做长期记忆召回。聊天记忆 ≠ 聊天历史。ChatMemory面向的是「当前对话上下文」而不是全量消息归档需要完整审计或回溯时应另建表用 Spring Data 等方式单独存储。至此Spring AI 聊天记忆的几种常用落地方式就介绍完了。后续文章会继续围绕向量存储与 RAG 展开把「短期记忆 长期知识」组合成一套完整的对话应用方案。如果本文对你有帮助欢迎点赞收藏也欢迎在评论区交流你在实际项目中遇到的记忆管理问题。
延伸阅读

更多相关文章

2026/10/30 6:51:14

86万粉丝博主怀孕坐月子期间在快手把生意翻了5倍

从农村情侣到86万粉丝博主:怀孕坐月子期间她在快手把生意翻了5倍在快手,有这样一对夫妻。他们来自湖南农村,没车没房,谈恋爱很多年才登记结婚,连婚纱照都没拍。女生叫小萝卜,男生是她的丈夫,两人…

2026/10/30 6:51:14

短剧源码二次开发验收:短剧系统V2的模块边界与源码交付

短剧源码发展到现在,判断一套系统值不值得长期接手,已经不能只看演示站功能是不是齐全。尤其短剧系统从单一播放扩展到短剧、小说、实物商城、广告分销以后,代码规模、业务关系和第三方配置都会明显增加。对于准备长期运营或者继续二次开发的…

2026/10/30 6:46:14

微信小程序 map 地图组件实战案例

一、前言微信小程序自带 map 地图组件,可以快速在页面渲染腾讯地图,支持标记点 marker、弹窗、选点、定位、导航等功能。 本次为课程作业案例:在地图上展示三个标记点位(我的位置、黑龙江东方学院南门、第一学生公寓)&…

2026/10/30 8:06:19

系统规划与管理师-第九章-考点记忆

1、信息系统服务管理的主要活动。(1)服务战略规划(2)服务设计实现(3)服务运营提升(4)服务退役终止(5)持续改进与监督2、服务战略规划的主要活动。&#xff08…

2026/10/30 8:06:19

Python中apply与map的区别

在分享里面讲述apply与map二者之间的区别, 以及阐述使用技巧, 以此来助力你利用更好的方式掌握数据处理方法。1、 处理数据之际, 能够借由apply()函数、()函数以及map()函数来对数据施加操作, 其对此予以支持的有传入内置函数、经由def定义的自定函数或者匿名函数, 可灵活地达成…

2026/10/30 8:06:19

编程基础学习

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/30 8:06:19

Go语言Goroutine调度机制与负载均衡深度解析

1. Go Routine 调度机制深度解析Go语言的并发模型核心在于goroutine,这是一种比线程更轻量级的执行单元。每个goroutine初始仅需2KB栈空间(可动态扩容),创建和销毁成本极低。与操作系统线程1:1绑定的传统模型不同,Go采…

2026/10/29 17:05:52

全国计算机二级C语言真题PDF高效刷题指南

简介:这份PDF是全国计算机二级C语言考试的试题与答案解析,专为备考二级C语言的考生准备。内容涵盖C语言特点与设计理念、程序基本组成单位、main函数执行流程、函数嵌套调用规则、标识符命名规范、整型实型字符型数据类型、八进制十六进制数合法形式、运…

2026/10/29 17:06:02

requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南

简介:一份以实战为导向的Python爬虫入门资源,面向具备基本编程基础、希望快速掌握网络数据抓取的初学者,可解决从零编写简单爬虫的常见困惑。实例以requests库发送HTTP请求、BeautifulSoup解析HTML文档,完整演示从网页中提取 标题…

2026/10/29 17:06:13

递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现

简介:这是一篇面向控制系统、信号处理与机器学习等领域学习者的最小二乘参数辨识程序说明文档。资源共包含一个docx格式文档,压缩包大小七十八KB,内容围绕递推最小二乘算法和渐消记忆最小二乘算法展开:先构造一个极点位于单位圆内…

2026/10/30 0:00:46

NCP1342高频准谐振反激控制器:从设计约束到验收的完整方案

简介:NCP1342 高频准谐振反激控制器应用介绍译文文档,面向电源设计工程师、硬件研发人员及开关电源学习者,帮助读者跨越英文原版资料的语言门槛,系统理解这款高集成度 QR Flyback 控制器的设计要点。文档围绕芯片核心特性展开&…

2026/10/30 0:00:46

NCP1342准谐振反激电源设计:从选型到验收的全流程解析

简介:这份文档面向电源设计工程师与开关电源学习者,系统梳理NCP1342高频准谐振反激控制器的应用要点,帮助读者快速理解这款高集成度离线电源变换器控制芯片的工作机制与设计方法。内容涵盖芯片核心特性、引脚功能定义、电气特性参数表、最大额…

2026/10/30 0:00:46

Web系统自动登出机制设计与实现全解析

1. 系统自动登出机制的设计初衷在各类Web应用和后台管理系统中,自动登出功能早已成为标配。这个看似简单的功能背后,实际上涉及用户安全、资源管理和体验优化的多重考量。作为从业十多年的老开发,我见过太多因为登出逻辑设计不当导致的安全事…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑