犀牛鸟 2026 研究笔记 estelledc.github.io

Ch07: 记忆系统的设计约束——存什么、怎么存、何时忘

Part 2: 记忆基础概念 前置章节:Ch06: Memory vs RAG——经验和知识不是一回事 后续章节:Ch08: DB-Agent-Memory——四层金字塔与零依赖哲学


1. 从”图书馆管理员”说起

想象你是一座图书馆的管理员。你的工作不只是”把书放到架子上”那么简单——你要回答五个问题。

第一,什么时候收书? 每天有几十个人送书来,有些是刚出版的新书,有些是别人捐赠的旧书,有些是读者读完还回来的。你不能每来一本书就跑一次上架——那样你一天什么都不用干了,光上架就够忙的。但你也不能攒一个月才上架——读者想看的新书可能早就等不及了。你需要一个”收书节奏”。

第二,书怎么分类放? 你可以按作者姓氏排,可以按主题分类排,可以按出版年份排,也可以按读者的借阅频率排。每种排列方式都有好处——按主题排的人找书方便,按频率排的人知道哪些是热门。但没有一种排列方式能满足所有人的所有需求。

第三,读者要书时怎么找? 如果图书馆只有一百本书,你闭着眼睛都能找到。但如果有十万本书,你需要目录、索引、搜索系统。最简单的做法是把所有书的目录背下来(全量注入),但那不可能。更实际的做法是按需求搜索——但搜索本身也需要时间和成本。

第四,旧书怎么处理? 科技类书籍三年就过时了,法律条文年年改,但是经典文学不会过时。有些书虽然内容过时了,但还有历史参考价值。你不能简单地把所有超过五年的书扔掉——但也不能让过时的科学教材继续误导读者。

第五,不同读者怎么隔离? 张教授的借阅记录和李同学的借阅记录不能混在一起。但有些书是共享的——参考工具书谁都能查。而且张教授的研究组可能需要共享一组书,不能只对张教授一个人开放。

这五个问题——写入时机、存储模型、检索策略、遗忘机制、隔离与共享——就是记忆系统的五个设计约束。每一个约束都有多种选择,每种选择都有权衡。十个系统在五个约束上做出了不同的选择,这些选择构成了它们各自的设计指纹。

这一章逐一展开五个约束,最后用一个五维矩阵表格做横向对比。


2. 约束一:写入路径——什么时候存、怎么提取

写入路径回答两个问题:什么时候把信息存进记忆系统(写入时机),以及用什么方式从原始对话中提取出值得存储的结构化信息(提取方式)。

2.1 三种写入时机哲学

写入时机是最能体现一个系统设计哲学的选择。三种哲学对应三种对”信息丢失”和”写入成本”的权衡。

哲学一:持续学习——每轮对话后立即写入

代表系统:Claude Code。

Claude Code 在每一轮用户-Agent 交互后,启动一个后台 agent(extractMemories),从当前对话中提取值得记住的事实,写入 .claude/memory/ 目录下的 markdown 文件。这是一个 fire-and-forget 操作——后台 agent 在运行时不阻塞主对话,用户不需要等待。

持续学习的核心权衡:

Fire-and-forget 是持续学习的关键实现技巧。”Fire”是发起后台提取任务,”forget”是主对话不等提取完成就继续。这保证了用户体验不受影响——你不会感觉到”Agent 在花时间记笔记”。但 fire-and-forget 意味着写入可能失败——后台 agent 可能崩溃、LLM 调用可能超时——而你不会知道。这是一个”用可靠性换即时性”的权衡。

哲学二:回顾学习——会话结束后批量写入

代表系统:Codex Phase1。

Codex 的 Phase1 在整个会话(rollout)结束后,才并行启动提取任务。它从历史 rollout 中提取结构化 JSON,每个 rollout 独立提取,并行度(concurrency)为 8。

回顾学习的核心权衡:

哲学三:抢救保存——在上下文压缩前紧急写入

代表系统:OpenClaw Pre-Compaction Flush。

