☰
CLAUDE.md拆分实战:用规则地图解决上下文膨胀
2026/9/30 10:22:37 网站建设 项目流程

坦白说,我的项目根目录下那份 CLAUDE.md 之前已经到了 1300 行。上周要重构认证模块,Claude 明明读到了规则,行为却没按最早定的那条"数据库变更必须写迁移文件"来执行,连加粗强调三次都没救回来。这不是第一次了。CLAUDE.md 越长,模型越容易把规则当背景噪音。最近我趁着项目迭代,把所有规则重新按目录拆了一遍,总算把根目录收敛到 200 行左右。这篇就聊聊我在拆分里沉淀下来的判断标准、迁移步骤和坑,想给同样被 CLAUDE.md 折磨的朋友一点可参考的思路。

我在处理这类问题时最大的体会是:规则文件不是不能长,而是不能"始终加载"。Claude Code 本身有按路径加载子目录 CLAUDE.md 的机制,很多人没用起来,所以只能把所有内容都塞进根目录,最后把上下文拖垮。下面按我实际操作的顺序来写。

1. CLAUDE.md 膨胀,问题出在"始终加载"上

1.1 多层 CLAUDE.md 机制天然就是为拆分准备的

Claude Code 的 CLAUDE.md 有多个层级:用户目录下的全局文件、项目根目录的文件、各个子目录下的文件。运行时,模型会根据当前处理的文件路径决定加载哪些内容。

具体说,如果模型正在看src/auth/login.go,它会同时加载全局配置、项目根目录 CLAUDE.md,以及src/auth/下存在的 CLAUDE.md;但它不会加载src/payment/CLAUDE.md这种无关目录的规则。换句话说,子目录规则是"用到才加载",根目录规则是"每次都会加载"。

这个机制解决的就是我的痛点:规则系统像一个路由表,匹配范围越大,被无关任务触发的概率就越高;把规则下沉到子目录,等于把匹配范围收窄到专属目录。这跟规则引擎里的"作用域"、表单校验里的"分组校验"是一个逻辑——先定位范围,再执行规则。

可惜我最初没理解这点。当时觉得项目根目录放一份文件最省事,所有规则都往里写,最后成了一个大杂烩。

1.2 规则全堆在根目录,会出现三个典型症状

第一个症状是上下文预算被浪费。每次对话都要固定加载全部规则,包括那些只跟某个老模块有关的约定。比如我项目里有一条"assets 图标必须放 public/icons"的规定,写代码时几乎每周触发一次,但处理后端事务时这条规则完全无关,白白占据 token。

第二个症状是主旨规则被稀释。规则越多,真正刚性的要求就越容易被淹没。这和在表单校验里写了几十条字段规则后,必填项这种最基础的校验反而会被忽略是一个道理。CLAUDE.md 的顶部往往是最显眼的区域,一旦被一堆低频规则占据,模型对中后部指令的重视频率明显下降。

第三个症状是维护变得畏首畏尾。上千行的文件,删掉任何一行都担心"万一之后要用呢",于是所有旧规则都躺着不动。新规则不断追加,最后没人敢重构这个文件。

1.3 我的实测:拐点大概在 500 到 800 行之间

我没做过严格对照实验,但几十个会话实测下来,根目录 CLAUDE.md 超过 500 行后,模型对后部规则的执行率就开始下滑;超过 800 行后,即使我在任务描述里特意强调,模型也可能忽略某些历史规则。超过 1500 行后,规则之间开始互相干扰,出现"明明写了 A 规则,行为却是 B 规则"的情况。

这不是模型能力问题,是上下文聚焦的自然现象。关键结论很简单:根目录 CLAUDE.md 尽量控制在"一屏能看明白"的体量,具体操作细节交给子目录。

2. 哪些规则该进子目录:三条判断标准

2.1 范围、频率、粒度,三条标准缺一不可

判断一条规则该放根目录还是子目录,我一般问自己三个问题:

第一个问题,作用范围是不是限定在某个目录或模块?是,就放子目录;是对所有任务都生效的,留在根目录。比如"src/db 下禁止裸 SQL"这种规则只跟数据库模块有关,放子目录最合适。

第二个问题,使用频率是不是每次都高?每次都要遵守的规则,比如"提交前必须跑测试""禁止提交 .env 文件",这些应该留在根目录,因为它们对任意任务都成立;反过来,"发布前需要检查 CHANGELOG"这种低频流程,就不应该天天加载。

