拆开 Claude Code 的记忆系统:一次源码泄露,让我们看清了 AI 是怎么“记事“和“做梦“的
一句话总结Claude Code 的记忆不是把对话全存下来而是一套**三层加载 四种类型 严格写入纪律 自动整理做梦**的工程系统。它的核心信念只有一句——记忆是提示hint不是真理truth。如果你用过 Claude Code 跨天、跨会话地写同一个项目可能会有种奇怪的感觉它好像记得你上次踩过的坑、记得你偏好 pnpm 而不是 npm、记得这个项目的构建命令——但你明明没有手动告诉它。这套记性是怎么实现的过去只能靠猜。直到一次意外把答案直接摆在了所有人面前。0. 为什么这次能讲清楚一次本该被删掉的源码2026 年Claude Code 的某个版本v2.1.88在发布时误带了一个本应被剥离的 source map 文件——将近 60MB、约 51 万行可读的 TypeScript 源码包括完整的记忆系统实现、Auto Dream 整理流程甚至还有一个尚未对外发布的常驻守护进程 KAIROS。这个文件很快被镜像到 GitHub社区据此把整套机制扒了个底朝天。所以下面讲的东西大部分不是逆向猜测而是有源码佐证的真实实现。我们一层层来。1. 全局视角三层结构 四种类型1.1 按加载频率分的三层把地图而不是百科全书放进口袋Claude Code 的记忆文件全是放在你磁盘上的纯 Markdown路径在~/.claude/projects/你的项目/memory/ ├── MEMORY.md # 索引文件每次启动都加载 ├── debugging.md # 某个主题的详细笔记topic file ├── api-conventions.md └── ...它的关键设计是按加载频率严格分层——不是所有记忆都一直占着上下文层是什么加载时机CLAUDE.md你手写的规则文件“永远用 X”“构建命令是 Y”每次启动全文加载MEMORY.md记忆目录的索引/目录页每次启动加载但只加载前 200 行 / 25KB谁先到算谁Topic Files各主题的详细正文debugging.md 等启动时不加载模型觉得需要时才用文件工具按需读为什么要分因为上下文窗口是稀缺资源。我们拿着Memory.md这张地图去往Topic Files这个目的地然后把对应主题文件加载进入上下文。⚠️ 这里埋着一个非常重要的边界MEMORY.md 超过 200 行就会被静默截断。后面第 6 节专门讲这个坑。1.2 按内容性质分的四种类型记忆不是一坨没结构的文本源码里把它分成四类用户偏好你喜欢的工作方式、沟通风格、工具选择pnpm not npm反馈/纠正你说过不对这样写的地方项目上下文构建/测试约定、架构要点、模块关系引用指针reference pointers指向外部系统的地址——bug 在哪个 tracker、该看哪个 Slack 频道为什么要分类因为不同类型的寿命不一样。架构决策可以长期有效而进度类记忆几天后就该淡出。分类是为后面自动整理埋的伏笔。1.3 一条最容易被忽略、却最关键的设计约束这是很多讲记忆系统的文章都没点透的一句话记忆只存从当前项目状态推导不出来的东西。凡是能靠读代码、grep、git log拿到的信息——代码模式、当前架构、提交历史——一律不存进记忆。因为这些东西现场看一眼代码就有了存进记忆只会过期、占地方、互相矛盾。记忆要装的是那些**代码本身告诉不了你的缝隙信息**为什么当初选了这个方案、你这个人的偏好、上次那个诡异 bug 的根因。理解了这条约束你才能理解后面所有取舍。2. 写入Auto Memory 是怎么记笔记的Auto Memory 写入通道。它让 Claude 在干活的过程中自己给自己记笔记你一个字都不用写。它什么时候写源码里有一个后台抽取 agent在每一轮查询循环结束时悄悄跑一次回看刚才这段对话判断有没有值得长期保存的信息有就写、没有就算了——不打断你正在进行的主流程。你也可以显式下命令“记住我们用 pnpm不用 npm”“注意 API 测试需要本地起一个 Redis”——它会直接写进对应的 topic file。写入纪律质量比数量重要这是整套系统里我最欣赏的部分。记忆系统最容易出的问题不是存得不够而是存了垃圾——一条过时的记忆比没有记忆更危险它会主动误导 agent 做错决策。所以写入有两条铁律铁律一先写文件再更新索引。只有当 topic file 成功落盘之后才去更新 MEMORY.md 里的索引指针。这样可以防止一次失败的写入污染索引——你绝不希望 MEMORY.md 里有一条指向不存在的文件的指针。铁律二使用前先验证。这是整个系统的灵魂——记忆是提示hint不是真理truth。Agent 在真正使用一条记忆之前会先核实它是否还成立。比如记忆说项目用 React 17它在操作前会先去翻package.json确认而不是闭着眼睛相信记忆。源码里甚至有一个memoryFreshnessText()函数专门给超过一天的记忆生成过期提醒把这条可能不新鲜了明明白白写给模型看。这就是hint 不是 truth落到代码层面的样子。3. 召回两条互补的路径注意不是二选一——下面这两条路径是同时存在、互相补位的。路径 A常驻索引每次启动就在MEMORY.md 作为索引每次会话开始就加载进上下文前 200 行。主模型读这份目录知道有哪些 topic file 存在当对话里出现相关线索它就用标准文件工具按需把对应 topic file 整篇读进来。简单说目录常驻正文现取。路径 BSonnet 旁路选择每轮异步挑相关项这条更精巧也是源码里实锤的每一轮只要你发来一句多于一个词的提问系统会单独调用一个便宜的 Sonnet 旁路查询来做召回。流程是扫描记忆目录下每个 topic file 的头部读前 30 行抽出 frontmatter 里的 description、类型、时间戳最多扫 200 个文件把这些头部元信息注意不含正文拼成一份清单交给 SonnetSonnet 按一个明确的系统提示词来挑——“只挑你确定有用的最多 5 条不确定就别挑一条都没明显有用就返回空列表”把选中的 ≤5 个 topic file 的完整正文作为relevant_memories附件塞进当前这一轮上下文。这一步本质是语义相关性筛选但它不是向量检索——只是一个语言模型在读文件名 一行描述的清单做判断。理解这点很重要第 6 节会说为什么这是个局限。两条路径的分工路径 A 保证全局目录始终在线路径 B 保证每轮把最相关的几篇正文精准送达。一个管广度一个管精度。4. 做梦Auto Dream 是怎么整理记忆的光会写、会读还不够。你想象一下一个人天天往笔记本里塞便签从不整理——三个月后这本子就成了垃圾场自相矛盾的条目我们用 Express和后来的改用 Fastify并存、“昨天”上周这种早已失效的相对时间、引用了已经被删掉的文件……Auto Dream 就是给记忆做的那场 REM 睡眠。它是一个后台子 agent在你不干活的间隙把 Auto Memory 攒下的乱账重新整理成干净、有条理的知识库。触发时机两个条件必须同时满足距离上次做梦已过24 小时且期间累积了至少 5 个新会话。两个条件缺一不可。所以如果你只是一个长会话跨了两天会话数不够Dream 不会触发。也可以用/dream手动触发。执行方式Fork 一个受控子 agentDream 会 fork 出一个拥有独立上下文的子 agent复用父对话已经缓存的 system prompt 和工具定义省去重复加载的开销专门去做记忆整理这一件事。它有一段专属的系统提示词大意是把最近学到的东西综合成持久、有条理的记忆让未来的会话能快速进入状态并且明确要求它用窄范围的 grep而不是把整个 transcript 读一遍——这是为了省 token。四阶段流程Dream 的整理严格走四步这也是你可以直接写进文章的完整指令骨架Orient定位读取现有记忆目录和 MEMORY.md 索引先在脑子里建一张我现在都有啥、怎么组织的的地图动手之前先盘点。Gather recent signal采集新信号去翻最近的会话 transcript本地存的 JSONL 文件但不整篇读而是用窄关键词定向搜——只找用户纠正、明确的保存请求、反复出现的主题、关键决策。读 500 篇完整 transcript 是纯烧 token 的低回报操作所以这一步刻意挑着读。Consolidate合并把新信息并进已有的 topic file。关键维护都发生在这一步——合并重复、删掉被推翻的旧条目、把昨天决定用 Redis这种相对时间改写成2026-03-15 决定用 Redis的绝对时间从根上消除时间错乱。Prune and index修剪与重建索引重建 MEMORY.md把它压回 200 行 / 25KB 以内——删掉指向已不存在文件的死指针、把太啰嗦的条目降级索引里只留一句话详情挪进 topic file、解决索引和正文打架的矛盾。没改动的文件原样保留Dream 是外科手术式的不会每次把所有东西重写一遍。整个过程结束MEMORY.md 索引被更新、Topic Files 被重新组织。Agent 下次醒来时面对的是一个更干净、更准确、更有条理的知识库。5. 安全设计梦境不能污染现实这是 Dream 最重要、也最容易被忽略的一条护栏。Auto Dream 子 agent 的工具访问是受限的它只能读写记忆文件不能动你的项目源码、配置也不允许联网 / 使用 web 工具。为什么要这么严想象一下如果做梦过程能联网、能改代码——一次错误的整理判断就可能把你的记忆连同项目文件改得面目全非而且这种污染很难追溯。所以设计上划了一条死线整理记忆的过程只能影响记忆本身不能影响外部世界。梦里发生的事留在梦里。6. 局限与坑坑一200 行静默截断。MEMORY.md 一旦超过 200 行超出的部分会被悄悄截断。最要命的是——它会在被截断的内容里追加一句警告但这句警告只有你去翻文件才看得到模型那边看到的是一份干净的 system prompt完全不知道索引被砍过。于是当你用了三个月、攒到第 201 条时最旧的记忆会无声无息地掉队模型不是幻觉、不是坏了它只是忘了而且没法告诉你它忘了。坑二召回靠关键词不是语义。前面说过Sonnet 旁路是在读文件名 一行描述。而真正的检索层面更接近grep——字面关键词匹配。如果你的笔记写的是docker-compose 端口映射你搜端口冲突可能什么都召不回来。这也是 mem0、Hindsight、memsearch 这些第三方记忆层存在的理由它们用向量/语义检索补上了这块。坑三单仓库范围。Auto Memory 默认是按 git 仓库根目录隔离的。A 项目里学到的东西不会自动跨到 B 项目。彩蛋KAIROS。泄露源码里还有一个尚未发布的常驻守护进程 KAIROSalways-on daemon mode任何公开版本里都还没启用——这是 Claude Code 记忆系统未来形态的一点剧透。7. 收尾它到底牛在哪绝大多数 AI 记忆系统在优化召回率——尽可能把可能相关的全捞上来。而 Claude Code 优化的是**“恰当的召回”appropriate recall**在对的时机、送上对的几条、还诚实地标注这条可能过期了。把这套系统浓缩成三句话也是你可以直接拿去当结尾的存得克制只存代码里推导不出来的东西取得精准常驻索引 Sonnet 旁路双路径每轮最多 5 条信得保留记忆是 hint 不是 truth用之前先验证、过期了有警告、闲时还会自己做梦整理。工程上最不起眼、却最能复利的能力往往就是这种——它安静地运行直到有一天你发现没有它的 AI 工具简直没法用。本文基于 Claude Code 官方文档及社区对泄露源码的分析整理。其中涉及 Sonnet 旁路选择、四阶段 Dream、200 行截断、memoryFreshnessText()等实现细节均来自源码层面的公开分析KAIROS 等未发布特性以实际版本为准。