OpenClaw 发现了一个独特的问题:当 Agent 的上下文窗口快要满时,系统会执行”上下文压缩”(compaction)——把旧的对话内容摘要化,腾出空间。但压缩过程中,大量细节信息会被丢弃——而这些信息中可能包含值得记住的内容。

OpenClaw 的解决方案是在压缩前主动 flush——运行一个嵌入式 agent,扫描即将被压缩的上下文,把重要信息写入日记文件(daily/YYYY-MM-DD.md)。这就像你在会议快结束、大家准备散会前,赶紧把白板上的要点拍个照——不是最优雅的方式,但保证信息不丢。

抢救保存的核心权衡:

2.2 三种提取方式

确定了”什么时候存”,接下来是”怎么提取”——从原始对话中提取出结构化记忆信息的方式。

方式一:LLM 提取

代表系统:mem0、Claude Code。

核心思路是把对话内容交给 LLM,让 LLM 判断哪些信息值得记住,然后以结构化格式输出。

mem0 的实现:每次对话结束后,将完整对话发送给 LLM,prompt 要求提取独立的事实条目(如 “用户喜欢 Python”、”用户对花生过敏”)。LLM 输出 JSON 格式的事实列表,然后存入向量数据库。

Claude Code 的实现类似——extractMemories agent 读取当前对话,决定哪些信息值得写入 .claude/memory/ 下的文件。

LLM 提取的权衡:

方式二:Agent 自操作

代表系统:Letta。

Letta 不由系统自动提取——而是让 Agent 自己通过 tool call 来操作记忆。Agent 有 core_memory_append、core_memory_replace、archival_memory_insert 等 tool,由 Agent 自己决定什么时候存、存什么。

这是一个本质性的设计差异。LLM 提取是”系统替 Agent 记”——Agent 不知道自己被记住了什么。Agent 自操作是”Agent 自己记”——Agent 明确知道自己的记忆状态,可以主动更新和修正。

Agent 自操作的权衡:

方式三:管线化处理

代表系统:cognee ECL(Extract-Cognify-Load)。

cognee 不在对话进行中提取记忆——它把提取过程设计为一个三阶段管线:

  1. Extract:从原始数据(对话、文档)中抽取节点和关系
  2. Cognify:根据 Ontology(本体论)定义的知识结构,将抽取的节点映射到类型化的概念和关系上
  3. Load:将处理后的知识图写入图数据库

管线化处理的权衡:

2.3 写入路径的权衡总结

三种写入时机和三种提取方式可以交叉组合——但实际系统中,它们有自然的搭配倾向:

核心权衡一句话:实时写入成本高但不丢信息,批量写入成本低但可能遗漏


3. 约束二:存储模型——用什么结构存

确定了写入路径,接下来是存储模型——用什么数据结构组织记忆。存储模型直接决定了记忆可以被怎样检索、怎样更新、怎样被用户理解。

3.1 五种存储模型

模型一:扁平列表

代表系统:mem0。

mem0 把每条记忆存为一个独立的事实条目,格式通常是 “user_id fact_text embedding_vector”。所有记忆在同一个平坦的表里,没有层级、没有分类、没有关联。

扁平列表就像一个便利贴盒子——每条记忆是一张便利贴,写好了塞进盒子里。找的时候你只能一张一张翻(或者用向量搜索做语义匹配),没有”先找大分类再找小条目”的结构化路径。

扁平列表的好处是简单——写入只需一次 insert,不需要判断”这条记忆属于哪个分类”“和已有记忆有什么关系”。代价是检索依赖向量搜索——如果你存了一千条记忆,找相关的必须做语义匹配,而语义匹配的精度受嵌入模型能力限制。

模型二:分层金字塔

代表系统:DB-Agent-Memory。

DB-Agent-Memory 的四层金字塔从下到上是:Observation(原始观测)-> Fact(独立事实)-> Summary(主题摘要)-> Profile(用户画像)。信息从底层向上逐层压缩——底层的多条 Observation 被提炼为一条 Fact,多条 Fact 被聚合为一条 Summary,多条 Summary 被综合为一个 Profile。

