/ By 煎鱼 / / #agent #ClaudeCode #context-engineering #Cookbook解读 / AI 入口

Cookbook解读|大型代码库里,先别让 Agent 读完整个仓库

带读 Claude Code 的大型代码库指南:从一个找错登录链路的任务出发,理解目录规则、受限读取、代码智能与跨包计划如何让 Agent 看见恰好够用的代码。

让 Agent 修复登录失败,它却先读了 dist/、十年前的 legacy/,又翻到了另一个团队的前端约定。等它真的找到 AuthService,上下文里已经塞满了和这次故障无关的东西。

这不是模型“不够聪明”的一个好例子。更像是让一个刚入职的同事拿着整层楼的门禁卡查线上问题:门都能进,反而不知道应该先敲哪一扇。

这篇文章带读的原始材料不是 OpenAI Cookbook,而是 Anthropic 的 Claude Code《Set up Claude Code in a monorepo or large codebase》。它被放进本博客的「Cookbook 解读」系列,是因为它同样提供了一套可以照着落地的工程做法。

先说结论:大仓库里最有用的不是“把更多代码塞进 Agent”,而是让任务决定它从哪里开始、哪些规则随身带着、哪些文件根本不该打开;当任务真的跨出边界时,再显式扩大范围并把关键决定写下来。

数据快照

#原文先解决什么问题:不是仓库太大,而是注意力被用错了

假设一个电商 monorepo 有订单、支付、优惠券和后台管理四个包。登录出错只发生在 API,可 Agent 一上来从根目录扫完整个仓库。它当然可能最终找到答案,但每一次无关读取都会让真正的证据变得更难辨认。

原文引用|指南对大仓库问题的定义

unrelated to the task, costing tokens and degrading Claude’s performance.

原文说明的是:随着代码库长大,为小项目设计的默认行为会让无关指令和文件读取填满上下文,从而增加 token 成本、降低表现。这段原文能证明官方把“任务无关的读取”当作问题;它不能证明某一种配置会自动理解业务,也没有给出不同设置的质量排名。

所以这份指南的核心不是一条“让 Agent 先探索”的抽象口号,而是把探索拆成几道能实际配置的边界。它们可以叠加,也不要求每个仓库一次性全上。

原文引用|各项设置之间的关系

They layer rather than replace each other.

这句话解释了为什么不能只加一份巨型 CLAUDE.md 就宣布完成 Context Engineering。启动目录、目录规则、读取权限、语言服务、worktree 和 Skill 分别管不同的事情;它们是工具箱,不是一道必答题。原文的设置总览列出了每个工具试图解决的具体问题。

#第一层:先决定从哪扇门进,而不是从大厅喊“我来了”

在大型仓库里,claude 从哪里启动,决定了默认能访问哪些文件、启动时会带上哪些 CLAUDE.md,以及哪一份项目设置生效。原文的启动位置对照给出了两个很不一样的入口:

  • 从仓库根目录启动,适合要同时改多个 package 的任务;
  • packages/api/ 启动,适合只影响 API 的任务,默认视野先收在这个子树中。

这不是“目录越深越好”。如果登录改动还要更新 packages/shared/ 的 Session 类型,把 Agent 困在 packages/api/ 只会让它在发现依赖后再次申请访问。正确的判断是:先按已知影响面给一个默认范围,新的证据出现后再扩大。

#原文的做法:把规则放在代码附近

原文方案引用|官方示例中的分层目录结构(节选)

monorepo/
CLAUDE.md
packages/
api/
CLAUDE.md
.claude/skills/
web/
CLAUDE.md

上面的结构来自官方示例 monorepo。根目录保存全局布局、提交约定等通用信息;api/ 目录保存 API 的测试、迁移和框架约束;读取更深目录时,再按需加载那里自己的说明。

把它想成医院的导诊:大厅只告诉你楼层和总规则;真的走到放射科,才需要知道那里的预约流程。把所有科室的细节印进同一张大厅海报,只会让病人和导诊护士一起看花眼。

本文解读CLAUDE.md 更像“这一片代码的使用说明”,不该是百科全书。根目录放所有人、所有任务都需要的约定;某个 package 的测试命令和禁忌,就留给这个 package 的 owner 维护。原文也提醒,短期只想专注一个目录时,优先从该目录启动,而不是频繁修改 claudeMdExcludes 这类长期排除策略。

#第二层:挡住不该读的文件,再让工具找到该读的符号

规则只解决“该带什么说明”;Agent 真正消耗上下文的大头,通常还是文件内容。

#生成物不是证据,除非任务正是在查生成物

node_modules/、构建产物、提交进仓库的 vendor SDK,常常会在搜索结果中显得很热闹,却很少能解释业务故障。官方指南区分了两种情况:已在 .gitignore 中的常见目录通常不会进入内容搜索;对已提交的生成代码或 vendor 依赖,则可以用 permissions.denyRead 规则阻止打开。原文关于受限读取的说明还特别限定:这类规则并不会过滤递归搜索的输出,也不能管住任意子进程。

原文方案引用|阻止读取构建产物与 vendor 代码(节选)

