Featured image of post Claude Code 的记忆系统是怎么工作的?源码级拆解

Claude Code 的记忆系统是怎么工作的?源码级拆解

Claude Code 有一个让人惊讶的能力:周五跟你调试到半夜,周一开新会话它还记得你用 pnpm、测试要 Redis、代码风格用驼峰。本文从源码级别拆解它的三层记忆架构、四种记忆类型、以及那个叫"autoDream"的夜间整合机制。

Claude Code 的记忆系统是怎么工作的?源码级拆解

你周五用 Claude Code 调 bug 到凌晨两点,周一打开新会话,它张嘴就说:“上次那个 auth 模块的 bug 修好了吗?测试记得起 Redis。“你愣住了——你没告诉过它这些。

一、一个细思极恐的瞬间

用过 Claude Code 的开发者大概都经历过这个瞬间:

你关掉终端,第二天重新打开,开始一个新对话。Claude 什么都没问,直接开始干活。它知道你的项目用 pnpm 而不是 npm,知道测试需要本地 Redis,知道你讨厌 verbose 的输出。

你仔细回想:我什么时候告诉它这些的?

答案是:你没有直接告诉它。是它自己"记住"的。

Claude Code 每次启动都是一个全新的上下文窗口,不携带任何历史对话。所有它"知道"的东西,都必须在第一轮对话之前从磁盘加载。

我花了一周时间读 Claude Code 的 TypeScript 源码(是的,它是开源的),发现它的记忆系统比官方文档描述的复杂得多。今天来拆解一下。

二、两套记忆系统:你写的 vs AI 写的

Claude Code 有两套完全独立的记忆系统,各司其职:

关键区别:

维度CLAUDE.mdAuto Memory
谁写的你手动写Claude 自动写
存储位置项目目录 / 用户目录~/.claude/projects/
同步方式Git(可提交)本地(不同步)
更新频率你改才变每轮对话可能更新
大小限制40KB(建议)索引 200 行 / 25KB
加载方式启动时全部加载索引全加载,topic 按需

2.1 CLAUDE.md 的加载链

优先级(高→低):

1. 管理级  /etc/claude-code/CLAUDE.md     ← IT 管理员写的,无法覆盖
2. 用户级  ~/.claude/CLAUDE.md            ← 你的个人偏好
3. 项目级  ./CLAUDE.md 或 ./.claude/CLAUDE.md  ← 团队共享(git 提交)
4. 本地级  ./CLAUDE.local.md              ← 你的个人覆盖(不提交)

加载顺序:
  工作目录 → 向上遍历目录树 → 加载沿途所有 CLAUDE.md
  离工作目录近的优先级高

一个细节: CLAUDE.local.md 不在官方文档里,但源码中存在。它在项目 CLAUDE.md 之后、Auto Memory 之前加载。适合放个人的数据库密码、调试开关之类的——不提交到 git,但本地有效。

2.2 Auto Memory 的四种类型

Auto Memory 不是随意的笔记,而是有严格的分类:

为什么这样分? 因为团队协作时,你的编码偏好(user)不应该泄露给队友,但项目进度(project)应该共享。这种分类直接影响记忆的加载和同步策略。

三、三个时间尺度:实时 / 会话 / 梦境

这是整套系统最精妙的部分。Auto Memory 不是"Claude 注意到什么就随手记下来”,而是有三个独立的后台进程,在不同时间尺度上提取信息:

3.1 Per-Turn:实时提取(每轮对话后)

触发:每次完整查询循环结束
方式:fork 一个子 Agent 在后台运行
成本:极低(共享父 Agent 的 prompt cache)
权限:只读项目文件,只写记忆目录