第三个问题,内容粒度是不是很重?如果规则包含大量示例代码、模板、目录结构说明,哪怕它全局通用,也不该全堆进根目录。根目录只留一句话摘要,把模板放到独立文档或子目录,让模型按需读取。

我把判断逻辑整理成了一张表,实际操作时对着看很快:

判断维度适合留在根目录适合下沉子目录
作用范围全局通用、跨模块仅特定目录、服务、模块
使用频率每次会话都要遵守特定任务才触发
内容粒度简短约束、一句话规则长示例、模板、步骤流程
冲突可能性不允许被局部覆盖的红线允许在局部调整的偏好

2.2 根目录只留骨架,它的核心职责是"导航"

拆分后,根目录 CLAUDE.md 的定位变了:它不再是唯一规则库,而是整个项目的规则入口。我会放这几类内容:

  • 项目一句话说明和技术栈清单,帮模型快速建立背景认知;
  • 通用命令,包括如何安装依赖、如何跑测试、如何 lint;
  • 统一的提交规范、分支命名、代码评审要求;
  • 安全红线,比如禁止提交密钥、禁止改动迁移文件、禁止绕过审核流程;
  • 一条规则地图,告诉模型"更细的规则分别放在哪些子目录,遇到什么任务去读哪个文件"。

最后这条特别重要。它相当于路由系统的索引,让模型知道该主动加载什么,而不是靠运气瞎猜。否则你拆了子目录,模型不知道有这些文件,照样按旧习惯处理。

2.3 子目录适合装细节,越贴近具体代码越好

子目录 CLAUDE.md 适合装那些只对特定代码生效的规则。举几个我项目的实际例子:

  • src/api/CLAUDE.md里写接口设计约定:RESTful 路径风格、错误码格式、分页参数命名;
  • src/db/CLAUDE.md里写数据访问约束:所有查询必须走 repository 层、禁止批量更新、索引变更要附带评估说明;
  • docs/CLAUDE.md里写文档维护约定:新增文档必须补目录索引、示例代码要和实际版本号一致;
  • scripts/CLAUDE.md里写脚本规范:禁止在 shell 脚本里硬编码绝对路径、输出必须带前缀。

这类规则有个共同点:它们只在处理对应目录时才真正有意义。如果模型在写 API 层代码,src/db那套约束就不该被加载进来,否则就是纯粹的上下文噪音。

2.4 特殊情形:全局通用但低频的规则,用"条件触发"处理

最难归类的规则是那些"全局通用但一个月只触发一两次"的流程。比如发布上线流程、批量数据迁移脚本、安全审计检查。它们不是模块专属,但塞进根目录会让日常会话变臃肿。

我的做法是:把完整流程放到独立文档或专有子目录,然后在根目录 CLAUDE.md 里留一行触发条件。例如写"当任务涉及发布时,先阅读 docs/release-check.md 再执行"。

这相当于给规则加了一个"懒加载"开关。模型平时不需要加载这堆信息,等真正碰到发布任务时,它会根据根目录的指令主动去读那份文档。这种条件触发写法在规则系统里很常见,和路由规则的懒匹配思路一致,关键是触发条件要写得足够明确,别让模型猜。

3. 实操:子目录 CLAUDE.md 怎么建、怎么写、怎么迁移

3.1 先设计目录结构,再迁移规则

拆分前不要直接动手删文件,先画出目标结构。下面是我现在用的结构模板,可以直接抄:

repo/ ├── CLAUDE.md # 全局骨架:项目概述、通用命令、安全红线、规则地图 ├── src/ │ ├── CLAUDE.md # 后端通用约定:代码风格、测试要求、依赖管理 │ ├── auth/ │ │ ├── CLAUDE.md # 认证模块专属:JWT 规则、会话过期处理、权限判断 │ │ └── ... │ └── db/ │ ├── CLAUDE.md # 数据库专属:SQL 约束、迁移要求、索引规范 │ └── ... ├── docs/ │ ├── CLAUDE.md # 文档维护规则、更新流程 │ └── templates/ │ └── CLAUDE.md # 模板文件相关的边界条件 └── scripts/ ├── CLAUDE.md # 脚本约定、执行环境要求 └── ...