分层金字塔就像一个从”原始笔记”到”总结报告”到”核心结论”的提炼管线。底层的 Observation 保留了完整细节但不方便检索;顶层的 Profile 丢失了细节但能快速定位方向。

分层金字塔的好处是提供了”先粗后细”的检索路径——你可以先查 Profile 知道”这是一个后端工程师”,再查 Summary 知道”他的数据库配置偏好”,再查 Fact 知道”他用 PostgreSQL 9.6,主从架构”。代价是分层提取本身需要 LLM 调用,而且每一层的提炼都可能引入信息损失或错误。

模型三:文件系统

代表系统:Claude Code、Codex、OpenClaw。

三个生产级 Coding Agent 都选择了文件系统作为记忆存储——记忆是 markdown 文件,存在项目目录下(.claude/memory/、MEMORY.md、daily/)。

文件系统作为存储模型有几个独特优势:

代价是:检索能力弱。你不能在文件系统上做向量搜索——你需要额外的索引层(Claude Code 的 MEMORY.md 就是索引文件)或者全量注入(Codex 把 MEMORY.md 的全部内容塞进 system prompt)。

模型四:知识图谱

代表系统:Graphiti、cognee。

知识图谱把记忆存为”实体-关系-实体”的三元组(如 “用户” -[使用]-> “PostgreSQL”),所有实体和关系构成一个图。图数据库(Neo4j)提供图遍历和子图匹配的查询能力。

知识图谱的独特优势是关联推理。如果你知道”用户使用 PostgreSQL”和”PostgreSQL 支持 JSONB”,你可以通过图遍历推断出”用户可以使用 JSONB 查询”——即使没有人直接说过这句话。这是扁平列表做不到的——在扁平列表中,”用户使用 PostgreSQL”和”PostgreSQL 支持 JSONB”是两条独立的事实,没有关联。

代价是:构建和维护图谱的成本高。每条新增记忆都需要做实体识别和关系抽取(通常用 LLM),而且需要处理实体消歧(”PG”和”PostgreSQL”是同一个东西吗?)和关系冲突(”用户使用 PostgreSQL”和”用户使用 MySQL”矛盾吗?)。图数据库的运维成本也高于简单的文件系统或向量数据库。

模型五:认知容器

代表系统:MemOS MemCube。

MemOS 引入了 MemCube 的概念——一个”记忆容器”,内部可以包含四种认知科学类型的记忆:Episodic(情景记忆)、Semantic(语义记忆)、Procedural(过程记忆)、Profile(画像记忆)。

认知容器和分层金字塔的区别在于:分层金字塔是”提炼深度”的分层(从原始到精炼),认知容器是”记忆类型”的分类(从经验到技能到知识到画像)。一个 MemCube 可以同时包含同一主题的情景记忆(”上周三遇到一次连接池耗尽”)、语义记忆(”pgbouncer 可以解决连接池耗尽”)、过程记忆(”配置 pgbouncer 的步骤是…“)和画像记忆(”用户偏好用连接池中间件而非直连”)。

认知容器的抽象层次在五种模型中最高,也最接近认知科学的理论框架。但代价是实现复杂度高——你需要为每种记忆类型设计不同的存储和检索策略,而且类型之间的转换(比如从情景记忆提炼语义记忆)需要额外的机制。

3.2 两极分化现象

观察十个系统的存储模型选择,会发现一个明显的两极分化:

这不是偶然的。生产系统的用户是开发者——他们需要看到、理解、编辑、版本控制自己的记忆。文件系统天然满足这些需求。开源库的用户是其他开发者——他们需要将记忆库集成到自己的应用中,需要复杂的查询能力(向量搜索、图遍历),数据库天然满足这些需求。

