Claude Code subagent 结果回传不到主会话

Task/Agent subagent 跑完了,但主会话什么都没收到、或者总结对不上。多半是 final message 格式、上下文窗口、或者线程路由出了问题。

你用 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_usetool_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_reachedmax_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_limitmax_tokenserror。找到的话,缩窄 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。

相关

标签: #Claude Code #subagent #agent #排查