CLAUDE.md 与记忆:写好项目说明
每次开新会话都要重新交代「用 pnpm、接口统一包装返回值」?这一课讲清 CLAUDE.md 各层级的位置和加载顺序、/init 与 @ 导入的用法、自动记忆的分工,以及怎样写出一份短而有效的项目说明。
学完这一课,你能
- 能说出用户级、项目级、本地级 CLAUDE.md 的位置和加载顺序
- 能用 /init 生成初稿,并按「删掉会不会出错」的标准精简
- 能用 @ 导入和 .claude/rules/ 组织较长的说明
- 能分清哪些内容该写进 CLAUDE.md,哪些该交给自动记忆、Skill 或 hook
上一课说过,每个会话都从空白的上下文开始。于是很多人陷入一个循环:周一告诉 Claude「我们用 pnpm,不要用 npm」,周二它又跑了 npm install;你提醒「后端返回值统一用 Result 包一层」,第二天新会话里它又直接返回了裸对象。
问题不在模型记性差,而在于你没给它一个每次都会读的地方。这个地方就是 CLAUDE.md。
两套记忆:你写的和它记的
Claude Code 有两种跨会话保留信息的机制,每次会话开始时都会加载:
| CLAUDE.md | 自动记忆(auto memory) | |
|---|---|---|
| 谁来写 | 你 | Claude 自己 |
| 内容 | 指令和规则 | 它从你的纠正、偏好中学到的东西 |
| 存在哪 | 项目、用户、组织各层级 | ~/.claude/projects/<项目>/memory/,同一仓库的各 worktree 共用 |
| 加载多少 | 会话启动时加载的文件全部内容 | 索引 MEMORY.md 的前 200 行或 25KB |
自动记忆默认开启。你对它说「记住,跑 API 测试前要先起本地 Redis」,它就会存进自动记忆;你说「把这条加进 CLAUDE.md」,它才会去改 CLAUDE.md。输入 /memory 可以浏览、编辑这两类文件,也能开关自动记忆。
有一点必须清楚:这两者都只是上下文,不是强制配置。CLAUDE.md 的内容会作为一条消息交给模型,它会尽力遵守,但不保证。真正必须执行的规则,比如「绝不能改 .env」,要交给权限规则或 hook(见第 3 课和第 7 课Hooks 自动化)。
文件放在哪里,按什么顺序加载
CLAUDE.md 可以放在多个位置,作用范围不同:
| 层级 | 位置 | 用途 | 和谁共享 |
|---|---|---|---|
| 组织策略 | macOS:/Library/Application Support/ClaudeCode/CLAUDE.md;Linux 和 WSL:/etc/claude-code/CLAUDE.md;Windows:C:\Program Files\ClaudeCode\CLAUDE.md |
公司统一的规范、合规提醒 | 本机所有用户 |
| 用户级 | ~/.claude/CLAUDE.md |
你个人在所有项目里的偏好 | 只有你 |
| 项目级 | ./CLAUDE.md 或 ./.claude/CLAUDE.md |
团队共享的项目约定,提交到 git | 团队成员 |
| 本地级 | ./CLAUDE.local.md |
你在这个项目里的私人设置,加入 .gitignore |
只有你 |
加载规则可以记成三句话:
- 向上全部加载。 在
foo/bar/启动,foo/bar/和foo/以及更上层目录里的 CLAUDE.md、CLAUDE.local.md 都会在启动时读入。 - 拼接而不是覆盖。 所有找到的文件依次放进上下文,顺序是从文件系统根目录往下到你的工作目录,所以离启动目录越近的越晚被读到;同一目录里,CLAUDE.local.md 排在 CLAUDE.md 之后。整体看,组织策略最先,然后是用户级,再然后是项目级。
- 子目录按需加载。 工作目录下面子目录里的 CLAUDE.md 不在启动时加载,而是等 Claude 读写那个子目录里的文件时才加载。
如果仓库里已经有给其他编码工具写的 AGENTS.md,而工作目录及以上都没有 CLAUDE.md 或 CLAUDE.local.md,新版 Claude Code(v2.1.277 起)会直接读 AGENTS.md。两者都有时,默认只读 CLAUDE.md;想共用一份,可以在 CLAUDE.md 第一行写 @AGENTS.md 导入它。
想确认哪些文件真的被加载了,运行 /context,看 Memory files 下面的列表。
你在 monorepo 的 apps/admin/ 目录下启动 Claude Code。仓库根目录和 apps/admin/ 里各有一个 CLAUDE.md,内容里对缩进的要求不一样。会发生什么?
用 /init 起个头
在项目根目录启动 Claude Code,输入 /init。它会分析代码库,生成一份包含构建命令、测试方法和它发现的项目约定的初稿;如果已经有 CLAUDE.md,它会给出改进建议而不是覆盖。项目里有 .cursorrules、.cursor/rules/ 或 .github/copilot-instructions.md 时,/init 也会把其中相关的部分吸收进来。
想要更细致的流程,可以在启动前设置环境变量 CLAUDE_CODE_NEW_INIT=1。这时 /init 会先问你要配置哪些内容(CLAUDE.md、Skill、hook),用子智能体探索代码库,追问它拿不准的地方,最后给出一份可以审阅的方案再写文件。
/init 生成的只是起点。它能从代码里推断出来的东西,恰恰是最不需要写的;真正有价值的是它推断不出来的部分,需要你补上。
什么该写,什么不该写
官方给了一个很好用的判断标准:对每一行都问自己,删掉它,Claude 会不会因此出错? 不会,就删。
| 应该写 | 不该写 |
|---|---|
它猜不到的命令:pnpm dev:server、make seed |
读代码就能知道的东西,如目录结构、依赖列表 |
| 和语言默认习惯不同的代码风格 | 语言通用规范,如「变量名要有意义」 |
| 测试怎么跑、优先用哪个测试命令 | 详细的 API 文档(放链接或做成 Skill) |
| 分支命名、提交信息格式等协作约定 | 经常变化的信息 |
| 项目特有的架构决策和原因 | 长篇教程和背景介绍 |
| 开发环境的坑:必需的环境变量、本地服务 | 逐个文件的说明 |
| 不明显的陷阱:哪个目录不能动、哪里有历史包袱 | 「写干净的代码」这类空话 |
写法上,规则要具体到能验证:「用 2 空格缩进」比「格式要规范」好,「提交前运行 pnpm lint」比「注意质量」好。
一个好的 CLAUDE.md 示例
假设你在做一个后台管理系统:前端 Vue 3,后端 NestJS,pnpm monorepo。一份合格的 CLAUDE.md 大概是这样,不到 40 行:
# 项目:商城后台(pnpm monorepo)
## 常用命令
- 安装依赖:`pnpm install`(不要用 npm 或 yarn)
- 启动:`pnpm dev:admin`(前端 5173 端口)、`pnpm dev:server`(后端 3000 端口)
- 只测一个模块:`pnpm --filter server test -- order`,不要每次跑全量测试
- 提交前必须通过:`pnpm lint && pnpm typecheck`
## 代码约定
- 后端接口统一返回 `Result.ok(data)` 或 `Result.fail(code, msg)`,不要直接返回实体
- 金额一律用「分」存整数,前端展示时再除以 100
- 前端组件用 `<script setup lang="ts">`,状态管理用 Pinia,不引入 Vuex
- 注释和提交信息用中文;提交格式:`feat(order): 新增退款审核`
## 不要碰
- `apps/server/src/migrations/` 下已有的迁移文件只能新增,不能修改
- `packages/legacy-sdk/` 是对接旧 ERP 的历史代码,改动前先问我
## 环境的坑
- 本地 MySQL 必须是 utf8mb4,否则商品名里的表情符号会写入失败
- 支付回调在本地无法触发,用 `pnpm mock:pay` 模拟
## Compact Instructions
- 压缩上下文时,保留本次会话改过的文件列表和用过的测试命令
<!-- 维护者备注:这条注释会在加载前被去掉,不占上下文 -->
几个值得学的地方:命令写全,不让它猜;每条约定都能检查;「不要碰」写清边界;最后的 Compact Instructions 一节是官方建议的写法,告诉 Claude 在上下文被压缩时优先保留什么。块级 HTML 注释会在注入上下文之前被去掉,适合给人看的备注。
@ 导入:组织长文件,但不省上下文
CLAUDE.md 里可以用 @路径 引入其他文件,比如 参见 @README.md 和 @docs/git-flow.md。规则如下:
- 相对路径以写导入的那个文件所在目录为基准,不是以工作目录为基准;
- 被导入的文件还能继续导入,最多嵌套 4 层;
- 写在行内代码或代码块里的
@不会被导入,想单纯提到一个路径就用反引号包起来; - 路径里有空格时,在空格前加反斜杠;
- 项目级文件导入工作目录之外的文件(比如
@~/.claude/my-notes.md)时,第一次会弹窗让你确认,防止别人提交的文件偷偷引入你机器上的内容。
要特别注意:导入只帮你整理文件,不减少上下文消耗,被导入的内容会和 CLAUDE.md 一起全部加载。真正能省上下文的是两种做法:
- 路径规则:把规则放进
.claude/rules/下的.md文件,在开头的前置信息里写paths(如src/api/**/*.ts),只有 Claude 处理匹配的文件时才加载; - Skill:只在需要时加载的流程和参考资料,第 5 课Skill 入门细讲。
保持精简
官方建议每个 CLAUDE.md 控制在 200 行以内,越长越占上下文,遵守程度也越差。如果你发现 Claude 明明有规则却总是不照做,很可能是文件太长、规则被淹没了;如果它总问 CLAUDE.md 里已经写过的问题,可能是措辞有歧义。
几个维护习惯:
- 同样的错误出现第二次,再把规则加进去,不要预先写一堆;
- 某一条反复被忽略,只给这一条加「IMPORTANT」,全部加粗等于都没加粗;
- 定期运行
/doctor,它会建议删掉能从代码推断出的内容;/doctor prompt-audit则会检查过时、矛盾或引用了不存在文件的指令,只出报告,你同意后才改。
你的 CLAUDE.md 已经 400 行,你把其中 250 行挪到 docs/conventions.md,再在 CLAUDE.md 里写一行 @docs/conventions.md。对上下文占用有什么影响?
动手:让 Claude 帮你审一遍
请审查本项目的 CLAUDE.md(如果没有,先运行等同于 /init 的分析生成初稿,但不要写入文件)。
按下面的标准逐条检查,输出一张表格,列出「保留 / 删除 / 改写」和理由:
- 删掉这一条,你会不会因此犯错?不会就建议删除;
- 这条内容能否从代码、package.json 或配置文件里直接看出来?能就建议删除;
- 规则是否具体到可以验证?不够具体就给出改写后的版本;
- 是否只和某个目录有关?是的话建议移到 .claude/rules/ 并写好 paths。
最后补充你在阅读代码时发现、但文件里没写的坑(比如必需的环境变量、不能改的目录)。先给我看方案,我确认后再修改文件。
动手清单
0/5参考来源
- How Claude remembers your project(Claude Code 官方文档)
- Best practices for Claude Code(Claude Code 官方文档)
- Extend Claude Code(Claude Code 官方文档)
- Commands(Claude Code 官方文档)
工具更新很快,若与官方文档不一致,以官方为准。
学完了吗?
标记完成会记录在本机浏览器,计入你的学习进度和连续天数。