一个有趣的例外是 DB-Agent-Memory——它选择了分层金字塔模型,但在实现上用 SQLite 作为底层存储。这意味着它的”逻辑模型”是分层的,但”物理存储”是数据库。它试图在两种路线之间找到平衡点——既提供结构化的分层逻辑,又利用数据库的查询能力。

3.3 存储模型的权衡总结

核心权衡一句话:文件系统透明但检索弱,数据库强大但不透明

模型 优势 劣势 适用场景
扁平列表 简单、写入快 检索依赖向量搜索、无结构 轻量级应用、快速集成
分层金字塔 检索有层级路径、信息有压缩 提取成本高、层间信息损失 需要深度个性化的场景
文件系统 透明、可编辑、可 git 检索能力弱、需要额外索引 Coding Agent、开发者工具
知识图谱 关联推理、结构化知识 构建成本高、运维复杂 需要推理的知识密集型应用
认知容器 类型化记忆、科学理论基础 实现复杂度最高 学术研究、记忆操作系统

4. 约束三:读取路径——怎么找到相关记忆

存好了记忆,接下来是”怎么找到相关的”。检索策略直接影响 Agent 的回答质量——找到正确的记忆,回答就准确;找到不相关的记忆,回答可能被干扰;找不到记忆,Agent 就”失忆”。

4.1 四种检索策略

策略一:全量注入

代表系统:Codex。

Codex 把 MEMORY.md 的全部内容注入 Agent 的 system prompt。如果 MEMORY.md 有 2000 token,Agent 在每次对话开始时就”看得到”全部 2000 token 的记忆。

全量注入就像把所有便利贴摊在桌面上——你一眼就能看到所有信息。但如果便利贴太多(记忆太多),桌面就放不下了——上下文窗口有容量限制。

全量注入的核心权衡:

策略二:LLM 语义检索

代表系统:Claude Code。

Claude Code 用一个两步检索流程:第一步,将 MEMORY.md(索引文件)的全部内容注入 system prompt——但 MEMORY.md 只包含每条记忆的标题和一句话摘要,不是完整内容。第二步,由 LLM(Sonnet 模型)根据当前对话内容,从不超过 200 个记忆文件中选出最相关的 5 个,将这 5 个的完整内容注入上下文。

LLM 语义检索就像一个图书管理员——你告诉管理员你在找什么,管理员根据她的理解从书架上挑出最相关的几本书给你。管理员的理解力(LLM 的能力)直接决定了检索精度。

LLM 语义检索的核心权衡:

策略三:向量搜索

代表系统:mem0、supermemory。

向量搜索是最”正统”的检索策略:把每条记忆的文本用嵌入模型转化为向量,存入向量数据库。检索时把查询也转化为向量,在数据库中做近似最近邻搜索(ANN),返回最相似的 k 条记忆。

向量搜索就像在一个巨大的图书馆里用”找和这个主题相似的书”的方式检索——你不需要知道书的精确标题或作者,只需要描述”我在找什么”,系统返回最相关的前 k 条。

向量搜索的核心权衡:

策略四:图遍历

代表系统:Graphiti、cognee。

图遍历利用知识图谱的结构进行检索:从一个已知的实体出发,沿着关系边遍历到相邻的实体,形成一条推理链。比如从”用户”出发,沿”使用”边到”PostgreSQL”,再沿”支持”边到”JSONB”,你就得到了”用户可以使用 JSONB”这个推论。

图遍历就像沿着关联线索一步一步调查——你先知道”用户用 PostgreSQL”,然后查”PostgreSQL 有什么特性”,然后发现”支持 JSONB”。每一步都是沿着结构化的关系前进的,不是”模糊搜索”。

图遍历的核心权衡:

4.2 检索策略的三角权衡

四种检索策略可以放在一个”精度-延迟-成本”的三维空间中理解:

在实际系统中,很多采用了混合策略。supermemory 用三条流(BM25 + 向量 + 图遍历)做检索,然后融合排序。Graphiti 用两阶段架构(向量粗筛 + Cross-Encoder 精排)。这些混合策略试图在三角权衡中找到更好的平衡点——但每次”加一路召回”都意味着更高的系统复杂度和维护成本。

