CLAUDE.md 太长怎么办:我重新拆了一遍 AGENTS.md、Skills 和 CI 的边界
如果你长期用 Codex、Claude Code 这类 Coding Agent,项目里的 AGENTS.md、CLAUDE.md 很容易越写越长。这个过程通常不是谁一开始设计错了,反而经常是因为你真的用得多:Agent 改多了文件,就补一句“不要动无关范围”;测试没跑就说完成,再补一句“必须拿真实输出证明”;某次数据库迁移把失败现场清掉了,再补一条禁止自动清理。每一条单独看都合理,最后根文件却越来越像一份混着开发规范、事故复盘、工作流和提醒事项的内部手册。
我自己真正开始在意这个问题,也不是因为多几百行 Markdown 看着不舒服,而是发现这些规则一旦全部常驻,很多原本只属于某个目录、某类任务的东西,也会跟着每一次任务一起出现。你只是改一段文档,前端组件边界、发布流程、协议兼容、数据库迁移注意事项可能都还在;再往后,根文件写一遍,Skill 里写一遍,docs 里又解释一遍,CI 里还有真正执行的一份,慢慢就会变成“大家都在管,但谁都不是唯一 owner”。
前几天我在 GitHub 上看到 Nession 的 issue #1437,它正好把这个问题做了一次比较彻底的迁移。这个仓库原来的根 CLAUDE.md 大约 760 行,里面塞了非常多项目级和任务级规则。作者后来没有继续在原文件上做删减,而是把 root、局部规则、Skills 和可执行 Gate 重新分了层。对应的实现最后合进了 PR #1442。
这个思路我觉得很对,但我不太想直接把它改写成“某个仓库用了四层架构,所以大家也应该这样”。这种文章写起来很快,信息增量却不大。我更想知道的是:把规则拆开以后,默认常驻的东西到底少了多少;这些边界能不能被 CI 检查;以及现在 Codex 和 Claude Code 自己的加载规则,和很多旧文章里讲的还是否一样。
所以这篇不是把 Nession 的 issue 翻成中文。我先拿它迁移前后的固定 commit 做了一次只读分析,又在自己的 Content Lab 里写了一个很小的 instruction-contract validator。做到后面,我反而越来越觉得,真正值得保留的不是“CLAUDE.md 最好多少行”,而是另一个更简单的判断:这条规则到底应该由谁拥有,什么时候才需要加载,它能不能交给机器验证。
我真正想解决的不是“文件太长”
如果只是嫌 760 行太长,最容易的办法当然是拆文件。但把 700 行从 CLAUDE.md 挪到另一个每次都要读的文件里,数字是变漂亮了,问题其实一点没解决。
我现在更愿意把根指令理解成“几乎任何任务都绕不过去的最小公共层”。项目最核心的目标、安全边界、指令从哪里继续往下找、所有改动都必须遵守的验证原则,这些可以放在 root。可一条规则如果只在 web/ 目录里成立,那它更适合跟着这个目录走;如果它讲的是“发布一次 staging 应该做什么”“做 code review 应该怎么走”,那它本质上是任务流程,放进 Skill 会更自然;至于像“根文件不能无限增长”“Skill 名不能重复”“某个兼容链接不能失效”这种能被程序判断的事情,我现在反而不愿意继续只写成一句提醒,而是希望 CI 直接挡住。
也就是说,我最后并没有把问题理解成“AGENTS.md 和 CLAUDE.md 应该怎么分”,而是把它看成一张 ownership graph。根文件只是其中一个 owner,目录级 instruction 是另一个 owner,Skill 负责把重复工作流串起来,docs 保存那些不该常驻但以后还要查的解释和背景,能确定性判断的东西则尽量交给代码。
这个边界其实比“所有经验都做成 Skill”更实用。Skill 很容易被用成另一本百科全书,最后只是把 root 的臃肿搬家。我现在判断一条规则放哪,通常先问几个很土的问题:如果我只是改 README,它还必须出现吗?如果只有进入某个目录才需要,为什么不让它靠近那个目录?如果它是一套会反复执行的流程,为什么不让 Skill 来负责?如果机器已经能判断对错,又为什么要每次都拜托 Agent “请务必记得”?
Nession 这次迁移让我觉得有意思的地方,也正在这里。它迁移后的知识并没有消失,而是重新有了位置。root 变小了,局部 owner 反而更多了,这和单纯“删文档”完全是两件事。
我拿迁移前后两个版本重新测了一遍
为了避免直接引用 PR 里的自报数据,我没有运行 Nession 自己的脚本,而是用 GitHub API 读取它两个固定 SHA 的 tree 和 blob。迁移前是 2d57f38ca917a327e5e4bdd7545d1e5dc90bc0bb,迁移后是 7d89c460ea302ea3eeb4380341055b6f2a1da39c。分析器只读文件内容,不 checkout,也不执行第三方 hook、package script 或 workflow。
重新算出来以后,root instruction 从 760 行降到了 116 行,减少约 84.74%;字符数从 55,160 降到 5,734。8 个主要 Skill 的入口文件也从合计 4,079 行变成 1,327 行,少了约 67.47%。
| 指标 | 迁移前 | 迁移后 | 降幅 |
|---|---|---|---|
| root instruction 行数 | 760 | 116 | 84.74% |
| root decoded characters | 55,160 | 5,734 | 89.60% |
| 8 个 Skill 主入口总行数 | 4,079 | 1,327 | 67.47% |
| 8 个 Skill 主入口字符数 | 161,324 | 42,710 | 73.53% |
但这张表最容易被误读。它不代表迁移后项目只剩 116 行“规则”,更不代表 token 成本就按 84% 的比例下降。迁移后的仓库里,root、Web、protocol、agent runtime、design、scripts、GitHub workflows、docs 都有自己的 canonical AGENTS.md。知识还在,只是不再默认都挤在一个入口里。
我反而觉得这才是 progressive loading 真正有价值的地方。你不是在比赛谁的 CLAUDE.md 更短,而是在尽量减少“每次任务都必须常驻”的内容。一个前端任务需要前端约束时再去读前端 owner,一个 CI 任务需要发布规则时再把那部分带进来。对长期维护来说,这种局部性比单纯压行数重要得多。
这次我也顺手把结构本身做成了一个小实验。Content Lab 里现在有一个和 Nession 路径无关的 validator,它只看一张抽象的 instruction graph:root 有没有超预算,CLAUDE.md 兼容面是不是又变成了可编辑副本,本地链接有没有断,Skill 名有没有冲突,frontmatter 是否缺字段,目录里只有兼容文件却没有 canonical owner,symlink 是否已经指向不存在的位置。
我给这些情况各做了正负 fixture,8 组测试全部按预期触发了对应错误。这个结果谈不上复杂,但对我来说反而很重要,因为它说明“保持指令架构干净”不一定要靠人记。很多最容易慢慢腐化的地方,其实几十到几百行普通代码就能守住。
真正麻烦的是,Codex 和 Claude Code 的规则还在变化
做到这里以后,我本来很容易顺手写出一个看起来很漂亮的目录:
AGENTS.md
CLAUDE.md -> AGENTS.md
.agents/skills -> .claude/skills
Nession 自己现在确实有类似的兼容面,但我最后没有把它当成文章里的“标准答案”,因为当前 Codex 和 Claude Code 的行为已经比很多旧文章里讲的复杂。
OpenAI 现在的 Codex prompting guide 明确说明,Codex 会从全局位置和仓库 root 一路向当前工作目录读取 AGENTS.md,越靠近工作目录的 instruction 越晚进入上下文。所以“root 保持最小,目录里再放 scoped owner”这条路是有官方行为基础的。
Skill 目录就没那么适合写死了。OpenAI 当前不同资料里能看到不止一种 repo-scope 的组织方式,比如 Skill eval 文章 和 Claude Agent SDK migration guide 里出现的路径并不完全一样。对我来说,这反而强化了前面的判断:真正该稳定的是内容 owner 和兼容关系,不是某个目录名。
Claude Code 这边变化也很明显。现在的 memory / instruction 文档 已经支持直接读取 AGENTS.md,同时仍然建议 CLAUDE.md 保持精简,官方给出的目标大约是 200 行以内;路径相关规则可以交给 scoped rules,多步骤流程则更适合放 Skills。Skills 文档 也明确是按需加载主体内容,而不是把所有 Skill 正文一直塞在上下文里。
所以“为了兼容 Claude,所有项目都必须维护 CLAUDE.md -> AGENTS.md”现在已经不是一个可以随手下的结论。如果项目同时存在 CLAUDE.md 和 AGENTS.md,加载行为还和版本以及 Project instructions 配置有关;symlink 在 macOS/Linux 下很顺手,到了 Windows 又要考虑 Git symlink 的 checkout 和编辑问题。
这也是为什么我现在更愿意维护一张很小的 compatibility matrix,而不是维护两份几乎一样的指令文件。哪边是 canonical owner,哪边只是 adapter,当前客户端版本实际会发现哪些 Skill 路径,Windows 是否需要换成脚本生成副本,这些都应该是明确的工程决策。路径只是实现细节,owner 才是架构。
如果现在让我重新整理一个仓库,我会先管“谁负责”,再管“写什么”
如果是一个刚开始用 Agent 的小项目,我不会一上来就搭十几个目录,也不会为了看起来“专业”把 root 拆得很碎。几十行 AGENTS.md 能说清楚的东西,就先让它保持几十行。
真正值得动手重构,通常是当你开始反复遇到几种信号:根文件越来越像内部手册;同一条规则在 root、Skill、docs 里来回复制;不同目录的要求互相干扰;或者每次 Agent 出错以后,你唯一的处理方式还是“再往 prompt 里补一句”。
到了这个阶段,我会先把规则逐条问一遍“谁负责”。所有任务都需要的,留在 root;只跟某个目录有关的,放到那个目录附近;重复执行的动作,交给 Skill;很长的解释和背景,回到 docs;能确定性判断的约束,直接写成 validator 或 CI。这个过程看起来像是在整理 Markdown,实际是在给项目补一层很轻的配置治理。
我尤其不建议把“高风险规则”只留在自然语言里。比如迁移失败后不能清理现场、发布前必须跑某一组检查、某个目录不能出现 mock implementation,这类事情一旦代价比较大,就不应该只靠 Agent 每次都刚好记住。Prompt 可以解释为什么,Skill 可以告诉它怎么走,最后真正兜底的还是 executable gate。
这次实验能证明的也就到这里。我能确认 Nession 的 root 和 Skill 入口确实发生了明显的结构缩减,也能确认这种 instruction graph 可以用一个很小的 validator 抓住不少常见回归。我没有在这里跑一个完整的 Codex/Claude Code 客户端 benchmark,所以不会把它写成“这样改以后成功率提高多少”或者“token 一定能省多少”。
但这已经足够让我改变一个习惯:以后再看到 CLAUDE.md 或 AGENTS.md 越写越大,我不会先想着“怎么把这 500 行写得更聪明”,而会先看里面到底混了多少本不该常驻的东西。
很多时候,真正该优化的不是提示词本身,而是这些规则有没有一个清楚的家。
如果你也在继续把 Coding Agent 往日常开发里接,可以顺着看 新版 Codex 有什么变化:为什么它现在更像一个开发环境;如果你更关心模型外面的权限、上下文和安全边界,那篇 Claude Code 源码泄露后,我们能学到什么:系统提示词、权限分级和记忆系统 会更接近这篇文章的下一层问题。