大仓库的题不是「读得慢」,是「读进来就占着窗口,而且后面每一轮都带着」。 实测两百个文件:全量读 21797 token,搜索后读 1872,子代理只回 38。 省的不是时间,是「带上」。
前三节讲钱、讲查、讲守,这一节讲规模——窗口就那么大,代码有几百万行。 这一节有真跑:现造一个两百文件的仓库,三种读法各量一次进主窗口多少。
一、三种读法
全量读:把相关目录全读一遍。最保险,也最先撞窗口。 搜索后读:先按关键词搜,只读命中的那几个。快,但可能漏掉间接相关的。 子代理:派一个去读,它读多少都行,只把结论回给主线。
三种不是「新旧」的关系,是三种不同的取舍:全量的代价是窗口,搜索的代价是召回, 子代理的代价是「你看不见中间过程」。
二、真跑:两百个文件找一个函数
造一个目录结构像真项目的仓库:src 下二十个模块各八个文件,tests 下四十个,合计两百个文件。 其中十三个文件里定义了同一个函数,散在不同模块里——按目录名猜不出它在哪儿。 全量读要 21797 token,占两百 k 窗口的百分之十一。这还只是代码,没算指令、没算工具结果。
搜索后读只要 1872 token,是全量的百分之九。因为命中那十三个文件本来就只占仓库的一小角。 子代理读的是同一批文件,但只回三行结论,主窗口只增 38 token。 三个数差三个数量级,而它们做的是同一件事。
三、演示里的仓库太小了
这里的每个文件只有三十来行、八百字符。真实项目一个文件几百行,两百个就是几百万字符。 那时候全量读不是「占一成窗口」,是「根本进不去」——光代码就是上限的好几倍。 所以这三个数要当趋势看,不要当比例记:全量是基准,搜索省到一成,子代理再省到五十分之一。
也正因为真实项目进不去,才需要「引导搜索」而不是「让它自己找」——你告诉它去哪个目录、搜哪个词, 比它自己扫一遍省得多。
四、子代理省的不是读,是「带上」
子代理也要把那十三个文件读完,读的时候一样吃窗口——吃的是它自己的窗口。 省下来的关键是:那块窗口用完就销毁,主上下文只留一份结论。 所以判断要不要派子代理,不是看任务大不大,是看「中间过程需不需要留在主线上」。
查一个模块的用法、跑一次批量搜索、审一遍日志——这些都属于「过程不必留」。 反过来,需要反复回看原文的活儿(比如逐行改一个文件),就别派出去:结论回来了,原文还得再读一遍。
五、分层指令解决的是另一半
根目录一份指令写全局规矩,子目录各一份写本模块的约定。 这样 agent 走到哪个模块,只读那一份,不用把全项目的约定都背着。 这一条和「搜索后读」是配套的:搜索决定读哪些文件,分层指令决定读哪些规矩。
还有一个常被忽略的点:压缩。 窗口快满时会自动压缩,而压缩会重排前缀,缓存当场失效(10.1 节那笔账)。 所以少读不只是省钱,也是让压缩晚点发生。
六、动手 Demo:两百个文件,三种读法的窗口账
现造一个两百文件的仓库,同一件事用三种读法各做一次,只量一件事:进了主窗口多少。 时间不是重点,占住的窗口才是。
hit_chars = sum(len(get(tmp, os.path.relpath(p, tmp).replace(u"\\", u"/")))
for p in hits)
# 全量读:tokens(u"x" * n_chars)
# 子代理:tokens(u"x" * 150) —— 只回三行结论
实跑输出:
--------------------------------------------------------------------
两百个文件,三种读法的窗口账
--------------------------------------------------------------------
大仓库的题不是「读得慢」,是「读进来就占着窗口,而且后面每一轮都带着」。
这里真造一个两百文件的仓库,三种读法各量一次。
① 三种读法
--------------------------------------------------------------------
| 做法 | 怎么读 | 代价 |
|---|---|---|
| 全量读 | 把所有文件读一遍 | 最保险,也最先撞窗口 |
| 搜索后读 | 先搜关键词,只读命中的 | 快,但可能漏掉间接相关的 |
| 子代理 | 派一个去读,只回结论 | 主窗口只增结论那几行 |
② 真跑:造一个两百文件的仓库
--------------------------------------------------------------------
造好了:src 下 20 个模块各 8 个文件,tests 下 40 个,合计 200 个、87188 字符。
其中 13 个文件里定义了 format_date——f0.py、f5.py、f2.py,散在不同模块里。
所以「找它」这件事本身就得翻一遍:文件按模块分目录,按名字猜不出它在哪儿。
顺带说一句:这 13 个文件总共才 7488 字符,只占仓库的 9%。
所以「全量读」浪费的不是一点点,是九成。
③ 三种读法各读进来多少
--------------------------------------------------------------------
| 做法 | 读了几处 | 读进来多少 | 进主窗口 |
|---|:---:|:---:|:---:|
| 全量读 | 200 个 | 87188 字符 | 21797 token |
| 搜索后读 | 13 个 | 7488 字符 | 1872 token |
| 子代理 | 13 个 | 约 150 字符 | 38 token |
全量读要 21797 token,占两百 k 窗口的 11%——这还只是代码,没算指令、没算工具结果。
搜索后读只要 1872 token,是全量的 9%——命中那 13 个文件本来就只占仓库的一小角。
子代理读的是同一批文件,但只回三行结论,主窗口只增 38 token。
④ 演示里的仓库太小了
--------------------------------------------------------------------
这里每个文件只有三十来行、八百字符。真实项目一个文件几百行,两百个就是几百万字符。
那时候全量读不是「占一成窗口」,是「根本进不去」——光代码就是上限的好几倍。
所以这里的比例要当「趋势」看:全量是基准,搜索省到一成,子代理再省到五十分之一。
⑤ 子代理省的不是读,是「带上」
--------------------------------------------------------------------
子代理也要把那 13 个文件读完,它读的时候一样吃窗口——吃的是它自己的窗口。
省下来的关键是:那块窗口用完就销毁,主上下文只留一份结论。
所以子代理的判据不是「任务大不大」,是「中间过程需不需要留在主线上」。
查一个模块的用法、跑一次批量搜索、审一遍日志——这些都属于「过程不必留」。
⑥ 分层 CLAUDE.md 解决的是另一半
--------------------------------------------------------------------
根目录一份写全局规矩,子目录各一份写本模块的约定。
这样 agent 走到哪个模块,只读那一份,不用把全项目的约定都背着。
! 仓库是本沙箱现造的,比例按字符数实算;token 按英文四字符一个估。
①三种读法:全量读(代价是窗口)、搜索后读(代价是召回)、子代理(代价是看不见过程)。 ②真造两百个文件:全量 21797 token、搜索后 1872、子代理 38。 占两百 k 窗口是百分之十一、不到百分之一、可以忽略。 ③演示里每个文件只有三十来行。真实项目一个文件几百行,全量读不是占一成,是根本进不去。 ④子代理省的不是读,是「带上」——它读的时候一样吃窗口,只是吃完就销毁。 判据是「中间过程要不要留在主线上」。
30 秒实验
- 把仓库从两百个文件加到四百个(每个文件不变),重跑一遍。三行数字各涨多少倍?哪一行几乎没变?
- 让搜索命中率从十三分之一降到三十分之一,重跑一遍。搜索后读的成本怎么变? 漏掉的那部分代价怎么算?
- 给子代理的结论从三行扩到三十行,重跑一遍。它相对全量读还剩多少优势?
常见误区
- 让它自己扫一遍。真实项目扫不动,而且扫进来的内容后面每一轮都带着。
- 以为子代理「读得快」。它不快,它只是读完就把窗口扔了。
- 什么事都派子代理。需要反复回看原文的活儿,结论回来了还得再读一遍。
- 只靠目录名猜位置。函数散在不同模块时,猜不出来——得靠搜索。
- 忽略压缩的代价。窗口一满就压缩,压缩重排前缀,缓存当场失效。
自测三问(点开对答案)
1. 大仓库真正的难题是什么? —— 不是读得慢,是读进来就占着窗口,而且后面每一轮都要带着它。 2. 子代理到底省了什么? —— 省的是「带上」。它读的时候一样吃窗口,但吃完就销毁,主上下文只留结论。 3. 什么时候不该派子代理? —— 需要反复回看原文的活儿。结论回来了,原文还得再读一遍,等于白派。读得少了,下一步是让它读对——指令怎么写才不是「希望」。
- ☐ (本篇完,下一篇 10.5 指令:写好 CLAUDE.md 与 Prompt)
相关笔记
- 成本控制:让 Token 为你工作,而不是烧钱:缓存为什么会被压缩打断
- 指令:写好 CLAUDE.md 与 Prompt:指令怎么写才算要求
- Claude Code 橙皮书 · 索引
- 🐎 Claude Code 实战 · 索引