主题
05.6 CLAUDE.md 项目记忆
一次配置,长期收益。CLAUDE.md 是让 Claude Code "懂你项目"的核心机制。
读完你能做什么:写出一份既被遵守又不浪费上下文的项目记忆,并诊断"它为什么不听 CLAUDE.md"。
一、两套记忆系统
Claude Code 有两个互补的记忆机制,都在每次会话开头加载:
CLAUDE.md 文件 | Auto Memory(自动记忆) | |
|---|---|---|
| 谁写的 | 你 | Claude |
| 内容 | 指令和规则 | 学到的经验和模式 |
| 作用 | 编码规范、工作流、架构 | 构建命令、调试洞察、你的偏好 |
| 加载 | 每次会话全量 | 每次会话(前 200 行 / 25KB) |
分工:你想主动指导它 → 写 CLAUDE.md;让它从你的纠正中自己学 → 靠 Auto Memory。
二、CLAUDE.md 的位置与加载顺序
多个位置,从广到窄,按顺序加载(后加载的更靠近当前工作,权重更高):
| 范围 | 位置 | 用途 |
|---|---|---|
| 管理策略 | 系统级路径(IT 下发) | 组织级强制规范 |
| 用户 | ~/.claude/CLAUDE.md | 你个人所有项目的偏好 |
| 项目 | ./CLAUDE.md 或 ./.claude/CLAUDE.md | 团队共享(提交到 git) |
| 本地 | ./CLAUDE.local.md | 个人项目偏好(加 .gitignore) |
2.1 加载机制的三个关键点
- 向上遍历目录树 —— 在
foo/bar/启动,会加载foo/bar/CLAUDE.md、foo/CLAUDE.md及沿途的CLAUDE.local.md。 - 子目录的 CLAUDE.md 按需加载 —— 不在启动时加载,而是当 Claude 读该子目录的文件时才载入。
- 拼接而非覆盖 —— 所有找到的文件拼在一起,从根到工作目录排序,越近的越靠后(权重越高)。
2.2 验证到底加载了什么
text
> /context看 Memory files 这一节,确认你的 CLAUDE.md 真的被加载了。如果它不在那里,Claude 就看不见它。
text
> /memory浏览和编辑所有记忆文件。
三、怎么写一份好的 CLAUDE.md
3.1 该写什么
| ✅ 该写 | ❌ 不该写 |
|---|---|
| 构建/测试命令 | Claude 自己能从代码推断的(目录结构、依赖列表) |
| 与工具默认行为不同的约定 | 通用的编程常识 |
| 项目特有的坑和陷阱 | 冗长的架构叙述 |
| "永远做 X" / "永远不做 Y" 规则 | 只对一个子目录成立的规则(用 rules) |
| 重要决策及其理由 | 多步操作流程(用 Skill) |
3.2 一份高质量的示例
markdown
# 支付网关
## 构建与测试
- 构建:`make build`
- 测试:`make test`(需要本地 Redis,先 `make redis-up`)
- 单个测试:`go test ./internal/reconcile -run TestXxx`
## 绝对规则
- 金额一律用 `decimal.Decimal`,禁止 float64
- 不修改 `internal/legacy/` 下任何文件(等迁移完再动)
- `IPaymentCallback` 接口签名不能改(3 个外部系统依赖)
## 约定
- 错误用 `fmt.Errorf("...: %w", err)` 包装,保留错误链
- 新渠道适配器放 `internal/channels/<name>/`,实现 `Channel` 接口
- 数据库迁移用 `migrate`,不要手写 SQL 改 schema
## 已知坑
- 支付宝 SDK 的 client 非线程安全,不要缓存在 package 级变量
- 测试环境的银联 mock 经常挂,跑集成测试前先 `curl localhost:8081/health`
<!-- 维护者注释:这段 HTML 注释会被剥离,不占 Claude 的上下文 -->
## 提醒
再次强调:金额禁止使用浮点类型。3.3 三条硬性原则
| 原则 | 数值 | 理由 |
|---|---|---|
| 控制长度 | 目标 < 200 行 | 越长越占上下文,且遵守率越低 |
| 具体可验证 | "用 2 空格缩进" 而非 "格式规范" | 模糊指令等于没指令 |
| 无内部矛盾 | 定期清理 | 矛盾时 Claude 会随机选一条 |
四、用 rules 拆分大项目
当 CLAUDE.md 要写的东西超过 200 行,用 .claude/rules/ 拆分:
text
your-project/
├── .claude/
│ ├── CLAUDE.md # 主指令(精简)
│ └── rules/
│ ├── code-style.md # 编码风格
│ ├── testing.md # 测试约定
│ └── api-design.md # API 设计4.1 路径作用域规则(关键特性)
用 frontmatter 的 paths 字段,让规则只在处理匹配文件时加载:
markdown
---
paths:
- "src/api/**/*.ts"
---
# API 开发规则
- 所有 endpoint 必须做输入校验
- 用统一的错误响应格式
- 加 OpenAPI 文档注释这条规则只在 Claude 读 src/api/ 下的 .ts 文件时才进上下文。其他时候零成本。
支持多模式和 brace 展开:
markdown
---
paths:
- "src/**/*.{ts,tsx}"
- "tests/**/*.test.ts"
---没有 paths 的 rules 无条件加载,优先级同 .claude/CLAUDE.md。
4.2 rules vs CLAUDE.md vs Skill
text
每次都要遵守的事实 → CLAUDE.md
只对部分文件成立的规范 → .claude/rules/(带 paths)
按需触发的多步流程 → Skill五、Auto Memory
Claude 会自己记录跨会话有用的东西:构建命令、调试经验、你的偏好。
5.1 存储位置
text
~/.claude/projects/<project>/memory/
├── MEMORY.md # 索引,每次会话加载前 200 行 / 25KB
├── debugging.md # 详细笔记,按需读
└── ...MEMORY.md 是索引;详细内容在主题文件里。注意 200 行 / 25KB 上限 —— 超出部分下次加载会被丢弃,所以要保持 MEMORY.md 精简。
5.2 开关与管理
json
// 项目级关闭
{ "autoMemoryEnabled": false }bash
# 环境变量关闭
CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 claudetext
> /memory # 查看、编辑、开关5.3 主动喂给它
text
> 记住:这个项目的集成测试需要先启动本地 Redis它会存到 auto memory。想存进 CLAUDE.md 而非 auto memory,明确说:
text
> 把"金额禁止用浮点"这条加到 CLAUDE.md 的绝对规则里六、导入其他文件
CLAUDE.md 可以用 @path 语法导入:
markdown
See @README for overview and @package.json for available commands.
# 额外指令
- git 工作流 @docs/git-instructions.md- 相对路径基于文件所在位置
- 最多 4 层递归
- 想提到路径但不导入,用反引号包起来:
`@README`
跨 worktree 共享个人偏好,从 home 目录导入:
markdown
# 个人偏好
- @~/.claude/my-project-instructions.md(首次遇到项目内的外部导入会弹确认对话框,防止别人往共享项目里塞恶意导入。)
6.1 兼容 AGENTS.md
如果你的仓库用 AGENTS.md(其他 AI 工具的标准),Claude Code 只读 CLAUDE.md。让它也读 AGENTS.md:
markdown
@AGENTS.md
## Claude Code 专属
在 src/billing/ 下的改动用 plan 模式。七、诊断"它不听 CLAUDE.md"
这是最常见的困惑。按顺序排查:
7.1 它到底加载了吗
text
> /contextMemory files 里没有你的文件 = 位置不对或没被发现。用 /memory 打开确认。
7.2 指令够具体吗
text
❌ "格式化代码" → 改成
✅ "用 gofmt 格式化,导入分组:标准库、第三方、本项目"7.3 有矛盾吗
检查用户级、项目级、.claude/rules/ 之间有没有打架的指令。矛盾时 Claude 会随机选。
7.4 它本质上是"上下文"不是"配置"
关键认知:CLAUDE.md 作为系统提示词之后的用户消息递送,不是系统提示词本身。Claude 会读它、尽力遵守,但不保证严格合规。
因此:
text
"倾向性"规则(风格、偏好) → CLAUDE.md 够用
"必须 100% 执行"的规则(安全门禁) → 用 hook,不要指望 CLAUDE.md比如"每次提交前必须跑测试",写在 CLAUDE.md 里是概率性的;写成 PreToolUse hook 匹配 Bash(git commit *) 才是确定性的。见 06.5。
7.5 /compact 后指令丢了
项目根目录的 CLAUDE.md 在 /compact 后会重新从磁盘注入。但子目录的嵌套 CLAUDE.md 不会自动重注入,要等下次读该目录文件时。如果一条指令压缩后消失,它可能只是在对话里说过(没进 CLAUDE.md),或者在还没重新加载的嵌套文件里。
八、用 /doctor 精简
v2.1.206+ 的 /doctor 会主动为已提交的 CLAUDE.md 提精简建议:
text
> /doctor它会砍掉 Claude 能从代码库自己推导的内容(目录布局、依赖列表、架构概览),保留它推导不出来的(坑、决策理由、与默认不同的约定)。
九、一份可复制的起步模板
markdown
# <项目名>
## 构建与测试
- 构建:`<命令>`
- 测试:`<命令>`(<前置条件>)
- 单测:`<命令>`
## 绝对规则
- <永远/禁止 X>
- <永远/禁止 Y>
## 约定
- <命名/结构约定>
- <错误处理约定>
## 已知坑
- <坑 1 及规避方法>
## 提醒
<最重要的一条规则,在此重申>