深入 Claude Code 的 CLAUDE.md
01 它不是 README,是常驻上下文
Claude Code 的每个会话都是从零开始的:模型不记得上一次对话,也不认识你的仓库。它对项目的全部「先验知识」,来自会话启动那一刻被自动注入上下文的一组 Markdown 文件——这就是 CLAUDE.md,官方文档称之为 memory(记忆)机制。
理解它的关键,是纠正一个直觉性的误会:CLAUDE.md 看起来像文档,但它不是躺在硬盘上等人查阅的文档,而是每个会话都要装进上下文窗口的常驻内存。这个区别引出两条推论:
- 它是杠杆率最高的提示词界面。 写对一行「测试用
pnpm test -- --run,不带--run会挂起 watch」,能消灭之后每个会话里的同一类翻车。你在别处调提示词是一次性的,在这里调是复利的。 - 每一行都有租金。 这些内容占据上下文窗口、参与 prompt cache、和用户请求与代码一起争夺模型的注意力。README 写给人看,可以事无巨细;CLAUDE.md 写给模型看,臃肿的代价是真正重要的指令被稀释。
它和 settings.json 的关系也值得先划清楚:settings.json 的 deny 规则是无条件生效的硬约束,CLAUDE.md 里的「不要改 legacy 目录」只是一条被高度重视的建议——模型极大概率遵守,但没有架构级保证。这个「强制 vs 引导」的区别,决定了后文第 7 节里什么该写在哪。
02 五层记忆与加载顺序
和 settings.json 一样,CLAUDE.md 也是分层的,但语义完全不同:settings.json 是覆盖(高层赢),CLAUDE.md 是叠加——所有层的内容全部注入上下文,按「从宽泛到具体」的顺序排列。
| 层级 | 位置 | 归属 | 加载时机 |
|---|---|---|---|
| 企业级 | 见下表 | 组织策略,IT 下发 | 启动时 |
| 用户级 | ~/.claude/CLAUDE.md |
跟人走,所有项目生效 | 启动时 |
| 项目级 | ./CLAUDE.md 或 ./.claude/CLAUDE.md |
提交进 Git,团队共享 | 启动时 |
| 项目本地 | ./CLAUDE.local.md |
个人差异,进 .gitignore | 启动时,追加在同目录 CLAUDE.md 之后 |
| 子目录级 | 子目录/CLAUDE.md |
模块专属指令 | 按需(见第 3 节) |
企业级文件的位置随平台不同:
| 平台 | 路径 |
|---|---|
| macOS | /Library/Application Support/ClaudeCode/CLAUDE.md |
| Linux / WSL | /etc/claude-code/CLAUDE.md |
| Windows | C:\Program Files\ClaudeCode\CLAUDE.md |
递归向上查找
启动时 Claude Code 还会从当前工作目录向上逐级查找到文件系统根,沿途所有的 CLAUDE.md 和 CLAUDE.local.md 都会被加载。在单仓(monorepo)里这非常实用:在 repo/packages/web/ 里启动会话,repo/CLAUDE.md 的全仓约定和 repo/packages/web/CLAUDE.md 的包内约定会一起生效,且后者排在后面、语义上更「具体」。
两个不起眼但省 token 的细节:块级 HTML 注释(<!-- 备忘 -->)在注入前会被剥掉,可以放心在文件里给人留维护笔记;代码块内的注释则原样保留。
容易踩的坑
「叠加」意味着冲突不会被自动裁决。用户级写了「回答用中文」、项目级写了「所有输出用英文」,两条都会进入上下文,模型的行为会摇摆。跨层写指令前,先用/memory看一眼实际加载了哪些文件、拼起来是什么——它是这个体系唯一的「总装视图」。
03 子目录与 rules:按需加载的记忆
启动即加载的内容是「常驻内存」,价格最贵。Claude Code 提供了两个「按需分页」机制,把长尾指令的租金降到接近零。
子目录 CLAUDE.md。 工作目录下方的子目录里的 CLAUDE.md 不在启动时加载,而是当 Claude 读取该目录下的文件时才被拉进上下文。由此得出单仓的标准布局:根文件保持极简(全仓命令、提交规范),每个包的怪癖写在包内——不碰 packages/billing/ 的会话,永远不用为计费模块的注意事项付费。
路径限定 rules。 .claude/rules/ 目录下的规则文件用 YAML frontmatter 声明适用范围,只在 Claude 操作匹配文件时加载:
---
paths: src/**/*.ts
---
- 一律开启 strict 模式,禁止 `any` 逃逸
- 公共 API 必须有 TSDoc 注释
判断一条指令放哪里的标准很简单:它对多大比例的会话有用? 几乎每次都用到的(构建命令、提交规范)→ 根 CLAUDE.md;只在碰某类文件时有用的 → rules;只在碰某个模块时有用的 → 子目录 CLAUDE.md。
04 import:把记忆拆成模块
CLAUDE.md 支持用 @路径 语法引入其他文件,这让它从单个文件长成了一个可组合的模块系统:
项目概览见 @README.md
- Git 工作流 @docs/git-instructions.md
- 个人偏好(不进仓库)@~/.claude/console-project.md
规则要点:
- 相对路径相对于包含 import 的那个文件所在目录,不是工作目录——嵌套引入时容易想当然。
- 支持绝对路径和
~/家目录路径;递归引入最多 4 层。 - 代码段和代码块里的
@路径不会被解析。想在正文提及路径而不触发导入,用反引号包住:`@README.md`。 - 首次在项目中遇到指向项目外的 import 时,会弹出审批对话框列出涉及的文件。拒绝之后该 import 永久禁用、对话框不再出现——「为什么我的 import 不生效」的答案常常是几周前随手点过一次拒绝。
CLAUDE.local.md 与跨 worktree 场景
CLAUDE.local.md 用来放不进仓库的个人偏好(沙箱 URL、本地测试数据),官方并未弃用它。但它有个结构性短板:作为被 gitignore 的文件,它只存在于创建它的那个目录——如果你用多个 git worktree 并行开发同一仓库,其他 worktree 里它根本不存在。官方推荐的替代写法是在项目 CLAUDE.md 里导入家目录文件:
@~/.claude/my-project-instructions.md
文件跟人走,每个 worktree 都能引到,效果等价而没有副本问题。
05 /init 与日常维护
空仓库起步不必手写。/init 命令会分析代码库——构建系统、测试框架、代码规范、目录约定——自动生成一份初始 CLAUDE.md;如果文件已存在,它会建议增量改进而不是覆盖。它还会读取其他代理工具的配置文件(AGENTS.md、.cursorrules、.devin/rules/、.windsurfrules),把有效内容整合进来,方便从别的工具迁移。
设置环境变量 CLAUDE_CODE_NEW_INIT=1 可以启用更强的交互式流程:先问你要生成哪些工件(CLAUDE.md、skills、hooks),再派子代理探索代码库,用追问补齐空白,最后提交一份可审查的提案才落盘。
日常维护的入口是 /memory:查看当前会话实际加载了哪些记忆文件,并直接跳转编辑。排查「指令为什么没生效」时,它永远是第一站。
06 写作原则:每一行都在付租金
官方文档和 best-practices 博文对「怎么写好」给出了相当一致的意见,可以归纳成四条。
一、目标 200 行以内。 超长文件不仅费 token,更会实质性降低指令遵循度——重要的规则淹没在噪音里。官方给出的裁剪标准锋利到近乎苛刻:对每一行问「删掉它,Claude 会出错吗?」不会,就删掉或者移出去。
二、具体压倒模糊。 模型不缺善意,缺的是无歧义的判据:
| ❌ 模糊 | ✅ 具体 |
|---|---|
| 格式化代码 | 使用 2 空格缩进 |
| 测试你的改动 | 提交前运行 npm test,全绿才允许 commit |
| 保持文件有序 | API 处理器一律放在 src/api/handlers/ |
三、写「推断不出来」的,不写「读代码就知道」的。 官方的包含/排除清单:
- ✅ 该写:猜不到的构建/测试命令、偏离语言默认的代码风格、仓库礼仪(分支命名、PR 规范)、架构决策(「业务逻辑只能在 core 包」)、环境怪癖、反复踩过的坑。
- ❌ 不该写:读代码能推断的结构、标准语言约定、详细 API 文档(放链接)、频繁变化的信息、长篇教程、逐文件的代码库导览。
四、像对待代码一样对待它。 Claude 没遵守指令时回来审查这份文件;定期剪枝矛盾和过时条目;新模型版本发布后重新评估——为旧模型写的 workaround 很可能已经不需要,留着只会占租金。对屡教不改的规则可以加 IMPORTANT、YOU MUST 这类强调来提升遵循度,但官方同时给了一个反直觉的诊断:如果你发现自己需要到处加强调,真正的问题多半是文件太长了。
07 分工边界:什么不该写进 CLAUDE.md
CLAUDE.md 早期承担了「所有自定义」的角色,如今 Claude Code 的配置面已经分化出多个专门机制,很多内容有了更好的去处:
| 机制 | 加载时机 | 约束力 | 适合放什么 |
|---|---|---|---|
| CLAUDE.md | 启动常驻 | 建议 | 编码标准、架构约定、项目速览 |
.claude/rules/ |
匹配路径时 | 建议 | 只针对某类文件/目录的规则 |
| skills | 模型判定相关时 | 建议 | 多步骤的可复用流程(发布、迁移) |
| hooks | 生命周期事件 | 强制 | 必须发生的动作(格式化、审计、门禁) |
| settings.json | 启动 | 强制 | 权限、沙箱、模型等技术配置 |
| subagents | 显式或自动委托 | — | 需要隔离上下文的高消耗任务 |
判断顺序建议倒着来,先排除硬机制:
- 这件事必须 100% 发生? → hook。「每次改完文件跑 prettier」写进 CLAUDE.md 是许愿,写成 PostToolUse hook 是保证。
- 这是权限或技术配置? → settings.json。「不许读 .env」放
permissions.deny,不要放 CLAUDE.md——前者拦得住,后者拦不住被诱导的模型。 - 这是个多步骤流程? → skill,按需加载,不占常驻租金。
- 只和一部分文件有关? → rules 或子目录 CLAUDE.md。
- 以上都不是,才轮到根 CLAUDE.md——留给真正全局、真正高频的行为约定。
08 与 AGENTS.md 共存
社区里不少工具(Codex、Cursor 等)在推 AGENTS.md 作为跨代理的通用指令文件。需要明确:Claude Code 不读取 AGENTS.md,官方文档也没有表露兼容计划。但共存并不麻烦,官方给了三条路:
方案一:import(官方推荐)。 一份 CLAUDE.md 只做两件事——引入通用文件、追加 Claude 专属内容:
@AGENTS.md
## Claude Code 专属
- 涉及 `src/billing/` 的改动先进 plan mode
方案二:符号链接。 ln -s AGENTS.md CLAUDE.md,零维护成本,代价是没法写 Claude 专属内容;Windows 上建符号链接还需要管理员权限或开发者模式。
方案三:/init 整合。 在已有 AGENTS.md 的仓库跑 /init,它会读取并把相关部分整合进生成的 CLAUDE.md。适合一次性迁移,但之后两份文件就要各自维护了。
多工具团队用方案一是最稳的:通用约定收敛在 AGENTS.md,各工具的入口文件只做引用加特化。
09 缓存、auto memory 与企业注入
会话中途改 CLAUDE.md:不生效是特性
CLAUDE.md 参与 prompt cache。会话进行中编辑它,缓存不会失效,改动也不会生效——当前会话继续使用启动时的版本,新内容要等 /clear、/compact 或重启才加载。更细一层:/compact 之后项目根 CLAUDE.md 会被重新读取注入,但已按需加载过的子目录 CLAUDE.md 不会自动重注入。「我明明改了为什么它还在用旧规则」——十有八九是这个原因。
另一个相邻的坑:用 --add-dir 或 /add-dir 挂载的额外目录,默认不加载其中的记忆文件,需要设置环境变量 CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 才启用。
auto memory:Claude 自己记笔记
v2.1.59 起,Claude Code 默认启用 auto memory:模型在工作中把值得跨会话保留的发现写进 ~/.claude/projects/<项目>/memory/ 目录。其中索引文件 MEMORY.md 每次启动只加载前 200 行或 25KB(先到为准),具体的主题文件(如 debugging.md)按需加载。可以通过 /memory 或 autoMemoryEnabled 设置开关。
它和 CLAUDE.md 是互补关系:CLAUDE.md 是你写给模型的规则,auto memory 是模型写给自己的笔记。subagent 也可以通过定义里的 memory: 字段获得跨对话存活的持久记忆目录。
企业注入
管理员可以在 managed-settings.json 里用 claudeMd 键直接注入一段组织级指令,无需单独分发文件,且用户无法排除——组织的合规要求(「响应中不得包含 PII」这类)由此获得全员保证。反方向的 claudeMdExcludes 则用 glob 模式跳过指定路径的记忆文件加载。这与 settings.json 一文的治理逻辑一脉相承:个人和团队的自由度之上,始终留有一个管理侧闸门。
10 模板与排错清单
一份值得参考的项目 CLAUDE.md
# 项目速览
Next.js 14 + TypeScript 的 B2B 控制台,pnpm workspace 单仓。
## 常用命令
- pnpm dev # 本地开发,3000 端口
- pnpm test -- --run # 单测(vitest;不带 --run 会挂起 watch)
- pnpm typecheck # 提交前必须通过
## 约定
- 一律用命名导出,禁止 default export
- 提交信息遵循 Conventional Commits,scope 用包名
- IMPORTANT: 不要手动改 packages/api-types/,它由 codegen 生成
## 架构要点
- API 路由只做参数校验与鉴权,业务逻辑一律在 packages/core
- 数据库访问只能经由 packages/db 的 repository 层
## 环境怪癖
- Windows 下 husky 需要 git config core.hooksPath .husky
- @internal/* 包来自私有 registry,首次安装先 pnpm login
## 深入阅读
- 发布流程 @docs/release.md
- 个人偏好(跨 worktree)@~/.claude/console-project.md
不到 30 行,每一行都是「读代码推断不出来」的信息:带原因的命令、偏离默认的约定、架构红线、环境怪癖,长尾内容全部 import 出去。
排错清单
| 症状 | 最常见原因 | 处置 |
|---|---|---|
| 指令时灵时不灵 | 文件太长,或各层指令互相矛盾 | 按「删掉会出错吗」裁剪到 200 行内;/memory 检查叠加后的全貌 |
| 会话中途改了文件不生效 | prompt cache 固定了启动时版本 | /clear 或重启;子目录文件连 /compact 都不会重注入 |
| 子目录 CLAUDE.md 没加载 | 按需机制,还没读过那个目录的文件 | 属正常;需要常驻就上移到根文件或改用 import |
| import 没生效 | @路径 写在代码块/反引号里;或首次审批被拒过 |
移出 code span;审批一旦拒绝即永久禁用,需重新启用 |
| CLAUDE.local.md 在别的 worktree 里消失 | gitignore 文件只存在于创建处 | 改用 @~/.claude/<项目>.md 导入家目录文件 |
--add-dir 目录的记忆没加载 |
附加目录默认不加载记忆文件 | 设 CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 |
| 团队成员的 Claude 行为不一致 | 有人的用户级 / local 文件夹带私货 | 各自 /memory 对比实际加载列表 |
| AGENTS.md 里的规则没被遵守 | Claude Code 不读取 AGENTS.md | 建一份 @AGENTS.md 的 CLAUDE.md 桥接 |
结语
CLAUDE.md 的全部技艺,可以压缩成一门「上下文经济学」:常驻的内容要贵而精——每一行都通过「删掉会出错吗」的测试;长尾的内容按需分页——子目录、rules、skills、import 各管一段;必须保证的事情根本不放这里——交给 hooks 和 settings.json 的硬约束。它本质上是团队与模型之间的接口契约,而契约的维护方式和代码没有区别:进版本库、被审查、随模型版本演进而重构。下一次 Claude 在你的仓库里犯了同一个错误两次,别急着在对话里纠正它第三次——那正是该打开 CLAUDE.md 写下一行的时刻。
浙公网安备 33010602011771号