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

Task subagent 明明跑完了,主会话却什么都没显示、给个通用总结、或者硬说还在跑。几分钟修好 final message、输出过长、或路由的问题。

你用 Task 工具起了一个 subagent,跑了几分钟、日志也打了、最后顺利结束——结果主 Claude Code 会话要么甩出一个完全通用的总结、要么硬说 subagent「还在跑」、要么直接跳过结果继续往下走。

最快的修法: 让 subagent 这一轮以一条真正的 assistant 文本消息收尾,里面写清楚它动过的文件路径加一段简短总结,并且别原样吐整个文件内容。在你的 Task prompt 里加上这句:「完成后,回复你创建或修改的绝对路径,加 3 条 bullet 的总结。最后一步不要停在工具调用。输出超过约 500 字就写进一个文件、回复路径。」 光这一改就能解决绝大多数回传失败。

原理在架构里。每个 subagent 跑在自己独立的上下文窗口里,只有它最后那条 assistant 消息会回传给 parent——中间的工具调用、日志、文件读取都留在 subagent 内部、直接丢弃(Claude Code 文档:subagents)。如果这条 final message 没了、太长了、或者被原始 transcript 噪音顶替了,parent 就拿不到能用的东西。问题几乎总是出在 subagent 怎么结束这一轮,而不是它有没有做事。

你属于哪一类?

主会话里的症状最可能的原因跳到
Parent 给个通用「done」、没细节Subagent 停在工具调用、没有 final 文本原因 1
Parent 只显示一个开头 / 什么都没有Final message 太长、回传时被截断原因 2
跑完了 parent 还说「还在跑」Subagent 崩了或撞 usage limit原因 3
日志里明明有结果、parent 却无视Prompt 从没说怎么回报原因 4
Parent 知道「done」、但不知道做了什么Subagent 写盘了、没给路径原因 5
Parent 收到一大坨 JSON/JSONL后台 agent 的 TaskOutput bug原因 6
结果进了 transcript、下一轮却没用它Ctrl-C 打断后的竞态原因 7

常见原因

按命中率从高到低。

1. Subagent 最后一步是工具调用、不是 final assistant 消息

如果 subagent 最后一个动作是 WriteEditBash 调用、之后再没有一条收尾的 assistant 文本消息,parent 收到的就是空的或残缺结果。Subagent 必须以一段真正的文字回复结尾。

怎么判断: 打开 subagent transcript(见 Step 1),看最后一条记录。如果 typetool_use、或者这行是 toolUseResult 而不是带 text 块的 assistant 消息,bug 就在这。

2. Final message 太长、超过了回传 buffer

Subagent 结果是作为单条消息回传的。极长的输出(大致几千 token 往上)可能被截断,parent 只能看到一个开头甚至什么都看不到。

怎么判断: 看 subagent 最后一条消息长度。如果是在原样吐整个文件而不是总结,多半超长了。

3. Subagent 崩了或撞 usage limit,没产生结果

Subagent token 用完、撞 rate limit、或者抛了内部错误,可能直接退出、根本没产生 final message。Parent 经常把这种「没回」理解成「还在跑」。

怎么判断: 看 subagent 最后几条记录里有没有 error、usage-limit 信号、或者最后那条 assistant turn 上的 max_tokens 停止原因。

4. Parent 没告诉 subagent 怎么回报

Subagent 严格按它收到的指令做事。你说了「做 X」但没说「把结果按 Y 格式回我」,它可能只往 log 打、以为 parent 会自己读——可它读不到。

怎么判断: 重读你写的 Task 描述。有没有写「回复时给我文件路径加一段总结」?没有的话就是这。

5. Subagent 把结果写到磁盘了、但没告诉 parent 路径

很多 subagent 把大量输出存进文件然后用「done」结尾。Parent 完全不知道去哪读,所以在它看来这个结果就是「done」——没用。

怎么判断: 问 parent「subagent 创建了哪个文件?」答不上来,就是 subagent final message 忘了带路径。

6. 后台 agent 回传了原始 transcript、而不是总结