核心权衡一句话:精度、延迟、成本——三者不可兼得,你需要根据场景选择取舍


5. 约束四:遗忘机制——什么时候删、怎么删

遗忘是最容易被忽略但影响最深远的约束。一个只增不减的记忆系统就像一个从不扔东西的储藏室——刚开始很有用,但东西越来越多,找东西越来越难,过时的东西还会误导你。

5.1 两类过时:显式 vs 隐式

显式过时:用户明确表示某条记忆不再适用。比如用户说”我不再用 React 了”或”我换了工作”。这类过时相对容易处理——检测到矛盾信息时更新或删除旧记忆。

隐式失效:记忆没有显式地被否定,但已经不再适用。比如用户半年没提 React,可能已经不用了但没明说。或者用户从”Python 后端”转到了”Rust 后端”,但他从来没说”我不再用 Python”——只是不再在对话中提到 Python 相关的话题。

隐式失效远比显式过时难处理。原因有三:

5.2 现有方案

十个系统中,只有三个有值得一提的遗忘机制。大多数系统选择了”只增不减”——只管往里存,不管删。这不是因为遗忘不重要,而是因为遗忘太难设计了。

方案一:Graphiti 的 invalid_at 双时间轴

Graphiti 给知识图谱的每条边加了两个时间戳:valid_at(事实生效时间)和 invalid_at(事实失效时间)。当检测到矛盾信息时(比如”用户住北京”和”用户搬到了上海”),旧边的 invalid_at 被设置为当前时间,新边的 valid_at 被设置为当前时间。

双时间轴的好处是:你可以做时间维度的查询——”在 2024 年 6 月,用户住哪里?”答案是”北京”;”在 2025 年 1 月,用户住哪里?”答案是”上海”。信息没有被物理删除,只是被标记为”在某时间点之后不再有效”。

代价是:需要矛盾检测机制——系统需要判断新信息是否与旧信息矛盾。Graphiti 用 LLM 来做这个判断,但这不是百分之百可靠的。如果 LLM 没有检测到矛盾(比如”用户搬到上海”和”用户住北京”在措辞上不够直接),旧记忆就不会被标记为失效。

方案二:Codex 的 citation 正反馈循环

Codex 用一个间接的方式来处理遗忘:不是显式地”删”旧记忆,而是通过 citation(引用)机制让有用的记忆”浮现”,让没用的记忆”下沉”。

机制是这样的:每次 consolidation agent(整合 agent)决定保留一条记忆时,它会记录”这条记忆是从哪些来源中提炼的”。下次整合时,如果一条记忆的 usage_count(被引用次数)高,说明它经常被用到,更可能被保留。如果一条记忆的 usage_count 低,说明它很久没被引用了,整合 agent 可能选择不保留它。

这是一个”隐式淘汰”机制——没有显式地删,但低引用的记忆会在下次整合时被自然淘汰。好处是温和——不会因为一次没用到就删掉可能还有用的信息。代价是迟缓——一条过时的记忆可能需要经过多次整合周期才会被淘汰。

方案三:Claude Code 的 freshness caveat

Claude Code 在记忆文件中加了一个 freshness(新鲜度)标注。如果一条记忆超过 24 小时且经过 5 次以上会话,autoDream 机制会触发整合——把多条细粒度的记忆合并为更精炼的摘要,并在摘要中添加 caveat(警告),比如”此信息来自 5 天前的对话,可能已过时”。

Freshness caveat 不是真正的遗忘——它不删,只是”提醒你这条可能过时了”。这就像食品包装上的”最佳食用日期”——过了这个日期不代表食物一定坏了,但你需要更谨慎地判断。

5.3 “只增不减”的现实

为什么大多数系统选择只增不减?三个原因。

