教程Claude Code系列第 3 / 7 篇

CLAUDE.md 怎么写:从 /init 生成,到让 Claude Code 真的照着做

CLAUDE.md 是 Claude Code 每次开工都会读的项目说明。本站在 Express 的源码上实测:/init 生成了什么,加三条规矩后它照没照做,按路径生效的规则什么时候加载,AGENTS.md 怎么被读到。并整理了官方对写什么、不写什么、放在哪的说明。

发布 · 约 11 分钟读完 · 经人工审核()· 本站的内容怎么来的

本站已实测,测试日期:2026-10-11。

本页目录(11 节)
  1. 第一步:用 /init 生成一份
  2. 第二步:确认它被读到了
  3. 第三步:加上你自己的规矩,看它照不照做
  4. 写什么,不写什么
  5. 放在哪:四个位置
  6. 只对一部分文件生效的规则
  7. 项目里已经有 AGENTS.md
  8. 它自己记的笔记:自动记忆
  9. 它不照做的时候怎么查
  10. 这一趟花了多少
  11. 这次实测没有覆盖到的

这篇教程解决一个问题:怎么让 Claude Code 不用你每次重复交代,就知道这个项目的规矩。 办法是在项目里放一份 CLAUDE.md。

每个会话开始时,Claude Code 的上下文都是空的,上一次聊过什么它不记得。CLAUDE.md 是它每次开工都会读的一份 Markdown 文件:测试怎么跑、代码怎么写、提交怎么提,写在这里就不用再说第二遍。

本站接着上一篇用的 Express 仓库,把生成、修改、验证这几步实际做了一遍。

实测环境
日期 2026-10-11
版本 Claude Code 2.1.296
系统 Ubuntu 24.04
模型 Opus 5.5
连接方式 通过酷站AI 中转站,不是直连官方
示例仓库 Express 的源码,v5.3.0

1用 /init 生成一份

在项目目录里启动 claude,输入:

/init

它跑了三条命令去读 package.json、lib/、测试、lint 配置和 CI 配置,42 秒后在项目根目录写出一份 51 行的 CLAUDE.md。开头是这样的:

# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

This is the Express.js framework itself (v5.x, CommonJS, Node.js >= 18), not an app built on it.

## Commands

- `npm test` — run the full mocha suite (`test/` and `test/acceptance/`) with `--check-leaks` and `test/support/env` preloaded
- `npm run lint` / `npm run lint:fix` — ESLint over the whole repo, including `examples/`
- `npm run test-cov` — coverage report via nyc (HTML in `coverage/`)
- Single test file: `npx mocha --require test/support/env --check-leaks test/req.subdomains.js`
- Single test by name: add `--grep "should return an array"` to the command above

Always include `--require test/support/env` when running mocha directly. It sets `NODE_ENV=test` and silences
`express`/`body-parser` deprecation warnings, and some tests depend on those settings.

后面还有三节:Architecture(架构)、Tests(测试怎么组织)、Conventions(约定)。其中有些内容是读代码才知道的,比如:

  • 路由、请求体解析、静态文件这些大部分在依赖包里,不在 lib/ 里,“这些地方的 bug 多半该去上游修”。
  • lint 规则禁止直接用全局的 Buffer。
  • CI 会测 Node 18 到 26,所以不要用 Node 18 不支持的语法。

三点观察:

  • 它生成的是英文。 我们只输入了 /init,没有说用什么语言。
  • 官方的定位是“起步版”。 文档说 /init 会分析代码库,写出它发现的构建命令、测试说明和项目约定;之后要靠你补上它自己发现不了的内容。已经有 CLAUDE.md 时,/init 是提出改进建议,不会覆盖。
  • 架构那一节写得偏细。 按下面官方的标准,“读代码就能知道的东西”其实不该放进来。这份文件拿来就能用,但值得删一删。

2确认它被读到了

官方给的检查办法是在会话里运行 /context,看 Memory files 那一栏。

