07|Subagent:不要把所有任务塞进一个聊天窗口
本篇是《从头重新学 AI 编程》系列第 7 篇。
还是先说一句,如果你现在所有活都在一个对话窗口里干,也完全够用,这篇可以先当思路看。等你哪天觉得一个窗口越来越挤、越来越乱的时候,再回来看,会更有共鸣。
上一篇聊了 MCP,怎么给 Agent 接外部工具和数据。这一篇接着聊一个工具层面的东西,Subagent。
这个词拆开就好懂了。Sub 是子,Agent 是代理。Subagent 就是子代理,主 Agent 派出去干专项活的小队。
我先讲讲它是用来解决什么问题的。
假设你在做一个稍微复杂点的任务,比如重构待办事项应用那个列表组件。这一个任务里其实塞了好几件事,写新功能、审查代码有没有 bug、翻历史代码找有没有类似的写法、补测试用例。
如果你把这些事全堆在一个对话窗口里做,会发生什么?
主 Agent 一边写新代码,一边翻旧代码找参考,一边看 diff 做审查,一边补测试。它的上下文里很快就塞满了各种东西,旧代码的内容、diff 的细节、测试的输出、你之前纠正过它的那几句话。
聊到第十轮,它又开始忘事了。前面说好的约束又犯了,改了三遍的地方又改回去了。
这个场景你应该眼熟,就是第 5 篇讲的上下文膨胀。
而 Subagent 的核心价值,就是来解决这个的,隔离上下文。
主 Agent 只负责做决策和实现。需要搜索、审查、验证的时候,派一个 Subagent 出去。Subagent 有自己独立的上下文窗口,它在自己那一摊里折腾,干完之后只把结论带回来,不会把一堆中间过程倒进主 Agent 的脑子里。
打个比方,主 Agent 是项目经理,Subagent 是各个工种的工人。项目经理不用亲自去搬砖,他只需要知道砖搬完了,或者砖不够了还差二十块。具体怎么搬的、搬的时候手破没破皮,他不需要全程盯着。

说起来有点抽象,我用那个列表组件的例子走一遍你就懂了。
假设你要把这个列表组件从一个大组件拆成几个小组件。你可以这么分工。
主 Agent 负责实现。它干的是写新组件、调整数据流、保证功能正常这些活。它不需要知道旧代码的每个细节,只需要知道目标是什么。
然后你派一个 Search Agent 去查历史。让它在 codebase 里翻翻,之前有没有类似的组件拆分方式,数据是怎么传的。
在 src/components/ 下找所有列表类组件,看看它们是怎么处理数据获取和渲染分离的。只搜索,不改代码。给我一个清单。
这个 Search Agent 可能读了二十个文件,最后带回来一个清单。但主 Agent 的上下文里只多了这一行结论,那二十个文件的内容它一个字都没沾。
代码写完之后,你再派一个 Review Agent 去看 diff。
看一下这次的 diff,重点检查,
1. 有没有遗漏的 props 传递
2. 有没有引入不必要的 re-render
3. 命名是否和现有风格一致
这一步我觉得特别关键。因为 Review Agent 没参与写代码的过程,它不带着「我刚才是这么想的」那种心理包袱,看 diff 反而更容易挑出毛病。
你想想看,让一个人写完代码立刻自己审自己,他多半审不出问题,因为他脑子里还是写的时候那套逻辑。换个干净的脑子来看,效果完全不一样。
最后再派一个 Test Agent 去补测试。
基于这次改动,补充对应的测试用例。重点覆盖筛选功能的边界情况。
同样的道理,Test Agent 不受「我已经写对了」这种暗示影响,它就是冷冰冰地照着改动写测试,该覆盖的边界一个不漏。
你看,一个重构任务,主 Agent 当指挥,三个 Subagent 各干各的,谁的上下文都不会被别人的活弄脏。