原因一:删除的代价不对称。删错一条有用记忆的代价远高于多保留一条无用记忆的代价。多保留一条无用记忆,最坏情况是检索时多了一条干扰项;删错一条有用记忆,Agent 就”忘了”重要信息,可能导致错误的行为。在代价不对称的情况下,保守策略(只增不减)是理性的。

原因二:隐式失效判断极难。前面说过,你很难确定一条记忆是否真的”失效”了。在没有明确信号的情况下做删除,风险太高。

原因三:成本收益不对等。设计一个好的遗忘机制需要大量工程投入(矛盾检测、时间衰减、引用计数、用户确认流程等),但收益是”检索时少看几条不相关的”——这通常可以通过更好的检索策略来解决,而不需要删掉记忆本身。

所以大多数系统选择了”让检索来过滤,而不是让遗忘来删除”——存就存了,检索时只返回相关的。这不是最优解,但在当前的工程约束下是务实的选择。

核心权衡一句话:遗忘比记忆更难设计,大多数系统选择只增不减,用检索过滤替代物理删除


6. 约束五:隔离与共享——不同用户、不同项目、不同 Agent 之间

最后一个约束回答的是:谁能看到谁的记忆

6.1 三种隔离粒度

粒度一:per-user

每条记忆绑定一个 user_id,不同用户的记忆完全隔离。这是最常见的粒度——mem0、Letta、DB-Agent-Memory 默认都按 user_id 隔离。

per-user 隔离的好处是隐私和安全——用户 A 的偏好不会泄露给用户 B。代价是信息孤岛——同一个项目、同一个团队的人无法共享记忆。如果用户 A 和用户 B 在做同一个项目,他们分别告诉各自的 Agent “项目用 PostgreSQL”,这个信息被存了两遍,而且无法交叉验证。

粒度二:per-project

记忆按项目分组,同一个项目内的所有用户共享记忆。这是 Claude Code 的默认策略——记忆文件存在项目目录下(.claude/memory/),任何在这个项目目录下工作的用户都能看到这些记忆。

per-project 隔离的好处是协作——同一个项目的开发者共享项目级的知识(技术栈、代码规范、架构决策),Agent 不需要每个开发者重新”教”一遍。代价是隐私——如果你在这个项目目录下告诉 Agent 你的个人偏好(”我喜欢深色主题”),其他开发者也能看到。

粒度三:global

所有用户、所有项目共享同一份记忆。Codex 是最接近 global 的——它的 MEMORY.md 是全局的,不按用户或项目隔离。所有使用同一个 Codex 实例的人看到相同的记忆。

global 的好处是简洁——不需要管理隔离逻辑。但代价是混乱——不同用户、不同项目的记忆混在一起,可能互相干扰。你用 PostgreSQL 的记忆可能和另一个人用 MySQL 的记忆冲突。

6.2 三个系统的隔离选择对比

系统 隔离粒度 实现方式 好处 代价
Codex Global 单一 MEMORY.md 简洁,无隔离逻辑 不同用户/项目记忆混合
Claude Code Per-project 记忆文件在项目目录下 同项目协作,跨项目隔离 项目内无用户隔离
mem0 Per-user user_id 字段过滤 隐私安全,个性化 项目内信息不共享

6.3 多 Agent 共享记忆服务的冲突问题

当多个 Agent 实例共享同一个记忆服务时,会出现更复杂的隔离问题。

场景:你有一个 Coding Agent 和一个写作 Agent,它们都连接同一个 mem0 实例、用同一个 user_id。Coding Agent 记住了”用户偏好 Python”,写作 Agent 记住了”用户偏好中文散文”。两条记忆绑定同一个 user_id,但属于不同的 Agent 上下文——当 Coding Agent 检索记忆时,它可能看到”用户偏好中文散文”这条不相关的记忆,造成干扰。

更严重的情况是矛盾:Coding Agent 记住了”项目使用 Python”,而一个数据库管理 Agent 记住了”项目使用 Go”(因为它看到的是数据库工具链部分的代码是 Go 写的)。两条记忆绑定同一个 user_id 但互相矛盾——Agent 无法判断哪个正确。

