定义
将 Agent 记忆拆分为 L1 持久画像(用户是谁)、L2 程序性记忆(标准做法)和 L3 历史检索(过去做了什么)三层,防止记忆腐化并按需加载以控制 token 成本。
三层结构
| 层级 | 回答什么 | 存储位置 | 更新频率 | 加载时机 |
|---|---|---|---|---|
| L1 持久画像 | 用户是谁 | 单文件(user.md) | 半年/一年改一次 | 需要理解用户背景时 |
| L2 程序性记忆 | 这种事的标准做法 | 超薄索引 + 按主题切碎的小文件 | 踩坑后沉淀 | 启动时只读索引,命中再加载 |
| L3 历史检索 | 过去做过什么 | 按天分日志 + 语义检索引擎 | 每次对话自动写入 | 只在主动查询时加载 |
记忆腐化
记忆腐化(Memory Corruption)来自 NousResearch 开源的 hermes-agent 项目:当"我是谁""我该怎么做""昨天发生了啥"全混在一个文件里时,时间一长三种东西互相污染——昨天的临时决定被当成长期判断,半年前的画像被今天的情绪覆盖,过去踩坑的细节被改写得面目全非。
三层分离的核心纪律:混淆 = 记忆腐化。每次写入记忆前先判断属于哪一层,分错了宁可不写。
加载策略
关键不是"记了什么",而是"什么时候读什么":
- L1: 仅在需要确认用户偏好、语气、忌讳时读取
- L2 索引: 每次对话启动时读取(仅几 KB),命中钩子后再加载对应小文件
- L3: 默认不读,仅在用户主动查询历史时检索
按需加载将每次对话启动的 token 成本从 3 万降到不到 3000。
语义去重
L2 的 memory_update.py 在写入前做语义比对:
- 发现同义条目 → 跳过不写
- 发现补充细节 → merge 进现有条目
- 只有真正新的判断 → 追加
防止 L2 在长期使用后充满同义重复的废条目。
"吃一堑长一智"闭环
三层记忆的最高价值体现在跨对话经验复用:
- 第一次踩坑 → 花数小时修复 → 写入 L2 程序性记忆
- 三周后新对话 → Agent 启动时读 L2 索引 → 命中钩子 → 加载小文件
- Agent 自动绕开同一个坑 → 无需用户提醒
关键数据点
- 单文件 memory.md(3000+ 行)→ 三层分离后,对话启动从 3 万 token 降到不到 3000 token
- L2 索引仅几 KB,包含"标题 + 一句话钩子"格式
- L3 语义检索使用 OpenViking(字节跳动火山引擎 2026 年 1 月开源的上下文数据库)
- 作者实测 L2 下挂着 38 个经验教训小文件,每条背后都是一段实际踩坑
对话结束协议(5 步交班)
每次对话结束时 Agent 自动执行:
- A. 经验沉淀 — 可复用经验/决策/偏好 → 写 L2
- B. 事实流水 — 完成任务/决策/待跟进 → 写 L3 日志
- C. 知识编译(distill) — 高密度产出 → 编译为独立笔记
- D. 文件统计 — 笔记增删移动 → 刷新 Vault 总览
- E. Self-check — 下一个 session 能否从已有记录拿到所有信息?
前提与局限性
- 三层划分高度贴合个人开发者 + Obsidian Vault 场景;企业级多人/多角色/权限分层场景需要扩展
- 语义去重依赖 LLM 判断,误合并(不同经验判为同义)比漏写更危险
- L1 持久画像假设用户特征稳定,但人的偏好和角色会演化,需要定期审视
- token 数据(3 万→3000)来自单人实践,缺乏控制组
关联概念
- Multi-Layer-Memory — Anthropic Claude Code 的多层记忆设计,三层记忆的个人实践版本
- Context-Engineering — 三层记忆是上下文工程的落地实践
- CLAUDE-md — L1/L2 的具体载体之一
- Agent-Harness — 三层记忆是 Harness 记忆组件的细化实现
- Capability-Map — 与三层记忆互补的工具发现机制
- Knowledge-Compilation — L2 的 distill 步骤本质上是知识编译