大模型本身是无状态的。一次 API 调用结束后,它不会自然保留刚才发生的事情;即使模型支持百万 token 上下文,也只代表“这次能读更多”,不代表它知道什么值得长期保留、旧事实何时失效、用户 A 的记忆不能泄露给用户 B,或者失败经验如何变成下一次可复用的技能。
这正是 Agent 记忆系统要解决的问题。
过去两年里,Agent memory 经历了三个阶段:最初是把完整聊天记录塞回 prompt;随后是把摘要或事实写进向量数据库;现在则开始演化成一套独立的上下文数据系统,同时管理短期状态、长期事实、事件时间、用户画像、执行经验、技能、来源、权限和遗忘策略。
本文不只比较产品功能,而是基于 2026-07-30 的官方文档和源码快照,沿着一条记忆的完整生命周期展开:
Capture -> Write Gate -> Extract -> Resolve -> Store -> Retrieve -> Rerank -> Inject -> Consolidate -> Forget -> Evaluate
调研覆盖 Letta/MemGPT、Mem0、Graphiti/Zep、LangGraph、Cognee、OpenAI Agents SDK、Claude Code、OpenViking、Hindsight、Google ADK/Vertex Memory Bank、AWS AgentCore Memory,并结合 OpenHands、OpenClaw、Hermes 等实际 Agent 的做法。核心源码基线如下:
| 项目 | 2026-07-30 Star | 主要定位 | 本文源码基线 |
|---|---|---|---|
| Mem0 | 62,056 | 通用长期记忆层 | d4869d2 |
| LangGraph | 38,455 | 有状态 Agent 编排与持久化 | 4134145 |
| Graphiti | 29,360 | 双时态知识图谱记忆 | 71a719b |
| Cognee | 29,562 | 图谱化认知与 session distillation | 88aa09b |
| OpenAI Agents SDK | 28,277 | Session、压缩与两阶段沙箱记忆 | 992abf7 |
| OpenViking | 27,631 | 分层上下文数据库 | 44c6df2 |
| Letta | 24,019 | Stateful Agent 与分层记忆 | b76da90 |
| Hindsight | 18,905 | Retain / Recall / Reflect 认知记忆 | cc1eaee |
Star 只用于说明生态规模,不等于技术结论。真正有意义的是:这些系统把哪种状态当作记忆、何时写入、如何更新,以及如何证明它真的帮助 Agent 完成任务。
01 先澄清:上下文窗口不是记忆
可以把大模型调用抽象成:
output_t = LLM(system_prompt + context_t + user_input_t)
context_t 只是当前调用可见的 token。它有三个天然限制:
- 容量有限:工具日志、代码、网页和长对话会迅速挤占窗口。
- 没有写入策略:模型不知道哪句话应该保存半年,哪段日志下一轮就可以丢弃。
- 没有状态语义:同一事实的旧版本、新版本、来源、有效期和权限都只是文本。
因此,完整聊天回放只能算最原始的 short-term memory。真正的记忆系统至少还要提供:
- 选择性:不是所有输入都值得记。
- 持久性:跨进程、跨会话、跨设备可恢复。
- 可寻址性:能按用户、任务、时间、实体、语义或路径定位。
- 可修正性:新事实可以覆盖、并存或使旧事实失效。
- 可遗忘性:支持 TTL、衰减、删除、压缩和隐私治理。
- 可评测性:能分别测写入、检索、回答和任务收益。
一个实用判断是:
如果系统只是把历史消息重新拼回 prompt,它有上下文管理;只有当它能在调用之外维护可更新、可检索、可治理的状态时,才有记忆系统。
02 记忆不是一张表,而是多个层次
图 1:不同记忆的寿命、结构和检索方式不同。把所有内容塞进同一个向量集合,会同时损害准确率、成本和可治理性。
借用认知科学术语,但以工程实现为准,可以把 Agent 记忆分为六类:
| 类型 | 保存内容 | 典型寿命 | 常见实现 | 例子 |
|---|---|---|---|---|
| 工作记忆 | 当前目标、计划、工具结果、临时变量 | 秒到分钟 | prompt、graph state、scratchpad | “正在修复支付回调,测试还剩 2 个” |
| 会话记忆 | 本线程消息、阶段摘要、checkpoint | 小时到数天 | SQLite、Postgres、event log | 断线后恢复同一轮调试 |
| 情景记忆 | 发生过的事件和执行轨迹 | 天到长期 | timestamped episode、JSONL、图节点 | “7 月 18 日部署失败,因为 migration 未执行” |
| 语义记忆 | 去掉具体情境后的稳定事实 | 周到长期 | 文档、KV、向量、实体图 | “生产数据库使用 PostgreSQL 17” |
| 程序记忆 | 如何完成任务的步骤和策略 | 长期 | skills、rules、playbook、code | “发布前依次执行 lint、test、migration check” |
| 画像/关系记忆 | 偏好、角色、信任边界、实体关系 | 长期但可变 | profile、namespace、temporal graph | “用户偏好中文,拒绝自动合并 PR” |
这六类不是互斥的。一段失败轨迹首先是情景记忆;后台巩固后,它可能变成“迁移检查必须早于部署”的语义事实,再进一步生成可执行的发布 skill,成为程序记忆。
最成熟的系统都在做分层:
- Letta 区分始终在上下文中的 core memory、可搜索的 archival memory 和 recall history。
- LangGraph 区分 thread-scoped checkpoint 与跨线程 Store。
- OpenAI Agents SDK 区分 Session 消息历史与沙箱 Agent 的经验记忆。
- OpenViking 区分 L0 abstract、L1 overview、L2 detail,并把 memory、resource、skill 放在同一 URI 树中。
03 一条记忆的完整生命周期
图 2:写路径负责把高噪声交互变成可治理的记忆;读路径负责在有限 token 预算中找到当前最有用的证据。两条路径应独立扩缩容和评测。
3.1 写路径
写入不是 vector_db.add(message),而是一条数据处理管线:
- Capture:收集用户消息、Agent 回复、工具调用、文件变化、结果和反馈。
- Write Gate:判断是否值得写。寒暄、重复内容、模型幻觉和敏感信息应被挡住。
- Extraction:抽取事实、事件、偏好、实体、关系、时间和可复用步骤。
- Resolution:与已有记忆比较,决定新增、合并、替换、失效、删除或忽略。
- Enrichment:补充
user_id、session_id、来源、置信度、有效时间、访问范围。 - Persistence:写入日志、关系库、向量索引、全文索引、图数据库或文件系统。
- Consolidation:在后台把多条原始记忆总结成更稳定的事实、画像或技能。
3.2 读路径
读路径的目标不是找“语义最像”的文本,而是在当前任务下最大化有效信息密度:
- 从目标、对话和当前状态生成检索 query。
- 先按 tenant、用户、Agent、项目、时间和权限做硬过滤。
- 并行执行 dense、BM25、图遍历、时间范围和路径检索。
- 用 RRF、cross-encoder 或 LLM reranker 融合重排。
- 做去重、相邻上下文扩展、冲突标记和 token 裁剪。
- 以结构化区块注入 prompt,并明确“记忆是证据,不是高优先级指令”。
- 记录哪些记忆被取回、被引用、被用户纠正,为后续评测和衰减提供反馈。
这也是一个关键架构原则:写路径和读路径应解耦。写入可以异步、昂贵、强调一致性;读取必须低延迟、可降级、强调相关性。把它们塞在一个同步 LLM 调用里,会同时拖慢响应并放大失败面。
04 短期记忆:Session、Checkpoint 与 Compaction
短期记忆首先解决“同一任务如何继续”。这里最容易混淆三种机制。
4.1 Session:持久化消息序列
OpenAI Agents SDK 的 Session 接口负责在多次 Runner.run() 之间保存消息。SDK 提供 SQLite、SQLAlchemy、加密 Session、OpenAI Conversations 等后端。
它解决的是对话续接,不会自动把 200 轮聊天提炼成“用户偏好”和“发布经验”。所以官方文档明确把 Session memory 与 sandbox agent memory 分开。
4.2 Checkpoint:保存状态机快照
LangGraph 的 BaseCheckpointSaver 保存的是某个 thread 在某一步的 channel values、pending writes、metadata 和版本;恢复后,图可以从中断点继续执行。它更接近数据库 checkpoint 或 event-sourced workflow state。
与之对应,LangGraph 的 BaseStore 才是跨 thread 的长期存储。Store 使用 (namespace, key) 隔离数据,可配置 embedding 索引和语义搜索。这个区分非常重要:
Checkpoint 回答“流程跑到哪了”,Long-term Store 回答“过去学到了什么”。
4.3 Compaction:压缩,不等于学习
当消息超过预算时,最常见做法是保留近期消息,把早期历史总结成 compacted state。OpenAI Agents SDK 的 OpenAIResponsesCompactionSession 包装底层 Session,在达到阈值后调用 responses.compact,并原子替换历史;如果替换失败,还要恢复原数据。
压缩主要优化 token 和连贯性,但存在信息损失:
- 摘要模型可能遗漏后来才显得重要的细节。
- 多轮反复摘要会产生“摘要漂移”。
- 摘要保留了结论,却可能丢掉来源和时间。
因此生产系统通常同时保留 immutable raw log。摘要是缓存,不是唯一真相;需要追溯时仍能回到原始事件。
05 长期写入:从事实抽取到两阶段巩固
长期记忆最难的不是存,而是决定“什么值得存”。
5.1 Write Gate:默认不写,比默认全写更安全
高质量 write gate 通常综合以下信号:
- 信息是否跨会话仍有用。
- 是否来自用户明确陈述、可靠工具结果或已验证环境。
- 是稳定偏好,还是一次性的临时要求。
- 是否与已有事实重复或冲突。
- 是否包含凭据、隐私、受监管数据或第三方信息。
- 写入收益是否大于提取、embedding、存储和未来干扰成本。
可以把决策写成一个概念评分:
write_score = utility * confidence * expected_lifetime
- duplication - sensitivity - contradiction_risk
实际系统不一定显式计算这个公式,但应该能观察每个因素,否则“记忆越来越多”最终会变成检索污染。
5.2 Mem0:事实抽取、作用域和批量持久化
Mem0 的接口要求用 user_id、agent_id 或 run_id 约束作用域。其经典设计是先抽取候选事实,再与已有记忆比较,执行 ADD / UPDATE / DELETE / NONE;当前源码仍保留这套 update prompt、历史事件与显式更新/删除 API。
但在本文分析的最新提交里,Memory._add_to_vector_store() 的 V3 主路径已经更偏向 additive pipeline:
- 读取最近消息和最相近的 10 条已有记忆。
- 单次 LLM 调用做 additive extraction。
- 批量生成 embedding。
- 用内容 hash 做已有记忆和本批次去重。
- 批量写向量与 history。
- 批量抽取实体,链接实体与 memory id。
这是一个值得注意的版本变化:旧的“四动作 LLM 决策”更擅长原地维护单条事实,但昂贵且容易误删;additive + 去重保留历史更稳,再通过过期时间、显式更新和检索策略控制可见性。工程上没有绝对优胜,关键是你需要“当前真值”还是“可追溯事件”。
5.3 OpenAI Agents SDK:在线捕获,离线巩固
OpenAI Agents SDK 当前 beta 的 sandbox memory 展示了很清晰的两阶段设计:
- Phase 1:conversation extraction。每个 rollout 的 JSONL 在后台生成
raw_memory和rollout_summary;system、developer 和 reasoning 内容不会进入提取输入,超长轨迹保留头尾并截断中间。 - Phase 2:layout consolidation。会话关闭后,巩固 Agent 读取多份 raw memory,必要时打开 rollout summary 取证,再更新
MEMORY.md和memory_summary.md。
读取时采用 progressive disclosure:启动只注入很小的 memory_summary.md;发现相关主题后,再搜索 MEMORY.md 并按需打开具体 rollout summary。新 raw memory 超过上限时优先保留最近内容,形成简单的 recency forgetting。
这套实现的价值不在文件格式,而在三个边界:
- 用户主请求不必等待昂贵的巩固流程。
- 原始记忆、证据摘要和最终索引分层保存。
MemoryLayoutConfig显式隔离不同 Agent 的记忆目录,而不是依赖 Agent 名称猜测权限。
5.4 Claude Code:让模型自己维护文件记忆
Claude Code auto memory 使用项目级目录保存 MEMORY.md 和主题文件。MEMORY.md 作为简短索引,在每次会话开始时加载前 200 行或 25KB;模型在工作过程中自行决定何时写入、修正或拆分主题。
它和 CLAUDE.md 的职责不同:CLAUDE.md 是人写的规则,auto memory 是 Claude 积累的经验。文件方案透明、可审计、可手改,也天然适合 coding agent;代价是检索能力较弱,记忆质量依赖模型维护索引的纪律。
06 存储表示:文件、向量、图和上下文数据库
记忆的逻辑类型与物理存储不应一一绑定。同一条记忆可以同时出现在事件日志、关系表、全文索引和向量索引中。
| 表示 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| Markdown/JSON 文件 | 透明、可版本化、Agent 可直接编辑 | 并发、权限、复杂检索较弱 | coding agent、规则、技能、个人知识 |
| 关系数据库/KV | 事务、过滤、TTL、租户隔离成熟 | 语义召回需额外索引 | session、profile、workflow state |
| 向量索引 | 模糊语义召回简单高效 | 精确词、时间、否定和冲突处理弱 | 长文本事实、相似案例 |
| 全文/BM25 | 专有名词、错误码、路径匹配好 | 同义表达召回不足 | 代码、日志、产品名、ID |
| 知识图谱 | 关系、多跳、来源和时间表达强 | 提取、去重、运维成本高 | 动态实体关系、调查、CRM |
| 分层上下文树 | 可浏览、渐进加载、路径权限清晰 | 写入理解与树维护较重 | 文档、技能、记忆统一管理 |
“向量数据库就是记忆”是最常见的误区。向量只是索引之一,它不负责真值、事务、冲突、权限或遗忘。生产系统通常采用 source of truth + derived indexes:原始事件和结构化事实是主数据,向量/BM25/图索引可以重建。
07 Letta:Core Memory 与 Archival Memory
Letta 延续 MemGPT 的核心思想:Agent 应该像操作虚拟内存一样主动管理有限上下文。
Memory 由带 label、description、字符上限和只读属性的 Block 组成,并渲染成结构化 prompt。常见 block 包括 persona、human/user、项目状态等,它们始终可见,相当于工作集。
超出工作集的内容进入 archival memory,由 ArchiveManager 通过 embedding provider 和向量存储管理。历史消息构成 recall memory;上下文溢出时,summarizer/compactor 生成 summary memory。
它的 ContextWindowOverview 甚至显式统计 system prompt、core memory、memory filesystem、summary、messages、archival 和 recall 各自占用的 token。这比“让框架自动拼 prompt”更接近操作系统视角:
Core memory 是当前驻留集,archival memory 是外存,检索工具是 page-in,压缩是回收策略。
Letta 的优势是 Agent 对自己的记忆有操作权;风险也来自这里:模型可能写坏 core memory。因此 block size、read-only、工具权限和审计历史是必要护栏。
08 Graphiti:为什么记忆需要双时态知识图谱
图 3:事实“何时在现实中成立”和“系统何时得知它”是两个维度。保留 episode 来源后,新事实可以使旧边失效,而不是覆盖历史。
假设用户先说“我在上海工作”,三个月后说“我已调到杭州”。简单向量库可能同时召回两条;覆盖旧记录又会丢失历史。Graphiti 的解决方式是双时态图:
- Valid time:事实在现实世界中从何时到何时成立。
- Transaction time:系统何时创建、修改或使这条记录失效。
在 Graphiti.add_episode() 中,一段消息先成为 episodic node,然后提取 entity nodes 和 entity edges,执行节点/边去重、摘要更新、时间抽取与矛盾边失效。边模型保存 created_at、valid_at、invalid_at、expired_at 和 episodes 来源。
检索则不只跑向量搜索。Graphiti 的 search pipeline 可以组合 semantic similarity、BM25/full-text、graph traversal,再用 RRF、MMR、cross-encoder 或 node distance rerank。这样既能查“与部署失败语义相似的事件”,也能查“某个服务依赖哪些组件”“7 月之前有效的负责人是谁”。
时态图适合事实频繁变化、必须追溯来源的领域,但成本明显高于 KV 或向量库:实体解析会错,实体去重需要模型,图数据库运维更重。因此不要为了“看起来高级”上图谱;只有当关系、多跳、时间和 provenance 确实是产品需求时,它才值得。
09 2026 新趋势:从 Memory Layer 到 Context Database
9.1 Hindsight:Retain、Recall、Reflect
Hindsight 将接口收敛成三种认知动作:
retain:抽取事实、时间、实体和关系,并归一化写入 memory bank。recall:并行运行 semantic、BM25、graph、temporal 四种检索,用 RRF 融合,再经 cross-encoder 重排。reflect:不只返回原始命中,而是基于事实和经历生成新的观察或 mental model。
它把记忆分为 world facts、agent experiences 和通过反思形成的 mental models。这个设计比“事实仓库”多了一层学习:Agent 不仅记得发生了什么,还能从重复成败中形成可迁移判断。
但 reflect 生成的是推断,不是事实。生产上应给反思结果单独的类型、置信度和来源链,避免二次生成的结论反过来污染事实层。
9.2 OpenViking:统一 memory、resource 与 skill
OpenViking 把上下文组织为 viking:// 虚拟文件系统:
viking://user/<user_id>/
├── memories/
├── resources/
├── skills/
└── peers/
每个文件和目录都预生成三层内容:L0 是约 100 token 的 abstract,L1 是约 2K token 的 overview,L2 才是完整详情。检索先定位高分目录,再递归下钻,并保留浏览轨迹。与扁平 top-k 相比,它解决了两个现实问题:返回片段缺乏目录上下文,以及无法解释“系统为什么找到这段内容”。
源码中的 session commit 分成两阶段:Phase 1 在锁内归档并排队,Phase 2 异步生成摘要、长期记忆和执行经验;最新实现还包含 trajectory -> experience 的二阶段提炼,以及 create/merge/delete/skip telemetry。它代表了 2026 年一个清晰方向:
记忆、文档和技能不再是三套孤岛,而是统一的可浏览上下文空间;差别主要体现在生命周期和读写权限。
这一路线的代价是写入更重:每个节点都要维护摘要层和索引,树结构也可能被错误分类固化。它适合上下文规模大、需要渐进加载与可观测检索轨迹的 Agent 平台。
10 Cognee 与云服务:Session Distillation 成为基础设施
Cognee 的新 session 模块把反馈检测、trace、上下文提取和 distillation 放在同一生命周期中。analyze_turn_for_session_context() 会把当前用户消息、上一问答以及上次实际提供给模型的 context 一起交给结构化 LLM,输出候选更新和 served-context rating;失败时返回空分析,不阻断主回复。
它还按 trace count watermark 判断是否积累了足够的新轨迹,再用重叠窗口批量提炼 Agent lessons。这里有两个成熟做法:
- 只评估真正注入过的记忆,而不是把“库里存在”误当成“对回答产生作用”。
- 记忆维护降级不影响主链路,否则 memory LLM 超时会拖垮用户请求。
云厂商也把类似能力产品化:
- Google ADK MemoryService 明确区分 Session/State 的短期历史与 MemoryService 的长期知识,并提供 in-memory、Vertex AI Memory Bank、RAG 等实现。
- Vertex AI Memory Bank 托管记忆生成与检索,适合不想自建抽取、索引和权限基础设施的团队。
- AWS AgentCore Memory 同时管理 session 内 short-term events 与跨 session 的 long-term insights,内置偏好、事实和会话摘要等策略。
托管服务的优势是鉴权、扩缩容、监控和框架集成;限制是数据驻留、调试透明度、模型/索引可控性和供应商锁定。涉及严格删除证明、跨云或本地代码仓库时,文件/数据库自托管通常更容易建立清晰边界。
11 Coding Agent 的记忆为什么不一样
对话助手主要记“用户是谁、说过什么”;coding agent 还要记环境事实和执行经验:构建命令、目录结构、测试习惯、失败根因、代码所有权、发布流程和工具用法。
不同项目采取了不同组合:
| 系统 | 会话层 | 长期层 | 程序层 | 关键取舍 |
|---|---|---|---|---|
| Claude Code | transcript + compact | MEMORY.md / topic files | CLAUDE.md、rules、skills | 文件透明,本地可编辑 |
| OpenHands | event stream / conversation | microagent knowledge、workspace state | repo instructions、skills | 强调执行环境与事件持久化 |
| OpenClaw | session + compaction flush | MEMORY.md、memory search、dreaming | skills | Gateway 长期运行,压缩前主动 flush |
| Hermes Agent | session DB + context compressor | memory manager、session search | skills + 自维护 | 在线记忆/技能维护连接 harness |
| OpenAI Sandbox Agent | SDK Session | raw memory、rollout summary、MEMORY.md | skills directory | 两阶段异步巩固、progressive disclosure |
Coding agent 的一条经验必须绑定环境证据。例如“测试命令是 pnpm test”应记录来源文件、适用路径、验证时间和 commit;否则仓库迁移到 Bun 后,旧记忆会持续误导 Agent。
这也是为什么程序记忆最好落成可执行 skill 或规则,并带验证步骤,而不是只存一句自然语言建议。
12 检索:Dense Top-k 只是起点
一个可用的检索器通常先召回较大的候选集,再重排和裁剪。概念上可以写成:
score(m, q) = a * semantic(m, q)
+ b * lexical(m, q)
+ c * graph_distance(m, entities(q))
+ d * temporal_fit(m, time(q))
+ e * importance(m)
+ f * recency(m)
- g * staleness(m)
其中权重不必固定。查错误码时 lexical 应更高;查“上个月做过的类似设计”时 semantic 与 temporal 更重要;查当前负责人时必须先过滤 invalid_at。
业内最新实践有五点:
- Hybrid retrieval:dense + BM25 是低成本基线,Graphiti/Hindsight 再加入图与时间通道。
- Query expansion:从当前目标提取实体、时间范围、同义词和子问题,而不是直接 embedding 用户原句。
- RRF + reranker:先用 reciprocal rank fusion 合并不同召回,再用 cross-encoder 精排,避免比较不可校准的原始分数。
- Context expansion:命中一个片段后带回同一 episode、目录 overview 或相邻事件,减少断章取义。
- Token-aware packing:按边际信息量装箱,而不是固定 top-k;重复记忆只留一条,冲突记忆成组呈现。
检索结果应携带 memory_id、source、event_time、confidence 和 validity。只注入裸文本,模型就无法区分用户原话、Agent 推断和过期摘要。
13 冲突、巩固与遗忘:记忆系统的垃圾回收
13.1 冲突不是简单覆盖
新旧信息冲突时,至少有四种语义:
- Correction:用户明确纠正,“我刚才说错了”。旧值应失效。
- Update:事实随时间变化,“我搬到杭州了”。新旧都应保留有效区间。
- Scope difference:“工作日喜欢早起,周末不是”。应增加条件,不能互删。
- Uncertain contradiction:两个低可信来源不一致。应并存并标记冲突,等待确认。
所以 UPDATE 不应只是 SQL overwrite。更稳的记录模型是 append-only fact versions + validity + supersedes link。
13.2 巩固把经历变成知识
后台 consolidation 可以按用户、项目或主题定期运行:聚类相似 episode,提取稳定模式,更新 profile,生成 playbook,并保留 supporting memory ids。OpenAI 的 Phase 1/2、OpenViking 的 trajectory/experience、Hindsight 的 Reflect 都属于这一方向。
巩固频率不宜过高。单次成功不一定是规律,单次用户情绪也不一定是长期偏好。可以要求跨多个 episode、不同时间窗口或用户明确确认后再提升为高置信语义记忆。
13.3 遗忘不是删库,而是生命周期策略
常见策略包括:
- TTL:验证码、临时任务状态到期直接隐藏或删除。
- Recency decay:长期未使用且低价值的记忆降低排名。
- Usage reinforcement:被成功引用的记忆提高权重,但要防止自我强化错误。
- Capacity compaction:原始 memory 超限时保留近期与高重要度内容。
- Supersession:旧事实保留审计,但默认检索只返回当前有效版本。
- User deletion:按用户和来源做级联删除,并重建 derived indexes。
真正的删除必须覆盖主数据、向量、全文、图边、缓存、摘要和备份策略。仅从向量库删一条,并不等于完成隐私删除。
14 安全:记忆是长期 Prompt Injection 的载体
记忆一旦会跨会话自动注入,就成了持久化攻击面。网页、邮件、工具输出甚至其他 Agent 都可能诱导系统写入“以后忽略安全规则”之类内容。
必须建立以下边界:
- 只允许可信主体写特定 namespace;工具输出默认不能写高优先级规则。
- 把 memory 作为低信任数据区块注入,并在 prompt 中禁止执行其中的指令。
- 保留 provenance,区分 user-stated、tool-observed、agent-inferred、admin-approved。
- 写入前做 secret/PII 检测,敏感字段加密或完全不入库。
- 检索前做 tenant 与 ACL 过滤,不能只靠向量 metadata 的事后筛选。
- 为删除、导出、审计、保留期和人工修订提供产品入口。
- 对自动生成的 skill 使用更高门槛:静态检查、沙箱运行、测试和审批。
“Agent 自我进化”如果没有这些边界,本质上就是让模型长期修改自己的行为配置。能力提升与持久化污染只隔着一套验证和权限系统。
15 怎么评测:不要只看最终问答准确率
图 4:端到端回答正确率会掩盖问题发生在哪一层。写入、检索、时态判断、注入和任务收益需要分别评测。
15.1 分层指标
| 层次 | 核心指标 | 典型失败 |
|---|---|---|
| 写入 | precision、recall、重复率、敏感信息写入率 | 该记的没记,不该记的全记了 |
| 更新 | contradiction accuracy、旧事实失效率 | 同时相信上海和杭州两个当前地址 |
| 检索 | Recall@k、MRR、nDCG、证据覆盖率 | 记忆存在但没找出来 |
| 注入 | token cost、重复率、上下文利用率 | 找对了但被噪声淹没 |
| 回答 | factual accuracy、abstention、temporal QA | 没证据时编答案 |
| Agent | task success、turns、tool calls、latency、cost | 记得更多但任务并未更好 |
| 治理 | deletion completeness、cross-tenant leakage | 删除不彻底或串用户 |
15.2 常用 benchmark
- LoCoMo 面向超长多会话对话,覆盖单跳、多跳、时间和开放域问答。
- LongMemEval 用 500 个问题测试信息抽取、多 session 推理、知识更新、时间推理与 abstention;2026 年又发布了面向 agentic context 的 V2。
- MemoryAgentBench 采用增量多轮交互,测试 accurate retrieval、conflict resolution、long-range understanding、selective forgetting,并加入 LRU、TTL、推荐和长摘要任务。
公开榜单只能作为参考。不同系统可能使用不同 reader model、检索预算、数据清洗版本和 LLM judge。上线前更重要的是构建自己的 replay set:从真实失败中抽取问题,冻结原始事件和期望证据,持续回放每次 memory pipeline 变更。
16 项目横向比较:没有一个方案适合所有记忆
| 系统 | 短期状态 | 长期表示 | 写入/巩固 | 检索 | 最适合 |
|---|---|---|---|---|---|
| Letta | message + summary | core blocks + archive | Agent 主动编辑 + compaction | core 常驻 + archive search | 长期人格化、自管理 Agent |
| Mem0 | conversation/run scope | vector + history + entity links | LLM extraction、batch write、显式更新/过期 | vector、filter、可选 rerank | 快速接入用户偏好和事实 |
| Graphiti | episodes | 双时态知识图谱 | 实体/关系抽取、去重、边失效 | semantic + BM25 + graph + rerank | 动态关系、时间与 provenance |
| LangGraph | checkpoint | namespace/key Store | 应用自定义,支持热路径或后台 | filter + semantic index | 已使用 LangGraph 的工作流 |
| OpenAI Agents SDK | Session + compaction | files + rollout summaries | 异步 Phase 1/2 | progressive disclosure | 沙箱任务、coding/research agent |
| Claude Code | transcript + compact | Markdown topic files | 模型会话中自维护 | 索引文件 + 搜索/读取 | 本地 coding agent |
| Cognee | session + trace | graph/context entries | feedback rating + trace distillation | graph/RAG/context builder | 需要反馈闭环和图谱认知 |
| Hindsight | memory bank | facts + experiences + mental models | Retain + Reflect | 四路召回 + RRF + rerank | 个性化和经验反思 |
| OpenViking | session archive | URI tree + L0/L1/L2 | commit 后异步抽取与经验巩固 | 目录递归 + semantic | 大规模统一上下文管理 |
| 云 Memory Service | 托管 session | 托管 insights/index | 内置策略 | 托管 API | 优先交付、少运维团队 |
选择时先回答需求,而不是先选框架:
- 只要断线恢复:Session/checkpoint 足够。
- 只要少量明确偏好:结构化 profile 表通常比向量库更可靠。
- 需要模糊召回大量经历:向量 + BM25 + reranker。
- 需要事实变化、多跳与来源:temporal graph。
- 需要代码经验和可审计规则:文件 memory + skills。
- 需要统一管理文档、记忆和技能:分层 context database。
17 一套生产级参考架构
图 5:推荐架构把在线请求、异步记忆维护、主数据与派生索引分开;所有结果都通过 provenance、namespace 和评测日志串联。
对多数业务 Agent,我更推荐渐进式建设,而不是一开始上全套图数据库。
第一阶段:可靠短期状态
- 用 append-only event log 保存原始消息和工具事件。
- 用 Session/checkpoint 支持恢复、重放和幂等。
- 做 token budget 与 compaction,但保留 raw history。
- 每次注入记录 context manifest,知道模型实际看到了什么。
第二阶段:结构化长期记忆
- 为 profile、preference、decision、task outcome 建明确 schema。
- 写入先过规则和 LLM gate,默认低置信不自动覆盖。
- 所有记录携带 tenant、subject、source、event time、confidence、TTL。
- 先用 Postgres + FTS/pgvector,避免过早引入多套基础设施。
第三阶段:混合检索与后台巩固
- dense 与 BM25 并行,RRF 融合,必要时 cross-encoder 精排。
- 将 episode 异步巩固为 semantic fact / profile / skill。
- 建冲突队列、人工编辑和删除链路。
- 只有出现明确多跳和时态需求时,再增加 Graphiti 类图层。
一个简化的在线读取伪代码如下:
async def build_memory_context(request, budget):
scope = authorize(request.tenant_id, request.user_id, request.agent_id)
query = await extract_query(request.goal, request.recent_messages)
dense, lexical, temporal = await gather(
vector_search(query, scope, limit=40),
bm25_search(query, scope, limit=40),
time_search(query.time_range, scope, limit=40),
)
candidates = reciprocal_rank_fusion(dense, lexical, temporal)
candidates = await rerank(query.text, candidates[:60])
candidates = resolve_versions_and_conflicts(candidates)
packed = pack_by_information_gain(candidates, token_budget=budget)
audit.log_retrieval(request.id, [item.id for item in packed])
return render_as_untrusted_evidence(packed)
写路径则通过队列异步处理,并使用 transactional outbox 保证“主事件已提交但记忆任务没发出去”时可恢复。向量、全文和图索引都视为 derived data;一旦模型、chunk 或 schema 改变,可以从 source of truth 重建。
18 最终判断:Memory 的终点是 Context Engineering
Agent 记忆系统正在从一个功能点,变成 Agent runtime 的数据平面。
成熟方案不再问“用哪个向量数据库”,而是问:
- 当前任务必须常驻哪些工作记忆?
- 哪些事件值得跨会话保留?
- 事实、经历、画像和技能应该如何转换?
- 新信息如何修正旧信息而不丢失时间和来源?
- 检索如何在准确率、延迟、token 与权限之间平衡?
- 自动学习如何被验证、审计、撤销和遗忘?
从 Letta 的 core/archive,到 Graphiti 的双时态图,再到 OpenAI 的两阶段 consolidation、Hindsight 的 Reflect 和 OpenViking 的分层 context database,方向已经很一致:
下一代 Agent 记忆不是“无限保存历史”,而是持续把高噪声交互整理成少量、可验证、可寻址、可更新、可遗忘的上下文资产。
真正决定效果的,也不是记忆条数,而是 Agent 在正确的时刻,拿到了正确作用域内、仍然有效、带来源且恰好够用的那几条信息。
19 参考资料
- Letta memory schema 与 ArchiveManager
- Mem0
Memoryimplementation 与 memory prompts - Graphiti source
- LangGraph checkpoint/store
- OpenAI Agents SDK Sessions 与 Sandbox Memory
- Claude Code: How Claude remembers your project
- Cognee session infrastructure
- Hindsight architecture
- OpenViking architecture
- Google ADK Memory 与 AWS AgentCore Memory
- LoCoMo、LongMemEval、MemoryAgentBench