这里有个容易踩的地方。刚生成完,在同一个会话里看 /context,是没有这一栏的,我们试了。CLAUDE.md 是在会话启动时加载的,要退出重开一个会话才看得到:

     Memory files · /memory
     └ CLAUDE.md: 2.4k tokens

2.4k 是它占的上下文(这个数是加了下一步那三条规矩之后量的)。每个会话都要付一次,所以文件不是越长越好。

3加上你自己的规矩,看它照不照做

/init 写的是它能从代码里发现的东西。你的工作习惯它发现不了,要自己加。我们在文件末尾加了三条:

## 本项目的工作约定

- 回答和总结一律用中文。
- 改完 `lib/` 下的文件,先跑 `npm run lint`,再只跑对应的那一个测试文件,不跑全量的 `npm test`。
- 提交说明用英文,一行,不超过 72 个字符,以 `fix:`、`docs:` 或 `test:` 开头。

重开一个会话,给一个小任务:

给 lib/request.js 里 req.subdomains 上面那段注释补一句,说明 hostname 是 IP 地址时会怎样。改完提交。

屏幕上的记录(有删节):

● Update(lib/request.js)
  ⎿  Added 3 lines
      379 + * If the hostname is an IP address, it is not split on dots and is treated
      380 + * as a single part, so req.subdomains is `[]` unless "subdomain offset" is 0.

● Bash(npm run lint 2>&1 | tail -5 && npx mocha --require test/support/env --check-leaks test/req.subdomains.js 2>&1 | tail -5)

