# 记忆 · 从零写一份 CLAUDE.md · Claude Code · 终端里的编排台

**作者** Aklman · **章节** 04 / 12 · **首发** 2026.08 · **语言** 中文为主, 中英双语
**原文** https://library.aklman.com/books/claude-code/04-memory
**全书 markdown** https://library.aklman.com/books/claude-code/llms.md
**上一章** https://library.aklman.com/books/claude-code/03-loop/llms.md
**下一章** https://library.aklman.com/books/claude-code/05-codify/llms.md

> 本章来自 Aklman · Library, 完整免费阅读。允许 AI 摘读、引用、问答; 转载请保留作者署名与原文链接 (CC BY-NC-ND 4.0, https://library.aklman.com/license)。

---

> 同一件事你跟它说了十遍。这一章从零写一份真的 CLAUDE.md,并且用一组前后对照证明它确实改变了行为 —— 不是「感觉好点了」,是同一句提示、同一个仓库,做出来的东西不一样。顺带把四层记忆的位置与优先级、@import、rules 目录的路径限定,和一条容易踩的时间差讲清:它在会话开始读进内存,你中途改了不生效。

用这个包管理器,不是那个。测试要跑 npm test 不是 npx jest。migrations 目录别碰。—— 这些话你跟它说过十遍,因为模型不跨会话记事。这一章从零写一份真的 CLAUDE.md,然后用一组前后对照证明它确实改变了行为:同一句提示、同一个仓库,做出来的东西不一样。不是「感觉好点了」,是看得见的差别。

### I · 第一步:先拍一张「没有记忆」的底片

对照要成立,得先有前片。挑一个**你的项目有特殊做法**的小任务 —— 加一个组件、加一条接口、加一个测试 —— 用一句最普通的提示交给它,然后**什么都别纠正**,把结果原样记下来。

**提示词 · 底片提示 · 故意不给任何约定**

```text
在这个仓库里加一个 <最小的、你们有特殊做法的东西>,照你觉得合适的方式做。
做完告诉我三件事:
1. 你把文件放在了哪里、为什么选这个位置;
2. 你跑了什么命令来验证,输出是什么;
3. 你对这个项目的约定做了哪些假设。
```

**示例**

```text
在这个仓库里加一个 带单元测试的日期格式化工具函数,照你觉得合适的方式做。
做完告诉我三件事:
1. 你把文件放在了哪里、为什么选这个位置;
2. 你跑了什么命令来验证,输出是什么;
3. 你对这个项目的约定做了哪些假设。
```

第 3 问是这一步的重点。它列出的每一条「假设」,都是一条**它本来该知道、但没人告诉过它**的事实 —— 那张假设清单,就是你 CLAUDE.md 的第一版目录。

---

### II · 第二步:写那份文件,一行一个否决问题

起点不用手写:跑一次 `/init`,它会分析代码库,把探出来的构建命令、测试方式、项目惯例生成一份初稿;已经有 CLAUDE.md 的话,它提改进而不是覆盖。然后你做减法。

CLAUDE.md 的敌人不是太短,是太长:它整份进入每次会话的上下文,臃肿的文件会让真正要紧的规则被淹没 —— 官方直说,写得太满,Claude 反而开始忽略你的指令。[^2]纪律是对每一行问同一个问题:**删掉这行,它会不会因此犯错?**不会,就删。[^2]

| 值得一行 | 不值得 |
|---|---|
| 它猜不到的构建 / 测试命令 | 读代码就能推断的结构 |
| 和默认不同的风格规则 | 语言的通行惯例 |
| 禁区目录、仓库礼仪、环境怪癖 | 经常变动的信息 |
| 踩过的坑和非直觉行为 | 逐文件的代码库导览 |

_一份真实形状的项目记忆:每一行都通过了「删掉会不会出错」_
```text title="CLAUDE.md"

```markdown
# 这个仓库的规矩

## 命令
- 测试:`npm test`(不是 npx jest —— 我们的 jest 配置只在这个脚本里生效)
- 单文件测试:`npm test -- <路径>`
- 构建前必须先 `npm run codegen`,否则类型对不上

## 约定
- 组件放 `src/components/<名字>/`,一个目录含组件、测试、样式三个文件
- 日期一律用 `src/lib/date.ts` 里的工具,不要直接 new Date()
- 新增依赖前先问我 —— 这个项目对包体积有硬预算

## 禁区
- 不要改 `src/db/migrations/`,那是线上迁移历史
- 不要碰 `.env*`
- 不要在提交信息里加 co-author 尾注

## 坑
- `src/legacy/` 下的代码没有测试覆盖,改那里要手动验证并说明怎么验的
```

```

什么时候写?触发律是**第二次**:同一个错误出现第二遍,就是写一行的时候。[^3]什么时候搬走?一段记忆长成了多步流程,它就不该住在这里 —— 那是 skill 的形状(第 5 章)。

---

### III · 第三步:重跑同一句,看差别

关键的一步,也是最容易被跳过的一步:`/clear` 开一个新会话(记住 —— 中途改文件不生效,下一节讲),把第 I 步**一字不改**的那句提示再发一次。

#### 没有 CLAUDE.md

 文件落在 `src/utils/`(它的通用直觉),直接用了 `new Date()`,顺手 `npx jest` 跑测试 —— 报了个配置错,它又装了一个依赖去绕。交回来的东西能跑,但**三条项目约定全踩了**,而它列出的「假设」正好就是这三条。

#### 有 CLAUDE.md

 文件落在 `src/components/` 的目录约定里,日期走 `src/lib/date.ts`,验证用的是 `npm test` 并贴了输出;想加依赖时它**停下来问你**。没有一次纠正,因为不需要纠正 —— 这就是那份文件替你说的十遍话。

这组对照的意义不只是「它变听话了」。它把一件抽象的事变具体:**一份写得好的 CLAUDE.md,对产出质量的改善常常大于换一个更强的模型** —— 因为多数「模型不够聪明」的错,其实是「没人告诉它」的错。也正因如此,这份文件值得像代码一样进版本库、被 review。

---

### IV · 放哪一层,以及两个时间差

四层记忆按**从宽到窄**的顺序加载,项目层出现在用户层之后。[^1]

| 层级 | 位置 | 放什么 |
|---|---|---|
| 组织 | managed policy(各系统固定路径) | 全公司的编码与合规规范 |
| 用户 | `~/.claude/CLAUDE.md` | 你个人的偏好,跟你走所有项目 |
| 项目 | `./CLAUDE.md` 或 `./.claude/CLAUDE.md` | 团队共享:命令、架构、惯例,进版本库 |
| 项目 · 个人 | `./CLAUDE.local.md` | 你的沙盒地址、测试数据,进 .gitignore |

查找方式值得知道:它**从当前工作目录向上逐级走**,沿途每一层都查 CLAUDE.md 与 CLAUDE.local.md —— 所以在 monorepo 的子包里开工,上层仓库的规矩照样带得到。反方向则是懒加载:当前目录**之下**的子目录 CLAUDE.md 不在启动时加载,等 Claude 真读到那些子目录里的文件才带进来。[^1]这条设计很值钱:子包各自的规矩不用挤占你的开场上下文。

文件长了有两个泄压阀。`@path` 把别的文件拼进来,最多递归**四层**,相对路径按「含这条导入的文件」解析而不是工作目录 —— 这一条踩错过很多人。[^1]另一个是 `.claude/rules/`,按主题拆文件,还能用 `paths` 限定只在 Claude 碰到匹配文件时加载。[^1]

> 已经在维护一份 `AGENTS.md`(给别家 agent 用的)?Claude Code 读的是 CLAUDE.md,**不读 AGENTS.md**。官方给的桥接写法很省事:CLAUDE.md 里只写一行 `@AGENTS.md`,或者做个符号链接 —— 一份内容两边都认。[^1](想在 CLAUDE.md 里提一个路径又不想把它导进来,用反引号包住:导入解析会跳过行内代码与围栏代码块。[^1])

两个时间差别踩。其一:CLAUDE.md 在会话开始读入内存,**中途改了不生效** —— 新内容要等下次 `/clear`、`/compact` 或重启才加载。[^4]改完发现它还按旧规矩走,不是它叛逆,是你还没翻页。其二:除了你写的记忆,它也在自己记 —— auto memory 把「下次会用上」的东西写进项目记忆目录,每次会话加载 MEMORY.md 的前 200 行或 25KB。[^1]`/memory` 能看到两套记忆的全部文件:定期翻一翻,删掉过时的 —— 记忆也会腐烂,烂记忆比没记忆更误事。

> 把预期校准准确:文档对两套记忆的定性是「上下文,不是强制配置」—— 它读了、大概率照做,但没有任何机制保证。[^1]所以「提交前必须跑 lint」写在这里是叮嘱,写成 hook 才是闸门(第 8 章);「绝不许碰 .env」想要硬拦,官方指名用 PreToolUse hook,不是加粗字体。[^1]记忆负责让它**懂你的项目**,hooks 负责让它**越不过线** —— 两件事,别用一个工具硬扛。

本章验收 · 这份文件立起来了吗:

- 拍过底片:同一句提示在没有 CLAUDE.md 时跑过一次,它列出的「假设」你留着。

- 写好了文件:跑过 /init 打底,并用「删掉会不会出错」的尺子至少删掉三行。

- 做过对照:/clear 后一字不改地重跑同一句,亲眼看见差别 —— 这是你说服自己和队友的证据。

- 分清了层:个人偏好在 ~/.claude/,团队规矩进版本库,私人地址进 CLAUDE.local.md。

> 同一句话说第三遍之前,
> 把它变成一行记忆

## 引用与参考

01 · Claude Code Docs · How Claude remembers your project —— 四层 CLAUDE.md 按加载顺序从宽到窄:managed policy(macOS /Library/Application Support/ClaudeCode/CLAUDE.md、Linux 与 WSL /etc/claude-code/CLAUDE.md、Windows C:\Program Files\ClaudeCode\CLAUDE.md)、用户 ~/.claude/CLAUDE.md、项目 ./CLAUDE.md 或 ./.claude/CLAUDE.md、本地 ./CLAUDE.local.md;项目层出现在用户层之后。查找方式是从当前工作目录向上逐级走,沿途每一层都查 CLAUDE.md 与 CLAUDE.local.md;当前目录之下的子目录 CLAUDE.md 不在启动时加载,而是 Claude 读到那些子目录里的文件时才带进来。@path 导入相对路径按「含该导入的文件」解析而非工作目录,最多递归四层;导入解析会跳过 markdown 行内代码与围栏代码块,想提路径又不想导入就用反引号包住。项目文件里的导入有信任对话框保护(防别人往共享仓库里塞),用户层导入是你自己写的,不弹。Claude Code 读 CLAUDE.md 而不是 AGENTS.md,官方给的桥接写法是在 CLAUDE.md 里写 @AGENTS.md 或做符号链接。两套记忆都在每次会话开始加载,并且「Claude treats them as context, not enforced configuration」—— 要不论模型怎么决定都拦住某个动作,用 PreToolUse hook。auto memory 每次会话加载 MEMORY.md 的前 200 行或 25KB。截至 2026-08-05。  (Claude Code Docs · Memory)
02 · Claude Code Docs · Best practices —— 写 CLAUDE.md 的取舍表:收录它猜不到的构建命令、和默认不同的风格规则、测试跑法、仓库礼仪、项目特有的架构决定与坑;排除读代码就能推断的、语言通识、频繁变动的信息、逐文件的代码库描述。每一行问一句「删掉它会不会导致出错」,不会就删;臃肿的 CLAUDE.md 会让真正的规则被忽略。截至 2026-08-05。  (Claude Code Docs · Best practices)
03 · Claude Code Docs · Extend Claude Code —— 各扩展机制的触发时机:同一个约定或命令错第二次,写进 CLAUDE.md;反复粘贴的多步流程,做成 skill;必须每次发生的,写 hook。指令写在 CLAUDE.md 或 skill 里「是请求,不是保证」。截至 2026-08-05。  (Claude Code Docs · Extend Claude Code)
04 · Claude Code Docs · How Claude Code uses prompt caching —— 项目根与用户级 CLAUDE.md 在会话开始读入内存,中途编辑不废缓存、但也不生效,下次 /clear、/compact 或重启才加载新内容。截至 2026-08-05。  (Claude Code Docs · Prompt caching)

---

*记忆 · 从零写一份 CLAUDE.md · Claude Code · 终端里的编排台 · Aklman 著 · CC BY-NC-ND 4.0 · https://library.aklman.com/books/claude-code/04-memory*
