你用 Task 工具起了一个 subagent,跑了几分钟、日志也打了、最后顺利结束——结果主 Claude Code 会话要么甩出一个完全通用的总结、要么硬说 subagent「还在跑」、要么直接跳过结果继续往下走。底层架构是:subagent 在一个隔离的上下文里跑,它最后那条 assistant 消息是唯一会回传给 parent 的东西。如果这条 final message 没了、太长了、或者被一堆工具调用噪音淹没了,parent 拿不到能用的东西。修复的关键几乎总是 subagent 怎么结束这一轮,而不是它有没有做事。
常见原因
按命中率从高到低。
1. Subagent 最后一步是工具调用、不是 final assistant 消息
如果 subagent 最后一个动作是 Write 或 Bash 调用、之后再没有一条收尾的 assistant 文本消息,parent 收到的就是空的或残缺的结果。Subagent 必须以一段真正的文字回复结尾。
怎么判断:看 subagent transcript JSON 的最后一条。如果是 tool_use 或 tool_result 而不是 assistant.text,bug 就在这。
2. Final message 太长、超过了回传 buffer
Subagent 结果是作为单条消息回传的。极长的输出(一万 token 以上)可能被静默截断或拒绝,parent 只能看到一个开头甚至什么都看不到。
怎么判断:看 subagent 最后一条消息长度。如果是在原样吐整个文件而不是总结,多半超长了。
3. Subagent 崩了或撞 usage limit,没产生结果
Subagent token 用完、撞 rate limit、或者抛了内部错误,可能直接退出、根本没产生 final message。Parent 经常把这种「没回」理解成「还在跑」。
怎么判断:看 subagent 最后几条条目里有没有 error、usage_limit_reached 或 max_tokens 截断标记。
4. Parent 没告诉 subagent 怎么回报
Subagent 严格按它收到的指令做事。你说了「做 X」但没说「把结果按 Y 格式回我」,它可能只往 log 打、以为 parent 会自己读——可它读不到。
怎么判断:重读你写的 Task 描述。有没有写「回复时给我文件路径加一段总结」?没有的话就是这。
5. Subagent 把结果写到磁盘了、但没告诉 parent 路径
很多 subagent 把大量输出存进文件然后用「done」结尾。Parent 完全不知道去哪读。在 parent 看来这个结果就是「done」——没用。
怎么判断:问 parent「subagent 创建了哪个文件?」答不上来,就是 subagent final message 忘了带路径。
6. Parent 状态老化——subagent 还没回 parent 就往前走了
少数情况(特别是手动 Ctrl-C 之后),parent 在 subagent 回报之前就推进了自己这一轮。结果会落进 transcript、但下一条 assistant 消息根本没消费它。
怎么判断:看消息顺序。如果在 subagent dispatch 和 subagent return 之间出现了 parent 这边的 assistant 消息,就是撞了这种竞态。
开始前
- 想清楚 subagent 能不能重跑,还是说成本太高重复不起。
- 在 transcript 被覆盖前先存一份(Claude Code 存在
~/.claude/projects/...下)。 - 把你用的 Task prompt 原文记下来,多半要改写。
- 准备一个小复现任务用来测验证修复。
需要收集的信息
- Claude Code 版本:
claude --version。 - 你起 subagent 用的完整 Task prompt。
~/.claude/projects/<project>/下的 subagent transcript JSON。- Subagent 声称跑完之后 parent 的回复。
- Subagent 创建的文件(路径和大小)。
- 会话结束时显示的 token 使用量(如果有)。
一步一步修复
Step 1:找到 subagent transcript
ls -lt ~/.claude/projects/*/sessions/ | head -10
打开最近的那个 session JSON,找到 subagent 那一段。最后一条会告诉你它到底是怎么结束的。
Step 2:确认 final message 存在
健康的 subagent 长这样结束:
{ "role": "assistant", "content": [ { "type": "text", "text": "Done. Wrote /tmp/report.md ..." } ] }
如果最后是 tool_result 后面什么 assistant 文本都没接,subagent 就是没产出 final 回复。
Step 3:改写 Task prompt、明确要结构化回复
不要写「调查 X」,写成:
调查 X。完成后回复时严格包含:
- 你创建或修改的文件路径(绝对路径)
- 3 条 bullet 的发现总结
- 任何遗留问题
最后一步不要停在工具调用。
明确格式要求能显著降低回传失败率。
Step 4:把 subagent 输出长度封住
让 subagent 总结而不是原样吐:
如果结果超过约 500 字,把完整输出写到一个文件、
回复时只给路径加一段简短总结。
避开 relay buffer 截断这种情况。
Step 5:用小任务复现
用同一个 subagent 跑一个琐碎任务(「列出 src/ 下 3 个文件」)。小任务能正常回传,那原来的失败就是大小或格式问题。小任务也失败,怀疑崩溃或 usage limit。
Step 6:查 usage-limit 或 token-cap 报错
在 subagent transcript 里搜 usage_limit、max_tokens、error。找到的话,缩窄 subagent 的范围、或者拆成两个 subagent。
Step 7:竞态情况下,重跑时别用 Ctrl-C 打断
Subagent 在跑的时候别按 Ctrl-C,让它自己跑完。一定要取消,就整轮取消重起,不要半途打断。
怎么验证修好了
- Parent 下一条 assistant 消息引用了 subagent 真实的发现,不是通用的「done」。
- Subagent 写的文件在 parent 回复里有具体路径。
- 同一个 prompt 重跑,结果回传稳定。
- Subagent transcript 以一条 assistant 文本消息结尾,不是以工具调用结尾。
长期预防
- 写 Task prompt 时用一段固定模板,包含「按结构化总结回复,最后一步不要停在工具调用」。
- 任何输出超过几百行的 subagent 都走文件 handoff。
- 给 subagent 任务范围硬上限;一个 subagent 一个明确小任务。
- 定期翻 transcript,找反复出现的回传失败、迭代你的 prompt 模板。
- 范围一大就用多个小 subagent,不要一个长 subagent 撑下来。
容易踩的坑
- 以为 parent 能「看到」subagent 中间的日志,看不到,只有 final message 会回。
- 让 subagent 把整个文件内容原样吐回来、不做总结。
- Ctrl-C 半路打断、然后指望 parent 自动恢复优雅状态。
- 忘了 subagent 跑在隔离上下文里,除非明说它读不到 parent 的变量或文件。
- 给一个根本不需要隔离的任务也起 subagent,有时候 inline 更合适。
常见问答
- 为什么 subagent 跑完了 parent 还说在跑? Final message 缺失或空。Parent 没收到完成信号,要么等、要么乱猜。
- Parent 能读到 subagent 的工具调用吗? 读不到,只有收尾的 assistant 文本消息会回传,其它都丢掉。
- 回传消息有大小上限吗? 实际操作上几千 token 之内最稳。再多就总结或写盘。
- 插件定义的 agent 也会这样吗? 会。通过 Task 起的所有 subagent 都遵循同一套回传契约。
- 是不是每次都该要求结构化回复? 是。结构化回复是单项收益最大的可靠性改进。
- subagent transcript 在哪看? 存在
~/.claude/projects/<project>/sessions/下,打开最新的 JSON。