深入理解 Claude Code 的记忆系统
AI 编程助手有一个天然短板:每次会话都是"失忆"的。你昨天告诉它的编码规范、上周纠正过的错误习惯、项目里那些约定俗成的坑,新开一个会话就得重新解释一遍。
Claude Code 用一套分层的记忆系统来解决这个问题。这套系统由两大部分组成:你写给 Claude 的指令记忆(CLAUDE.md 体系),和 Claude 自己写给自己的自动记忆(Auto Memory)。
一、CLAUDE.md:分层的指令记忆
CLAUDE.md 是由人主动维护的持久化指令文件,每次会话启动时自动注入上下文。它按作用域分为五层:
| 层级 | 位置 | 作用范围 |
|---|---|---|
| 托管策略(Managed Policy) | Windows: C:\Program Files\ClaudeCode\CLAUDE.mdmacOS: /Library/Application Support/ClaudeCode/CLAUDE.mdLinux/WSL: /etc/claude-code/CLAUDE.md |
整台机器所有用户,由 IT 统一下发,无法被排除或覆盖 |
| 用户记忆 | ~/.claude/CLAUDE.md |
你本人的所有项目 |
| 项目记忆 | <项目根>/CLAUDE.md 或 <项目根>/.claude/CLAUDE.md |
随仓库提交,全团队共享 |
| 本地记忆 | <项目根>/CLAUDE.local.md |
只属于你、只在本项目(应加入 .gitignore) |
| 子目录记忆 | <子目录>/CLAUDE.md |
按需懒加载 |
加载机制:向上遍历 + 拼接
Claude Code 启动时会从当前工作目录一路向上遍历到文件系统根,把沿途所有 CLAUDE.md / CLAUDE.local.md 收集起来。所谓"文件系统根",在 Linux/macOS 上是 /,在 Windows 上是盘符根目录(如 D:\)。假设你在 D:\projects\my-app\backend 启动,它会依次检查:
D:\projects\my-app\backend ← 当前目录
D:\projects\my-app
D:\projects
D:\ ← 文件系统根,到此为止
收集到的文件按"根 → 当前目录"的顺序拼接进上下文——注意是拼接,不是覆盖。而当前目录以下的子目录 CLAUDE.md 不在启动时加载,只有当 Claude 实际读取那个子目录里的文件时才按需加载,避免浪费上下文。
"后加载的优先级更高"是真的吗?
这个问题值得单独说清楚:没有硬性的"后者覆盖前者"规则。LLM 的上下文不像 CSS 那样有明确的层叠优先级,所有指令都会被"看到"并综合权衡。
但实践中有两个事实:
- Claude Code 故意把离工作目录更近(更具体)的文件排在后面加载。位置靠后、离当前对话更近的内容在模型注意力上通常权重略高,所以项目规则实际上倾向于压过用户全局规则——这是一种软倾向,不是保证。
- 官方文档明确警告:如果两条指令真的互相矛盾,Claude 可能任选其一,行为不可预测。
所以正确的做法不是依赖"后写的赢",而是定期审查各层文件,主动消除冲突。
Monorepo 场景:排除不相关的上层文件
Monorepo(单一大仓库)指一个 git 仓库里装了多个独立项目或团队的代码:
company-monorepo/
├── CLAUDE.md ← 公司级总规则
├── team-a/payment/ ← A 团队的支付服务
├── team-b/frontend/ ← B 团队的前端
└── team-c/data-pipeline/ ← C 团队的数据管道
当你在 team-b/frontend/ 里工作时,向上遍历会把仓库根、甚至中间层其他团队的 CLAUDE.md 全部加载进来——它们可能与你毫无关系,白白消耗上下文还可能引入矛盾指令。此时可以在 .claude/settings.local.json 中用 claudeMdExcludes 按 glob 模式排除:
{
"claudeMdExcludes": [
"**/company-monorepo/CLAUDE.md",
"/path/to/monorepo/other-team/.claude/rules/**"
]
}
托管策略层的 CLAUDE.md 是唯一的例外——它无法被排除。
二、@import:把文件内容"钉"进上下文
CLAUDE.md 内可以用 @路径 语法导入其他文件:
项目概览见 @README,npm 命令见 @package.json
# 附加指令
- git 工作流:@docs/git-instructions.md
- 个人偏好:@~/.claude/my-project-instructions.md
语法要点:
- 相对路径相对于包含该导入的文件解析(不是工作目录),也支持绝对路径和
~/家目录路径 - 最大递归深度 4 层
- 代码块和行内代码中的
@xxx不会被解析——想字面提到某个路径而不触发导入,用反引号包住即可:`@README` - 项目里首次遇到外部导入时会弹一次批准对话框,拒绝后导入保持禁用
@import 与纯文本提及路径的本质区别
这是一个容易被忽视但很关键的设计取舍——核心区别在于内容进不进上下文,以及什么时候进:
@docs/api.md |
纯文本写"参见 docs/api.md" | |
|---|---|---|
| 加载时机 | 启动时立即注入整个文件内容 | 只是一个字符串,什么都不加载 |
| 是否保证 Claude 看到 | 保证(内容已在上下文里) | 不保证——Claude 需要自己决定用读文件工具去读,可能读也可能不读 |
| Token 成本 | 每次会话都消耗(无论用不用得上) | 零成本,直到真被读取 |
选择原则:每次会话都必须遵守的规则用 @import(保证在场);偶尔才需要的参考资料用文本路径提及(省 token,按需读取)。这也解释了官方文档那句提醒——"import 帮你组织文件,但不省 token":它本质上等于把内容复制进了 CLAUDE.md。
三、.claude/rules/:模块化与路径作用域规则
当项目规则越写越多,塞在一个 CLAUDE.md 里既难维护又浪费上下文。.claude/rules/ 目录允许把指令拆成模块化的规则文件,目录下所有 .md 文件会被递归发现:
your-project/
├── .claude/
│ ├── CLAUDE.md
│ └── rules/
│ ├── code-style.md
│ ├── testing.md
│ └── frontend/
│ └── react.md
它最有价值的能力是路径作用域规则:通过 YAML frontmatter 声明 paths,让规则只在 Claude 操作匹配文件时才加载:
---
paths:
- "src/api/**/*.ts"
---
# API 开发规范
- 所有接口必须做输入校验
- 使用标准错误响应格式
没有 paths 字段的规则文件则与 CLAUDE.md 一样,在启动时无条件加载。个人通用规则可以放在 ~/.claude/rules/(先于项目规则加载,因此项目规则的实际影响力更高);跨项目共享规则可以直接用符号链接。
插一句:什么是 YAML frontmatter?
指 Markdown 文件开头用两行 --- 包起来的元数据块,内容是 YAML 格式的键值对。它给"读这个文件的程序"提供结构化信息,正文才是给人或模型看的内容。这个约定最早来自 Jekyll 等静态博客生成器,如今已是 Markdown 生态的通用惯例。Claude Code 生态里多处用到它:rules 文件的 paths、技能文件(SKILL.md)的 name/description、子代理定义(.claude/agents/*.md)的模型与工具声明,以及自动记忆文件的元信息。
四、日常操作:#、/memory 与 /init
# 快捷键:消息以 # 开头,内容会被存入记忆,例如:
# 提交前必须先跑测试
存储目标不是固定的。较早的版本会弹出选择器让你挑目标文件(项目 CLAUDE.md、用户级 CLAUDE.md 还是 CLAUDE.local.md);引入自动记忆后的版本则由 Claude 根据内容性质判断归属——像"规则/指令"的内容倾向写入 CLAUDE.md,像"经验/发现"的内容倾向写入自动记忆。想精确控制去向,直接在消息里说明即可:# 把这条加到项目 CLAUDE.md:所有日期用 ISO 8601 格式。
/memory 命令:查看当前会话到底加载了哪些记忆文件、开关自动记忆、直接打开某个文件编辑。排查"Claude 不听指令"问题时,第一步永远是用它确认文件有没有被加载。
/init 命令:分析代码库自动生成初始 CLAUDE.md(已存在则给出改进建议而非覆盖),会自动发现构建命令、测试方式和项目约定,还会读取 .cursorrules、AGENTS.md、.windsurfrules 等其他工具的规则文件并吸收其内容。设置环境变量 CLAUDE_CODE_NEW_INIT=1 可启用交互式多阶段初始化流程。
关于 AGENTS.md
AGENTS.md 是一个跨工具的开放约定(见 https://agents.md ),可以理解为"写给 AI 编程代理看的 README"。它的背景是:每家 AI 编程工具都发明了自己的指令文件——Claude Code 用 CLAUDE.md、Cursor 用 .cursorrules、Windsurf 用 .windsurfrules——团队同时用多个工具时,同样的规范要维护好几份。AGENTS.md 就是为统一这件事而生,OpenAI Codex、Cursor、Google Jules 等多家工具已采纳。
Claude Code 对它的支持有两条路:一是导入复用,在 CLAUDE.md 里写一行 @AGENTS.MD,通用规则放 AGENTS.md 供所有工具共享,Claude 专属规则继续写在 CLAUDE.md 里;二是 /init 初始化时自动读取并整合其内容。
五、Auto Memory:Claude 写给自己的记忆
这是较新的功能(Claude Code v2.1.59+),与 CLAUDE.md 的根本区别一句话就能说清:CLAUDE.md 由你写,自动记忆由 Claude 自己写。Claude 在会话过程中把值得记住的东西记录下来——构建命令、调试发现、你的纠正反馈、代码风格习惯、架构笔记——供以后的会话使用。
存储结构
每个项目在用户主目录下有独立的记忆目录:
~/.claude/projects/<项目标识>/memory/
├── MEMORY.md # 索引文件(启动时加载前 200 行或前 25KB)
├── debugging.md # 主题文件(按需读取)
├── api-conventions.md # 主题文件(按需读取)
└── ...
- 项目标识基于 git 仓库派生,同一仓库的所有 worktree 和子目录共享一份记忆;不在 git 仓库中则按项目根目录计算
- 记忆是本机私有的,不跨机器同步
- 全部是纯 Markdown,你可以随时打开审查、编辑或删除
精巧的加载设计
自动记忆的上下文成本控制得很好:
- 启动时只加载
MEMORY.md的前 200 行或前 25KB(先到为准),因此 Claude 会主动保持索引精简,把细节挪到主题文件 - 主题文件不占启动上下文,Claude 需要时才用普通文件工具按需读取
- 界面上出现 "Writing memory" / "Recalled memory" 提示时,就是 Claude 在读写这个目录
开关与配置
自动记忆默认开启,三种方式控制:
/memory命令中直接切换settings.json中设置"autoMemoryEnabled": false- 环境变量
CLAUDE_CODE_DISABLE_AUTO_MEMORY=1
还可以用 "autoMemoryDirectory": "~/my-memory-dir" 自定义存储位置(必须是绝对路径或 ~/ 开头;写在项目级 settings 时需要先通过工作区信任确认)。子代理(subagent)也可以单独启用自己的持久记忆。
六、选型:什么信息放哪里
把两类记忆放在一起对比:
| 维度 | CLAUDE.md | Auto Memory |
|---|---|---|
| 谁来写 | 你 / 团队 | Claude 自动 |
| 内容性质 | 规则、指令、标准 | 经验、发现、习惯 |
| 上下文成本 | 全文加载 | 仅索引前 200 行 / 25KB |
| 适合放什么 | 编码规范、架构决策、工作流 | 构建命令、调试心得、易变信息 |
更广义的选型原则——记忆系统不是万能的,官方明确建议分流:
- 稳定的项目规则 → CLAUDE.md(目标控制在 200 行以内;写具体可验证的指令,比如"用 2 空格缩进"而不是"格式规范一点")
- 只对某些目录/文件生效的规则 →
.claude/rules/路径作用域规则 - 多步骤操作流程 → Skills(按需加载,不占每次启动的上下文)
- 必须在固定时机强制执行的动作(如每次提交前 lint) → Hooks。这一条尤其重要:Hooks 由客户端强制执行,不依赖 Claude 的判断;而 CLAUDE.md 只是"影响行为",不是硬约束
- 经常变化的信息 → 交给自动记忆
七、常见问题排查
"Claude 不遵守我的 CLAUDE.md"
先跑 /memory 确认文件确实被加载了;再检查指令是否够具体;最后排查多层文件之间有没有互相矛盾的规则——冲突时 Claude 可能任选其一。需要精确诊断时,可以用 InstructionsLoaded hook 记录哪些指令文件在何时、为何被加载。
"/compact 之后指令丢了"
项目根的 CLAUDE.md 会在上下文压缩后自动重新注入,但子目录的嵌套 CLAUDE.md 不会——要等 Claude 下次读取那个目录的文件才重新加载。只在对话里口头说过的指令,压缩后就没了;重要的规则一定要落到 CLAUDE.md。
"不知道自动记忆存了什么"
/memory → 进入 auto memory 文件夹浏览,全是纯 Markdown,不满意直接改或删。
"CLAUDE.md 太大了"
拆成路径作用域规则、删掉不是每次会话都需要的内容。注意 @import 拆分只改善组织结构、不减少 token。另外 CLAUDE.md 支持 HTML 块级注释 <!-- ... -->,注入上下文前会被剥离,可用来写只给人看的维护说明。
结语
Claude Code 的记忆系统本质上是一套分层的上下文管理策略:托管策略管住底线,用户记忆承载个人偏好,项目记忆沉淀团队共识,路径作用域规则精准投放,自动记忆让 Claude 自己积累经验——每一层都在"保证指令在场"和"节约上下文"之间做取舍。
用好它的关键不在于把所有东西都塞进记忆,而在于理解每类信息的正确归宿:规则给 CLAUDE.md,流程给 Skills,强制动作给 Hooks,经验交给自动记忆。当你发现自己第三次向 Claude 解释同一件事时,那就是该写入记忆的信号。
浙公网安备 33010602011771号