记忆系统设计原则
你用 AI 编程超过一周,会发现一个尴尬的问题:每次开新会话,助手像失忆了一样。你得重新解释“我是谁”“我在做什么项目”“上次我们约定好不要一次性丢多道题”。
这就像你雇了一个非常聪明的助理,但他每天早上来上班时完全不记得昨天的事。每天第一件事是给他重新看一遍员工手册。
记忆系统就是这本员工手册。它让助手在新会话开始时自动读到关键信息,不需要你每次复述。
工具对照
Claude Code 有内置
/memory与CLAUDE.md加载;进阶的三层索引(下文)是自建方案。Codex 侧更常见的是把稳定约定写进AGENTS.md,把长背景放进仓库笔记再按需引用——分层、可追溯、先 grep 再新增这些原则两边通用。
本文以 Jason 的真实记忆系统为原型,逐层拆解设计思路。读完你会知道为什么需要三层分类、每条记忆怎么追溯来源、以及什么该放进记忆文件而不是项目指令文件。
- 你已经用过至少一个 AI 编程助手至少一周,遇到过“新会话不记得之前约定”的痛点
- (Claude)读过 记忆系统,知道
CLAUDE.md会在启动时被读取 - (Codex)读过 AGENTS.md,知道仓库规则如何被加载
类比:记忆像什么
Section titled “类比:记忆像什么”想像你的手机通讯录。你不会记住每个朋友的电话号码,你只记住他们在通讯录里。打电话时,手机自动按名字找到号码。
Claude Code 的记忆系统完全一样。你不需要让 Claude 记住所有细节(那会塞满上下文窗口,费 token 又费钱),你只需要让它知道去哪找。
更精确的类比是图书馆的索引卡片。你走进图书馆不会把所有书翻一遍,而是先查索引卡片,找到目标书所在的书架和位置,然后直接走过去拿。MEMORY.md 就是这个索引卡片柜,memory/ 下的每个文件就是书架上的一本书。
三层稀疏记忆
Section titled “三层稀疏记忆”这是整个系统最核心的设计。所有记忆文件分三层:
A 层:始终加载(约 13 个文件)
Section titled “A 层:始终加载(约 13 个文件)”每个新会话,Claude Code 都会读取 CLAUDE.md。Jason 的全局 CLAUDE.md 底部有一行强制指令:
## 强制加载的记忆索引@~/.claude/projects/<encoded-project-path>/memory/MEMORY.md这一行告诉 Claude:每次启动都读 MEMORY.md。而 MEMORY.md 就是 A 层索引——列出了约 13 个始终适用的记忆文件:
- 用户背景(零基础,解释从日常类比开始)
- 教学节奏(小段讲解 + 即时一问,禁一次性丢多题)
- bash sigpipe 陷阱(避免
cat | head误报失败) - 公开输出无业务上下文
- 等等
共同特征:任何项目、任何任务都适用,不依赖具体技术栈或项目名。
B 层:关键词触发(约 5 个文件)
Section titled “B 层:关键词触发(约 5 个文件)”MEMORY.md 里写了一条规则:
命中公司基建关键词(公司代码平台 / 内网 npm / 泳道 / AIGC 网关 / Bedrock / 图床 / MDM)→ Read MEMORY-COMPANY.md
Claude 读到这条规则后,不会主动去读 MEMORY-COMPANY.md。只有当前对话中出现这些关键词(比如用户说“帮我推到图床”),Claude 才会触发 Read 操作。
为什么这样设计?B 层记忆是公司的具体技术栈和流程,只在用到时才加载,不浪费通用任务上下文的 token。
C 层:项目名触发(约 7 个文件)
Section titled “C 层:项目名触发(约 7 个文件)”类似 B 层,但触发条件是项目名:
命中项目名(prize / quanzhiping / activity-planner / 视频评价 agent 等)→ Read MEMORY-PROJECTS.md
C 层是项目专属记忆——某个项目里的特殊约定、历史决策、踩坑记录。只有在该项目的会话中才需要加载。
为什么要分层?
Section titled “为什么要分层?”直观的数字对比:
- 如果所有记忆文件每会话都加载:约 25 个文件,每个平均 30 行,总计约 750 行。这会吃掉上下文窗口的很大一部分,而且大部分内容当前任务根本用不上。
- 三层设计后:A 层约 13 个始终加载,B 和 C 层的约 12 个文件按需加载。一个不涉及公司基建、不在特定项目的通用任务,只需要读 A 层。
核心原则:让 Claude 知道信息存在哪里,但不占用上下文空间去存储那些用不到的信息。 这一设计哲学的更多背景详见设计哲学。
类比:CPU 缓存层级
Section titled “类比:CPU 缓存层级”如果你了解一点计算机原理,这和 CPU 的 L1/L2/L3 缓存很像:
- L1 缓存(A 层):每次都查,速度最快但容量最小
- L2 缓存(B/C 层):按需查询,容量稍大但需要额外加载时间
- 主内存:所有其他信息,需要时再搜索
每条记忆的来源可追溯
Section titled “每条记忆的来源可追溯”打开 memory/ 下的任意文件,看它的 YAML frontmatter:
---name: 避免 cat | head 类管道模式(SIGPIPE 陷阱)description: 用 Read 工具或原生命令参数代替管道,避免 SIGPIPE 误报type: feedbackoriginSessionId: <redacted-session-id>---originSessionId 可以作为本地追溯字段,但真实值不应出现在公开教程、截图或 Issue 中。公开示例应使用明显的占位符:
originSessionId: <redacted-session-id>这意味着每条记忆都能追溯到“谁、在什么时候、因为什么”产生的。用 Jason 系统里的实际例子:
feedback_teaching_style.md可以在本地记录来源会话,公开时只保留“用户明确提出教学偏好”的脱敏结论feedback_bash_sigpipe.md可以记录触发问题的命令模式和日期,不公开可关联到个人会话的标识
为什么要追溯?
Section titled “为什么要追溯?”三个月后你看到一条记忆规则“禁止 cat | head”,你可能不记得为什么。打开文件,看到 Why 段:“2026-05-08 daily-learn 跑 cat manifest.json | python3 -m json.tool | head -40 就被这个坑了——JSON 完全有效,命令‘失败’了”。
这就是记忆不是死规则,而是活教训。每条记忆都带着当时的具体场景和问题,让你(和 Claude)能理解规则背后的意图,而不只是机械执行。
记忆即 git 历史
Section titled “记忆即 git 历史”这是 Jason 系统里最有创意的设计决策:记忆文件不存数据库、不用外部存储,直接放在 git 仓库里,和代码一样用 git 管理。
memory/ 目录是 intern-journal 仓库的一部分(通过 symlink 暴露给 Claude Code)。每次修改记忆都走正常的 git commit。
这意味着你可以用 git 回答这些问题:
# "我四月份学到了什么?"git log --after=2026-04-01 --before=2026-05-01 -- dotfiles/home-claude/memory/
# "关于 sigpipe 的记忆是什么时候加的?"git log -- dotfiles/home-claude/memory/global/feedback_bash_sigpipe.md
# "最近一个月新增了哪些规则?"git log --oneline --since="1 month ago" -- dotfiles/home-claude/memory/每次 git log 的输出就是你的学习时间线。commit message 本身也是记忆的一部分——比如 026c5d9 GitHub 整理 Phase 8 复盘:写入 gh CLI 陷阱到 memory,这条 commit 告诉你这条记忆是 “Phase 8 复盘” 时产生的。
为什么不用数据库或笔记软件?
Section titled “为什么不用数据库或笔记软件?”- 数据库:需要额外维护,和你的日常 git 工作流脱节
- 笔记软件:没有版本历史,改了就丢了之前的样子
- git:你已经在用它管理代码,记忆跟代码一样待遇——有历史、可回溯、可协同
更重要的是:git blame 能告诉你每条记忆在什么时候添加、为什么修改。本地来源字段可以补充追溯;对外发布前必须脱敏。
添加新记忆前先 grep 三个索引
Section titled “添加新记忆前先 grep 三个索引”记忆系统最大的敌人不是遗忘,而是矛盾积累。
比如:你之前有一条规则 “学习类任务默认走带学+提问模式”。过了两个月,你遇到一个场景觉得 “直接出题更快”,于是加了一条 “快速学习场景可以批量出题”。这两条规则不矛盾吗?什么时候用哪条?
Jason 系统里有一条硬规则,写在了 MEMORY.md 的最底部:
新事实出现时:先 grep 三个索引文件看有没有冲突条目,再决定更新还是新增。
三个索引文件是:
MEMORY.md(A 层索引)MEMORY-COMPANY.md(B 层索引)MEMORY-PROJECTS.md(C 层索引)
操作流程:
- grep 检查冲突:新规则的关键词是否已经出现在任何索引中?
- 如果已存在:打开冲突文件,更新它。可能补充例外情况,可能改写表达让它更精确,可能发现规则已过时需要删除。
- 如果不存在:创建新文件,在合适的索引中注册。
这防止了最常见的问题:两个互相矛盾的规则同时存在,Claude 读到时不知道该遵守哪个,用户自己也忘了哪个更“正确”。
一个实际例子
Section titled “一个实际例子”Jason 最初有一条规则:feat 分支结束后删掉工作树。后来他发现有时需要回来查上下文,决定保留工作树。这次不是加一条“有时保留工作树”的新规则——而是直接修改了原规则文件,把“删除”改成“保留一周后再删”。更新,不叠加。
记忆 vs 项目指令文件 — 该放哪里?
Section titled “记忆 vs 项目指令文件 — 该放哪里?”这是新手最困惑的问题。两个地方助手都会读,为什么要分开?
| 放 CLAUDE.md / AGENTS.md | 放 Memory / 长笔记 |
|---|---|
| 每次会话都读 | 需要时按关键词触发 |
| 影响助手的即时行为 | 提供背景知识和长期教训 |
| 短(1-2 句) | 可以写 Why 和完整场景 |
| “做什么” | “为什么这样做” |
以 “教学节奏” 规则为例:
项目指令文件里写的(短规则):
- 教学节奏:学习类任务默认"小段讲解+即时一问+等回答",禁一次性丢多题Memory / 长笔记里写的(完整背景):
学习类任务默认走"带学+提问"模式:1. 每轮讲一个小概念2. 讲完立刻问一个小题3. 等用户答完再讲下一段
Why:用户在一次教学会话中明确反馈过这个偏好(公开材料不记录内部项目路径和会话标识)"用带我学习然后提问的方式,而不是现在这种直接出题目"是原话。经验法则:
- 需要助手 每次启动都知道 的行为指令 → 放
CLAUDE.md/AGENTS.md - 需要助手 在特定场景下查阅 的背景知识 → 放 Memory / 按需笔记
- 规则本身 + 为什么有这条规则的历史 → 指令文件放规则,Memory 放 why
Codex 用户若没有独立 Memory 目录:把短规则放 AGENTS.md,把 Why 与场景放 docs/ 或 learnings/,在规则里写「详见 docs/xxx.md」即可——分层思想不变。
1. 记忆膨胀:把 A 层当万用抽屉
Section titled “1. 记忆膨胀:把 A 层当万用抽屉”最危险的趋势:每次学到新东西就往 A 层加。A 层始终加载,文件越多,每个会话的上下文就越多。如果 A 层从 13 个膨胀到 30 个,效果反而不如没有记忆系统——Claude 被大量不相关信息淹没了。
对策:加新记忆时先问自己 “这真的是任何任务都需要知道的吗?”。大多数新知识应该进 B 层或 C 层(按关键词触发)。
2. 忘记 grep 就加新规则
Section titled “2. 忘记 grep 就加新规则”不加 grep 就直接加新规则,等于在索引卡片柜里塞了一张可能跟已有卡片矛盾的卡片。几个月后 Claude 读到两条互相冲突的规则,它会遵循哪条?你不知道。
对策:把 “新事实出现时,先 grep 三个索引” 变成肌肉记忆。或者让 Claude 帮你做(在 CLAUDE.md 里写这条规则,Claude 自己就会执行)。
3. 只写不更新
Section titled “3. 只写不更新”记忆文件的创建只是一个开始。规则会过时、会需要补充例外、会因为技术栈变化而失效。如果永远只加新文件不回头更新旧文件,记忆系统会变成垃圾场。
信号:当你看到一条记忆规则想说 “但是现在不一定了吧?”,那就是该更新它的时候了。
4. 把 CLAUDE.md 和 Memory 混用
Section titled “4. 把 CLAUDE.md 和 Memory 混用”CLAUDE.md 里塞长篇解释(Why + 场景 + 例子),或者 Memory 里只写一句不写 why。前者浪费每次会话的 token,后者让 Memory 变成没有上下文的死规则。
对策:用上面的决策框架做判断。不确定的时候,默认放 Memory(信息更完整),CLAUDE.md 只放最简指令。
约 10 分钟,把今天的一条教训落成可追溯记忆:
- 选一条真实教训(例如「不要用
cat | head读大 JSON」或「批量删除必须先确认」) - 新建一个 markdown 文件,至少包含:一句话规则、Why(具体场景)、你判断的层级(始终加载 / 关键词触发 / 项目专属)
- 在项目指令文件或索引里注册它(Claude:写入/引用 MEMORY 索引;Codex:在
AGENTS.md加一行「详见docs/...」) - 用
grep(或编辑器搜索)确认没有与旧规则矛盾的表述
成功标准: 文件能回答「规则是什么 + 为什么有它」;索引或 AGENTS.md 能指到它。
Checkpoint
Section titled “Checkpoint”- □ 我能说明为什么要分层:始终加载 vs 按需加载(省上下文)
- □ 我知道新记忆要先 grep 再决定「更新还是新增」
- □ 我能判断一条内容该放指令文件还是 Memory/长笔记
- □ 我完成了上面的「动手试一试」,至少写过一条带 Why 的记忆
- □ 我理解:记忆膨胀(什么都塞进始终加载层)比漏记几条更危险
- 继续 工作流编排思路 —— 把记忆和其他机制串成完整循环
- Claude:CLAUDE.md 与记忆系统 —— 内置路径与自建索引
- Codex:AGENTS.md —— 把稳定约定写进仓库规则
- 上下文窗口管理 —— token 预算怎么分配
- 回 学习方法论