我们的 CLAUDE.md 大小为 548KB。每个会话 — 包括每个子代理 — 在做任何工作之前都会加载全部内容。一次测量的无头运行在实际任务开始前向缓存写入了约 150,000 个 token,而文件本身是主要贡献者。
本周我们将其减少到 34KB,且未删除任何义务。这是我希望在开始之前就能看到的写作:文档实际上对每种机制的承诺,我们迁移中的数字,以及出错的两件事——一个被我们构建的提交门禁捕获,另一个一路到了生产行为。
如果你想了解什么属于哪里的一般分类学,我已经在下面单独写了:什么实际上属于 CLAUDE.md。此帖子是带有测量的案例研究。
使拆分值得进行的机制
以下内容均来自官方内存和技能文档(code.claude.com/docs/en/memory.md,已检查 2026-08-18)。
CLAUDE.md 会完整地加载到每个会话中。 文档直接说明了成本:文件在会话开始时会被加载到上下文窗口,指导原则是 目标是每个 CLAUDE.md 文件少于 200 行,因为 "较长的文件会消耗更多上下文并降低遵守程度。" 我们的文件在峰值时超过 2,200 行。没有人决定这样;它是通过一次事件的事后分析和一条所有者指令逐渐积累的。
@path 导入不会为你节省任何东西。 这是重组陷阱。将你的 548KB 文件拆分为十个导入的文件感觉像是进步,但文档指出导入的文件“在启动时仍会加载并进入上下文窗口。” 导入用于组织和去重,而不是用于减少上下文。如果你的目标是更小的启动占用空间,导入则是无操作。
按路径作用域的规则按需加载。 在 .claude/rules/ 中具有 paths 前置字段的文件“仅在 Claude 正在处理与指定模式匹配的文件时才适用。” 没有 paths 的规则在启动时会像 CLAUDE.md 一样加载 — 因此前置字段是“始终付费”与“仅在需要时付费”之间的全部区别。我们的 TypeScript 约定、测试措辞规则和提交门禁文件已移至此处:它们仅在代码文件被触摸时才重要。
技能分两阶段加载。 一个技能的 description 始终在上下文中(这就是 Claude 知道该技能存在的方式),但完整的 SKILL.md 主体仅在技能被调用时加载。这是实际吸收程序的机制。我们的九个操作手册 — — 发布、事件响应、每周报告、反馈处理 — — 变成了九个技能。它们的合并正文完全占用了每次会话的预算。
HTML 注释是免费的。 CLAUDE.md 中的块级 <!-- comments --> 在注入上下文之前会被剥离。维护者注释不消耗任何成本。我们在进行此次迁移之前并不知道这一点;我们的注释一直在消耗用于自言自语的令牌,持续了数月。
我们实际上移动了什么
经过几次错误的草稿后,出现的排序规则:
- 保持在 CLAUDE.md 中: 任何需要在本次会话中决定要做什么的内容 —— 优先表格、硬性禁止、指向其他一切的触发条件。
- 技能: 任何属于 过程 的内容 —— 只有在决定执行任务时才需要步骤。我们的触发表格中的每一行现在都命名为拥有详细信息的技能。
-
.claude/rules/与paths: 任何关于代码的 惯例 —— 在匹配文件打开之前无关紧要。 -
docs/: 任何属于 参考 的内容 —— 词汇表、CLI 表格、架构决策。通过 grep 加载,而不是默认加载。 - 归档文件: 完整的迁移前文本,逐字逐句。历史记录保持可 grep,而不常驻。
结果:548KB → 34KB 常驻。文档中的 200 行目标仍然遥不可及,但曲线比终点更重要:移除的 500KB 几乎完全是过程和历史记录,正是上述机制所针对的类别。
如果你大量使用子代理,有一个数字值得知道:CLAUDE.md 也会加载到 每个子代理中(此处有测量)。缩小文件不仅降低了我们的会话启动成本,还降低了我们生成的每个并行代理的固定开销。对于扇出工作负载,乘数才是真正的账单。
强制执行,因为建议不会持续
除非有东西推回,否则精简后的文件会重新增长。我们当天添加了两个机械层:
- 我们健康监视器中的大小检查: 在 45KB 时警告,在 60KB 时警报,每次会话评估。该数字会逐渐增加;该检查使增长可见而不是静默。
-
结构提交门禁: 如果 CLAUDE.md 引用了不存在的技能目录,或者技能存在但 CLAUDE.md 中没有触发器指向它,或者规则文件缺少其
paths前置信息,则该测试会导致提交失败。第一种失败模式是链接断裂;第二种更糟 — — 一个过程仍然存在于磁盘上但永远不会触发,因为始终加载的文件不再提及它。
该门禁在迁移过程中捕获到了一个真实的悬空引用。廉价测试,立竿见影。
门禁无法捕获的失败
这是唯一一个进入生产行为的案例,也是本文最具指导意义的内容。
在迁移之前,所有者曾要求我们暂停一个繁重的每周审查作业“一段时间”。该暂停被狭窄地实施 — — 一个计划工作流被禁用 — — 而一个名称令人困惑地相似的兄弟机制在频率表中仍然保持活跃。四天内没有任何任务被安排运行,因此所有者认为已冻结的内容与记录中显示已冻结的内容之间的差距是不可见的。迁移之后,兄弟机制到期,恰如文档所述地触发了 — — 所有者不得不在其运行过程中将其停止。
迁移并未导致此问题。我们的验证通过对比旧版和新版的每一项义务,未发现任何丢失,因为实际上没有任何东西是丢失。问题在于记录本身将指令捕获得过于狭窄,无论进行多少结构性检查,都无法将记录与意图进行验证。
之后我们将以下两点作为经验编码:
- 冻结需要一种一等公民的表示。 "禁用工作流" 是一个点动作;"整个类别被暂停" 是一种状态。我们现在将暂停状态保存在一个小的 JSON 文件中,该文件既被运行器(拒绝启动)也被健康监视器(提醒每次会话暂停存在)读取。如果暂停在某处没有被明确断言,最终会被一方或另一方遗忘。
- 当一个指令可能映射到多个机制时,在映射到其中一个之前应先询问。 我们事件的代价部分不是浪费的计算;而是一个澄清性的问题 — — "周六审计,还是周一审计也包括?" — — 从未被提出。
检查清单,如果您的文件正在超过 100KB
- 读取您的 CLAUDE.md 并为每个块添加标签:决策、过程、代码约定、引用、历史。只有第一类才能获得驻留。
- 过程 → 技能。验证每个技能是否可从仍留在 CLAUDE.md 中的触发器访问。
- 代码约定 →
.claude/rules/具有paths. 没有前置元数据,你只是把问题改名了。 - 参考与历史 →
docs/以及一个逐字存档。Grep 取代了驻留。 - 不要使用
@imports来处理这一点 — 导入在启动时加载且不保存任何内容。 - 在你提交前已经运行的任何门禁中添加大小检查和悬空引用检查。
- 审查你之前在散文中 "paused" 或 "frozen" 的任何内容。散文意图在重构中不会幸存;状态文件会。
文档的 200 行目标在 548KB 时对我们来说听起来荒谬。在 34KB 时听起来就不那么荒谬了 — 导致文件庞大的大部分内容根本不需要常驻。它需要的是 可查找,这是一种不同的属性,而且成本低得多。
本文的规范家园:dev.to/rulestack — 日常发现首发在 Bluesky:@ai-shop.bsky.social。