如果你用 run_in_background=true 起了 subagent、再用 TaskOutput 拉它的结果,一个已知的回归 bug 可能把整段对话当成序列化的 JSONL 字符串回传——四十多条消息、几百 KB——而不是 agent 的 final 文本。症状是:parent 上下文暴涨,结果里出现像 {"parentUuid":...,"isSidechain":true,"type":"user",...} 这样的行(anthropics/claude-code #20531)。

怎么判断: 如果回传的结果是带 isSidechain / parentUuid 字段的 JSONL、而不是一段干净的总结,就是撞了这个。后续的 2.1.x 版本已修复——跑 claude --version,再 claude update。更新之前,用 Step 4 的文件 handoff 绕过。

7. 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(截至 2026 年 6 月,最新是 2.1.x)。
  • 你起 subagent 用的完整 Task prompt。
  • ~/.claude/projects/<project>/ 下的 subagent transcript JSONL。
  • Subagent 声称跑完之后 parent 的回复。
  • Subagent 创建的文件(路径和大小)。
  • 会话结束时显示的 token 使用量(如果有)。

一步一步修复

Step 1:找到 subagent transcript

Claude Code 把每个会话写成一个 JSONL 文件,放在按你项目路径命名的目录下。列出最近几个:

ls -lt ~/.claude/projects/*/*.jsonl | head -10

Subagent(sidechain)的运行单独存,一个 agent 一个文件:

ls -lt ~/.claude/projects/*/*/subagents/agent-*.jsonl | head -10

每一行是一条记录;subagent 的行带 "isSidechain": true、靠 parentUuid 串成链。想只挑出某个 session 文件里 subagent 的轮次:

grep '"isSidechain":true' ~/.claude/projects/<project>/<session-id>.jsonl | tail -5

最后一行会告诉你 subagent 到底是怎么结束的。

Step 2:确认 final message 存在

健康的 subagent 以一条内容里带 text 块的 assistant 记录结尾,比如:

{ "type": "assistant", "message": { "content": [ { "type": "text", "text": "Done. Wrote /tmp/report.md ..." } ] } }

如果最后一条是 toolUseResult(或者一条内容只有 tool_useassistant 记录)、后面再没接 assistant 文本,subagent 就是没产出 final 回复。这是原因 1。

Step 3:改写 Task prompt、明确要结构化回复

不要写「调查 X」,写成:

调查 X。完成后回复时严格包含:
- 你创建或修改的文件路径(绝对路径)
- 3 条 bullet 的发现总结
- 任何遗留问题
最后一步不要停在工具调用。

明确格式要求能显著降低回传失败率。如果你反复要起同一种 worker,就把这套约定固化成一个可复用的 subagent 定义,放在 .claude/agents/ 下(用 /agents 创建),这样每次运行都继承同样的回报规则。

Step 4:把 subagent 输出长度封住(文件 handoff)

让 subagent 总结而不是原样吐:

如果结果超过约 500 字,把完整输出写到一个文件、
回复时只给绝对路径加一段简短总结。

这能避开原因 2 的 relay buffer 截断,同时也是原因 6 里 TaskOutput 后台 agent bug 的绕法:parent 直接读文件,不去消费那个臃肿的 task.output 字段。

Step 5:用小任务复现

用同一个 subagent 跑一个琐碎任务(「列出 src/ 下 3 个文件」)。小任务能正常回传,那原来的失败就是大小或格式问题。小任务也失败,怀疑崩溃或 usage limit。

Step 6:查 usage-limit 或 token-cap 报错

在 subagent transcript 里搜 usage_limitmax_tokenserror

grep -E 'usage_limit|max_tokens|"type":"error"' ~/.claude/projects/<project>/<session-id>.jsonl

找到的话,缩窄 subagent 的范围、或者拆成两个 subagent。limit 这一侧详见 Claude Code 到一半因 usage limit 停下

Step 7:竞态情况下,重跑时别用 Ctrl-C 打断

Subagent 在跑的时候别按 Ctrl-C,让它自己跑完。一定要取消,就整轮取消重起,不要半途打断。

怎么验证修好了

  • Parent 下一条 assistant 消息引用了 subagent 真实的发现,不是通用的「done」。
  • Subagent 写的文件在 parent 回复里有具体绝对路径。
  • 同一个 prompt 重跑,结果回传稳定。
  • Subagent transcript 最后一条记录是一条 assistanttext 消息,不是工具调用、也不是一坨原始 JSONL。

长期预防

  • 写 Task prompt 时用一段固定模板:「按结构化总结回复,最后一步不要停在工具调用」。
  • 任何输出超过几百行的 subagent 都走文件 handoff。
  • 给 subagent 任务范围硬上限:一个 subagent 一个明确小任务。
  • 定期翻 transcript,找反复出现的回传失败、迭代你的 prompt 模板或 .claude/agents/ 定义。
  • 范围一大就用多个小 subagent,不要一个长 subagent 撑下来。
  • claude update 把 Claude Code 保持最新;2.1.x 这一路修了好几个回传和 TaskOutput 的回归 bug。

容易踩的坑

  • 以为 parent 能「看到」subagent 中间的日志。看不到,只有 final message 会回。
  • 让 subagent 把整个文件内容原样吐回来、不做总结。
  • Ctrl-C 半路打断、然后指望 parent 自动恢复优雅状态。
  • 忘了 subagent 跑在隔离上下文里,除非明说它读不到 parent 的变量或文件。
  • 给一个根本不需要隔离的任务也起 subagent,有时候 inline 更合适。

常见问答

  • 为什么 subagent 跑完了 parent 还说在跑? Final message 缺失或为空(原因 1 或 3)。Parent 没收到完成信号,要么等、要么乱猜。
  • Parent 能读到 subagent 的工具调用吗? 读不到。只有收尾的 assistant 文本消息会回传,中间的工具调用和结果都丢掉。
  • 回传消息有大小上限吗? 没有公开的硬数字,但实操上你要让 final message 控制在几千 token 这个量级。再多就总结、或写盘后回传路径。
  • 为什么我收到一大坨 JSONL 而不是总结? 那是 TaskOutput 后台 agent 的回归 bug(原因 6)。用 claude update 更新 Claude Code,同时让 agent 写文件、回传路径。
  • 插件定义的 agent 和自定义 agent 也会这样吗? 会。所有通过 Task 起的 subagent,包括内置的 Explore、Plan,以及 .claude/agents/ 里的,都遵循同一套 final-message 回传契约。
  • subagent transcript 存在哪?~/.claude/projects/<project>/ 下,是 .jsonl。Subagent 的运行在 <session-id>/subagents/agent-*.jsonl;subagent 的行带 "isSidechain": true

相关

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