Skip to content

写好 CLAUDE.md / AGENTS.md

几乎每个 coding agent 上手的第一件事,都是在仓库根目录放一个 Markdown 文件:Claude Code 读 CLAUDE.md,Codex、Jules、Cursor、Amp 等一大批工具读 AGENTS.md,Aider、Gemini CLI 可以通过配置接入同一个文件。这个文件看起来 trivial——纯文本,没有 schema,没有校验——但它是你对 agent 行为性价比最高的杠杆:写得好,agent 每个会话都像一个已经入职三周的新人;写得差,它要么被无视,要么把真正重要的规则淹死在噪音里。

Claude Code 案例已经讲过这套机制的「是什么」(加载顺序、惰性加载、与 hooks 的对举)。本文只回答一个问题:这份文件怎么写好。 结论先行:把它当作代码来写——小步提交、定期重构、删掉比加上更需要理由。

本质:一层持久化的提示层,而不是配置

先纠正一个广泛存在的误解。名字里带「md」、放在仓库根目录、入版本库——这些特征让人以为 CLAUDE.md 是类似 .eslintrc配置文件。它不是。

官方文档说得很清楚:CLAUDE.md 的内容在每次会话启动时,作为 user message 注入到系统提示之后的上下文里;模型会读它、尽量遵守它,但没有任何强制保证。它是上下文,不是配置("context, not enforced configuration")。想要真正拦死一个动作——比如禁止写入 migrations/ 目录——正确工具是 PreToolUse hook 或权限规则,它们以退出码说话,不经过模型的判断。

所以这类文件的准确定位是:一层持久化的提示层(persistent prompt layer)。它和你每次在对话框里敲的指令走同一条通道、服从同一套「模型可能不听」的概率逻辑,唯一的区别是它每个会话都在场。这个定位直接推出本文后面的几乎所有写作原则:

  • 因为它走提示通道,所以写法影响遵守率——模糊、冗长、自相矛盾的指令会被任意取舍;
  • 因为它每会话都在场,所以每一行都是常驻成本——对每一轮对话收 token 税、稀释注意力;
  • 因为它没有强制力,所以安全红线不能只写在这里——硬约束必须落到 hooks 和权限层

一个推论

「我在 CLAUDE.md 里写了不许 X,它还是干了 X」不是 bug,是机制的正常表现。调试顺序应该是:先 /context 确认文件真的加载了,再把指令改得更具体,最后——如果这是零例外的硬要求——把它从 CLAUDE.md 降级成一句提醒、升级成一条 hook。

写什么,不写什么

Anthropic 官方最佳实践给出了一张直接的取舍表,本节在它的基础上展开。判断标准可以用一句话概括:这份文件应该只装「模型读代码推不出来、但每个会话都需要知道」的东西。

该写的四类:

  • 命令速查:构建、测试、lint、本地起服务的准确命令——尤其是非标准的那部分(pnpm test --filtermake dev-docker)。模型猜不对的东西才值得写。
  • 项目约定:与语言默认惯例不同的代码风格(「用 ES modules,不用 CommonJS」)、目录边界(「API handler 都放 src/api/handlers/」)、提交与分支纪律。
  • 边界与禁区:不许动的目录、不许跑命令的环境、部署前必须先做什么。写禁区时附上原因或替代路径,遵守率显著高于裸禁令。
  • 工作流偏好:改完代码先跑哪个检查、单测优先于全量测试、遇到模糊需求先问还是先给方案。

不该写的四类:

  • 教程与解释。CLAUDE.md 不是新人培训文档。「我们的架构采用 CQRS 模式,因为……」这类内容模型不需要每次都读;需要时它会自己读代码。
  • 显而易见的废话。「写干净的代码」「做好错误处理」「保持文件组织良好」——这类指令没有可验证的判据,模型无法执行,只会稀释其他规则的权重。官方的对比很直白:「Use 2-space indentation」有效,「Format code properly」无效。
  • 模型自己能推出的东西。目录结构、依赖清单、技术栈——lspackage.json 里都有。官方的 /doctor 修剪检查砍的正是这类内容,留下的是坑、理由和与默认不同的约定。
  • 会过期的事实。版本号、当前负责人、本周的临时方案。过期事实比没有事实更糟:模型会一本正经地遵守一条已经不再成立的规则。

