AI Agent入门实战第9篇,Spring AI Alibaba短时记忆系统入门--给AI装上“大脑皮层”
让Java开发者像写Spring Boot一样开发AI应用——第二阶段第三篇
写在前面
前两篇,我们让Agent学会了“动手”——从单个工具到多工具协同,Agent已经能调用外部API、查询天气、推荐景点。
但还有一个致命问题:Agent记不住。
你刚告诉它“我叫张三”,下一轮对话它就问“你叫什么名字?”;你刚说完“我在杭州”,它转头就问“你在哪个城市?”
这就像一个患有严重失忆症的实习生——能力很强,但每次对话都要从头认识你。
今天,我们给Agent装上“大脑皮层”——短时记忆系统。
📌 本文是第二阶段“核心能力实战篇”的第三篇。前两篇我们解决了“动手”问题,今天解决“记忆”问题——让Agent从“一次性对话”走向“持续对话”。
一、为什么Agent需要“记忆”?
大模型的“无状态”本质
在LLM的底层原理中,模型本身是无状态(Stateless)的。每一次API调用对模型来说都是全新的开始,它并不知道你3秒前问了什么。
课堂演示:无状态对话的问题
<span leaf="">第一次调用:</span>
所谓的“多轮对话”,本质上是在每次请求时,将历史消息列表连同当前用户输入一起打包发送给模型:
<span leaf=""><span>[系统提示词]</span> + <span>[历史消息1]</span> + <span>[历史消息2]</span> + ... + <span>[用户最新提问]</span> → LLM → 回复</span>
💡 大模型就像一个“健忘的天才”——你问什么他都能答,但你刚说完他就忘了。所谓的“多轮对话”,全靠应用程序在背后帮他把历史消息重新塞回去。
二、Agent记忆体系全景图
在Spring AI Alibaba中,记忆体系分为三个层次:
| 记忆类型
|
定义
|
存储介质
|
生命周期
|
典型实现
| 短时记忆 |
当前会话窗口内的上下文
|
内存 / Redis
|
会话期间,随会话结束而消失
|
MemorySaver / RedisSaver
| | 长时记忆 |
跨多个会话的持久化知识
|
向量数据库
|
永久(除非主动删除)
|
Vector Store + RAG
| | 用户画像记忆 |
用户偏好、身份、习惯
|
结构化存储
|
永久(除非用户修改)
|
自定义存储
|
💡 今天聚焦短时记忆——它是实现多轮对话连贯性的基础。长时记忆和用户画像将在后续课程中专门讲解。
三者关系示意图:
<span leaf=""><span>┌─────────────────────────────────────────────────────────────┐</span></span>
三、ChatMemory核心概念
ChatMemory vs ChatHistory
在使用之前,先区分两个容易混淆的概念:
| 概念
|
定义
|
用途
| ChatMemory(聊天记忆) |
大模型保留并用于保持上下文感知的信息
|
用于当前对话的推理
| | ChatHistory(聊天历史) |
整个对话历史的完整记录
|
用于审计、分析、归档
|
💡 ChatMemory是“模型正在用的上下文”,ChatHistory是“完整的聊天记录归档” 。两者是不同的概念——前者用于推理,后者用于审计或分析。
滑动窗口策略
短时记忆面临的核心矛盾:
-
对话越长,上下文越大
-
上下文越大,越可能撑爆模型的上下文窗口
-
Token越多,调用成本越高
解决方案:滑动窗口(Message Window)
MessageWindowChatMemory维护一个最大指定大小的消息窗口。当消息数量超过最大值时,会删除较旧的消息,同时保留系统消息。
<span leaf=""><span>// 配置滑动窗口:保留最近10条消息</span></span>
窗口大小的权衡:
| 窗口大小
|
优点
|
缺点
| 大(如50条) |
上下文完整,信息丢失少
|
Token消耗大,成本高,可能超限
| | 小(如5条) |
Token消耗小,成本低
|
可能丢失关键上下文信息
| | 适中(如20条) |
平衡成本和上下文完整性
|
框架默认值
|
四、短时记忆的两种实现方式
在Spring AI Alibaba中,短时记忆有两种核心实现:
| 方式
|
核心组件
|
特点
|
适用场景
| MemorySaver |
内存存储
|
简单快速,服务重启即丢失
|
开发测试
| | RedisSaver |
Redis持久化
|
持久化,多实例共享
|
生产环境
|
方式一:MemorySaver——内存存储
MemorySaver是会话级缓存,它的粒度是threadId,不是userId。
<span leaf=""><span>// 最简单的配置:使用内存存储</span></span>
MemorySaver的特点:
| 特点
|
说明
✅ 简单
|
无需额外配置,开箱即用
| |
✅ 快速
|
内存读写,速度最快
| |
❌ 易失
|
服务一重启,记忆就没了
| |
❌ 单机
|
不适合多实例部署
|
💡 MemorySaver就像白板上的笔记——写上去快、看得清楚,但一擦就没了。
方式二:RedisSaver——Redis持久化
RedisSaver将Agent状态持久化到Redis中:
<span leaf=""><span>// 配置RedisSaver</span></span>
RedisSaver的优势:
| 优势
|
说明
✅ 持久化
|
服务重启,对话历史还在
| |
✅ 多实例共享
|
多个服务实例可以共享同一个Redis
| |
✅ 高性能
|
Redis读取速度很快
|
实现原理:
<span leaf="">┌──────────────────────────────────────────────────────────────┐</span>
五、threadId与会话管理
什么是threadId?
threadId是会话的唯一标识符,用于实现对话状态的隔离和持久化。
threadId的两大作用:
-
作为多轮对话上下文管理器的键(key)
-
实现不同会话的状态隔离
💡 threadId就像聊天窗口的ID——每个聊天窗口都有自己的threadId,不同窗口的对话互不干扰。
关键理解:threadId的粒度是会话,不是用户。同一个用户可以拥有多个threadId(多个会话),不同用户也可以拥有各自的threadId。
如何使用threadId?
<span leaf=""><span>// 为不同用户/会话指定不同的threadId</span></span>
使用threadId的关键要点:
-
threadId用于关联同一对话的上下文
-
无需每次执行都生成新的threadId(否则无法保留多轮对话状态)
多会话隔离管理
核心原则:不同threadId的对话互不干扰。
<span leaf=""><span>// 用户A的会话</span></span>
六、实战:构建带记忆的Agent
实战一:用MemorySaver实现基础对话记忆
<span leaf=""><span>@Configuration</span></span>
测试验证:
<span leaf=""><span># 第一次对话</span></span>
实战二:用RedisSaver实现会话持久化
Step 1:添加依赖
<span leaf=""><span><!-- Redis客户端 --></span></span>
Step 2:配置RedisSaver
<span leaf=""><span>@Configuration</span></span>
测试持久化:
<span leaf=""><span># 第一轮对话</span></span>
💡 课堂演示:在课堂上演示“重启服务后记忆仍然存在”——让学员亲眼见证RedisSaver的持久化能力。
七、短时记忆的最佳实践
开发 vs 生产环境选型
| 环境
|
推荐存储
|
理由
| 开发/测试 |
MemorySaver
|
简单快速,无需额外依赖
| | 生产环境(单机) |
RedisSaver
|
持久化,服务重启不丢数据
| | 生产环境(集群) |
RedisSaver
|
多实例共享,会话状态一致
|
⚠️ 生产环境绝对不要用MemorySaver——服务重启、滚动更新都会导致用户对话历史丢失,用户体验极差。
滑动窗口大小的选择
| 场景
|
推荐窗口大小
|
理由
客服机器人
|
10-15条
|
对话通常较短,过多历史增加延迟
| |
编程助手
|
20-30条
|
需要记住代码上下文
| |
长文本创作
|
5-10条
|
每次输入较长,窗口不宜过大
|
常见问题与避坑指南
| 问题
|
原因
|
解决方案
| 服务重启后记忆丢失 |
使用了MemorySaver
|
切换到RedisSaver
| | 不同用户对话互相干扰 |
使用了相同的threadId
|
每个用户使用独立的threadId
| | 上下文超限 |
对话太长,窗口太大
|
减小滑动窗口大小
| | 记忆“串台” |
threadId复用或生成策略不当
|
确保threadId唯一且稳定
|
八、课后挑战
任务:实现一个支持多用户、多会话的聊天Agent
要求:
-
基于RedisSaver实现对话记忆的持久化
-
支持多用户(不同userId)的会话隔离
-
支持同一用户的多个会话(通过不同的threadId)
-
实现以下API:
-
POST /chat:发送消息(需传入userId和sessionId) -
GET /history:查看某个会话的历史 -
DELETE /clear:清除某个会话
验收标准:
-
Redis已正确配置并启动
-
服务重启后,对话历史仍然存在
-
不同用户的对话互不干扰
-
同一用户的不同会话互不干扰
九、本日核心收获
-
大模型是无状态的——每次API调用都是全新的开始,需要应用程序管理对话历史
-
记忆体系分三层——短时记忆(今天)、长时记忆(后续)、用户画像(后续)
-
两种实现方式——MemorySaver(内存,适合开发)和RedisSaver(Redis,适合生产)
-
threadId是会话的身份证——不同threadId的对话互不干扰,实现多会话隔离
-
滑动窗口控制成本——默认保留最近20条消息,可根据场景调整
-
生产环境用RedisSaver——服务重启不丢数据,多实例共享状态
📌 本文是第二阶段“核心能力实战篇”的第三篇。前两篇我们让Agent学会了“动手”,今天让Agent学会了“记忆”——从“一次性对话”走向“持续对话”。下一篇,我们将进入长时记忆与RAG(检索增强生成),让Agent能够访问私有知识库,回答专属问题。
有任何问题,欢迎在评论区留言交流!
作者:Java老兵搞AI,专注Java生态下的AI应用开发
如果觉得有用,点个「在看」支持一下吧,下期见!
- 原文作者:知识铺
- 原文链接:https://index.zshipu.com/edudaily/post/20260823/AI-Agent%E5%85%A5%E9%97%A8%E5%AE%9E%E6%88%98%E7%AC%AC9%E7%AF%87Spring-AI-Alibaba%E7%9F%AD%E6%97%B6%E8%AE%B0%E5%BF%86%E7%B3%BB%E7%BB%9F%E5%85%A5%E9%97%A8--%E7%BB%99AI%E8%A3%85%E4%B8%8A%E5%A4%A7%E8%84%91%E7%9A%AE%E5%B1%82/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 免责声明:本页面内容均来源于站内编辑发布,部分信息来源互联网,并不意味着本站赞同其观点或者证实其内容的真实性,如涉及版权等问题,请立即联系客服进行更改或删除,保证您的合法权益。转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。也可以邮件至 sblig@126.com