news 2026/10/18 9:41:08

AI Agent的记忆分层:短期状态块与长期向量检索实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI Agent的记忆分层:短期状态块与长期向量检索实践

1. 记忆设计的出发点:先分清 Agent 到底该记住什么

做 Agent 的人迟早都会撞上一堵墙:对话一长,模型就开始“失忆”。最开始我以为是上下文窗口不够大,后来换成更大窗口的模型,问题依然存在——因为根本矛盾不在窗口大小,而在于“什么都往上下文里塞”这件事本身就不对。

“2.3 短期记对话长期存向量”这个标题其实已经点破了核心思路:记忆不应该是一锅乱炖,而是分层的。短期记忆负责“当下这轮对话发生了什么”,长期记忆负责“跨会话仍然有用的信息”,而向量存储负责让长期记忆能被快速检索到。这三者各司其职,才能让 Agent 既不失忆,又不被冗余信息拖垮。

先说清楚一个容易被误解的点。很多人一听到“短期记忆”,第一反应是 Redis 里存个 JSON,设个过期时间,完事。这思路没错,但太粗了。短期记忆的真正价值不在于“暂存”,而在于“参与当前决策”。如果只是把聊天记录原样存下来,那不叫记忆,叫日志。短期记忆应当经过结构化处理——谁说了什么、话题是什么、当前任务状态如何、还有哪些待办事项——然后以紧凑的形式拼进下一次请求的上下文里。

长期记忆则完全是另一回事。它不追求“完整”,追求的是“有用”。一段对话结束后,真正值得留下来的不是每句话,而是从中提炼出的用户偏好、项目背景、关键结论、长期目标这类信息。这些信息经过向量化之后存入向量数据库,下次遇到相似场景时通过相似度检索召回,再拼进上下文。

所以记忆设计的第一步,不是选技术栈,而是先回答一个问题:你的 Agent 面对的是什么样的任务场景?客服机器人需要记住的是用户身份和订单状态,编程助手需要记住的是项目结构和编码偏好,个人助理需要记住的是日程和人际关系。场景不同,记忆的内容和生命周期完全不同。别指望一套通用方案打天下,那不现实。

我当时的做法是先列了一张表,把 Agent 会遇到的信息按“生命周期”分类:几秒钟内失效的(比如用户刚说的半句话)、单次会话内有效的(比如本轮对话的中间状态)、跨会话长期有效的(比如用户是左撇子、偏好简洁回复、负责某个项目)。分完类之后,短期和长期的边界就自然浮现了。

2. 短期记忆的实现:别把原始聊天记录塞进上下文

短期记忆这块,最容易犯的错误就是图省事,直接把最近的 N 条消息原样拼进 prompt。这做法在小项目里跑起来没问题,但一旦对话轮次超过二十轮,token 消耗和模型理解质量都会急剧恶化。模型要在几十条混杂着寒暄、废话、重复信息的消息里找重点,出错率可想而知。

2.1 短期记忆的结构化提炼

我采用的方案是“结构化摘要 + 滑动窗口”的组合。简单来说,每轮对话结束后,不是把原文存下来,而是让 Agent(或者一条独立的提炼链路)从本轮对话中提取出结构化信息:当前话题、用户意图、关键实体(人名、项目名、时间、地点)、决策结论、待办事项。这些结构化字段拼成一个紧凑的“会话状态块”,存进短期存储。

下一次请求进来时,把最近的会话状态块和最近几轮关键消息一起拼进上下文,而不是把全部历史消息都搬进去。这样一来,上下文里保留的是“当前发生了什么”,而不是“历史上每一句话是什么”。

这里面有个细节很关键:会话状态块的生成频率。每轮对话都重新生成一次,成本太高,而且很多轮次根本没有新信息。我实测下来,比较合理的方式是“事件驱动更新”——只有当对话出现明显的状态变化时才触发更新。比如用户从“询问方案”切换到“确认下单”,或者从“讨论 A 项目”切换到“讨论 B 项目”,这时才生成一个新的状态快照。没有变化的寒暄轮次,直接跳过。

2.2 滑动窗口的动态计算

滑动窗口的大小也不是拍脑袋定的。它取决于两个因素:模型上下文窗口总大小,和长期记忆召回内容的预估长度。我当时给自己定了一个分配原则:短期记忆块 + 长期记忆召回块 + 任务指令,三者之和不要超过上下文窗口的 60%,剩下的 40% 留给模型推理和当前轮次用户输入。