现有方案对这类冲突的处理都很初步:

这是一个开放问题——目前没有成熟的工业级解决方案。


7. 五维矩阵:十个系统在五个约束上的选择

把前面五个约束的讨论整合成一张表,可以清晰看到每个系统的设计指纹。

系统 写入路径 存储模型 读取路径 遗忘机制 隔离粒度
DB-Agent-Memory LLM 提取(按层) 分层金字塔(SQLite) 按层检索(Profile 优先) 无显式遗忘 per-user
mem0 LLM 提取(每轮) 扁平列表(向量数据库) 向量搜索 LLM 去重更新 per-user
Letta Agent 自操作(tool call) Core Memory(prompt)+ Archival(数据库) Core Memory 全量注入 + Archival 向量搜索 Agent 自管理(core_memory_replace) per-user
Graphiti LLM 提取 + 图构建 知识图谱(Neo4j) 向量粗筛 + 图遍历 invalid_at 双时间轴 per-user
cognee ECL 管线 知识图谱(图数据库) 图遍历 无显式遗忘 per-user(可配置)
supermemory LLM 提取 + 多源捕获 扁平列表 + 图 BM25 + 向量 + 图三流融合 无显式遗忘 per-user
MemOS Dream Pipeline(后台巩固) 认知容器(MemCube) 调度器选择类型化检索 Dream Pipeline 隐式淘汰 per-user
Claude Code 持续学习 + LLM 提取(fire-and-forget) 文件系统(markdown) 索引注入 + LLM 语义检索 freshness caveat(超 24h + 5 会话加警告) per-project
Codex 回顾学习 + LLM 提取(两阶段 pipeline) 文件系统(markdown + JSON) 全量注入 MEMORY.md citation 正反馈循环(隐式淘汰) global
OpenClaw 抢救保存(Pre-Compaction Flush) 文件系统(三层文件布局) MEMORY.md 全量注入 + 日记检索 无显式遗忘 per-project

7.1 从矩阵中能看到的模式

模式一:写入路径两极分化。三个生产系统各自代表一种写入哲学——持续学习、回顾学习、抢救保存。七个开源项目则偏向 LLM 提取或管线化处理。生产系统更关注”不丢信息”,开源项目更关注”提取质量”。

模式二:存储模型两极分化。生产系统统一选文件系统,开源项目统一选数据库。这不是偶然的——两种路线分别优化了”用户可理解性”和”系统查询能力”。

模式三:读取路径各有侧重。全量注入(Codex)在记忆量小时最优,LLM 语义检索(Claude Code)在记忆量中等时最优,向量搜索(mem0)在大规模时最优,图遍历(Graphiti/cognee)在需要推理时最优。没有一种策略在所有场景下都最优。

模式四:遗忘几乎空白。十个系统中只有 Graphiti 有显式的遗忘机制(invalid_at),Codex 有隐式的淘汰(citation 循环),Claude Code 有轻量的提示(freshness caveat)。其余七个系统选择只增不减。遗忘是目前记忆系统最大的设计空白。

模式五:隔离粒度不够灵活。大多数系统只支持一种隔离粒度(per-user 或 per-project),不支持”同项目不同用户”或”同用户不同项目”的精细隔离。多 Agent 共享记忆时的冲突问题几乎没有系统处理。


8. 本章小结

回顾本章的核心要点:

这五个约束不是独立的——它们互相影响。写入路径决定了存储模型能包含什么信息,存储模型决定了读取路径能用什么策略,读取路径的精度影响遗忘机制的紧迫性,隔离粒度影响写入和读取的复杂性。设计记忆系统时需要同时考虑五个约束,而不是逐个优化。

下一章进入 Part 3,我们开始精读七个开源项目——从 DB-Agent-Memory 的四层金字塔开始。


导读目录:README.md 上一章:Ch06: Memory vs RAG——经验和知识不是一回事 下一章:Ch08: DB-Agent-Memory——四层金字塔与零依赖哲学