这样的结构本质是让每份 CLAUDE.md 只对自己目录负责,互不干扰。模型在处理src/auth下的文件时,最多加载三层:全局、项目根、auth 子目录,而不会带上 db、docs、scripts 那些规则。

3.2 迁移分成五步,不能一步到位

第一次迁移我吃过亏,一口气把所有规则打散到十个文件,结果跑任务时模型根本找不到规则。后来固定成五步走:

第一步,盘点现有规则。把根目录 CLAUDE.md 里所有规则一条条抄进表格,列清楚"规则内容、作用范围、触发频率、是否包含长示例"。这一步是基础,不盘点清楚后面的分类都是拍脑袋。

第二步,分类入桶。分成三桶:必须留根目录的、必须下沉到某个子目录的、需要拆出来做独立文档的。分类时严格套用第二章那三条标准,拿不准就先放根目录,宁少勿滥。

第三步,建立子目录 CLAUDE.md。把对应规则搬进去,同时精简语言。我一般会把规则压缩成"短句+指令动词"的格式,例如"所有查询必须走 repository 层"而不是"考虑到代码结构清晰和职责分离,建议尝试使用 repository 模式来封装数据访问逻辑"。规则文件不是论文,不需要解释太多理由。

第四步,在根目录写导航。每个子目录新增一条摘要,让模型知道哪类任务去读哪个文件。比如"涉及认证或会话时,阅读 src/auth/CLAUDE.md"。这一步经常被忽略,但没这一步,子目录规则就像图书馆里没编目的书,没人找得到。

第五步,开新会话验证。CLAUDE.md 通常在会话启动时加载,所以改完文件后一定要新开会话验证,别在旧会话里继续测试。我会构造一个典型任务,比如"给 auth 模块增加一个刷新令牌接口",然后看模型有没有实际遵守子目录规则。

3.3 嵌套优先级与冲突仲裁:写清楚比赌模型聪明靠谱

子目录规则和根目录规则冲突时,模型怎么选?我的习惯是在子目录 CLAUDE.md 头部显式写一句声明:"本文件是该目录下的最高优先级规则;若与根目录 CLAUDE.md 冲突,以本文件为准,但提交信息里需要注明本次偏离。"

这比赌模型自行判断要可靠得多。规则系统里最怕的就是优先级模糊,CLAUDE.md 也一样。冲突仲裁不能靠模型临场发挥,你要给它一个简单的裁决原则。

如果存在子目录里再套子目录的情况,还可以加一句"本规则仅适用本层目录,不向下传递",防止某个深层目录被祖先规则意外约束。这种局部覆盖全局、底层覆盖顶层的设计,和表单校验里的分组覆盖规则以及路由系统的优先级策略是一回事。

3.4 写规则时的格式建议:短句、一事一条、留更新记录

经过多次重建,我现在写子目录 CLAUDE.md 会遵循几个格式习惯:

  • 每条规则都限定适用路径,比如"本规则仅适用于 src/api 目录";
  • 用祈使句,少用"可能""应该""尽量"这类模糊词;
  • 一条规则只讲一件事,不要在一个条目里塞三个要求;
  • 文件顶部写明更新日期和改动原因,方便三个月后回看;
  • 需要长示例时,单独开一个示例片段,不要混进规则正文。

格式清晰直接影响模型的执行率。规则文件不该追求文学性,它更像一份配置清单,清晰、简短、无歧义才是核心。

提示:如果规则里出现"可能""或许""看情况"这类词,大概率后面会失效。要把它们改成明确的触发条件和动作,模型才不会犹豫。

4. 案例拆解:一个 1000 行的 CLAUDE.md 怎么瘦身

4.1 瘦身前:所有规则混在一个大文件里

我用实际项目的结构来说话。瘦身前,根目录 CLAUDE.md 大概有上千行,内容大致是这样分布的:

  • 项目概述和技术栈,约 50 行;
  • 通用命令,约 80 行;
  • 后端编码规范,约 200 行;
  • 前端命名规范,约 150 行;
  • 数据库操作注意事项,约 300 行;
  • 部署和发布流程,约 120 行;
  • 各种历史遗留的临时规则,约 200 行。

每次处理 API 任务时,前端命名规范、数据库注意事项、部署流程全都跟着加载。有一次我让模型帮我改前端组件,它居然把数据库索引规则也读进来了,浪费了大量上下文不算,还差点因为那条"禁止裸 SQL"误解了前端请求代码。这类高耦合低相关的加载,就是规则全堆根目录的必然结果。

