Ch18: 八大记忆设计模式——从 Flush 到 Rules-as-Memory
Part 5: 横切主题与前沿 前置章节:Ch17: OpenClaw 记忆——Pre-Compaction Flush 与 Dreaming 后续章节:Ch19: 十九个技术挑战——记忆系统的未解难题
1. 引言——为什么需要设计模式
1.1 从十个系统到八种模式
前十七章我们逐一精读了十个记忆系统——七个开源项目(DB-Agent-Memory / mem0 / Letta / Graphiti / cognee / supermemory / MemOS)和三个生产级系统(Claude Code / Codex / OpenClaw)。每个系统有自己的设计哲学、架构选择和实现路径,看起来各不相同。
但如果你退后一步,把十个系统放在一起看,会发现一个有趣的现象:不同系统在解决相似问题时,常常不约而同地采用了相似的方案。就像不同城市的交通系统虽然各有特色,但都会遇到”高峰期拥堵”这个共同问题,而解决方案不外乎限流、分流、扩容这几种思路。
软件工程中有一个经典概念来描述这种现象——设计模式。设计模式的定义是:在特定上下文中,针对反复出现的问题的、经过验证的经典解法。
这个概念来自建筑学家 Christopher Alexander 的观察:好的建筑不是凭空设计的,而是在特定文化中经过长期演化形成的模式。软件工程先驱们(Gang of Four)把这个思想搬到了编程领域——他们发现,不同的软件系统在解决相似的设计问题时,常常采用相似的结构。
记忆系统领域正在经历同样的过程。十个系统各自摸索,但解决的核心问题可以归类为有限几类,每类问题有几种经典解法。我们从前十章的源码精读和横向对比中,提炼出八种反复出现的记忆设计模式。
1.2 设计模式不是万能药
在深入每种模式之前,有三点需要明确。
第一,设计模式是”起步方案”不是”终极答案”。每种模式解决了它所针对的问题,但都带来了新的局限。选型时需要理解模式的 trade-off,而不是盲目套用。
第二,实际系统常常组合多种模式。Claude Code 同时用了 Index+Retrieval(模式二)和 Flush 变体(模式一),DB-Agent-Memory 同时用了 Semantic Pyramid(模式四)和 MCP Service(模式七)。模式是积木,系统是搭积木的结果。
第三,模式会演化。今天看到的八种模式是基于 2024-2025 年的技术现状提炼的。随着新的系统出现(比如未来的多 Agent 协作记忆),可能产生新的模式。设计模式是活的分类,不是死的教条。
1.3 八种模式总览
在逐一深入之前,先用一张表建立整体印象:
| # | 模式名 | 解决的问题 | 核心思想 | 代表系统 |
|---|---|---|---|---|
| 1 | Pre-Compaction Flush | 上下文压缩时记忆丢失 | 压缩前先抢救 | OpenClaw |
| 2 | Index + Retrieval | 记忆太多不能全量注入 | 索引目录 + 按需检索 | Claude Code |
| 3 | Two-Phase Pipeline | 实时提取和整合太耗资源 | 轻量提取 + 重量整合分开 | Codex |
| 4 | Semantic Pyramid | 所有记忆同等重要导致噪声 | 按抽象层次分层 | DB-Agent-Memory |
| 5 | Virtual Memory | 上下文窗口有限但记忆需无限 | 借鉴 OS 虚拟内存 | Letta/MemGPT、MemOS |
| 6 | Temporal Graph | 记忆有时间维度 | 知识图谱加双时间轴 | Graphiti |
| 7 | MCP Memory Service | 不同工具记忆不互通 | 记忆封装为 MCP Server | supermemory、DB-Agent-Memory |
| 8 | Rules-as-Memory-Bank | Agent 没有内建记忆 | 用 Profile 文件模拟记忆 | Cline Memory Bank |
这八种模式不是互相排斥的,而是可以组合的。后面会专门讨论组合策略。
2. 模式一:Pre-Compaction Flush(上下文压缩前抢救)
2.1 问题域——压缩那一刻的记忆丢失
想象你正在写一篇长篇论文,在一张 A4 纸上用极小的字密密麻麻地写。纸快写满了,你需要换一张新纸继续——但换纸的时候,旧纸上有些内容没有来得及誊抄就丢了。
LLM 的上下文窗口就是那张 A4 纸。对话越来越长,token 越来越多,当接近上下文窗口上限时,系统会触发 context compaction(上下文压缩)——把当前对话的内容压缩或截断,腾出空间给后续对话。
压缩的过程中,有些信息不可避免地会丢失。普通的压缩策略是”保留开头和结尾、压缩中间”——因为开头通常是 system prompt(不能丢),结尾是最近的对话(用户正在关注),中间的历史对话被压缩得最狠。
问题在于:中间的历史对话可能包含重要信息。用户可能在第 17 轮对话中说了一个重要偏好,但到了第 50 轮时,第 17 轮的内容被压缩掉了——这个偏好就永远丢失了,除非它已经被存储到了某个持久化的记忆系统中。
这就是 Pre-Compaction Flush 要解决的问题:在压缩发生之前,把重要信息”抢救”出来。
2.2 核心机制——嵌入式抢救 Agent
核心思路是在主对话流中嵌入一个专门的”记忆抢救 Agent”。这个 Agent 一直在后台监控对话,当检测到即将触发 compaction 时,它会立即激活,从当前上下文中提取重要信息,并写入持久化存储。
用流程图表示:
正常对话流:
用户消息 → Agent 回复 → 用户消息 → Agent 回复 → ...
↓
token 接近阈值
↓
Flush Agent 激活:
扫描当前上下文 → 提取关键信息 → 写入持久存储(日记/数据库)
↓
Compaction 安全执行:
压缩上下文 → 腾出空间 → 对话继续
关键的时序约束是:Flush Agent 必须在 compaction 之前完成。如果 Flush 还没写完,compaction 就已经把上下文清掉了,那 Flush Agent 本身的信息来源也丢失了。
2.3 代表系统——OpenClaw
OpenClaw 是腾讯 tRPC-Agent-Go 的 Coding Agent 模式,它是最早显式将记忆保存前置于上下文压缩的系统。
OpenClaw 的实现细节:
嵌入式 Agent 声明:Flush Agent 通过 system prompt 中的工具声明让主 Agent 知道自己的存在。主 Agent 在对话过程中可以主动调用 memory_flush 工具,将当前上下文中的重要信息写入日记文件。
三层文件布局:
MEMORY.md——长期记忆层,存储经过整合的核心信息daily/YYYY-MM-DD.md——日记层,每天一个文件,记录当天的关键事实DREAMS.md——整合日志,记录 Dreaming 操作的过程和结果
Flush 的触发:不是等到 compaction 发生时才被动触发,而是在对话过程中由 Agent 主动判断”这段对话包含了值得保存的信息”时主动触发。这比被动触发更可靠——因为被动触发时可能已经来不及了。
Dreaming 异步整合:Flush 之后,日记文件中的信息需要异步整合到长期记忆中。OpenClaw 的 Dreaming 机制在后台运行,扫描日记文件,去重、合并、更新 MEMORY.md。
2.4 适用场景
长对话场景:Coding Agent 的编码 session 常常超过数万 token,compaction 几乎必然发生。没有 Flush 机制的 Agent 在长对话中一定会丢失重要信息。
用户不主动保存的场景:大多数用户不会在对话中说”请记住这个”。Flush Agent 自动识别重要信息并保存,降低了对用户记忆管理意识的要求。
信息密度不均匀的对话:有些对话的 90% 是琐碎交互(”嗯”“好的”“继续”),但 10% 包含关键决策。Flush Agent 可以在关键决策出现时主动保存,而不用等待整个对话结束。
2.5 局限性
增加压缩延迟:Flush Agent 本身需要 LLM 调用来提取信息,这会增加 compaction 前的延迟。在临界状态下(token 已非常接近上限),Flush Agent 的调用可能反而加速 compaction 的触发——因为它消耗的 token 也在上下文窗口内。
Flush Agent 质量依赖模型能力:提取什么、怎么提取,完全取决于 Flush Agent 的 LLM 能力。弱模型可能提取出噪声而遗漏关键信息,强模型可能过度提取导致存储膨胀。这个问题的本质和”信号 vs 噪声”是同一个问题,只是发生在了 compaction 前的时间窗口内。
时序竞争条件:如果对话产生 token 的速度超过了 Flush Agent 保存的速度,compaction 可能在 Flush 完成之前就触发了。OpenClaw 通过让 Agent 主动调用(而非被动等待 compaction 信号)来缓解这个问题,但不能完全消除。
只能抢救当前上下文内的信息:如果信息在之前的 compaction 中已经丢失了,Flush Agent 无法”复活”它。Flush 只能防止未来的丢失,不能修复过去的丢失。
3. 模式二:Index + Retrieval(索引 + 检索)
3.1 问题域——记忆太多,不能全量注入
想象你有一个藏书十万册的私人图书馆。每次你想找一本书的内容时,不可能把十万本书全翻一遍——你需要一个目录。目录告诉你每本书的书名、作者、内容简介和书架位置。你先查目录,找到可能相关的几本书,再去书架上取出来看。
记忆系统面临完全相同的问题。当记忆条目从几十条增长到数百条甚至数千条时,把所有记忆全量注入到 LLM 的上下文窗口中是不现实的——不仅超出 token 预算,还会导致”lost in the middle”问题(LLM 对上下文中部信息的关注度最低)。
问题是:如何在大量记忆中快速找到最相关的几条?
3.2 核心机制——索引文件 + 语义检索
Index + Retrieval 模式的核心思路是两层架构:
第一层:索引文件——一个精炼的”目录”文件,包含所有记忆条目的摘要。索引文件本身足够小,可以全量注入上下文窗口。
第二层:按需检索——根据当前对话的上下文,从索引文件中选出最相关的 N 个记忆文件,然后加载这些文件的完整内容注入上下文。
用流程图表示:
会话启动:
加载索引文件(全量注入上下文)
↓
用户发送消息
↓
LLM 根据用户消息 + 索引文件
→ 选出最相关的 ≤N 个记忆文件
↓
加载选中文件的完整内容
→ 注入上下文
↓
LLM 基于完整上下文生成回复
关键的设计决策是:索引文件多大?N 取多少?索引文件的摘要多精炼?这些参数直接决定了系统的效果。
2.3 代表系统——Claude Code
Claude Code 是这个模式的最佳实践者。它的记忆系统叫 memdir,核心结构如下:
MEMORY.md:索引文件,硬顶限制 200 行 / 25KB。包含所有记忆文件的摘要和路径。每次会话启动时全量注入。
记忆文件:存储在 ~/.claude/projects/<project-hash>/memories/ 目录下,按项目和用户分层。每个文件是一个独立的 Markdown 文档,包含一类记忆(如”用户偏好”“项目架构”“踩坑记录”)。
sideQuery 检索:每一轮对话中,独立的 Sonnet 调用从索引文件中选出最相关的 5 个记忆文件(MAX_MEMORY_FILES_TO_INCLUDE = 5),加载完整内容注入上下文。
extractMemories 写入:会话结束后,后台 Agent 从对话中提取新记忆,更新或创建记忆文件,并同步更新 MEMORY.md 索引。
Claude Code 的巧妙之处在于:用 LLM 做检索决策,而不是传统的向量搜索。LLM 可以理解”用户在问数据库迁移”和”记忆文件中有一条’上周做了 PostgreSQL 迁移’“之间的语义关联——这种关联纯靠向量相似度可能抓不到。
3.4 适用场景
记忆量大(数十到数百个文件):当记忆文件数量超过可以全量注入的阈值时,Index + Retrieval 是最自然的解决方案。
人机协作编辑的场景:用户可以直接编辑索引文件和记忆文件,像整理笔记一样管理记忆。这种透明性是纯数据库方案做不到的。
记忆有自然分层:当记忆可以按项目、按主题、按时间自然分组为独立文件时,索引 + 文件的组织方式最清晰。
3.5 局限性
LLM 筛选成本高:每一轮对话都需要一次独立的 LLM 调用来做检索决策。如果用 Sonnet 级别的模型,每次调用约 0.01-0.03 美元。在高频对话中,这个成本会累积。
索引维护需要额外机制:每次新增或修改记忆文件时,索引文件需要同步更新。如果索引更新不及时,LLM 可能选不到最新的记忆文件。Claude Code 用 extractMemories 后台 Agent 来处理这个同步,但更新有延迟。
索引文件有硬顶限制:200 行 / 25KB 意味着当记忆文件数量增长到数百个时,每个文件在索引中的摘要必须越来越精简——精简到可能丢失关键信息,导致 LLM 无法准确判断文件的相关性。
不支持结构化查询:索引文件是自然语言文本,LLM 只能做语义理解来匹配。如果你需要”找出所有包含’PostgreSQL’的记忆文件”,LLM 可能不如全文索引精准。
4. 模式三:Two-Phase Pipeline(两阶段管线)
4.1 问题域——实时提取和整合的资源矛盾
想象一个新闻编辑部。记者们在前线源源不断地发回报道——有些是几句话的快讯,有些是几千字的深度稿。如果每来一篇稿件就立刻排版、校对、刊发,编辑部会被淹没——因为排版和校对是重量级操作,需要全量审视所有已有稿件来决定新稿件放在哪个版面、和哪些已有报道关联。
更合理的做法是:记者的稿件先存入一个”稿件池”(轻量操作),然后由资深编辑定期审阅稿件池,做全局性的排版和整合(重量操作)。
记忆系统的 Two-Phase Pipeline 就是这个思路。Phase1 是记者——轻量快速地从对话中提取候选记忆条目,存入暂存区。Phase2 是资深编辑——定期对暂存区做全局整合,去重、合并、更新记忆文件。
4.2 核心机制——并行提取 + 隔离整合
Phase1(实时提取):每次对话(或任务)结束后,立刻从对话中提取候选记忆条目。提取是并行的——多个对话的提取可以同时进行。提取结果是结构化的 JSON 条目,存入 staging 区。
Phase2(离线整合):在满足冷却期条件后,独立整合 Agent 读取所有 staging 条目 + 现有记忆文件,做全局性的合并、去重、更新。整合是串行的——同一时间只有一个 Phase2 在运行(需要全局锁)。
用流程图表示:
Session 1: [对话] → Phase1 并行提取 → staging 条目
Session 2: [对话] → Phase1 并行提取 → staging 条目
Session 3: [对话] → Phase1 并行提取 → staging 条目
↓
冷却期到达(6小时)
↓
Phase2 整合 Agent:
读取 staging + 现有记忆
→ 全局去重合并
→ 增量更新记忆文件
→ 清理 staging
4.3 代表系统——Codex
OpenAI 的 Codex(ChatGPT 的代码模式)是 Two-Phase Pipeline 的典型实现。
Phase1 的细节:
- 触发时机:每次 rollout(任务执行)结束后
- 并发度:concurrency=8,同时处理 8 个 rollout 的提取
- 提取内容:结构化 JSON,包含事实、决策、偏好等
- 存储:staging 表(SQLite)
Phase2 的细节:
- 冷却期:6 小时(
CONSOLIDATION_COOLDOWN_SECS = 21600) - 全局锁:同一时间只有一个 Phase2 运行
- 执行环境:隔离沙箱——整合过程不影响主流程
- 输出格式:unified diff(git 风格的增量更新)
- 反馈循环:每条记忆有
usage_count,被引用时递增。usage_count 低的记忆可能在下次整合时被淘汰
冷却期的设计意图:6 小时看起来很长,但有其道理。Codex 的用户通常一天内会有多个 session——如果每个 session 结束后都跑一次整合,不仅浪费资源,还会产生碎片化的记忆(每次只看到最近一个 session 的信息,缺乏全局视角)。6 小时冷却期确保整合 Agent 能看到足够多的新信息,做出更好的全局决策。
4.4 适用场景
高并发场景:多个 session 同时运行时,Phase1 的并行提取确保不阻塞主流程,Phase2 的全局锁确保整合的一致性。
资源敏感场景:整合是重量操作(需要读取所有记忆 + LLM 推理),放在后台异步执行不影响用户交互的延迟。
需要高质量整合的场景:Phase2 的全局视角能发现跨 session 的模式和矛盾,这是单 session 提取做不到的。比如用户在三个不同 session 中分别说了”在学 Rust”“在写 Rust 项目”“Rust 的性能比 Go 好”,Phase2 可以把这三条整合为”用户是 Rust 开发者,偏好 Rust 的性能优势”。
4.5 局限性
Phase2 冷却期的新鲜度延迟:6 小时冷却期意味着最新产生的记忆要等至少 6 小时才能被整合进主记忆文件。在这 6 小时内,新 session 看到的仍是旧版本的记忆。
整合质量不透明:Phase2 的整合 Agent 自动决定保留哪些记忆、删除哪些、合并哪些——但用户无法预知这些决策。如果整合 Agent 做了错误的合并,原始信息可能永久丢失(虽然 staging 条目还在,但用户通常不会去看)。
staging 表膨胀:如果冷却期设置过长或 Phase2 因故障未运行,staging 表会持续增长。staging 条目没有自动清理机制——只有 Phase2 成功运行后才会清理。
全局锁的单点瓶颈:Phase2 的全局锁确保了一致性,但也意味着整合是串行的——如果整合本身耗时很长(比如记忆量巨大时),后续的 Phase2 请求会排队等待。
5. 模式四:Semantic Pyramid(语义金字塔)
5.1 问题域——所有记忆同等重要导致检索噪声大
想象你有一个文件柜,里面塞满了各种文件——从出生证明到昨天的超市小票,从护照到外卖订单的确认短信。如果你需要找护照,你得翻过几百张超市小票才能找到——因为所有文件都被同等对待,没有分类,没有优先级。
记忆系统如果不对记忆做分层处理,就会变成这个文件柜。当你有 1000 条记忆时,检索结果中可能混杂着”用户三年前说了一句喜欢晴天”和”用户昨天决定切换技术栈”——前者早已过时,后者至关重要,但系统不区分它们。
语义金字塔的解决思路是:按抽象层次对记忆分层,越往上越精炼、越稳定、越通用。
5.2 核心机制——四层逐层蒸馏
语义金字塔将记忆组织为四个层次,从底到顶逐层蒸馏:
L0(观测层)——原始对话内容,最详细、最冗余。相当于快递分拣中心的原始包裹。
L1(事实层)——从观测中提取的独立原子事实。相当于分拣后的归类标签。
L2(摘要层)——对事实的阶段性聚合。相当于装箱汇总单。
L3(画像层)——长期稳定的用户/项目画像。相当于客户画像。
每一层的写入是自底向上的——L0 提取为 L1,L1 聚合为 L2,L2 提炼为 L3。每一层的检索是自顶向下的——先查 L3 有没有直接相关的画像,再查 L2 有没有更具体的摘要,最后才去 L1 找细节。
写入方向(从底到顶):
L0 Observation → L1 Fact → L2 Summary → L3 Profile
(原始对话) (原子事实) (场景聚合) (用户画像)
检索方向(从顶到底):
L3 Profile → L2 Summary → L1 Fact → L0 Observation
(最精炼) (较精炼) (有细节) (最详细)
5.3 代表系统——DB-Agent-Memory
DB-Agent-Memory 是语义金字塔模式的最佳实践者。
每层的存储和特征:
- L0:JSON 格式,包含时间戳、角色、内容。增长最快,信息密度最低
- L1:结构化文本,独立陈述句。有冲突检测机制
- L2:自然语言段落,概括一个时间段或主题的事实集合。可定期刷新
- L3:键值对形式,最稳定,跨会话持久
晋升触发条件:
should_summarize:新事实超过 N 条时触发 L1→L2should_update_profile:L2 中出现新模式时触发 L2→L3find_conflicts:新事实与已有事实矛盾时触发冲突处理
MMD 注入优化:将记忆分为稳定区(L3 画像 + L2 摘要,放在 prompt 前部,KV Cache 友好)和动态区(L1 事实 + L0 观测,放在 prompt 后部,每次变化),最大化 Cache 命中率。
5.4 适用场景
需要不同粒度记忆的场景:有些查询只需要粗粒度信息(”用户是什么角色”),有些需要细粒度信息(”用户上周三讨论了什么方案”)。金字塔结构让系统可以根据查询需求选择合适的层级检索。
重视 Prompt Cache 友好性:稳定区/动态区分离让 KV Cache 命中率最大化——在按 token 计费的模型中,这是实实在在的成本优势。
零依赖部署环境:不需要向量数据库、图数据库,SQLite + sqlite-vec 就够了。适合嵌入式部署和资源受限环境。
5.5 局限性
层间晋升逻辑复杂:L1→L2→L3 的晋升不是简单的”够 N 条就触发”——需要判断哪些事实可以聚合、聚合后的摘要是否准确、哪些模式足够稳定可以晋升为画像。这些判断目前都依赖 LLM,质量不稳定。
自动晋升容易丢信息:每次蒸馏都是一次信息压缩——压缩必然伴随损失。L0 中的细节在 L1 中被提炼为事实,事实的上下文可能丢失;L1 中的多条事实在 L2 中被聚合,个别异常值可能被忽略。越往上,信息损失越严重。
提炼管道的 LLM 成本线性增长:每一层晋升都需要 LLM 调用。对话越频繁、记忆越多,LLM 成本越高。在成本敏感的场景下,这可能成为瓶颈。
底层噪声向上层渗透:如果 L0→L1 提取时 LLM 把噪声误判为信号(比如把”用户随口说了句 Python 不错”提取为”用户偏好 Python”),这个错误会逐层向上放大——L2 的摘要基于错误的事实,L3 的画像基于错误的摘要。金字塔结构天然有”错误放大”效应。
6. 模式五:Virtual Memory(虚拟内存)
6.1 问题域——上下文窗口容量有限但记忆需要”无限”
想象一台电脑只有 8GB 内存,但你要运行需要 100GB 数据的程序。现代操作系统是怎么解决这个问题的?虚拟内存——把不常用的数据换到硬盘上,只把当前需要的数据放在内存里。程序以为它有无限的内存,实际上是在内存和硬盘之间不断换入换出。
Agent 面临完全相同的问题:上下文窗口是有限的”内存”(当前 GPT-4 约 128K token),但用户可能在数月甚至数年间和 Agent 积累了远超上下文容量的交互历史。Agent 需要”记住”所有这些历史,但又不可能全塞进上下文窗口。
6.2 核心机制——Core Memory + Archival Memory
Virtual Memory 模式将记忆分为两个区域,直接类比操作系统的 RAM 和硬盘:
Core Memory(核心记忆/RAM):始终在上下文窗口中,读取零延迟,但容量有限(通常约 2000 token)。存储 Agent 的自我定义(persona)和用户的核心信息。
Archival Memory(归档记忆/硬盘):存储在数据库中,容量无限,但需要搜索才能访问。存储所有历史交互和提取的事实。
Recall Memory(回忆记忆/缓存):最近对话的搜索缓存,可以按关键词或语义搜索。
Agent 通过工具调用(tool call)管理记忆的换入换出:
Core Memory 工具:
core_memory_append(key, value) ← 向核心记忆追加信息
core_memory_replace(old, new) ← 替换核心记忆中的信息
Archival Memory 工具:
archival_memory_insert(content) ← 向归档记忆插入信息
archival_memory_search(query) ← 搜索归档记忆
Recall Memory 工具:
conversation_search(query) ← 搜索对话历史
Agent 自主决定什么时候把信息从 Archival 换入 Core(page in),什么时候把 Core 中不常用的信息换出到 Archival(page out)。当 Core Memory 满了,Agent 必须决定删什么来腾出空间——这就像内存满了必须换页。
6.3 代表系统——Letta/MemGPT、MemOS
Letta/MemGPT 是虚拟内存模式的开创者。源自 2023 年的 MemGPT 论文,核心创新是把操作系统的虚拟内存概念显式引入 Agent 记忆管理。Agent 有 7 个记忆管理工具,完全自主决定何时读写。
MemOS 是 Letta 虚拟内存思想的学术延伸。Letta 借用的是虚拟内存(空间管理),MemOS 借用的是整个操作系统——不仅做空间管理(MemCube 容器化),还做调度(MemScheduler)、做后台服务(Dream Pipeline)、做跨模态存储。MemOS 可以被看作 Virtual Memory 模式的”升维版”。
两者在虚拟内存设计上的关键区别:
| 维度 | Letta | MemOS |
|---|---|---|
| 内存模型 | Core + Archival + Recall | MemCube(四种类型容器) |
| 管理方式 | Agent 自主(tool call) | 系统调度 + Agent 可干预 |
| 换入换出 | Agent 手动 page in/out | MemScheduler 自动调度 |
| 容量管理 | Core Memory 字符限制 | MemCube 优先级 + 衰减 |
6.4 适用场景
需要大量历史记忆但上下文预算有限:当用户和 Agent 有长期的交互历史,不可能全量注入上下文时,Virtual Memory 提供了”按需加载”的方案。
Agent 有强元认知能力:这个模式的核心前提是 Agent 能自主判断”什么时候该查记忆”“查什么”“记什么”。在 GPT-4 级别的模型上这个前提基本成立,但在更弱的模型上,Agent 经常忘记保存或检索。
研究/实验性场景:Virtual Memory 模式提供了最灵活的记忆管理方式,适合探索 Agent 自主记忆管理的能力边界。
6.5 局限性
Agent 必须有强元认知能力:这是 Virtual Memory 模式最大的局限。Agent 需要”意识到”自己不知道某件事,然后主动搜索归档记忆——这种元认知能力在弱模型上经常缺失。实践中,弱模型的 Agent 经常出现三种失败模式:
- 忘记保存:Agent 和用户讨论了重要偏好但没有调用
archival_memory_insert - 忘记检索:用户问了一个之前讨论过的问题,Agent 没有搜索归档记忆就凭”记忆”回答——可能回答错误
- Page fault 风暴:Agent 频繁在 Core 和 Archival 之间换入换出,每次都是完整的工具调用周期(2-5 秒),用户体验极差
每次记忆操作都是工具调用:读记忆、写记忆、搜索记忆——每个操作都是一次完整的工具调用周期(LLM 生成工具调用 → 系统执行 → LLM 处理结果)。这比系统自动管理记忆慢得多,也贵得多。
Core Memory 容量太小:约 2000 token 的限制意味着 Core Memory 只能存最核心的信息。一旦信息量超过容量,Agent 必须做艰难的选择——删什么?这种选择本身就需要推理能力,弱模型经常做错。
7. 模式六:Temporal Graph(时序图)
7.1 问题域——记忆有时间维度
“去年住北京”和”现在住上海”是两条不同的事实,但它们描述的是同一个属性(住址)在不同时间的值。如果你只存”住北京”和”住上海”两条记忆,检索”用户现在住哪”时可能两条都返回——你分不清哪个是当前状态。
再比如”用户喜欢 React”和”用户不再用 React 了,改用 Vue”——这两条记忆表达了完全相反的状态,但如果没有时间维度,系统无法区分”当前偏好”和”历史偏好”。
很多事实是有时间生命周期的:住址会变、工作会变、偏好会变、项目状态会变。简单的”添加事实/删除事实”模型无法表达这种时间性的变化。
7.2 核心机制——知识图谱 + 双时间轴
Temporal Graph 模式在知识图谱的每条边上添加两个时间戳:
valid_at:事实何时开始有效invalid_at(或expired_at):事实何时失效
当新事实和旧事实冲突时,不删除旧事实——只是把旧事实的 invalid_at 设为当前时间,然后创建一条新事实(valid_at = 当前时间,invalid_at = NULL)。两条事实都保留在图谱中,通过时间戳区分。
[2024-01] 用户 --住在--> 北京 (valid_at=2024-01, invalid_at=2025-03)
[2025-03] 用户 --住在--> 上海 (valid_at=2025-03, invalid_at=NULL)
查询 "用户现在住哪":
→ invalid_at IS NULL → 上海
查询 "用户2024年住哪":
→ valid_at ≤ 2024-01 AND invalid_at > 2024-12 → 北京
这个设计最优雅的地方在于:它不是”解决”冲突,而是”记录”冲突的完整历史。每条事实的来龙去脉都保留在图谱中,任何时刻的状态都可以通过时间过滤精确还原。
7.3 代表系统——Graphiti
Graphiti 是 Zep 团队出品的时序知识图谱引擎,是 Temporal Graph 模式的典型实现。
存储:Neo4j 图数据库。节点表示实体(人、地点、技术等),边表示关系(住在、使用、偏好等),每条边带双时间轴。
检索:两阶段架构:
- 向量粗筛:用 embedding 相似度找到候选节点和边
- Cross-Encoder 精排:用 Cross-Encoder 模型对候选结果做精排,确保返回最相关的结果
实体消解:当对话中出现”小明”和”明哥”时,Graphiti 需要 LLM 判断它们是否指同一个人。如果判断为同一人,两条边指向同一个节点——图谱更精简。如果判断错误,不同人的信息被错误合并。
图谱膨胀问题:每次事实变更都创建新边而非修改旧边——图谱只增不减。1000 轮对话后可能有数万个节点和边,查询性能逐渐下降。这是 Temporal Graph 模式最严峻的工程挑战。
7.4 适用场景
传记类信息:用户的住址、工作、关系等随时间变化的个人信息——这类信息天然适合用时序图表达。
状态变化频繁的场景:项目状态(规划中→开发中→已上线)、技术栈偏好(Python→Rust)、合作关系(同事→前同事)——这些频繁变化的事实需要精确的时间追踪。
需要历史追溯的场景:回答”用户什么时候开始用 Rust?”“用户之前用的是什么?”这类问题时,时序图可以直接给出答案,而不需要翻阅所有历史记忆。
7.5 局限性
图数据库依赖重:Neo4j 需要 JVM + 独立服务,推荐 4GB+ RAM。部署复杂度远超 SQLite 或文件系统方案。对于简单的记忆场景,这是典型的杀鸡用牛刀。
小规模记忆用图是杀鸡用牛刀:如果你只有几十到几百条记忆,关系也不复杂,用图数据库来存储和检索的成本远大于收益。一个简单的键值对或文本列表就够了。
Cross-Encoder 精排增加延迟:Graphiti 的两阶段检索(向量粗筛 + Cross-Encoder 精排)虽然精度高,但 Cross-Encoder 推理本身需要几百毫秒。加上图遍历的网络往返,总延迟可能超过 1 秒。
实体消解错误不可逆:如果 LLM 错误地把”小明”和”明哥”判断为同一个人,两个不同人的信息被合并到同一个节点——后续查询会返回混合了两个人的错误结果。这种错误的修复非常困难——需要在图谱中”拆分”节点,恢复所有关联的边。
8. 模式七:MCP Memory Service(MCP 记忆服务)
8.1 问题域——不同 AI 工具的记忆不互通
想象你有三个手机 App——日历、备忘录和邮件。日历记住了你的会议时间,备忘记录了你的待办事项,邮件里有你所有的商务通信。但这三个 App 之间完全不互通——日历不知道你的邮件里有明天的航班信息,备忘录不知道你的会议改期了。
你的 AI 工具也是这样。Claude Code 有自己的记忆(MEMORY.md),Codex 有自己的记忆(memories.md),Cursor 有自己的记忆(.cursor/rules/)。你在 Claude Code 里说”我偏好 TypeScript”,Cursor 不知道;你在 Codex 里讨论了项目架构决策,Claude Code 看不到。
MCP(Model Context Protocol)是 Anthropic 在 2024 年底推出的标准协议,目的是让 AI 工具能通过统一的接口访问外部服务。MCP Memory Service 模式就是利用 MCP 协议,将记忆引擎封装为一个标准化的外部服务——任何支持 MCP 的 AI 工具都可以连接。
8.2 核心机制——记忆引擎封装为 MCP Server
MCP Memory Service 的核心思路是将记忆操作封装为 MCP Server 暴露的工具:
MCP Server 暴露的工具:
memory_add(content, metadata) ← 添加记忆
memory_search(query, top_k) ← 搜索记忆
memory_list() ← 列出所有记忆
memory_delete(id) ← 删除记忆
memory_profile() ← 获取用户画像
任何支持 MCP 的 AI 工具(Claude Desktop、Cursor、Cline 等)都可以作为 MCP Client 连接到记忆 Server,通过标准化的工具调用来读写记忆。
三种部署拓扑:
拓扑一:Host-Adapter 模式(DB-Agent-Memory)
Agent → MCP Adapter → DB-Agent-Memory 核心库
Adapter 很薄,核心逻辑在 DB-Agent-Memory 库中。支持嵌入使用(不通过 MCP 也可以直接调用)。
拓扑二:Pure Server 模式(supermemory)
Agent → MCP Server (supermemory)
所有逻辑在 Server 侧。多 Agent 共享同一套记忆,适合跨工具场景。
拓扑三:混合模式(mem0)
Agent → MCP Adapter → mem0 SDK → 后端服务
三层间接:Agent 通过 MCP 调用 Adapter,Adapter 调用 mem0 SDK,SDK 连接后端向量数据库。灵活但复杂。
8.3 代表系统——supermemory MCP、DB-Agent-Memory MCP Adapter
supermemory MCP Server 是 Pure Server 模式的代表。supermemory 的三流检索(BM25 + 向量 + 图遍历)封装为 MCP 工具,Agent 通过 search_memory 工具搜索,结果在 sub-300ms 内返回。支持多个 Agent 共享同一个 supermemory 实例。
DB-Agent-Memory MCP Adapter 是 Host-Adapter 模式的代表。将 DB-Agent-Memory 的记忆能力(四层金字塔 + MMD 注入)包装为 MCP 工具,同时保留 Adapter 嵌入使用的能力。核心优势是”双模式”——既可以嵌入 Agent 内部(零延迟),也可以通过 MCP 对外服务(跨 Agent 共享)。
8.4 适用场景
需要跨工具共享记忆:开发者同时使用多个 AI 工具(Claude Code + Cursor + Codex),希望记忆在工具之间互通。MCP 提供了标准化的接入通道。
Agent 没有内建记忆能力:Cline、Continue 等开源 Agent 没有自己的记忆系统,通过 MCP 连接外部记忆服务是最简单的获得记忆能力的方式——不需要修改 Agent 源码。
已有记忆基础设施需要接入新 Agent:团队已经有 mem0 或 DB-Agent-Memory 作为记忆后端,新加入的 Agent 只需要支持 MCP 就能接入——不需要为每个 Agent 单独做集成。
8.5 局限性
每次读写都是网络往返:MCP 通信通常是 stdio/SSE 方式。纯本地 MCP 通信延迟约 50-300ms,远程 MCP(跨网络)延迟约 200-500ms。相比之下,嵌入式的记忆操作延迟接近零。
MCP 不定义记忆数据模型:MCP 只定义了工具调用的接口格式,不定义记忆条目的数据结构、关系语义、冲突解决策略。不同 MCP 记忆服务返回的 JSON 结构可能完全不同——Agent 需要适配每个服务的特定格式。
Tool Description 质量影响 Agent 使用效果:MCP 工具的效果很大程度上取决于工具描述(tool description)的质量。如果 memory_search 的描述写得太模糊,Agent 可能不知道什么时候该调用它;如果描述太复杂,Agent 可能误用。这类似于 API 文档的质量直接影响开发者体验。
事务语义缺失:当多个 Agent 同时通过 MCP 读写同一条记忆时,写入冲突和一致性问题没有标准协议解决。MCP 不定义锁机制、版本号或冲突解决策略——这些都需要记忆后端自行实现。
9. 模式八:Rules-as-Memory-Bank(规则即记忆库)
9.1 问题域——社区 Agent 没有内建 User Memory 能力
想象你刚买了一台新电脑,它没有硬盘,只有内存——每次关机后所有数据都会丢失。但你发现了一个 workaround:你可以把重要信息写在便签纸上,贴在屏幕边框上。每次开机时你先把便签纸上的内容重新输入电脑,关机前把新信息抄到便签纸上。
这个 workaround 听起来很原始,但这就是 Rules-as-Memory-Bank 模式的本质——当 Agent 没有内建的记忆系统时,用现有的 Profile/Rules 机制来”模拟”记忆功能。
很多社区 Agent(Cline、Cursor 的社区方案等)没有内建 User Memory 能力——它们不能自动从对话中提取和存储用户偏好。但它们有一个 Profile 机制——可以在 .cursorrules、.clinerules 或 memory-bank/ 目录中放置 Markdown 文件,Agent 在每次会话开始时会自动加载这些文件。
9.2 核心机制——用 Profile 文件模拟 User Memory
核心思路是将记忆组织为一组固定结构的 Markdown 文件,利用 Agent 的 Profile 加载机制来实现”记忆持久化”。
典型的 Memory Bank 文件结构:
memory-bank/
projectbrief.md ← 项目基础信息(类似 L3 画像)
techContext.md ← 技术栈和工具链上下文
systemPatterns.md ← 架构模式和设计决策
activeContext.md ← 当前工作焦点(类似 L1 当前事实)
progress.md ← 进展记录和待办事项
运作方式:
- 会话开始:Agent 读取所有 Memory Bank 文件,全量注入 system prompt
- 会话中:Agent 根据加载的记忆内容调整行为
- 会话结束:Agent 更新 activeContext.md 和 progress.md
Custom Instructions(.clinerules 或 .cursorrules)驱动这个读写循环:
.clinerules 中的指令:
"每次会话开始时读取 memory-bank/ 下所有文件"
"每次会话结束时更新 activeContext.md 和 progress.md"
"遇到重要决策时更新 systemPatterns.md"
9.3 代表系统——Cline Memory Bank、社区 Cursor 方案
Cline Memory Bank 是社区中最流行的 Rules-as-Memory-Bank 实现。它通过 .clinerules 自定义指令驱动 Cline Agent 在会话边界读写 Memory Bank 文件。在 Cline 的 Discord 社区中有大量用户分享自己的 Memory Bank 模板和使用经验。
社区 Cursor 方案:Cursor 用户在 .cursor/rules/ 目录下创建带 alwaysApply 模式的规则文件,内容是项目上下文和用户偏好。Cursor 会在每次会话开始时加载这些规则——本质上和 Memory Bank 是同一个思路。
9.4 适用场景
记忆能力匮乏的 Agent 的权宜之计:当 Agent 没有内建 User Memory 也不支持 MCP 时,Rules-as-Memory-Bank 是唯一的选择——用现有的 Profile 机制”凑合”出记忆功能。
小型项目:当记忆量有限(几十条以内),全量注入不会超过 token 预算时,这种模式足够用。
高度人机协作的场景:用户可以直接编辑 Memory Bank 文件,精确控制 Agent 知道什么——这种透明性在某些场景下是优势。
9.5 局限性——三个根本缺陷
Rules-as-Memory-Bank 有三个根本性缺陷,不是优化能解决的:
缺陷一:无语义检索。所有 Memory Bank 文件全量注入上下文——没有”只加载相关文件”的能力。当文件数量增长到几十个时,全量注入的 token 成本线性增长,而且 LLM 要在大量文本中找到相关信息,检索效率急剧下降。相比 Claude Code 的 Index + Retrieval(按需加载 5 个文件)或 mem0 的向量搜索(只返回 top-K 相关条目),全量注入是最低效的检索方式。
缺陷二:无冲突合并。当多个会话同时更新同一个 Memory Bank 文件时,没有自动合并机制——只能靠用户手动解决冲突。如果 Agent A 把 activeContext.md 改成了”正在开发认证模块”,Agent B 同时把它改成了”正在修复数据库 Bug”,最后一个写入的覆盖前一个——信息丢失。相比之下,mem0 的 LLM 去重和 Graphiti 的时序边都能自动处理冲突。
缺陷三:人工触发。记忆的读写依赖于 Agent “记住”在会话开始时读文件、在会话结束时更新文件——但 Agent 并不总是遵守这些指令。特别是当对话很长或很复杂时,Agent 经常”忘记”在会话结束时更新文件。这是 Profile 机制的固有局限——它靠指令驱动而非系统保障。
这三个缺陷决定了 Rules-as-Memory-Bank 只能是过渡方案,不能是终极方案。当 Agent 支持 MCP 时,应该迁移到 MCP Memory Service(模式七)——后者有语义检索、有冲突处理、有系统保障。
10. 选型决策树——根据场景选择模式组合
10.1 按 Agent 能力选型
选择记忆模式首先要考虑你的 Agent 有什么能力:
你的 Agent 有内建记忆系统吗?
├── 有 → 使用内建模式 + 考虑 MCP 对外共享
│ ├── Claude Code → Index + Retrieval (模式二)
│ ├── Codex → Two-Phase Pipeline (模式三)
│ └── OpenClaw → Pre-Compaction Flush (模式一)
│
├── 没有,但支持 MCP → MCP Memory Service (模式七)
│ ├── 需要跨 Agent 共享 → supermemory (Pure Server)
│ └── 需要嵌入使用 → DB-Agent-Memory (Host-Adapter)
│
└── 没有,也不支持 MCP → Rules-as-Memory-Bank (模式八)
└── 这是权宜之计,尽快升级到 MCP
10.2 按记忆特征选型
记忆的内容特征也影响模式选择:
| 记忆特征 | 推荐模式 | 理由 |
|---|---|---|
| 事实频繁变化 | Temporal Graph (模式六) | 时序边天然追踪变化 |
| 需要不同粒度 | Semantic Pyramid (模式四) | 四层金字塔满足多粒度查询 |
| 记忆量大(数百条+) | Index + Retrieval (模式二) | 按需检索避免全量注入 |
| 长对话为主 | Pre-Compaction Flush (模式一) | 防止压缩时丢失 |
| 多 Agent 共享 | MCP Memory Service (模式七) | 标准化接入 |
10.3 按约束条件选型
工程约束往往是最实际的选型因素:
| 约束条件 | 推荐模式 | 理由 |
|---|---|---|
| 零外部依赖 | Semantic Pyramid (模式四) | SQLite 就够 |
| 低延迟(< 100ms) | Semantic Pyramid (模式四) | 本地存储 + MMD 注入 |
| 高精度检索 | Temporal Graph (模式六) | 图遍历 + 精排 |
| 最小改动接入 | MCP Memory Service (模式七) | 不改 Agent 源码 |
| 模型能力弱 | 避免 Virtual Memory (模式五) | Agent 自管理不可靠 |
10.4 模式组合建议
实践中,单一模式很少能覆盖所有需求。常见的组合策略:
组合一:Pyramid + MCP(DB-Agent-Memory 的方向)
- 金字塔做内部分层存储和检索
- MCP 做对外服务接口
- 适合:需要结构化记忆 + 跨 Agent 共享
组合二:Flush + Pyramid(OpenClaw 的方向)
- Flush 防止长对话丢失
- Pyramid 做分层压缩
- 适合:长对话 Coding Agent
组合三:Index + Pipeline(Claude Code + Codex 的融合方向)
- Index 做实时检索
- Pipeline 做离线整合
- 适合:高频使用 + 需要高质量整合
组合四:Bank → MCP(Cline 的升级路径)
- 从 Rules-as-Memory-Bank 起步
- 迁移到 MCP Memory Service
- 适合:渐进式增强记忆能力
选型的核心原则是:从最简单的满足需求的模式开始,按需增加复杂度。不要一开始就上 Temporal Graph + Two-Phase Pipeline + MCP——组合越多,系统越复杂,调试越困难。先用一个模式解决 80% 的问题,再考虑组合解决剩余 20%。
11. 演进趋势——从单一模式到模式组合
11.1 短期趋势(2026 下半年):MCP 成为事实标准
MCP Memory Service(模式七)正在快速成为记忆系统的标准接入方式。原因有三个:
第一,MCP 是 Anthropic 推动的开放协议,得到了主流 AI 工具的支持(Claude Desktop、Cursor、Cline 等都已经支持 MCP)。这意味着选择 MCP 作为接入方式不需要说服任何人——生态已经形成。
第二,MCP 解决了最紧迫的互操作性问题——让没有内建记忆的 Agent 也能获得记忆能力。DB-Agent-Memory 的 Host-Adapter 模式在这个趋势中位置有利——既可以嵌入使用,又可以通过 MCP 暴露。
第三,MCP 的工具调用模式比 Agent 自主管理(Virtual Memory 模式)更实用——Agent 只需要在需要时调用工具,不需要管理记忆的换入换出。这降低了对 Agent 元认知能力的要求。
11.2 中期趋势(2027):模式融合加速
生产级 Agent 正在从单一模式向模式组合发展:
Claude Code = Index + Retrieval + Flush 变体。Claude Code 的 extractMemories 机制本质上是一个”延迟的 Flush”——不是在 compaction 前抢救,而是在会话结束后提取。加上 autoDream 整合(24 小时 + 5 次会话触发),Claude Code 实际上组合了模式二(Index)和模式三(Pipeline)的部分特征。
Codex = Two-Phase Pipeline + Citation 正反馈。Codex 的核心是模式三,但 citation 正反馈循环(usage_count)引入了类似 Semantic Pyramid 的分层效果——被频繁引用的记忆相当于金字塔的上层(更精炼、更稳定),低引用的相当于下层(可被淘汰)。
OpenClaw = Flush + Dreaming。OpenClaw 组合了模式一(Flush)和类似 MemOS Dream Pipeline 的后台整合。Flush 保证不丢失,Dreaming 保证不膨胀。
DB-Agent-Memory = Pyramid + MCP(演进方向)。当前 DB-Agent-Memory 主要是模式四(Pyramid),但 MCP Adapter 正在开发中——完成后将组合模式四和模式七。
这些组合不是随意的——每种组合都解决了一对互补的问题:
- Flush + Pyramid:防丢失 + 防膨胀
- Index + Pipeline:实时检索 + 离线整合
- Pyramid + MCP:内部分层 + 对外共享
11.3 长期趋势:从自动管理到人机协同
当前的记忆系统要么是全自动的(DB-Agent-Memory 的管道),要么是全自主的(Letta 的 Agent 自管理)。但最理想的模式可能是”系统自动 + 用户/Agent 微调”的混合:
- 系统自动做基础记忆提取和整理——保证下限(不会因为 Agent 疏忽丢失信息)
- 用户通过编辑记忆文件做精确控制——保证准确性(纠正系统提取的错误)
- Agent 通过工具调用做微调——保证灵活性(强调或删除特定记忆)
Claude Code 的 MEMORY.md 已经隐含了这个方向——用户可以直接编辑索引文件,系统自动维护记忆文件,两者共存。但这种共存目前还没有正式的协议来协调——用户编辑和系统更新的冲突需要更优雅的解决机制。
长期来看,记忆系统可能演变为”个性化知识库”——既有从交互中学到的经验(Memory),也有从文档中检索到的知识(RAG),统一由一个分层存储 + 多模态检索系统管理。Memory 和 RAG 的边界将进一步模糊——正如 Ch06 所讨论的,经验和知识不是一回事,但管理它们的基础设施可以是同一套。
12. 本章小结
回顾本章的核心要点:
- 设计模式是”反复出现的问题的经典解法”——在十个系统中提炼出八种可复用的记忆设计模式
- 八种模式分别解决八类核心问题:压缩时丢失(Flush)、记忆太多不能全量注入(Index+Retrieval)、实时和整合的资源矛盾(Two-Phase Pipeline)、同等重要导致噪声(Semantic Pyramid)、上下文有限但记忆无限(Virtual Memory)、记忆有时间维度(Temporal Graph)、工具间记忆不互通(MCP Memory Service)、Agent 没有内建记忆(Rules-as-Memory-Bank)
- 每种模式都有明确的适用场景和局限——没有万能药,只有针对特定问题的最佳折中
- 选型决策树按 Agent 能力、记忆特征、约束条件三个维度给出建议
- 演进趋势从单一模式向模式组合发展——Claude Code = Index + Flush 变体,DB-Agent-Memory = Pyramid + MCP
- Rules-as-Memory-Bank 有三个根本缺陷(无语义检索、无冲突合并、人工触发),只能作为权宜之计
- 长期方向是从自动管理到人机协同——系统保证下限,用户/Agent 提升上限
下一章我们将从”设计模式”转向”未解难题”——看记忆系统还有哪些技术挑战等待解决。
导读目录:README.md 上一章:Ch17: OpenClaw 记忆——Pre-Compaction Flush 与 Dreaming 下一章:Ch19: 十九个技术挑战——记忆系统的未解难题