一行一测

官方最佳实践给了一个极简的删减判据:对每一行问「删掉这一行,Claude 会犯错吗?」如果不会,删。反过来,官方文档给的增补判据同样简单:同一个错误犯了第二次、code review 抓到模型本该知道的事、你把同一句纠正敲了两遍——这时才往文件里加。

长度与信噪比:为什么越短越有效

官方 memory 文档给出了明确的长度目标:每个 CLAUDE.md 文件控制在 200 行以内,更长的文件消耗更多上下文且降低遵守率。这不是随口的建议,背后有两层机制:

第一层是 token 税。 CLAUDE.md 在每次会话启动时全量进入上下文,和你后续的每一轮对话一起反复参与计费与注意力分配。一份 800 行的文件意味着每次会话还没开始干活就先付掉一笔固定成本,而且成本随对话轮次复利。

第二层是注意力稀释。 这是更要命的一层。规则越多,单条规则被遵守的概率越低——官方最佳实践直接警告:「如果你的 CLAUDE.md 太长,Claude 会无视其中一半,因为重要规则被噪音淹没了」。常见症状是:你明明写了禁令,它照犯不误——不是模型不听话,是那条规则在三百行文本里根本没有被「看见」的权重。这正是上下文工程反复强调的:长上下文不等于有效上下文,注入的信息量与指令遵循度不是正相关,超过某个点之后是负相关。

两个值得记住的实操结论:

  • 想用 IMPORTANTYOU MUST 加权重?可以,但省着用。 官方承认强调词能提高遵守率——但强调是通货膨胀,全文大写等于全文没有大写。
  • 拆分不能降成本。 @import 导入的文件同样在启动时全量加载,拆成十个文件只是便于人维护,上下文里一个字节都没少。真正降成本的手段是按路径触发(下文的分层组织),让规则只在相关文件被碰时才进场。

分层组织:就近覆盖,按需加载

单个文件装不下所有指令时,正确的做法不是写长,而是分层。截至 2026 年中,Claude Code 的分层体系是这样的(越靠下越具体、越晚出现在上下文里):

text
┌──────────────────────────────────────────────────────────┐
│ 组织级  managed policy(/etc/claude-code/CLAUDE.md 等)    │  ← IT 下发,不可排除
│ 用户级  ~/.claude/CLAUDE.md + ~/.claude/rules/           │  ← 个人偏好,全项目生效
│ 项目级  ./CLAUDE.md 或 ./.claude/CLAUDE.md               │  ← 入 git,团队共享
│ 本地级  ./CLAUDE.local.md                                │  ← gitignore,个人专用
│ 目录级  子目录里的 CLAUDE.md / .claude/rules/(带 paths)   │  ← 惰性加载:模型读到
└──────────────────────────────────────────────────────────┘     那个目录的文件才注入

加载顺序是从文件系统根部向工作目录走,发现的文件拼接而非覆盖——越靠近启动位置的指令越后出现。这个设计有两个直接的写作推论:

  • 就近原则放规则:只对 src/api/ 生效的约定,放进 src/api/CLAUDE.md 或一条带 paths: ["src/api/**/*.ts"] 的 rule,不要污染根目录文件。子目录文件在模型实际读到那个目录的文件时才进入上下文——不为用不到的指令付 token。
  • 冲突要显式。多个层级的规则打架时,模型会任意挑一个遵守。官方建议是定期审查各层文件消除矛盾;与其指望模型猜,不如在文件里写明「X 与 Y 冲突时以 X 为准」。

@path 导入语法解决的是另一类问题:单一信息源。README、package.json、团队的 git 工作流文档都可以被 @ 链进来(相对路径相对于包含导入的文件解析,最大递归四层),避免同一份约定抄两份然后各自腐化。跨 worktree 的个人配置可以 @~/.claude/my-project-instructions.md;首次遇到指向仓库外的导入会有确认弹窗,这是针对「别人提交的共享项目」的安全闸。