{
"permissions": {
"deny": ["Read(./**/dist/**)", "Read(./vendor/**)"]
}
}

这段配置来自官方的 permissions.deny 示例。它证明的是“打开文件时可以设定边界”,不代表这些目录永远无用;排查打包产物或第三方补丁时,反而可能需要临时把它们放回视野。

#不要让 grep 承担语言服务的工作

现在回到“登录失败”。如果问题是“Session 类型在哪定义、谁在调用它”,全仓搜索关键词会得到一大堆同名命中。官方建议接入 code intelligence plugin,让语言服务器回答定义、引用和类型错误,把文件扫描换成符号级定位。原文对 code intelligence 的说明把它和排除规则放在同一节,原因很直白:前者移走噪声,后者在剩下的代码里更快找路。

不过,LSP 不是业务分析师。它能画出 RefundCreated 的调用关系,却不能告诉你退款后是否必须返还优惠券、优惠券是否有冻结期、幂等键放在哪。这些仍然要靠领域文档、测试和任务的验收条件。缩小检索范围,不等于省掉业务判断。

#第三层:跨包时扩大世界,但别丢掉已经做出的决定

设想登录问题最后被定位到共享类型:packages/api/ 需要修改接口,packages/shared/ 需要改类型,packages/web/ 还要改一个调用点。这时“只看 API”已经不成立。

图 1:本文解读|登录任务的范围如何随着证据扩大;图中示例是分析性场景,不是官方实测流程

flowchart LR
  S([开始:收到登录失败任务]) --> A[从 API 目录启动]
  A --> B[加载根目录与 API 规则]
  B --> C[跳过生成物与 vendor]
  C --> D[用语言服务定位 Session 引用]
  D --> E{发现 shared 类型?}
  E -- 否 --> F[修改 API 并运行对应测试]
  E -- 是 --> G[显式纳入 shared 与调用点]
  G --> H[写下变更计划与验证项]
  H --> I[同一任务中完成一致改动]
  F --> Z([结束:交付可验证改动])
  I --> Z

图里最重要的转折在 shared:它不是“越界失败”,而是要扩大范围的证据。官方指南给出两类工具:additionalDirectories--add-dir 处理当前 session 对兄弟 package/其他仓库的访问;worktree.sparsePaths 让 worktree 只检出任务需要的目录。原文的跨目录访问说明稀疏 worktree 说明分别覆盖这两层边界。

原文引用|跨 package 变更的一条建议

Save the plan to a file before editing.

原文建议在编辑前把计划写入仓库,因为较长 session 可能压缩上下文,而文件中的计划能留下已确认的决定。原文的跨包 scope 与 plan 建议能证明“计划文件可跨压缩保留”;它不代表每一个两行改动都必须新建一份计划。

这一点也解释了多 Agent 的边界。只读探索适合独立出去:把大量目录遍历、搜索命中留在子任务中,主任务只拿回文件位置、证据和未解项。但“要不要改共享类型”“哪个回归测试算完成”必须由同一个变更保持一致;把这些决定拆成几段互不相通的对话,才是真的让 Agent 迷路。

#把原文的工具箱换成一次可执行的判断

下面这张表不是官方的质量对比,而是基于上述指南整理的选择入口。

表 1:本文解读|按任务信号选择范围控制工具;结论基于 2026-08-10 核验的官方指南,不是性能测量

看到的信号先做什么不要误解成
任务只改一个 package从该子目录启动,加载根目录与本地规则永远不能读兄弟 package
根规则越来越长、越来越像项目百科将局部测试/框架规则放入目录级 CLAUDE.md 或 path-scoped rule每个目录都必须复制一份总规则
搜索不断命中构建产物、生成代码或 vendor.gitignoreRead deny 缩小读取面从此不可能调试这些目录
问题是“定义或调用者在哪里”用 code intelligence 查符号LSP 已经理解业务语义
任务涉及共享类型和多个调用点显式增加目录,先记录计划再改把每个 package 交给互不共享决定的 Agent

如果团队反复在同一目录做同类操作,原文还建议把局部流程做成按需加载的 Skill;如果分散的 CLAUDE.md 已经难以治理,再考虑由平台团队维护 plugin 或 MCP。目录级 Skill集中治理建议回答的是“规则如何长期维护”,不是一次登录故障的即时修复方案。

#总结:先把问题缩到可解释,再把范围扩大到足够完成

大型代码库并不要求 Agent 永远待在一个小盒子里。更好的策略是:起步时只带与任务有关的规则和文件;用符号工具减少盲目翻找;当共享类型、调用点或测试给出新证据时,再把世界扩大一圈,并把已经做出的决定写进计划。

下次让 Agent 改一项跨包功能时,可以先问四个问题:任务最初影响哪个目录?哪些说明必须加载?哪些文件只会制造噪声?一旦发现跨包依赖,哪些决定必须和计划一起保留?

能回答这四个问题,Agent 就不必先读完整个仓库,才有机会把真正需要读的那部分读明白。

#参考资料

Share this post

Mermaid 图表
100%