记忆机制:auto memory、三级记忆与持久化
一句话定位:记忆机制是 Harness 部件 ③状态 的「记性」半边——上下文是工作台、记忆是仓库,这套机制决定什么值得写进仓库、写在哪一层、什么时候取回来。前置:什么是 Harness、上下文压缩(压缩后的工作记忆是会话内的「临时仓库」)、记忆的产品行为(记忆文件怎么建、放哪级目录,产品篇已讲,本篇讲机制)。
先从一次「失忆重逢」说起
你跟 Agent 说「我们项目的接口都用 /api/v2 前缀」,它这一轮记得牢牢的,后面所有代码都乖乖带上前缀。
三天后新开会话,它把接口路径写回 /api/v1——会话断了,说过的话就没了。你把这条约定写进项目的记忆文件,下个会话果然生效;可换个项目,你自己的偏好(比如注释风格)又得重说一遍。而当你攒了几百条记忆之后,新问题又来了:每次注入的记忆太多,上下文被塞满,它反而变笨了。
Agent 的「记性」到底存在哪、什么时候写、什么时候取?为什么有的记忆跟项目走、有的跟人走、有的谁都不该看见?
答案是:记忆不是一块存储,而是一套分级的体系——会话内靠上下文、跨会话靠文件、海量了靠检索库;写入有触发判断、加载有位置讲究、隔离有键位设计。三级的读写路径各不相同,正因如此才各有各的失效模式。
图:记忆三级与隔离边界——「第 1 层」到「第 3 层」是本篇编号,右侧是每层该守的隔离线
渐进拆解
你自己的记性就是三级体系——脑子里正想着的(第 1 层,转眼就忘)、便签和笔记本(第 2 层,写下来才靠得住)、图书馆(第 3 层,海量的书按主题检索取回)。没有人把所有事都写进笔记本(太累、翻不过来),也没有人靠「努力回忆」记住图书馆里每本书——分级不是偷懒,是让每一层干自己擅长的事。
跟着一条记忆走完它的一生,写入、注入、退休三段各有机关:
图:一条记忆的一生——上行是写入链(判断、落盘、带来源),下行是注入链(载清单、相关才注入),末尾是退休
大白话:记忆机制就是三道判断题:什么时候写——重复出现的偏好、用户纠正过的事、确定的事实才值得写;写到哪——跟项目的进项目记忆,跟人的进全局记忆,量大的进检索库;怎么读——先读清单不读全文,相关才注入,注入时标明「这是记忆」。
auto memory(自动记忆)
不等你开口,Agent 自己判断「这值得记」并写入第 2 层文件,实现上有两个常被问到的机关(均为社区逆向口径)。
- 写入走的就是普通工具机制:记忆文件不是什么特殊存储,就是 Harness 用写文件工具落盘的普通文件,所以人能直接打开看、直接改、能进版本库,透明是它最大的优点。
- 注入走 system-reminder 而不是直接改系统提示词:系统提示词是「角色设定」,整场会话稳定不变(前缀稳定,提示词缓存才可复用);记忆是「会变的数据」,以带标记的提醒形式插进消息流——既让模型分得清「这是记忆不是当前对话」,又不扰动系统提示词。
这套「你写约定文件 + 它记主题文件 + 压缩兜底」的产品行为,记忆产品篇有完整展开。
三级体系与作用域
- 第 1 层 · 会话内上下文(含压缩后重建的工作记忆):读写零成本,但随会话蒸发。本篇的「工作记忆」取宽义,指会话内在场的全部内容;压缩篇特指重写后重建的工作集,是它的子集。
- 第 2 层 · 跨会话文件:作用域决定隔离——跟项目的锁在项目里,跟人的跨项目随人走。除了约定与偏好,项目的目录结构说明、历史修复的教训也属此层。
- 第 3 层 · 检索库:容量近乎无限,但每次取用都要付检索与注入的预算。
作用域错配是记忆事故的第一大来源:个人偏好写进项目记忆,换个项目就「失忆」;项目约定写进全局记忆,别的项目被无关约定污染。
全生命周期
- 写入:显式指令「记住这个」,或自动抽取候选——判断依据是重复性与纠正信号,同样的话说过两遍、用户刚纠正过一次都是强信号。
- 检索:发生在每轮组装上下文的时候,先按当前任务查出候选,再决定注入哪些;这属于 Context 工程的组装环节,本篇只管记忆侧的排序。
- 更新:约定变更时覆盖旧条目而非追加新条目——两条矛盾记忆并存是最坏的状态。
- 删除:长期不相关就清理。
- 可信度:每条记忆带来源——用户亲口说的、推断的、从旧会话摘要来的;可信度不同,使用时的把握不同。
文件 vs 检索库(第 2 层与第 3 层的分界)
| 文件(第 2 层) | 检索库(第 3 层) | |
|---|---|---|
| 长处 | 透明、可人审、可版本化 | 能语义查询海量记忆 |
| 短处 | 翻不动大量条目 | 对人不可读、调试困难、检索质量依赖排序算法 |
状态外部化是第 2 层的工程化用法:把待办清单、任务进度写成文件(而非指望对话历史),会话断了文件还在。Anthropic 的上下文工程一文把它叫「结构化笔记」,状态机篇的中断恢复靠的正是它。
业界现状(2026)
记忆已经从功能变成赛道:
| 主体 | 定位 | 机制 | 口径数字 |
|---|---|---|---|
| Mem0 | 即插即用的记忆层 | 混合多种存储后端 | 2026-04 算法更新后官方口径:LoCoMo 长对话基准 92.5 分、LongMemEval 94.4 分,单次检索控制在 7K token 以内;论文口径:对 OpenAI 原生记忆约 26% 的相对精度优势,p95 延迟较「把全部上下文塞给模型」的做法低约 91%——省的正是没被塞进去的那些上下文 token |
| Letta(前身 MemGPT) | 有状态 Agent 平台,而非记忆 API | 记忆分层且由 Agent 自己编辑——思路直接来自操作系统「Agent 自己管分页」 | — |
| 图记忆(Mem0 的图变体、Neo4j 系) | 关系查询 | 把人与事的关系存成图,适合「谁和谁是什么关系」这类查询 | — |
共同趋势:检索质量与 token 预算的平衡是这门生意的核心指标——记忆不再是「存得下」,而是「取得准、取得省」。
动手:写入判断与召回管线
写入端与读取端各一段伪代码(写入段呼应上文的「记忆一生」,召回段呼应下方的召回管线图):
def maybe_remember(turn, memory):
for fact in extract_candidates(turn): # 自动抽取候选事实
if not worth_keeping(fact, memory): # 已有同款 / 琐碎 / 已有更新版本
continue
memory.write(fact, scope=scope_of(fact), # 项目级 or 全局级
source=turn.user, when=now()) # 可信度:来源 + 时间
return memory
def recall(memory, query, session_key): # session_key 带租户/会话标识
hits = keyword_search(memory, session_key, query) # 字面一路
hits += vector_search(memory, session_key, query) # 语义一路
hits = mmr_diversify(hits, lam=0.7) # 既相关、又不互相重复
hits = decay_rank(hits, now()) # 时间衰减:新记忆靠前
return inject(hits[:K], as_="system-reminder") # top-k 注入消息流
「四层记忆架构」对照:记忆课程里还有一种按骨架分层的讲法(该课程第 08 讲「四层记忆架构设计」)。公开可考的是讲次标题与它的配套武器——混合搜索、MMR 去重与时间衰减(第 09 讲,目标「准、不重、够新」,正是上面召回管线的三步);四层各自的名目在公开资料中考无可考,本篇不硬凑。可以对照的是分层思想本身:存储按介质分层(对应本篇第 2/3 层)、召回管线独立成层(本篇的管线四步)、时间维度单独治理(时间衰减)——骨架的影子都在三级体系里。
召回端那段是第 3 层的门道,其中的 MMR(最大边际相关)值得单独解释:挑下一条注入记忆时,同时要求「与查询相关」和「与已选中的条目不重复」,两股力量按权重平衡——这就是去重的算法化。整条管线画出来更直观:
图:召回管线——隔离、双路检索、去重、衰减四步,守住「准、不重、够新」三条底线;最左的键位隔离是串味防线
同一概念的叫法对照:
| 本篇用词 | 文献中的其他叫法 | 深挖去处 |
|---|---|---|
| 记忆三级 | 短期 / 长期(时间轴)+ 项目 / 全局(作用域轴) | 记忆产品篇 |
| 四层记忆架构 | 记忆课程第 08 讲的骨架讲法:公开可考仅讲次标题+配套三件武器(混合搜索/MMR/时间衰减,第 09 讲),四层名目公开考无可考、正文已注明不硬凑(本篇重组为三级 + 召回管线) | 本篇 |
| auto memory | 自动记忆 · 主题文件记忆 | 本篇 |
| system-reminder 注入 | 提醒标记 · 动态上下文注入 | 本篇 |
| 状态外部化 | 结构化笔记 · 文件系统记忆 | 状态机 |
| 召回管线 | 混合检索 + MMR + 时间衰减 | 本篇 |
边界与取舍:记性太好也是一种病
记忆注入是有上下文代价的
每次召回都占当轮预算,记性「太好」的 Agent 会把大量似曾相识的历史塞回上下文——注意力被稀释,回答质量反而下降(这正是 Context 工程篇说的注意力预算问题)。取舍标准:注入的记忆要「当前任务用得上」,宁缺毋滥;召回管线的去重与衰减不是锦上添花,是预算防线。
文件与检索库的分界
自测第一问的答案,这里先给结论。判断标准就两条:人要不要直接看它、量有多大。约定、偏好、事实——条目少、需要人审、最好进版本库 → 文件(第 2 层);对话历史级别的海量记忆、靠语义查、人永远不逐条读 → 检索库(第 3 层)。多数团队该从文件起步:透明可调试,出了错一眼能看出来;等条目真的翻不动了再上库,别一上来就把记忆变成黑盒。
串味是记忆系统的信任杀手
多租户(一套系统同时服务多个客户)场景下,A 客户的偏好泄漏给 B 客户,比「失忆」严重得多——失忆是能力问题,串味是事故。防线是把隔离做进键位:每条记忆的键里带上租户与会话标识,检索第一步先按键位圈定范围(即召回管线的第一步),而不是检索完再过滤——事后过滤一旦有漏网就是事故,事前圈定才从结构上把串味堵死。
坏味道预警
记忆只写不清,三个月后没人敢信任何一条旧记忆——缺的是退休机制与可信度标注;如果你的 Agent 「总是提起无关旧事」,缺的是召回的相关性过滤而不是更大的记忆库。记忆的「写而不删」与上下文的「只进不出」是同一种病,药方也同源——判断标准见上下文压缩。
自测
合上文章先自己答,再展开对照:
记忆什么时候该写文件、什么时候该进向量库?判断标准?
判断标准就两条:人要不要直接看它、量有多大。约定、偏好、确定的事实——条目少、需要人审、最好进版本库 → 写文件(第 2 层);对话历史级别的海量记忆、靠语义查询、人不逐条读 → 进向量库/图库(第 3 层)。作用域配套:跟项目的约定锁在项目内,跟人的偏好放全局。实践路径多数团队是从文件起步、撞到量再上库——先用足透明性,再换检索性。
Session 隔离、多租户隔离下记忆怎么设计才不串味?
核心是把隔离做进键位而非事后过滤:每条记忆的键(session_key)带上租户标识与会话标识,检索第一步先按键位圈定范围,圈外的记忆不进候选池——事后过滤一旦漏网就是事故,事前圈定才从结构上把串味堵死。分层看:会话内靠会话本身的边界(见上下文压缩篇),跨会话文件靠文件夹作用域(跟项目的锁项目内、跟人的随人走),检索库靠键位隔离多租户。另配来源标注让污染可追溯;MMR 去重与时间衰减让串进来的旧记忆即使漏网也难以排到注入位。
参考与延伸
- Mem0《State of AI Agent Memory 2026》:LoCoMo 92.5 / LongMemEval 94.4、检索 7K token 以内的官方口径 — mem0.ai/blog/state-of-ai-agent-memory-2026
- Mem0 论文(2025-04):与 OpenAI 原生记忆对比相对精度 +26%、p95 延迟较全上下文基线低 91% — arxiv.org/abs/2504.19413
- Letta(前身 MemGPT)开源仓库:分层自编辑记忆的有状态 Agent 平台 — github.com/letta-ai/letta
- Anthropic《Effective context engineering for AI agents》(2025-09):结构化笔记与状态外部化(Agentic memory 一节)的出处 — anthropic.com/engineering/effective-context-engineering-for-ai-agents
- Anthropic《Building Effective Agents》(2024-12):人机协同与 Agent 设计原则 — anthropic.com/engineering/building-effective-agents
本系列的后续学习路线(推荐顺序):Skill 机制 → 状态机与 Checkpoint → 多 Agent 编排。