比如一个 8K 上下文的模型,短期记忆块控制在 40% 也就是 3.2K 以内,长期召回块控制在 20% 也就是 1.6K 以内,指令和系统提示固定占 1K,当前用户输入最多占 2.2K。超出部分就截断或者摘要压缩。这个比例按实际场景调,但原则不变:永远给模型留足推理空间。

2.3 会话状态块的字段设计

我的会话状态块长这样,供参考:

{ "session_id": "uuid", "topic": "当前话题", "user_intent": "用户当前意图", "entities": ["项目X", "某需求方", "周五截止"], "decisions": ["确定采用方案B"], "pending": ["等待某需求方回复报价"], "last_update": "timestamp" }

这几个字段看着简单,配合起来能解决一个大问题:上下文的“连贯性”。模型拿到这个状态块后,哪怕中间隔了几轮没看到完整对话,也能立刻接上话题。比如用户新开一个会话说“上次那个方案改一下”,如果没有状态块,模型完全不知道“那个方案”是什么;有了状态块,它可以直接从 entities 和 decisions 里定位到对应内容。

3. 长期记忆的向量化:从对话中沉淀真正的价值

长期记忆的核心不是“存储”,而是“沉淀”。每一轮对话结束后,该留下什么、该丢到什么,这需要一套清晰的规则,而不是随缘。

3.1 记忆提取的两条路径

我试过两条路径来生成长期记忆:一条是在线提炼,一条是离线批量处理。

在线提炼的意思是,每次对话结束后,直接让 Agent 判断“这轮对话有没有值得长期留存的信息”,如果有,就生成一条结构化的长期记忆记录,向量化后写入向量库。这条路径的优点是实时性好,对话刚结束,记忆就已经入库,下次对话立即能用。缺点是增加每次对话的响应延迟和 token 成本,而且提炼质量直接受模型能力影响。

离线批量处理则完全不同。对话日志先落到存储里,然后每隔一段时间(比如每小时或每天)跑一个批处理任务,把这段时间内的对话整体扫描一遍,提炼出长期记忆,再写入向量库。这条路径的优点是提炼质量更高——批处理可以用更强的模型、更长的上下文,可以从多轮对话中看到完整的来龙去脉;缺点是记忆入库有延迟,用户刚聊完的内容可能要等几十分钟甚至几小时后才能被记忆系统感知。

我自己的项目里用的是混合模式:关键对话(用户明确表达了偏好、做了决策、讨论完一个重要项目)走在线提炼,普通对话走离线批量。判断“关键”与否,看两个信号——对话中是否包含决策类词汇(确定、选择、决定、改成),或者是否涉及用户预设的重要实体(项目名、关键人名)。这种分层处理在成本和效果之间取得了不错的平衡。

3.2 向量化的核心参数:embedding 模型和 chunk 策略

长期记忆写好之后,向量化这一步有不少讲究。embedding 模型的选择直接影响检索质量。我对比过几类模型之后,结论是:小模型(几百维)速度快、成本低,但对语义细节的区分度有限;大模型(上千维)效果好,但速度慢、存储成本高。如果对话场景本身是垂直领域(比如编程、法律、医疗),建议优先选择在该领域语料上微调过的 embedding 模型,通用模型在垂直场景下经常会把关键术语的语义搞偏。

还有一个经常被忽略的点:chunk 策略。一条长期记忆如果太长,直接整体向量化,检索精度会下降。因为向量只代表整个文本块的“平均语义”,当文本块包含多个主题时,相似度检索就会失真。我做过一个对比测试,把同样一段 500 字的记忆分别按整段向量化和按句子级别拆分成多个向量,后者在检索命中率上明显胜出,尤其是当用户用口语方式查询时(比如“上次他说那个方案怎么改来着”),分布式向量能更好地辅助定位。

实际操作中,我对每条长期记忆做了这样的处理:超过一句话的内容,按语义完整性拆成多个片段,每个片段单独向量化;同时保留一条整体摘要的向量,用于粗粒度检索。检索时先召回摘要层,再定位到具体片段层,形成两级召回。这个方式在数据量超过一万条记忆时效果尤其明显。

3.3 向量库的选型和元数据设计

向量数据库的选型,市面上选项很多,但对中小规模场景(几百万条以内)来说,选择的标准其实很简单:查询性能能否满足你的延迟要求、是否支持元数据过滤、运维成本是否可控。

我特别想强调“元数据过滤”这一点。如果向量库里只有向量和原文,检索时你会遇到一个很难受的问题:所有相似内容一起涌出来,你根本分不清哪条来自哪个项目、哪个时间、哪种类型。给每条记忆加上元数据——项目标签、时间戳、类型(用户偏好/项目信息/决策记录)、重要性分数——检索时先按元数据过滤再算相似度,精度和速度都会显著提升。