4.2 瘦身后:根目录只留骨架,细节全部下沉

拆分后的目标结构如下:

  • 根目录 CLAUDE.md 保留约 200 行,内容是项目概述、通用命令、编码红线(比如统一用 error 包装、禁止 console.log 提交)、以及规则地图;
  • src/api/CLAUDE.md放接口约定,约 80 行;
  • src/db/CLAUDE.md放数据库规范,约 120 行;
  • src/frontend/CLAUDE.md放组件和样式规范,约 100 行;
  • docs/CLAUDE.md放文档规范,约 60 行;
  • scripts/CLAUDE.md放脚本和发布相关流程,约 90 行。

根目录里的规则地图长这样:

- 涉及 API 接口设计:阅读 src/api/CLAUDE.md - 涉及数据库查询或迁移:阅读 src/db/CLAUDE.md - 涉及前端组件和样式:阅读 src/frontend/CLAUDE.md - 涉及文档更新:阅读 docs/CLAUDE.md - 涉及发布或批量脚本:阅读 scripts/CLAUDE.md

有了这张地图,模型在遇到具体任务时会主动去读对应文件,而不是把所有细节都提前加载到上下文里。

4.3 效果对比:token 成本大约省一半以上

做一个粗算就能看出差距。按中文场景粗略估计,一行规则大约对应 20 到 40 个 token。假设原文件 1000 行,平均每行 30 token,那就是 3 万 token 起步。每次会话固定加载这 3 万 token,不管有没有用。

拆分后,根目录 200 行约 6000 token,某个子目录 100 到 150 行约 3000 到 4500 token,加起来日常会话差不多只加载 1 万 token 左右。相比原来省了一半以上,而且模型只需要聚焦真正相关的规则,执行准确率也会更高。不同项目差异很大,但趋势是一致的:拆完后的加载量远小于全量堆积。

4.4 一个可以直接抄的小技巧:规则地图就是规则系统的索引

规则地图是我这次拆分里收获最大的一个小设计。它和数据库索引一个思路:不提前把所有数据加载进来,而是先看索引,确定要读取哪一部分,再去取数据。

写规则地图有个细节:触发词要具体,别写"涉及后端时阅读 src/api/CLAUDE.md"这种模糊表述,而是写"涉及接口、路由、状态码、参数校验时阅读 src/api/CLAUDE.md"。触发词越具体,模型越容易命中正确文件。

5. 拆完之后的问题:规则不生效、冲突、加载过多怎么排查

5.1 规则不生效,先从这三方面排查

拆完子目录后最容易遇到的问题就是规则不生效。我的排查顺序是固定的:

先查文件路径和命名。CLAUDE.md 必须精确叫这个名字,位置必须放在对应目录根部,放错一层都可能不被加载。还要检查大小写,很多系统是区分大小写的,claude.md和CLAUDE.md不是同一个文件。

再查该规则是否真的适用于当前目录。子目录规则只在该子目录被访问时生效,如果模型在处理根目录下某个通用文件,它不会加载任何子目录规则。遇到这种情况,要么把规则提到根目录,要么在规则地图里显式说明"处理这类任务时主动读取 xxx 文件"。

最后直接问模型。开一个带上下文的会话,问一句"你现在读取了哪些 CLAUDE.md?关于某个目录的规则是哪条?"它会告诉你实际加载了哪些文件。这个办法最直接,比反复猜测快得多。

还有一点,改完 CLAUDE.md 后一定新开会话。模型不会在旧会话中途自动重读文件,旧会话里测不出新规则。

5.2 规则重复和冲突,需要一份"规则裁决表"

多个子目录出现重复规则很常见,比如src/api/CLAUDE.md和src/db/CLAUDE.md里都写了"错误信息不能直接暴露给用户"这类安全规则。重复本身问题不大,但两份规则表述不一致时,模型就不知道该听谁的。

我的做法是建一份规则裁决表,统一维护所有涉及"相同诉求但表述不同"的规则。表头是规则主题、根目录写法、子目录写法、最终生效版本、判定原因。一旦发现冲突,以最深目录且最新更新的一份为准,并把其他位置改成引述式写法,比如"安全相关规则见 src/api/CLAUDE.md 第 2 节"。

