Skip to content

CLAUDE.md 深度分析

一、为什么需要 CLAUDE.md

1.1 核心问题:每次对话都是"失忆"的

Claude Code 每次新会话都从一个全新的上下文窗口开始。它不知道:

  • 项目用什么构建命令
  • 团队有什么编码规范
  • 架构上有哪些约定和"坑"

没有 CLAUDE.md,你每次都要重复解释这些背景信息。

1.2 设计动机

CLAUDE.md 本质上是一种持久化的项目记忆。Anthropic 的设计原则是:

把你会反复向新同事解释的事情,写进 CLAUDE.md。

适合放入的内容:

  • 构建、测试、lint 命令
  • 编码约定和命名规范
  • 项目架构的关键决策
  • "永远做 X" 类的硬性规则

不适合放入的内容:

  • 多步骤的操作流程(应该用 Skill)
  • 只对某个子目录有效的规则(应该用 .claude/rules/
  • 通用的开发常识(浪费 token)

1.3 与同类方案的对比

特性CLAUDE.md.cursorrules.github/copilot-instructions.md
层级体系全局 → 项目 → 子目录,多层合并单文件,项目级单文件,项目级
动态加载子目录文件按需加载全量加载全量加载
@import 引用支持(最深 5 层)不支持不支持
路径条件作用域.claude/rules/ 支持 glob 匹配不支持不支持
个人 vs 团队分离CLAUDE.local.md(gitignore)
组织级策略支持 Managed Policy 位置

二、CLAUDE.md 的实现规则与规范

2.1 文件层级与加载顺序

CLAUDE.md 存在五个层级,优先级从低到高:

优先级低 ──────────────────────────────────────────── 优先级高

Managed Policy          用户级            项目级          本地个人         子目录
/etc/claude-code/     ~/.claude/       ./CLAUDE.md     ./CLAUDE.       子目录/
CLAUDE.md             CLAUDE.md      .claude/CLAUDE.md  local.md      CLAUDE.md
                                                       (gitignore)    (按需加载)

加载机制:

  1. 启动时,从当前工作目录向上遍历目录树,收集所有 CLAUDE.mdCLAUDE.local.md
  2. 同一目录内,CLAUDE.local.md 追加在 CLAUDE.md 之后
  3. 所有文件拼接(concatenate)而非覆盖
  4. 子目录的 CLAUDE.md 不在启动时加载,而是在 Claude 读取该子目录文件时才加入上下文
  5. Managed Policy 位置的文件不可被排除,确保组织级规则始终生效

2.2 @import 引用机制

markdown
# CLAUDE.md 中的引用语法
参考 @README.md 了解项目概述,参考 @package.json 了解可用命令。

# 额外指引
- Git 工作流 @docs/git-instructions.md

规则:

  • 支持相对路径和绝对路径
  • 相对路径基于包含引用的文件所在目录解析,而非工作目录
  • 最大递归深度 5 层
  • 首次遇到外部引用时会弹出审批对话框

2.3 .claude/rules/ 条件规则

markdown
---
paths:
  - "src/api/**/*.ts"
---

# API 开发规则
- 所有 API 端点必须包含输入验证
- 使用标准错误响应格式
  • 没有 paths 前置字段的规则文件在启动时无条件加载
  • paths 的规则仅在 Claude 读取匹配文件时触发
  • 支持 glob 模式和花括号展开:src/**/*.{ts,tsx}

2.4 大小与格式规范

维度建议
长度每个 CLAUDE.md 控制在 200 行以内
格式Markdown 标题 + 列表结构化组织
指令具体可验证("使用 2 空格缩进" 而非 "格式化好代码")
一致性不同文件间不要有矛盾规则,否则 Claude 会随意选一个
注释HTML 块注释 <!-- --> 会被剥离不消耗 token,代码块内的注释保留

2.5 claudeMdExcludes 排除机制

大型 monorepo 中可排除不相关的 CLAUDE.md:

json
// .claude/settings.local.json
{
  "claudeMdExcludes": [
    "**/monorepo/CLAUDE.md",
    "/home/user/monorepo/other-team/.claude/rules/**"
  ]
}

三、CLAUDE.md 如何与 System Prompt 结合

3.1 五层注入架构

Claude Code 采用分层上下文注入:

┌─────────────────────────────────────────────────────────┐
│ Layer 1: System Prompt                                  │
│   核心安全规则、工具定义、行为约束                          │
│   → 持久存在于整个会话                                    │
├─────────────────────────────────────────────────────────┤
│ Layer 2: CLAUDE.md + system-reminder                    │
│   项目约定,包裹在 <system-reminder> 标签中                │
│   → 每条消息都注入                                        │
├─────────────────────────────────────────────────────────┤
│ Layer 3: Slash Commands                                 │
│   用户触发的一次性工作流指令                                │
├─────────────────────────────────────────────────────────┤
│ Layer 4: Skills                                         │
│   按需加载的能力模块(从 SKILL.md 加载)                    │
├─────────────────────────────────────────────────────────┤
│ Layer 5: Sub-Agents                                     │
│   隔离执行环境,仅返回最终结果                              │
└─────────────────────────────────────────────────────────┘

3.2 具体注入方式

关键事实:CLAUDE.md 内容作为 user message 注入,位于 system prompt 之后,而非 system prompt 的一部分。

这是 Anthropic 官方文档明确说明的:

CLAUDE.md content is delivered as a user message after the system prompt, not as part of the system prompt itself.

注入时使用 <system-reminder> 标签包裹,并标注来源:

xml
<system-reminder>
  CLAUDE.md (project root):
  
  # CLAUDE.md
  本文件为 Claude Code 在本仓库中工作时提供指导。
  ## 常用命令
  ...
</system-reminder>

这种设计的含义:

  • <system-reminder> 标签告诉模型:这是上下文背景,而非强制指令
  • Claude 会尽力遵循,但不保证严格执行
  • 模型会根据当前任务的相关性自行判断是否采纳某条规则
  • 如果需要 system prompt 级别的强制注入,应使用 CLI 的 --append-system-prompt 参数

3.3 与 /compact 的交互

  • 项目根目录的 CLAUDE.md 在 /compact会从磁盘重新读取并重新注入
  • 子目录的 CLAUDE.md 不会自动重新注入,需等到 Claude 再次访问该子目录
  • 仅在对话中口头说的指令在 compact 后会丢失——这正是写入 CLAUDE.md 的价值

四、如何验证 CLAUDE.md 的有效性

4.1 基础验证:确认文件被加载

# 在 Claude Code 会话中执行
/memory

/memory 命令会列出当前会话加载的所有 CLAUDE.md、CLAUDE.local.md 和 rules 文件。如果文件未出现在列表中,说明 Claude 根本看不到它。

也可以使用 InstructionsLoaded Hook 来记录哪些指令文件被加载、何时加载、为什么加载:

json
// .claude/settings.json
{
  "hooks": {
    "InstructionsLoaded": {
      "command": "echo \"Loaded: $CLAUDE_INSTRUCTIONS_FILES\" >> /tmp/claude-instructions.log"
    }
  }
}

4.2 金丝雀测试(Canary Test)

在 CLAUDE.md 中放入一条容易验证的特殊规则:

markdown
## 编码约定
- 所有新函数必须以 `# MARK:` 注释标记所属模块

然后让 Claude 生成代码,检查是否遵循。如果这条规则被忽略,其他规则很可能也被忽略。

4.3 对比实验

步骤操作
1. 基线移除/重命名 CLAUDE.md,向 Claude 提问一个代码生成任务
2. 实验恢复 CLAUDE.md,用同样的提问重新开始会话
3. 对比检查生成代码的风格、命令使用、架构决策是否符合 CLAUDE.md 中的规则

可量化的指标:

  • token 效率:有 CLAUDE.md 时平均减少约 63% 的输出 token(减少不必要的解释和客套话)
  • 代码评审迭代次数:维护 CLAUDE.md 的团队减少约 34% 的代码评审轮次
  • 格式/命名错误率:减少约 80%

4.4 直接对话测试 vs CLAUDE.md 对比

  1. 在对话中直接告诉 Claude 一条规则,验证它能否遵循
  2. 把同样的规则只写在 CLAUDE.md 中,开启新会话验证

如果规则在直接对话中有效但在 CLAUDE.md 中无效,说明是注入/加载问题(文件位置错误、被排除等),而非规则本身的问题。

4.5 注意力衰减的诊断

CLAUDE.md 越长,后面的规则越容易被忽略。诊断方法:

markdown
# CLAUDE.md

## 规则 A(靠前位置)
所有变量用 snake_case

## 规则 B(靠后位置)
所有函数必须包含 docstring

分别让 Claude 生成代码,统计规则 A 和规则 B 的遵循率。如果 B 的遵循率明显低于 A,说明文件过长导致注意力衰减。

缓解策略:

  • 将最重要的规则放在开头
  • 控制总长度在 200 行以内
  • 将低频规则移到 .claude/rules/ 中按路径条件加载
  • 使用 @import 将详细内容拆分到外部文件

4.6 长会话中的持久性验证

在一个较长的会话中(20+ 轮对话后),再次要求 Claude 执行一个受 CLAUDE.md 规则约束的任务。如果规则不再被遵循:

  • 使用 /compact 压缩上下文(根目录 CLAUDE.md 会被重新注入)
  • 或开启新会话

这验证了 CLAUDE.md 在长会话中的衰减程度以及 /compact 的恢复效果。


五、总结

CLAUDE.md 不是配置文件,而是"给 AI 同事的备忘录"。

它的执行依赖模型的理解和遵循意愿,而非代码层面的强制。
写得越具体、越简洁、结构越清晰,被遵循的概率越高。

核心认知:

  1. 注入位置:作为 user message(非 system prompt),用 <system-reminder> 包裹
  2. 加载机制:向上遍历目录树拼接,子目录按需加载
  3. 非强制性:Claude "尽力遵循"而非"必须执行"
  4. token 成本:每次会话都消耗上下文窗口空间
  5. 有效性随长度递减:200 行以内效果最好,超过后注意力衰减显著