● Bash(git add lib/request.js && git commit -q -m "docs: note req.subdomains behavior for IP hostnames…)
  ⎿  1fa3b2b docs: note req.subdomains behavior for IP hostnames

● 我在 lib/request.js 里 req.subdomains 上方的注释补了一段,说明 hostname 是 IP 地址时的行为……
  npm run lint 没有报错,test/req.subdomains.js 的 11 个测试全部通过。

三条都照做了:

我们写的规矩 它实际做的
用中文回答 总结是中文
先 lint,再只跑对应的测试文件 npm run lint,然后只跑了 test/req.subdomains.js
提交说明:英文、一行、72 字符以内、带前缀 docs: note req.subdomains behavior for IP hostnames

第二条的效果可以和上一篇对比:没有这条规矩时,它修完 bug 把 1261 个测试全跑了一遍;有了之后只跑了那一个文件。

要注意,这只是一次的结果。官方文档说得很直接:CLAUDE.md 的内容是当作上下文交给 Claude 的,不是强制执行的配置。它会读、会尽量照做,但不保证每次都严格遵守,含糊的和互相冲突的规则尤其如此。

写什么,不写什么

官方的标准是一句话:对每一行问一下,删掉它会不会让 Claude 犯错?不会就删。 文件臃肿了,真正要紧的那几条反而会被它忽略。

该写 不该写
它猜不到的命令 读代码就能知道的东西
和默认习惯不一样的代码风格 它本来就懂的语言惯例
测试怎么跑、用哪个测试工具 详细的 API 文档(给链接就行)
仓库的规矩:分支怎么命名、PR 怎么提 经常变的信息
这个项目特有的架构决定 长篇的解释和教程
开发环境的特殊之处,比如必须设的环境变量 逐个文件介绍代码库
常见的坑、不明显的行为 “写干净的代码”这类不用说的话

什么时候往里加。 官方列了四种信号:

  • Claude 第二次犯同一个错。
  • 代码审查时发现了它本该知道的事。
  • 你在对话里打了一句上次就打过的更正。
  • 新来的同事也需要知道这件事才能干活。

怎么写才管用。 规则要具体到可以检查:

含糊的写法 具体的写法
把代码格式化好 用 2 个空格缩进
改完要测试 提交前运行 npm test
文件要放整齐 API 的处理函数放在 src/api/handlers/

另外几条:

  • 每个文件控制在 200 行以内。 更长的文件占更多上下文,遵守的程度也会下降。
  • 用标题和列表组织,不要写成大段文字。
  • 不要自相矛盾。 两条规则冲突时,它可能随便挑一条。
  • 某一条总被跳过时,只给那一条加上 IMPORTANT 这样的强调。 每条都强调,就等于都没强调。
  • 提交进 git,让团队一起维护。

想让它帮你查问题,可以在会话里运行 /doctor prompt-audit。它会找出过时的、互相矛盾的、引用了不存在的文件或命令的指令,给出修改建议,你同意之后才会改。这个命令我们没有实测。

不该写进 CLAUDE.md 的两类东西

  • 只在某些时候才用到的步骤和知识,写成 skill。skill 是用到时才加载的,不会占每个会话的上下文。写法见Claude Code skills 怎么写。
  • 必须每次都执行的动作,比如每次提交前、每次改完文件后一定要跑的检查,写成 hook。hook 是在固定时机执行的命令,不管 Claude 怎么决定都会跑。

放在哪:四个位置

CLAUDE.md 可以放在几个地方,作用范围不同:

范围 位置 用来放什么 谁看得到
组织 Linux 上是 /etc/claude-code/CLAUDE.md,macOS 和 Windows 另有路径 公司的编码规范和安全要求,由 IT 管 组织里所有人
个人 ~/.claude/CLAUDE.md 你在所有项目里都适用的偏好 只有你
项目 ./CLAUDE.md 或 ./.claude/CLAUDE.md 团队共用的项目说明 通过 git 共享给团队
项目里的个人部分 ./CLAUDE.local.md 只有你自己用的项目设置,要加进 .gitignore 只有你

加载的规则,文档里有三点值得知道:

  • 这些文件是拼在一起的,不是互相覆盖。 从文件系统的根一路到你启动的目录,离你启动的位置越近的越靠后读。
  • 上级目录里的会在启动时加载;子目录里的是用到才加载,Claude 读了那个子目录里的文件之后才会读它。
  • 可以引入别的文件:在 CLAUDE.md 里写 @路径,比如 @docs/git-instructions.md。引入的文件也在启动时加载,所以它能让文件更好整理,但省不了上下文。

在会话里输入 /memory,可以看到当前读了哪些文件,并直接打开编辑:

  Memory

  ❯ Auto-memory  true

  ❯ Project instructions   Checked in at ./CLAUDE.md
    User instructions      Saved in ~/.claude/CLAUDE.md
    Open auto-memory folder

只对一部分文件生效的规则

有些规矩只和一部分文件有关,比如测试文件怎么写。把它们都塞进 CLAUDE.md,每个会话都要为它们付上下文。办法是放进 .claude/rules/ 目录,并在文件头部用 paths 写明对哪些文件生效。

我们建了一个 .claude/rules/tests.md:

---
paths:
  - "test/**/*.js"
---

# 测试文件的规矩

- 新增用例的描述以 `should` 开头,和现有用例保持一致。
- 只用 `node:assert` 和 `supertest`,不要引入新的断言库。

然后让它去处理一个测试文件:

看一下 test/req.subdomains.js 里有没有覆盖“hostname 是带端口的 IPv4 地址”的情况。没有的话补一个用例,不用提交。

它一读那个测试文件,屏幕上就多出一行:

  Read 1 file (ctrl+o to expand)
  ⎿  Loaded .claude/rules/tests.md

接着它补的用例是 it('should return an array with the whole IPv4 with port', ...),描述以 should 开头,没有引入新的库,12 个测试全过。

两个细节:

  • 这条规则不在 /context 的 Memory files 里。 那一栏列的是启动时加载的文件;按路径生效的规则是用到才加载的,看的是上面那行 Loaded。
  • 没写 paths 的规则文件会在启动时加载,和 .claude/CLAUDE.md 同等对待。

paths 用的是 glob 写法:**/*.ts 匹配任何目录下的 TypeScript 文件,src/**/* 匹配 src/ 下的所有文件,src/**/*.{ts,tsx} 可以一次匹配两种扩展名。

项目里已经有 AGENTS.md

如果仓库已经为别的编程工具写了 AGENTS.md,不需要再抄一份。按官方文档,从 2.1.277 版起 Claude Code 默认这样处理:

仓库里有 Claude 读什么
只有 AGENTS.md 读 AGENTS.md
AGENTS.md 和 CLAUDE.md 都有 只读 CLAUDE.md
CLAUDE.md 里引入了 AGENTS.md 读 CLAUDE.md,AGENTS.md 的内容通过引入带进来

第一种我们试了:在一个只有 AGENTS.md 的目录里启动,/context 显示的是

     Memory files · /memory
     └ AGENTS.md: 71 tokens

有一个坑文档专门提到:CLAUDE.local.md 也算数。 在一个靠 AGENTS.md 的项目里,你为了放点个人设置加了一个 CLAUDE.local.md,Claude 就不再读 AGENTS.md 了。想两个都读,在会话里输入 /config,把 Project instructions 改成 claude-md-and-agents-md。

它自己记的笔记:自动记忆

除了你写的 CLAUDE.md,Claude Code 还会自己记笔记,叫自动记忆(auto memory),默认是开着的。

CLAUDE.md 自动记忆
谁写 你 Claude
内容 指令和规则 它学到的东西:你的偏好、你纠正过它的地方、从代码里看不出来的项目背景
存在哪 项目里,可以提交 本机的 ~/.claude/projects/<项目>/memory/,不跨机器
加载多少 全部 索引文件 MEMORY.md 的前 200 行或前 25KB

它不是每个会话都记。文档说它会判断一条信息以后用不用得上,并且跳过能从代码里看出来的内容,也跳过 CLAUDE.md 里已经写了的内容。

我们的实测符合这个说法:前后几个会话做完,自动记忆的目录建出来了,但里面是空的。我们没有纠正过它什么,也没有说过自己的偏好。

要关掉它,在 /memory 里切换,或者在项目的设置里写 "autoMemoryEnabled": false。

它不照做的时候怎么查

按官方文档的排查顺序:

  1. 先确认它读到了。 运行 /context,看 Memory files。不在里面,它就看不到。
  2. 看位置对不对。 文件是不是在会被加载的位置;子目录里的 CLAUDE.md 要等它读了那个目录的文件才加载。
  3. 把规则写具体。
  4. 找有没有互相冲突的规则,包括不同位置的几份 CLAUDE.md 和 .claude/rules/ 之间。
  5. 看是不是和 Claude Code 自带的说明撞了。 比如你写了提交和 PR 的规矩,而它自己也有一套内置的;可以用 includeGitInstructions 关掉内置的,用 attribution 改署名。

如果一条规矩是“必须在某个时刻执行”,不要再改措辞了,把它写成 hook。

这一趟花了多少

生成之后的那个会话,做了两个小任务,/cost 显示:

  Total cost:            $0.2942
  Total duration (API):  45s
  Usage by model:
       claude-opus-5-5:  1.2k input, 2.2k output, 223.3k cache read, 40.2k cache write ($0.2942)

这是 Claude Code 按官方价格估算的数字,不是中转站实际扣的金额。

这次实测没有覆盖到的

  • 规则的遵守情况只看了一次。 三条规矩这一次都照做了,不代表每次都会。
  • 组织级的 CLAUDE.md、CLAUDE.local.md、@ 引入没有实测,按官方文档写的。
  • /doctor prompt-audit 没有实测。
  • 官方文档说只有 AGENTS.md 时,会话里会出现一行提示,写明加载了哪个文件。我们启动后没有发消息就退出了,没有看到这一行,只在 /context 里确认了它被加载。

来源

通过 API 中转站使用这些模型?到 Veridrop 免费检测中转站给你的是不是它声称的模型,或者看看中转站排行榜前 100 名。

相关文章