一个细节: 如果主 Agent 在本轮已经写过记忆(你明确说"记住这个”),提取步骤会跳过那个范围,避免重复。

3.2 Per-Session:会话记忆(上下文增长时)

这是一个独立于 Auto Memory 的系统,目的是让当前会话的上下文在压缩后还能保留

触发条件(三个同时满足):

✅ 总上下文 ≥ 10,000 tokens
✅ 上下文增长 ≥ 5,000 tokens(自上次提取后)
✅ 工具调用 ≥ 3 次

提取的结构化笔记:

┌─────────────────────────────────────────┐
│         Session Memory 模板              │
├─────────────────────────────────────────┤
│ ## Current State                         │
│ 当前正在做什么,进展到哪一步              │
│ (上限 ~2000 tokens)                    │
├─────────────────────────────────────────┤
│ ## Files and Functions                   │
│ 涉及的文件、函数、关键代码位置            │
│ (上限 ~2000 tokens)                    │
├─────────────────────────────────────────┤
│ ## Errors & Corrections                  │
│ 遇到的错误、尝试过的方案、最终解决方案    │
│ (上限 ~2000 tokens)                    │
├─────────────────────────────────────────┤
│ ## Worklog                               │
│ 工作日志:做了什么、还剩什么              │
│ (上限 ~2000 tokens)                    │
└─────────────────────────────────────────┘
总计上限 ~12,000 tokens

为什么需要这个? 当对话太长需要压缩时,有这些结构化笔记,auto-compact 可以复用它们,而不是从头重新摘要整个对话。效果好很多——因为笔记是增量提取的,上下文还完整时就记录了关键信息。

3.3 autoDream:梦境整合(每天/每几天)

这是最让人惊讶的部分。源码中明确把这叫做 “DreamTask”,UI 中会显示进度条。

整合流程(四阶段):

Phase 1: 定向
  ├── 读取 MEMORY.md 索引
  └── 浏览现有 topic 文件
  目的:了解当前记忆状态

Phase 2: 发现
  └── 搜索日志和会话记录中的新知识
  目的:找到需要整合的信息

Phase 3: 写入
  ├── 写入/更新 topic 文件
  ├── 合并新信号到现有文件
  └── 相对日期 → 绝对日期("昨天" → "2026-05-11")
  目的:持久化新知识

Phase 4: 清理
  ├── 修剪 MEMORY.md 至 200 行以下
  ├── 移除过期指针
  └── 解决新旧事实矛盾
  目的:保证记忆质量

一个有趣的细节: 如果你杀掉整合进程,锁的修改时间会回滚,让下一个会话可以重试。这个错误处理很体贴。

它什么时候运行? 不是等你关掉电脑或睡觉,而是在一次对话结束时——只要满足那三个 Gate。只是因为 24 小时和 5 个会话的门槛,大多数时候不会触发。

四、记忆召回:Sonnet 替你选

记忆存下来了,但怎么在需要的时候找到正确的记忆?

Claude Code 的方案是:让 Sonnet 替你选,不是关键词匹配。

为什么用 Sonnet 而不是关键词? 因为语义理解更好。比如用户问"怎么跑测试",关键词匹配可能找不到 feedback_testing.md,但 Sonnet 能理解这个意图。

但这里有一个结构性缺陷: 没有向量搜索,没有语义索引。如果 MEMORY.md 中的描述写得不好,或者 topic 文件太多(最多扫描 200 个,按修改时间排序),记忆就可能被"遗忘"。

五、什么能活过压缩?

长对话会被压缩。压缩后,什么能保留?

源码中的清理代码:

// 压缩时,强制清除所有缓存,从磁盘重新加载
getUserContext.cache.clear()
resetGetMemoryFilesCache('compact')
clearSystemPromptSections()

教训: 如果你在对话中告诉 Claude 什么重要的东西,确保它写入了记忆。否则压缩后就没了。

六、与其他工具的对比

维度Claude CodeAiderCursorDeepSeek-TUI
记忆系统✅ 三层架构❌ 无⚠️ 简单 rules✅ 基础记忆
自动提取✅ per-turn⚠️ 手动
梦境整合✅ autoDream
记忆分类✅ 4 种类型
模型辅助召回✅ Sonnet 选择
团队同步⚠️ feature flag
记忆持久化✅ 文件系统⚠️ 配置文件✅ 文件系统

结论: Claude Code 的记忆系统是目前 AI 编程工具中最完善的。Aider 完全没有记忆,Cursor 只有简单的 rules 文件,DeepSeek-TUI 有基础记忆但没有自动提取和整合。

七、这套系统的局限

读完源码,我认为有几个结构性局限:

7.1 记忆是 per-repo 的

你在项目 A 教 Claude 的偏好,不会自动带到项目 B。用户级的 ~/.claude/CLAUDE.md 可以处理一部分,但它是静态的——Claude 不能在会话中自动更新它。

7.2 记忆是 machine-local 的

项目目录名经过 sanitize(非字母数字替换为连字符),但不同机器、不同用户名、不同挂载点会产生不同的目录名。同一项目,不同机器,不同记忆。

7.3 没有记忆衰减

所有记忆都是平等的。一个月前的偏好和今天的偏好权重一样。没有"热度"概念,没有自动遗忘机制。

7.4 依赖 Sonnet 的选择能力

如果 Sonnet 选错了记忆文件,或者描述写得不好,记忆就"丢了"。没有 fallback 机制。

八、对我们的启示

这套系统给我们什么启发?

8.1 记忆是 AI 工具的核心差异化

目前市面上的开源 AI 编程工具(Aider、OpenCode、Continue),没有一个有真正意义上的记忆系统。这是差异化机会。

8.2 梦境整合是一个优雅的设计

不是每次对话都提取记忆(太频繁,噪音多),也不是完全不提取(会丢失)。三个时间尺度的设计——实时、会话、梦境——是一个很好的平衡。

8.3 模型辅助召回比关键词匹配更好

让 Sonnet 选择相关的记忆文件,而不是 grep 关键词。这在语义理解上好很多,但成本也更高。

对于开源工具,可以考虑一个折中方案:先用关键词过滤缩小范围,再用小模型选择。

8.4 记忆质量 > 记忆数量

Claude Code 严格限制记忆的大小(200 行索引、4KB 单文件、60KB 会话累计)。这不是偷懒,而是有意为之——太多记忆反而会干扰。

九、写在最后

读完 Claude Code 的记忆系统源码,我最大的感受是:

好的 AI 工具不是模型能力的比拼,而是工程设计的比拼。

同样的 Claude 模型,有记忆系统和没有记忆系统,用户体验天差地别。记忆系统本身不需要更强大的模型,它需要的是精心的工程设计——什么时候提取、怎么存储、如何召回、何时整合。

这些工程细节,才是真正的壁垒。


本文基于 Claude Code TypeScript 源码分析。 源码地址:github.com/anthropics/claude-code 参考文章:How Claude Code memory actually works

© 2016-2026 Yison. All rights reserved.
使用 Hugo 构建
主题 StackJimmy 设计