CLAUDE.md 真正的工作机制是:它以 user message 的形式追加到系统提示之后,模型读到后会”试着遵守”,但没有强制保证。把这份理解落到工程实践,就得到四条强约束——别把它当权威事实源、别把”必须做”写进文件、明确交给 Hook 做的事、配套用语义索引补齐”代码当前是什么样”。下文逐条拆解。
一、为什么”上下文而非强制”这个定义改变决策
不少团队把 CLAUDE.md 写成”项目说明书 + 行为规范 + 代码索引”三合一大杂烩,结果 Claude 反而选择性忽略。原因就在机制本身:CLAUDE.md 是被读到的文本,不是被强制执行的契约。模型在同一时刻还要权衡对话上下文、工具返回、用户追问;当 CLAUDE.md 与更新更强的信号冲突时,它可能被任意丢掉。
官方文档明确写过:Claude 会”试着遵守”CLAUDE.md,但没有严格遵循的保证。
接受这个事实之后,工程实践的起点就变成了”我应当把它用在哪、不把它用在哪”。
二、四项工程约束
| 约束 | 误用形式 | 正确替代 |
|---|---|---|
| 不写权威事实 | “鉴权工具在 src/auth-v2/index.ts” | 用语义索引 / 自动生成的代码地图 |
| 不写强制动作 | “提交前必须跑 lint” | 写成 PreToolUse Hook |
| 篇幅受控 | 2000 行大杂烩 | 控制在 200 行内,深度内容放 rules/ 或 @import |
| 配套记忆层 | 全部塞进 CLAUDE.md | CLAUDE.md 写偏好,auto-memory 写自动学到的命令 |
三、CLAUDE.md 与 Hook 各自的分工
CLAUDE.md 适合表达”倾向性偏好”——使用 2 空格缩进、避免引入 Redux、API 错误统一抛 ApiError。Hook 适合表达”必须发生”——commit 前必跑 lint、禁止改写 protected/、生产写入前必须二次确认。
| 维度 | CLAUDE.md | Hook |
|---|---|---|
| 触发机制 | 上下文文本 | 生命周期事件钩子 |
| 强制力 | 软建议,可被忽略 | exit 2 直接拒绝 |
| 适用内容 | 风格、命名、约定 | 拦截、阻断、强制校验 |
| 失败行为 | 模型可能跳过 | 工具调用根本不发出 |
把”必须”与”倾向”放进错误的层级,是 CLAUDE.md 失效最常见的原因。
四、上下文预算与”指令税”
Claude Code 启动时系统提示本身就消耗了一部分指令额度。研究显示,主流 LLM 的指令遵循能力在总量 150–200 条附近开始衰减。CLAUDE.md 是按 token 体积持续占用这一预算的,每一个字符都在稀释其它指令被遵守的概率。
经验做法:
- 单文件 ≤ 200 行是起点;多团队项目里”够用”的水平往往在 60 行以内;
- 把只在某子目录生效的规范放进
.claude/rules/<topic>.md并用paths:glob 限定加载; - 用
@path/to/file.md引入补充文档,最多四层嵌套,避开代码片段; - 同一规则既出现在 CLAUDE.md 又出现在 Hook 时,Hook 优先;把重复条目从 CLAUDE.md 删除。
五、四层作用域的正确顺序
CLAUDE.md 不是单文件,而是一组按作用域叠加的层级,匹配顺序从广到窄:
| 作用域 | 路径 | 典型用途 | 是否随仓库分发 |
|---|---|---|---|
| 托管策略 | 系统策略路径 | 组织级合规 | 不下发 |
| 全局用户 | ~/.claude/CLAUDE.md |
个人通用偏好 | 否 |
| 项目 | ./CLAUDE.md 或 ./.claude/CLAUDE.md |
团队约定 | 是 |
| 本地私有 | ./CLAUDE.local.md(gitignore) |
沙箱 URL、临时实验 | 否 |
更具体的层级在冲突时优先,托管策略则无法被项目级覆盖。同一个项目里,monorepo 子目录下的 CLAUDE.md 会被父级与当前级合并加载。
六、与 AGENTS.md 协作的边界
AGENTS.md 是开放标准,多个 AI 工具都能读,但它不支持 path-scoped 规则、@path 导入与个人级 CLAUDE.local.md。工程团队如果同时使用多种 AI 工具,可以”双写”,但要接受 AGENTS.md 拿不到 CLAUDE.md 的全部能力。
<!-- CLAUDE.md 的最小可用示例 -->
# Project: 订单中台
## 构建与测试
- `pnpm i && pnpm test` 一次完成依赖与测试
- 提交前运行 `pnpm lint`,失败则不允许 commit
## 目录约定
- 新增工具函数统一放在 `packages/shared/src/`
- 公共类型从 `packages/shared/types.ts` 引入
## 风格
- 2 空格缩进,TS 严格模式
- 错误抛出统一用 `ApiError`,禁止裸 `throw new Error('...')`
## 强制项
- 不要直接修改 `packages/db/migrations/`,必须新建迁移文件
注意点:上面”不要修改 migrations”仍然只是软建议。如果想做到真强制——比如完全禁止模型重写迁移目录——正确做法是同时配一个 PreToolUse Hook,对 Edit/Write 工具的目标路径做正则拦截,命中就 exit 2。
七、auto-memory 与 CLAUDE.md 的边界
auto-memory 存的是模型在工作中”自动学到的东西”——常用构建命令、调试经验、用户偏好,它和 CLAUDE.md 一起加载进上下文。规则相同:把”机器能跑出来的事实”留给 memory,把”项目不会变的偏好”留给 CLAUDE.md。两者同时存在时,把”反复出现的命令”放 memory,把”约定”放 CLAUDE.md。
常见问题(FAQ)
Q1:CLAUDE.md 应该多长?
经验值单文件 ≤ 200 行;条目越多,模型越容易把关键规则稀释掉。
Q2:CLAUDE.md 里能写”禁止 X 行为”吗?
可以表达偏好,但若要”绝对禁止 X”,必须落到 Hook,否则只是软建议。
Q3:CLAUDE.md 和 AGENTS.md 怎么选?
只用 Claude Code 选 CLAUDE.md;多 AI 工具混用则维护 AGENTS.md 或双写并接受功能差。