举个例子,用户问“这个项目的截止日期是什么”,如果你只做向量相似度检索,很可能会召回一堆和项目相关但完全不包含截止日期信息的记忆。但如果先按“项目名=XX”过滤一遍,再在过滤结果里做相似度排序,命中率会高得多。这个思路和数据库索引的道理一样:先缩小范围,再精确定位。

4. 短期与长期的衔接:记忆的写入、更新和淘汰机制

很多人在设计记忆系统时只关注“怎么写进去”,忽略了“怎么更新”和“怎么淘汰”。实际上,记忆系统的健壮性恰恰体现在后两者。

4.1 记忆写入的时机选择

长期记忆的写入时机,我建议遵循一条基本原则:对话结束之后,而不是对话进行之中。对话进行中写入记忆,会和当前的推理过程互相干扰。尤其是在 Agent 边对话边思考“这句话要不要存下来”的时候,主任务的响应质量一定会受影响。

正确做法是:对话结束后触发一个异步记忆写入任务,用独立的上下文窗口来执行提炼和向量化。用户不会感知到这个过程(它不阻塞对话响应),记忆也能在用户开始下一轮对话前完成写入。这个“先回复、后记忆”的思路,同时兼顾了响应延迟和记忆完整性。

4.2 记忆更新的冲突处理

长期记忆必然会遇到“前后矛盾”的情况。比如用户周一说要 A 方案,周三改口说还是 B 方案比较好。如果你只是不停地追加新记忆,向量库里就会同时存在两条互相矛盾的信息,检索时模型会无所适从。

我的处理方案是引入“记忆置信度”概念。每条记忆除了内容和向量之外,还有一个置信度分数和时间戳。新记忆写入时,先做一次相似度检索,如果发现与已有记忆高度重合(相似度超过某个阈值),则触发更新流程而不是新增流程:新记忆的置信度设为 1.0,旧记忆的置信度降为 0.3,同时保留旧记忆但显著降低其检索权重。这样既保持了记忆的连续性,又不会让旧信息彻底消失造成“历史断层”。

4.3 记忆淘汰:定期清理低价值内容

长期记忆不是越多越好。向量库里面堆了几百万条低价值的碎片记忆,检索时会消耗大量算力在无效内容上,还可能召回不相关的噪声。我给自己定了一套淘汰规则:

  • 超过一定时间(比如 90 天)且置信度低、从未被检索命中的记忆,归档到冷存储。
  • 会话状态块按 TTL 过期(比如 24 小时),过期后只保留离线提炼的长期记忆。
  • 每周跑一次去重任务,把高度重复的记忆合并成一条。

这套规则看起来简单,但执行之后检索质量的提升非常明显——核心原因在于,减少了噪声之后,相似度排序的 Top K 结果里真正有用的比例大幅上升。

5. 常见问题与排查技巧实录

5.1 检索结果不相关:八成是元数据过滤没做好

如果你发现 Agent 经常把无关记忆当成相关记忆使用,先去查检索链路里有没有做元数据过滤。我踩过最深的坑就是:一开始只靠向量相似度,结果用户问“周末有什么安排”,系统把上个月讨论的“周末活动方案策划”也捞出来了。文本确实相关,但语境完全错位。

排查思路分三步:

  1. 确认 query 里可以提取出哪些实体或标签信息,基于意图做初步过滤。
  2. 在召回阶段,先按元数据过滤,再跑向量相似度。
  3. 在重排阶段,用打分规则(如时间衰减、置信度加权)对召回结果重新排序。

5.2 短期记忆挤占上下文空间:压缩策略要跟上

短期记忆块如果越攒越大,很快会把上下文窗口撑爆。我们的策略是给会话状态块设一个上限,超过上限时触发摘要压缩:把较早的状态块摘要化,只保留最新几轮的关键信息。这个过程和人的记忆衰退类似,新记忆优先保留,旧记忆变成更粗粒度的摘要。

具体做法:设定短期记忆块的最大条数(比如 20 条),超过后运行一次压缩任务,将最旧的 10 条合并为 1 条摘要,再重新向量化存储到长期记忆库。这样短期记忆块的大小始终保持在一个稳定范围内,不会随着对话轮次无限增长。

5.3 向量召回速度慢:索引参数调优思路

向量库数据量上来之后,召回速度下降是很正常的事。排查方向主要有两个:一是索引类型选择,如果数据量在百万级以内,HNSW 或 IVF 都是合理选择;二是查询的候选集大小,调大或者调小需要结合实际召回质量来权衡。我自己的经验是,在保持召回质量不下降的前提下,尽量缩小候选集,因为向量距离计算的耗时是非常线性的。

