结论先行
单个 Agent 跑不了大任务,不是它”不想干”,而是它的记忆有硬上限。
解法:用”主-子 Agent”模式——主 Agent 只负责调度,子 Agent 各自独立执行小任务,进度写到文件里持久化。
一句话总结:多个 Agent 各司其职、快进快出,把进度交给文件系统来记忆。
你可能遇到过这个问题
给 Claude Code 安排一个大任务,比如”把项目里大概 1000 个单元测试全部补全”。结果它跑了 200 个就停下来,问你”要继续吗?”
你回复”继续”,它又跑了一会儿,这次更快就停了,而且开始出现奇怪的行为——重复检查已经修好的测试、忘记之前的修改思路。
这不是 bug,是单 Agent 架构的物理限制。
根因:Context 的”记忆衰退”
要理解这个问题,先搞清楚一个核心概念:
Context window(上下文窗口):AI 模型一次对话中能”看到”的全部信息量,包括你的指令、它的回复、工具调用结果等。你可以把它想象成一张桌子——桌子大小固定,放的东西越多,就越挤。
当单个 Agent 执行大任务时,会经历这样一个过程:
阶段 1:高效期
刚开始,context 还很空旷。Agent 指令遵循度高,执行又快又准。就像一张干净的桌子,放什么都看得清。
阶段 2:逼近上限
跑了大约 80k tokens 后,context 开始接近 compact 阈值(compaction threshold,即系统自动压缩对话历史的触发点)。桌子快满了。
阶段 3:压缩与遗忘
系统触发 auto-compact——把之前的对话历史压缩成摘要。这就像把桌上的一摞文件塞进一个信封,写上”之前做的事情”。信封里的细节?没了。
Agent 开始忘记:第 53 个测试报错的具体原因是什么、它是怎么修的、为什么那样修。
阶段 4:退化与退出
再经过一两轮 compact,Agent 甚至会重复检查已经修好的测试(因为它不记得修过了)。当触发 maxTurns(最大轮次限制)且 response 里没有工具调用指令时,它判定”当前任务告一段落”,于是停下来问你。
整个过程用一条链来概括:
context 膨胀 → 触发 compact → 细节丢失 → 效率下降 → 行为异常 → 退出
为什么”回复继续”没用?
因为 compact 后的 context 已经是”残缺的记忆”。在这个基础上继续工作,就像拿着一份只有目录没有正文的笔记去接着干活——不仅效率更低,还更容易出错,退出得也更快。
解法:主-子 Agent 调度模式
既然单 Agent 的瓶颈是”一张桌子放不下所有东西”,那就用多张桌子:
- 主 Agent(Coordinator):只负责分配任务和追踪进度,不亲自写代码。它是项目经理。
- 子 Agent:每个子 Agent 领一个小任务,有自己独立的 context(独立的桌子),做完就退出。它们是开发者。
- 文件系统:充当共享白板,记录哪些任务完成了、哪些失败了、哪些还没开始。
这样每个子 Agent 都在”干净的桌子”上工作,不受之前任务的干扰,也不会撞上 context 上限。
具体怎么做:三个关键设计
1. 任务分解——把大象切成肉片
错误做法:给 Agent 一句”修复所有失败的单测”。
正确做法:
- 先让 Agent 扫描所有失败测试
- 按目录或模块分组,每组 15-30 个
- 为每组生成一个自包含的任务描述
“自包含”是关键——每个子任务的 prompt 必须写清楚:
- 文件路径是什么
- 错误现象是什么
- 期望行为是什么
绝对不能写”根据之前的分析来修复”,因为子 Agent 看不到主 Agent 的历史。这就像你给外包团队派活,必须把需求写得明明白白,不能假设他们知道你之前开了什么会。
2. 进度持久化——把记忆交给文件系统
在项目根目录维护一个 progress.json:
{
"completed": ["tests/user/login.test.ts", "tests/user/signup.test.ts"],
"failed": ["tests/payment/refund.test.ts"],
"pending": ["tests/order/create.test.ts", "tests/order/cancel.test.ts"]
}
工作流程:
- 主 Agent 每轮调度前读
progress.json,决定下一批任务 - 子 Agent 完成后更新对应条目
- 即使主 Agent 自己被 compact,重读文件就能恢复全部状态
这就是把”记忆”从 Agent 的脑子里(context)搬到了硬盘上(file system)。硬盘不会忘事。
3. 失败策略——不要无限重试
子 Agent 报错时,有三种应对:
| 情况 | 策略 | 原因 |
|---|---|---|
| 错误可修复(如少导入了一个模块) | 用 SendMessage 继续同一个子 Agent | 保留错误上下文,修复效率最高 |
| 方向完全错了(如用了错误的 API) | 启动新的子 Agent | 避免锚定效应,老 Agent 会沿着错误路径继续走 |
| 多次失败(如超过 3 次) | 上报用户,人工介入 | 不要无限重试烧 token |
类比:就像你管理团队——小问题让开发者自己修,大方向错了换个人重做,反复搞不定就升级到你这里决策。
在 Claude Code 中的实现
Claude Code 已经内建了这套能力,最直接的方式是启用 Coordinator Mode(输入 /coordinator):
- 主 Agent 自动变成纯调度者:不执行任何实际工具调用,只负责理解子 Agent 的返回、合成下一步指令、并行派发独立任务
- 每个子 Agent 通过 AgentTool 启动,拥有独立 context
- 独立子任务可以并行执行,进一步提升效率
常见误区
| 误区 | 为什么是错的 |
|---|---|
| ”我给足够详细的 prompt 就能让单 Agent 跑完” | context 物理上限无法靠 prompt 质量突破 |
| ”回复继续就能接着跑” | compact 后的 context 已经是残缺记忆,继续只会更差 |
| ”子 Agent 的 prompt 可以引用主 Agent 的分析” | 子 Agent 看不到主 Agent 的历史,prompt 必须自包含 |
| ”失败了就一直重试” | 重试烧 token 且可能锚定在错误路径上 |
总结
单 Agent 跑长程任务失败的本质原因是 context 有限且不可恢复。解决方案不是”让 Agent 更聪明”,而是改变架构:
- 任务分解:大任务拆成独立的小任务,每个 prompt 自包含
- 进度持久化:用文件系统记录状态,不依赖 Agent 的记忆
- 失败策略:可修复就继续,方向错就重启,反复失败就上报
这就是从”一个人干所有活”到”一个团队分工协作”的架构升级。
FAQ
Q:每个子任务分组 15-30 个测试,这个数字怎么来的?
A:经验值。太少(比如 3-5 个)调度开销大,主 Agent 要频繁启动/回收子 Agent;太多(比如 100 个)子 Agent 自己也会撞上 context 上限。15-30 是一个”子 Agent 在 compact 之前能舒服完成”的甜区,具体数字可以根据任务复杂度调整。
Q:progress.json 被子 Agent 写坏了怎么办?
A:可以在主 Agent 端加一层校验:读取后检查格式是否合法、条目是否重复、状态是否自洽。本质上就是给”共享状态”加一把锁。实践中用 JSON Schema 校验就够了。
Q:Coordinator Mode 和手动写调度脚本有什么区别?
A:Coordinator Mode 是 Claude Code 内建的轻量方案,开箱即用,适合中等规模任务。如果你的任务需要更复杂的调度逻辑(如优先级队列、依赖关系图、并发度控制),可以自己写 harness 脚本来精细控制。两者不冲突,可以组合使用。
Q:这套方案适用于所有 AI coding agent 吗?
A:核心思路(任务分解 + 进度持久化 + 主子调度)是通用的,不限于 Claude Code。任何有 context 上限的 LLM Agent 都适用。具体实现方式(如 AgentTool、SendMessage)是 Claude Code 特有的 API,其他工具需要用各自的 agent 调度接口。