注意:不要用 grep 找到重复就盲目删除。先确认重复的两份规则是否面向同一个任务,有些重复是刻意为之,比如根目录红线是"禁止提交密钥",子目录可能细化成"禁止把 token 写进日志",两者是层级关系,不是冗余。

5.3 跨目录任务导致加载过多,又变回了大文件

拆分后还有一个副作用:一个任务如果横跨多个子目录,模型可能会把所有相关子目录的 CLAUDE.md 都读一遍,加载量又涨上去了。比如"给认证模块加一个刷新令牌接口"同时涉及src/auth、src/api、src/db,模型一上来全读,token 又爆了。

我的应对办法有两个。一个是在规则地图里给每个子目录设置触发条件,让模型"先读最相关的,不够再读其他",不要一次全加载。另一个是把跨目录通用的规则上浮到根目录,避免同一个认知分散在多个文件里。比如全局都适用的错误处理风格,直接放根目录,不需要每个子目录重复写。

5.4 历史遗留规则怎么清理:三个月没触发的基本可以归档

拆完文件后,那些已经失效的历史规则仍然躺在子目录里。我的判断方法是:用 git log 或 git blame 看每条规则的更新时间,如果三个月内没有相关任务触发过它,也没有对应的 issue 或 commit 关联,就先移到归档文档区,而不是直接删除。

直接删除的风险在于你可能遗漏某个低频流程。归档后如果两个月内依然没人提及,再彻底删除也不迟。实际清理下来,我项目里至少三分之一的历史规则属于"当初为了某个一次性任务顺手加的",归档后整个文件清爽不少。

5.5 编辑 CLAUDE.md 的规范:每次改动都留痕

最后一个小习惯:每次给 CLAUDE.md 增删规则,都在 git commit message 里说明改了什么、为什么改。比如"docs/CLAUDE.md:增加文档索引强制要求,原因是上周发布时发现目录缺失"。这样以后回看文件历史时,能理解每条规则是怎么来的、还剩多少价值。

CLAUDE.md 本身就是一个需要持续维护的工程文件,不是一次写完就拉倒的备注。

6. 把 CLAUDE.md 当成规则系统来设计

6.1 规则分层的思想可以迁移到任何规则场景

拆完 CLAUDE.md 后我才意识到,这套思路其实到处都是。路由规则讲匹配范围,表单校验讲分组校验,订阅规则讲优先级和兜底,规则引擎讲条件触发。它们的核心就三件事:匹配范围、优先级、降级策略。

CLAUDE.md 的组织也一样。根目录是兜底规则,对所有任务生效;子目录是局部规则,只对特定路径生效;会话里临时给的偏好是最顶层规则,针对本次任务生效。这三层形成清晰的梯度,模型在具体任务里就知道该以哪层为准。

6.2 简单规则也能涌现复杂行为,不靠堆量

有个挺有名的鸟群模型,里面每只鸟只遵循三条简单规则:靠近同伴、对齐方向、避免碰撞,就能模拟出壮观的群体舞蹈。CLAUDE.md 其实同理——真正有效的往往是最顶上那几条核心准则,而不是一千条细枝末节的指令。

我最后把根目录的规则收敛到几个核心原则:安全红线永远第一;有全局约定先在全局找;冲突时局部优先但必须说明。剩下的细节全部交给子目录按需加载。结果是模型表现稳定,维护成本也降下来了。

6.3 给每次新增规则做一个"入口体检"

踩过几次坑之后,我总结出一个新增规则的体检清单。每次想往 CLAUDE.md 里加一条规则前,先过四关:

第一,这条规则会不会每月至少触发一次?不会的话,别放根目录。第二,它是否只服务于某个目录或模块?是,直接去对应子目录写。第三,仓库里有没有已经相似的规则?有,去合并而不是新增。第四,能不能用一段简短原则替代三条细则?能,就用原则说话,把细则留给文档。

过了这四关的规则才值得写进文件。如果过不了,大概率又是躺着吃灰的历史规则。

项目到目前为止,根目录的 CLAUDE.md 一直稳定在 200 行上下,各子目录按需加载。我最大的体会是,规则文件的管理其实和代码重构一样,关键在于让每条规则出现在它该出现的位置,而不是把责任全压在模型的理解能力上。每次想新增规则时,先搜一遍已有的,再去判断该放哪个目录,长期下来这个习惯能省掉大量维护成本。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询