另外一个小技巧:如果发现召回速度慢和服务器配置的关系不大,很可能是因为 query 向量没有做归一化处理。很多向量库在做距离计算时会直接使用点积或余弦相似度,如果两边向量长度差异很大,计算效率会受影响。统一归一化之后再写入和查询,速度会有肉眼可见的提升。

5.4 记忆冲突场景的实测案例

我印象最深的一个案例是:用户第一次说“我习惯早上九点开始工作”,一周后又说“我每天早上七点要到健身房”。两条记忆都被成功提炼,但第二天 Agent 在安排日程时出现了矛盾:既说“九点开始工作”又说“七点已经锻炼完”,逻辑上其实不矛盾,但模型在组织语言时给出了混乱的表达。

这个问题的根源不在模型,而在记忆系统本身——两条记忆的置信度相同,检索时同时被召回,模型不知道该以哪条为主。加入置信度机制并让后写入的记忆获得更高权重之后,这类问题基本消除。核心经验是:记忆系统不只是存储和检索,还必须承担“裁决”职责,决定哪条信息在当前情境下的优先级更高。

6. 关于记忆设计,我最后想补充的几点

记忆设计这件事,踩的坑越多,越能体会到一个道理:它本质上是一个资源分配问题。上下文空间是有限的,存储成本是有限的,模型注意力是有限的,你必须在这些限制条件中找到最优解。不要追求“记住一切”,那既不现实也没必要。

我个人的经验是,先从一个最小的闭环开始:短期用结构化状态块,长期用向量库,中间靠置信度和元数据衔接。跑通之后再逐步加策略——摘要压缩、离线提炼、两级召回——每一步都带着明确的“要解决什么问题”去加,而不是为了炫技。

另外,测试驱动的思路对记忆系统特别重要。找一个和你的实际场景相近的测试集,覆盖多轮对话、跨会话召回、信息冲突、长尾查询这几类情况,每次改动后都跑一遍。没有测试集就调参,效果好不好全看运气,这在工作场景里是不能接受的。

如果你正准备给自己的 Agent 加记忆,我的建议是:别上来就研究复杂算法,先把刚才说的四件事做清楚——短期状态块怎么写、长期记忆怎么提炼、向量库怎么建、冲突怎么处理。这四件事想明白了,你的 Agent 的“记性”已经能超过大多数同类产品了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 9:37:27

Postman 8.9.1 中文包替换指南:版本校验、备份与避坑

简介:Postman 8.9.1 中文包面向接口测试与 API 调试的开发者,尤其是习惯中文界面、希望降低英文菜单上手成本的测试与后端人员。资源以 zip 压缩包分发,内含约 2000 个文件,整体约 56.1MB,主体由大量 js 脚本、md 说明…

作者头像 李华
网站建设 2026/10/18 9:36:55

企业官网改版实战:从静态生成到SEO优化全流程指南

朋友跟我聊起官网改版的时候,我第一反应是:别又让我整一个“大名片”放网上。后来真接手了“幻乐科技官网”这个项目,才意识到现在做企业官网跟五年前完全是两码事。用户点开之前,已经默认你有官网,能不能在几秒内让他…

作者头像 李华
网站建设 2026/10/18 9:36:17

小文件上传接口设计:从Uniapp前端到Spring Boot后端完整实现

小文件上传接口,为什么能单独讲一讲在智能视频推荐小程序这类项目里,上传接口往往被当成“工具人”角色:前端调一个接口,后端存一下文件,完事。但真正做过生产环境的人都知道,文件上传是所有接口里最容易出…

作者头像 李华
网站建设 2026/10/18 9:35:15

OpenCV 3.2 源码编译安装全攻略:从依赖到验证

简介:OpenCV 3.2 是开源计算机视觉库的一个经典版本,面向计算机视觉、机器学习与人工智能方向的开发者及学习者,可用于图像处理、特征检测、目标识别、视频分析等实际项目。资源包共178个文件,以116个hpp头文件与57个h头文件为主&…

作者头像 李华
网站建设 2026/10/18 9:34:32

C++高并发内存池设计:三层缓存架构、锁优化与性能调优实战

最近又把高并发内存池这个项目完整过了一遍。说实话,这类型的项目看起来就是“把内存提前准备好,等线程来取”,但真正从单线程版本改造成高并发版本之后,你会撞上一堆预想不到的麻烦:锁竞争、内存碎片、线程退出后内存…

作者头像 李华