跳转至

2.2 Memory System

本节摘要

Claude Code 的Memory 系统有一个核心理念:用 Markdown 文件作为「外部大脑」,用 LLM 做检索引擎。本节按「记了什么 → 怎么写入 → 怎么召回」的主线展开,重点解析 CLAUDE.md 五层体系的内容设计和四种Memory 类型的自动提取机制。


一、CLAUDE.md 五层体系:记了什么

CLAUDE.md 是 Claude Code 的全部持久化Memory 载体——没有数据库,没有向量索引,就是 Markdown 文件。五层从组织级到自动生成,形成一个完整的知识覆盖体系。

五层结构总览

优先级 层级 路径 记录什么 谁来写
1(最低) Managed /etc/claude-code/CLAUDE.md + .claude/rules/*.md 组织安全策略:「禁止执行 rm -rf」「必须使用公司 VPN」 IT 管理员
2 User ~/.claude/CLAUDE.md + ~/.claude/rules/*.md 个人全局偏好:「我偏好 TypeScript」「使用 Vim 键位」 用户手动
3 Project 从项目根到 CWD 逐级 CLAUDE.md 项目约定:「使用 pnpm」「测试覆盖率 > 80%」「API 风格用 RESTful」 团队共享(committed)
4 Local CLAUDE.local.md(gitignore) 个人项目配置:「我负责 auth 模块」「本地 DB 端口 5433」 用户手动(不提交)
5(最高) AutoMem ~/.claude/projects/<slug>/memory/MEMORY.md 对话中自动提取的 Memory(详见下节「四种Memory 类型」) Agent 自动写入

优先级规则:高层覆盖低层同名配置。AutoMem 优先级最高但内容最不可靠(机器生成),Managed 优先级最低但最权威(管理员设定)。

Project 层:逐级加载

Project 层的「逐级加载」在 monorepo 中尤为重要。以 repo/packages/web/ 为工作目录时,依次加载:

repo/CLAUDE.md                    ← 全仓库约定("用 pnpm workspace")
  → repo/packages/CLAUDE.md       ← packages 层约定("共享组件在 @repo/ui")
    → repo/packages/web/CLAUDE.md ← web 子包约定("用 Next.js App Router")

内层覆盖外层。每一级同时检查 <dir>/CLAUDE.md<dir>/.claude/CLAUDE.md<dir>/.claude/rules/*.md 三个位置。

截断保护

为防止过大的 CLAUDE.md 挤占Context Window,truncateEntrypointContent() 做双重限制——先限行再限字节

常量 说明
MAX_ENTRYPOINT_LINES 200 硬性行数上限
MAX_ENTRYPOINT_BYTES 25,000 25KB 硬性字节上限
MAX_MEMORY_CHARACTER_COUNT 40,000 单文件建议最大字符数

为什么需要双重限制?源码注释揭示了原因:p100 observed: 197KB under 200 lines——有用户在 CLAUDE.md 里塞了 197KB 的超长行。


二、四种 Memory 类型:怎么写入

CLAUDE.md 前四层(Managed → Local)由人工维护。第五层 AutoMem 由后台 Fork Agent 自动提取——这是 Claude Code 最「AI-native」的设计。

提取机制:后台 Fork Agent

每次模型产生最终响应后,后台启动一个独立的 Fork Agent(extractMemories.ts),分析刚结束的对话片段。整个过程不阻塞主对话——用户感知到的是模型已经回复完毕。

graph LR
    A["模型完成回复"] --> B["后台启动 Fork Agent\nmaxTurns=5, 超时 60s"]
    B --> C["分析对话片段\n判断是否有值得保存的信息"]
    C --> D["按四种类型分类\n写入 AutoMem 目录"]
参数
maxTurns 5(最多 5 轮对话)
drainTimeout 60,000ms(60 秒超时)
节流 GrowthBook tengu_bramble_lintel(默认每 1 个合格 turn 触发)
工具权限 FileRead/Grep/Glob(无限制)、Bash(只读)、FileWrite 仅限Memory 目录

四种 Memory 类型

Fork Agent 将提取的信息按语义分为四种类型(memoryTypes.ts),写入 AutoMem 目录下的对应文件:

类型 记录什么 来源示例 写入示例
user 用户角色、目标、技能背景 对话中用户自述 "用户是 web 前端开发者,3 年经验,偏好 TypeScript"
feedback 用户对 Agent 行为方式的反馈 用户纠正或明确要求 "不要在 commit message 里加 emoji""先问再改,不要自作主张"
project 项目架构、正在进行的工作 对话中涉及的项目信息 "正在从 Express 迁移到 Fastify""monorepo,pnpm workspace"
reference 不容易从代码推导的外部知识指针 用户提到的地址、文档位置 "staging 环境:https://staging.example.com""API 文档在 docs/api/"

什么不应该保存

同样重要的是 WHAT_NOT_TO_SAVE——一份即使用户明确要求也会拒绝保存的负面清单:

不保存的信息 原因
代码模式 / 架构 可以通过读代码获得
文件路径 会变化,可以通过搜索获得
API 签名 可以通过阅读源码获得
Git 历史 已有版本控制系统
调试方案 一次性的,下次场景不同
已在 CLAUDE.md 中的内容 避免重复

设计原则:只记住 Agent 无法从环境中重新获得的信息


三、Memory 召回:Sonnet 异步预取

Memory 写入了文件,下一个问题是——怎么在需要时找回来?

Claude Code 的做法是:每个用户 turn,用 Sonnet 并行预取最相关的Memory 文件,与主 LLM 查询同时执行,不增加用户感知延迟。

三步流水线

graph LR
    A["Step 1: 扫描\n≤200 个文件\n每文件前 30 行\n~5ms"] --> B["Step 2: Sonnet 选择\n独立 Sonnet 调用\nmax_tokens=256\n选 ≤5 个文件\n~200ms"]
    B --> C["Step 3: 注入\n每文件 ≤4KB\n会话累计 ≤60KB\n~10ms"]
步骤 做什么 关键参数
扫描 读取 memory 目录文件的 frontmatter 最多 200 个文件,每文件前 30 行,按修改时间排序
Sonnet 选择 用 Sonnet 判断哪些文件与当前查询最相关 JSON schema 输出,最多选 5 个文件
注入 将选中文件内容追加到用户消息 每文件 ≤4KB / 200 行,会话累计 ≤60KB

整个过程与主 LLM 查询并行(GrowthBook flag tengu_moth_copse 控制),用户感知延迟 = max(召回, 主查询),几乎免费。

为什么用 Sonnet 而不用向量检索?

传统 RAG 用 Embedding 做向量检索。Claude Code 用 Sonnet 读 frontmatter 判断相关性——精确度高,不需要维护向量索引。代价是每 turn 消耗一次 Sonnet 调用,但对 Anthropic 而言边际成本极低。

Memory 信任验证

召回后还有一道防线——TRUSTING_RECALL 在使用 Memory 前验证引用的有效性:

  • Memory 提到文件路径 → fs.existsSync 检查是否仍存在
  • Memory 提到函数名 → grep 确认函数是否还在代码中

防止「幻觉 Memory」误导模型(如Memory 写着"项目用 Express",但实际已迁移到 Fastify)。


四、辅助机制

Session Memory:会话级摘要

除了跨会话的长期 Memory(CLAUDE.md),Claude Code 还在会话内维护 Session MemorysessionMemory.ts)——后台 Fork Agent 周期性提取当前对话要点,写入 summary.md。与Context 压缩联动:L4 压缩会利用已提取的 Session Memory 替代完整历史。

Sub-agent 独立 Memory

不同 Agent 类型维护独立的Memory 目录(agentMemory.ts),按 user / project / local 三种 scope 映射到不同路径,互不干扰。

KAIROS 夜间 Memory 整理

KAIROS 助理模式下的 DreamTask 在空闲时自动整理 Memory,经过 orient → gather → consolidate → prune 四阶段(类似人类睡眠时的 MemoryMemory 巩固固)。跨会话锁 consolidationLock 确保不会重复执行。


关键文件索引

文件 职责
src/utils/claudemd.ts CLAUDE.md 五层加载、合并、@include 解析
src/memdir/memdir.ts MEMORY.md 截断算法(行 + 字节双重限制)
src/memdir/memoryTypes.ts 四种Memory 类型定义 + WHAT_NOT_TO_SAVE
src/services/extractMemories/extractMemories.ts 后台 Fork Agent 自动提取
src/memdir/memoryScan.ts 召回 Step 1:扫描
src/memdir/findRelevantMemories.ts 召回 Step 2:Sonnet 选择
src/utils/attachments.ts 召回 Step 3:注入
src/services/SessionMemory/sessionMemory.ts 会话级摘要
src/tools/AgentTool/agentMemory.ts Sub-agent 独立 Memory
src/tasks/DreamTask/DreamTask.ts KAIROS 夜间整理