反面案例:一份典型的坏 CLAUDE.md

下面这份文件在现实中随处可见(拼接自常见的失败模式),先看它,再看改写:

markdown
# 项目说明

欢迎使用我们的项目!这是一个现代化的全栈应用,采用 React 17 + Node.js,
致力于为千万用户提供卓越的体验。

## 重要提示!!!
- 你必须写干净的代码!!
- 所有代码必须有完善的错误处理
- 记得写注释,注释是程序员的美德
- IMPORTANT: 保持代码优雅
- 永远不要写烂代码

## 架构
我们的系统采用微服务架构。2023 年 Q3 我们完成了从单体到微服务的
迁移,当时由张工主导……(此处省略 40 行历史回顾)

## 注意
- 数据库相关的东西要小心
- 测试要写好

它几乎犯了所有能犯的错:教程式开头(模型不需要被欢迎);会过期的事实(React 版本、人事信息);没有判据的废话(「写干净的代码」);空洞的警告(「要小心」——怎么小心?);滥用强调(全文 IMPORTANT 等于没有 IMPORTANT);以及大段与任务无关的历史叙事。这份文件里唯一可能有效的信息——如果有的话——已经被淹没了。

改写后:

markdown
# 命令
- 构建:`pnpm build`;单测:`pnpm vitest run -t "<用例名>"`(不要跑全量,太慢)
- 起本地环境:`make dev`(需要先 `docker compose up db redis`

# 约定
- ES modules(import/export),禁用 CommonJS require
- API handler 统一放 `src/api/handlers/`,返回标准错误格式(见 `src/api/errors.ts`
- 提交信息用 Conventional Commits

# 禁区
- 不要改 `migrations/` 下已合入的迁移文件;需要变更时新建迁移
- 不要在代码里写密钥;本地调试用 `.env.local`(已 gitignore)

# 工作流
- 改完代码必须跑 `pnpm typecheck`,红了不许说"完成"
-`src/api/` 下文件时同步更新 `docs/openapi.yaml`

对比两版:每一行都满足「删掉模型就会犯错」的判据;每条规则可验证(命令能跑、路径能查、检查有退出码);禁区附带了替代路径;长度从一屏多压到二十行。

团队共享与版本管理

项目级 CLAUDE.md 应该进 git,这是它与个人笔记的本质区别:它是团队的共同资产,随代码库一起演化。官方最佳实践的说法是「像对待代码一样对待它」——出问题时 review 它,定期修剪,改完观察 agent 行为是否真的变化。由此有几个团队场景的标准解法:

  • 多人多工具共存:AGENTS.md 是目前唯一跨工具的开放约定(下节展开),把共享约定放 AGENTS.md,工具专属内容放各自文件。
  • monorepo:各子项目放各自的嵌套文件;别的团队的 CLAUDE.md 会被祖先目录查找捞进来,用 claudeMdExcludes 排除掉,避免指令串台。
  • 个人偏好不进共享文件:沙箱地址、个人测试数据之类写 CLAUDE.local.md 并加入 .gitignore
  • 冷启动:会话里跑 /init 会分析代码库生成一份初始文件(已存在时给改进建议而不是覆盖);截至 2026 年中,/doctor 还能对已入库的 CLAUDE.md 提出修剪建议——砍掉模型自己能推出的内容,保留坑和约定。生成物只是起点,价值在于之后每次「它又犯了这个错」时的增量维护。

AGENTS.md:跨工具的公共层

CLAUDE.md 是 Claude Code 的私有约定;AGENTS.md 则是 2025 年由 OpenAI Codex、Amp、Google Jules、Cursor、Factory 等共同推出的开放格式,定位是「给 agent 看的 README」——一个放构建命令、测试说明、代码约定的可预期位置。截至 2026 年中,它已被超过 6 万个开源项目采用,并交由 Linux 基金会旗下的 Agentic AI Foundation 托管。

几个值得知道的机制差异:

  • 纯 Markdown,无 schema,没有任何必填字段—— agent 直接解析文本。
  • 就近覆盖:嵌套的 AGENTS.md 里,离被编辑文件最近的那份生效;用户当轮的显式指令高于一切文件。agents.md 官方举例称,OpenAI 主仓库里就有 88 份嵌套的 AGENTS.md。
  • Claude Code 原生不读它。双工具团队的标准做法是建一份 AGENTS.md 作为共享层,再让 CLAUDE.md 用 @AGENTS.md 导入它(下面还可以追加 Claude 专属指令);不需要追加内容时,一个符号链接 ln -s AGENTS.md CLAUDE.md 就够了。Aider 在 .aider.conf.yml 里配 read: AGENTS.md,Gemini CLI 在 settings 里指定 fileName,都能接入同一份文件。

策略建议:通用约定写 AGENTS.md(面向所有 agent 和未来的工具),工具专属行为写各自的私有文件。这和对编程语言的态度一样——押注开放层,隔离私有层。

与 skills、hooks 的分工

CLAUDE.md 不是知识注入的唯一通道,写之前先确认这条知识真的属于这里。三个机制的分工一句话说清:规则文件管「每次都生效的约束」,skill 管「按需激活的流程」,hook 管「零例外的保证」。

CLAUDE.md / 规则文件SkillHook
性质建议性(advisory)建议性确定性(deterministic)
注入时机会话启动常驻 / 路径触发模型判断相关时才加载不注入,生命周期事件上执行脚本
适合命令速查、约定、禁区多步操作流程、发版清单、领域 know-how每次编辑后必跑 lint、禁止写某目录
成本结构每会话收税平时零成本待命零上下文成本

典型误用是把一份八步的发版流程写进 CLAUDE.md——它只在发版时相关,却对所有会话收税;正确做法是封成 skill(详见 Skills)。反向误用是把安全红线只写进 CLAUDE.md——模型可能不遵守,硬约束必须落到 hook 或权限规则。判断的依据回到本文开头:CLAUDE.md 是提示层,提示层的承诺是「尽量」,不是「保证」。

可直接套用的模板

把上面的原则压缩成一份骨架,新项目可以从它开始,再按「犯了第二次错才加」的纪律生长:

markdown
# <项目名>
一句话说明这个项目是什么、给谁用。(就一句,多一句都删)

# 命令
- 安装依赖:`<cmd>`
- 构建:`<cmd>`;测试:`<cmd>`(跑单个用例的方式:`<cmd>`
- Lint / typecheck:`<cmd>`
- 本地起服务:`<cmd>`(前置条件:<如果有>)

# 约定
- <与语言默认不同的代码风格,一条一行>
- <目录边界:什么东西必须放哪>
- <提交 / 分支 / PR 纪律>

# 禁区
- 不要 <动作>;需要时改走 <替代路径>
- 不要动 <目录/文件>,原因:<一句话>

# 工作流
- 改完代码必须通过 <检查命令> 才算完成
- <遇到某类任务时的偏好:先问 / 先给方案 / 先写测试>

# 其他 agent 的共享约定见 @AGENTS.md(如有)

写完后做三件事:跑 /context 确认加载;数数行数(超过 200 行就分层或删减);把它提交进 git,让团队一起养。

最后一条元规则

这份文件的最佳长度不是写出来的,是修剪出来的。初版永远会过长——先用起来,每次发现「这条它本来就会」「这条已经过期」就删一行。一个季度后剩下的东西,才是你的项目真正需要告诉 agent 的。

延伸阅读

  • Claude Code 案例——CLAUDE.md 的加载机制、与 hooks 的对举,本文的机制背景
  • 上下文工程——为什么「越短越有效」:token 预算与注意力稀释的完整讨论
  • 技能与知识注入——静态注入、RAG、skill 三条通道的取舍,.claude/rules/ 与 skill 的分工
  • 权限与人机协作——提示层兜不住的硬约束如何实现
  • 记忆系统——CLAUDE.md 与 auto memory、更重记忆架构的对比
  • 设计原则——从本文延伸到整个 harness 设计的可迁移原则

参考资料