聊到这你大概能感觉到,不是所有任务都适合这么干。我自己摸下来,有几类活派 Subagent 特别值。
第一类,大范围搜索。比如「在整个 codebase 里找所有用了某个旧 API 的地方」。这种活要读一大堆文件,主 Agent 自己做上下文直接爆。派出去,它读五十个文件,只把结果拎回来。
第二类,独立的探索。比如「研究一下这个第三方库的最佳实践,给我点建议」。这种事跟你当前在写的代码没啥关系,放主会话里只会分散注意力。
第三类,并行的子任务。比如「分别给模块 A、B、C 写测试」。三个 Subagent 同时开工,比主 Agent 一个一个磨快太多了。
第四类,重复性的机械活。比如「把 src/ 下所有 console.log 换成 logger.debug」。这种事不值得占主 Agent 的注意力,扔给 Subagent 就行。
第五类,验证类的活。比如「跑测试、读输出、判断过没过」。让一个独立的 Agent 去验证,比让写代码那个自己验证靠谱,原因上面说过了,自己审自己永远审不出问题。
那反过来,哪些活不适合派出去呢?也有几种。
需要主 Agent 上下文的任务别派。Subagent 看不到主对话的历史,你让它去做一件需要了解前面讨论的事,它会一脸懵。
太小的任务别派。派出去本身是有开销的,一秒就能搞定的事,派 Subagent 反而更慢。
需要反复跟你来回确认的任务别派。Subagent 一般不直接跟你对话,它就是接指令、返结果。需要你盯着反复确认的事,还是老老实实在主会话里做。
这块还有个细节得提一下,就是给 Subagent 写指令的方式,跟跟主 Agent 聊天不一样。
主 Agent 你可以慢慢聊,边聊边调。但 Subagent 是一次性的,你把它派出去就基本不管了,所以指令得像写函数签名一样明确。
在 src/ 下找所有直接调用 process.env 的地方。
输出格式,每行 path:line 加上用了哪个变量。
不要修改任何文件,不要进入 node_modules。
完成标准,输出列表加总数。
指令越明确,它带回来的东西就越能直接用。你给个模糊的指令,它就还你个模糊的结果,你还得再花一轮去追问,那就白派了。

如果你发现自己老是派同一类 Subagent,比如每次都要一个帮你看 diff 的,那可以干脆把它预配置好。
Claude Code 支持在 .claude/agents/ 目录下放 Subagent 的定义文件。
.claude/agents/
├── code-reviewer.md 专注 PR 审查
├── explore.md 大范围只读探索
└── test-writer.md 只写测试
每个文件里定义好这个 Agent 的角色、能用哪些工具、system prompt 是什么。主 Agent 需要的时候按名字喊一声就行,不用你每次重新写指令。Cursor 也有类似的功能,在 .cursor/agents/ 下自定义。
聊到这,我想把 Subagent 跟前面的内容串一下。
第 5 篇讲上下文管理的时候,其实埋了一个原则,把那些会膨胀的输出,赶到 Subagent 去。
需要扫整个 codebase、读五十个文件、跑老长的 build log 的活,永远派 Subagent。它读完几千行,最后只把「第 1273 行有个未处理的 promise rejection」这一句话带回来。主 Agent 的上下文干干净净,就多了这一行关键信息。
所以我一直觉得,Subagent 不是什么高级技巧,它就是上下文管理的自然延伸。你把上下文管到一定程度,自然就会冒出「这堆东西不该占主会话」的念头,那一刻你其实已经在找 Subagent 了。
回过头看,到这一篇为止,前 7 篇各自讲了一个独立的能力。角色转变、Spec、AGENTS.md、冷启动、上下文管理、MCP、Subagent。
它们看着像七个零件,但其实是能拼成一台完整机器的。
给你一个小练习。下次你遇到一个需要「翻很多文件才能回答」的问题,别让主 Agent 自己去翻,单独开一个会话或者派一个 Subagent 去查,让它只把结论给你。体会一下主会话那种「没被一堆搜索结果灌满」的清爽感,你就明白这一篇在讲什么了。
下一篇是个中场总结,我会把前 7 篇这些零件拼起来,讲一套可复制的 AI Coding 流程,从需求到提交整条链路怎么走。
作者声明本文无利益相关,欢迎值友理性交流,和谐讨论~
