Subagent 是一个 seam:从进程内到委派 Claude Code
子 Agent 是能力接缝,进程内、远程、别家产品都能接
本页解决的问题
先给结论「Subagent 是一个 seam:从进程内到委派 Claude Code」要解决的关键问题是什么?
子 Agent 是能力接缝,进程内、远程、别家产品都能接
让这个结论先证明自己值得留下。 把这一页当成决策工具,而不是需要背下来的定义。把概念连到一个真实任务、一个可观察结果,以及一个能改变你判断的失败上。
写下一个问题:试完这个方法后,你能用什么证据回答它?
结论听起来很完整,却没有检查最关键的假设。
同一个子任务「调研这个模块并汇报」,切换四个 provider 分别委派一次。观察两件事:接缝两端哪些东西完全不变,哪些东西随实现而变。再打开「附带 persona 要求」开关,看能力门闩怎么把超纲请求拦在启动之前。
| 启动时能力 | spawn |
|---|---|
| outputSchema(结构化输出) | 支持 |
| depthLimit(委派深度上限) | 支持 |
| toolFilter(限工具) | 支持 |
| persona(换人设) | 支持 |
subagent-spawn-in-process/src/index.ts 第 42 行、subagent-fork-in-process/src/index.ts 第 62 行),Claude Code 与 Codex 均为 NO_START_CAPABILITIES(各自 src/index.ts 第 54、49 行)。报错文案逐字复刻 packages/subagent/subagent/src/index.ts 第 490 至 493 行。先说结论。DSH 没有做一个单独的子 Agent 功能,它做的是一个叫 ctx.subagents 的注册表:任何实现了 SubagentProvider 约定的传输层,都可以按名字注册进来。官方发行版注册了六个:spawn(进程内新开)、fork(进程内带上下文)、acp(协议桥)、codex、claude-code(各起一个真实的产品 CLI 进程)、sdk(远程 DSH 实例)。出处在 docs/subsystems/subagent.zh.md 第 5 至 7 行。
输入是什么:工具层把模型的委派请求组装成 SubagentStartRequest,带上 prompt、父 Agent、取消信号,外加四个可选项(结构化输出 schema、深度上限、工具过滤、persona)。发生什么:服务先查所选 provider 的静态能力表,四个可选项每一项都要有对应的能力 flag,缺一项就在启动之前抛 UNSUPPORTED_CAPABILITY。输出是什么:一个 SubagentRun 句柄,父 Agent 等它的 result,最后落成一条普通的工具结果。对父 Agent 来说,四种 provider 回来的都是同一种东西。
值得停一下的是 fork 的身份。很多框架把带不带父上下文做成一个布尔参数。DSH 把 fork 做成了一个独立 provider,原因是它俩差的不只是一个开关:fork 要从父日志里切出「已完成轮次的平衡前缀」当种子,切到最后一个 turn/end 为止,进行中的轮次不平衡、回放不了,必须排除。这是会话日志层面的约定,塞进一个 flag 里说不清楚。
fork 与 spawn 是两个 provider差别不在参数,在种子:fork 把父日志切到最后一个 turn/end 做子会话种子,spawn 从零开始。inheritsParentContext 只是描述性字段,供工具层生成不骗人的措辞。
一次性 run 与可继续 ActivationSubagentRun 是一锤子买卖:等结果、dispose、结束。可继续子 Agent 没有 run,它是一份持久会话加至多一个驻留 Activation,父级用 send_message 追加轮次、interrupt_agent 打断、收 report。
后台结束不会静默可继续子 Agent 结算时,管理器无条件给父级投一条 subagent-settled 通知,带最终输出。它与子 Agent 主动的 report 用不同的消息来源 kind,transcript 不会把运行时的记账算成子 Agent 说的话。
第一段证据是能力门闩本体,逻辑不贴代码也说得清。assertCapabilities 把请求里的四个可选项排成一张需求清单:请求带了 outputSchema,就要求 provider 能力表里 outputSchema 为真;带了 maxDepth 就要求 depthLimit;toolFilter 和 persona 同理。然后逐项对照,第一个对不上的当场抛 SubagentError,错误文案直说哪个 provider 不支持哪个能力,错误码 UNSUPPORTED_CAPABILITY。没有降级、没有警告后继续,子进程在这之前一个都不会启动。
出处:packages/subagent/subagent/src/index.ts 第 481 至 495 行,核对日期 2026-08-13。演示区的报错文案逐字复刻自第 490 至 493 行。
第二段证据是 fork 的种子函数,七行说完带上下文到底带的是什么:
function completedTurnPrefix(parent: Agent): SessionEvent[] {
const events = parent.session.events
const lastEnd = events.findLast(e => e.type === 'turn/end')
if (lastEnd === undefined) return []
// seq === array index (the append contract), so slice up to and including it.
return events.slice(0, lastEnd.seq + 1)
}
packages/subagent/subagent/src/index.ts 与 packages/subagent/subagent-fork-in-process/src/index.ts,核对日期 2026-08-13。代码块保留源码原文。还有两处机制不贴代码,用文字标出处。Claude Code provider 的整个实现只是:解析 claude 可执行文件,在父会话的 cwd 里通过官方 Agent SDK 起一个真实 CLI 进程,把它挂到共享的 subprocess owner 之下(subagent-claude-code/src/index.ts 第 62 至 91 行)。发行版的四个官方 preset 里,codex 与 claude-code 的委派工具行都带着 disabled: true 出厂,注释写明:复制 preset、删掉 disabled,就能只对复制版的会话开放这个产品后端(apps/cli/config/agent-presets/standard/agent.cordis.yml 第 200 至 219 行)。委派别家产品在 DSH 里是一个配置开关的事。
Claude Code 把委派做在产品层。Task 工具(AgentTool)一个入口,参数里塞进各种形态:subagent_type 挑角色、run_in_background 后台、isolation: "worktree" 开隔离副本、model 换模型(书稿 study/chapters/05-multi-agent.md 第 32 至 52 行引 AgentTool.tsx 原文)。往上还有 Coordinator Mode 和 Agent Teams 两套模式。表达力很强,但每种形态都是这一个产品里的功能分支:委派对象永远是另一个 Claude Code 实例,把任务交给另一个产品这件事没有位置。DSH 的选择相反,产品差异下沉到 provider 一层,接缝之上只有一个词汇表。
Grok Build 走的是第三条路:把子 Agent 的配置解析抽成纯逻辑库 xai-grok-subagent-resolution,按 explicit override > role > persona > parent 的优先级解析出生效配置(该 crate src/lib.rs 第 7 至 8 行),执行仍在自家 shell 进程内。它抽象的是子 Agent 长什么样,DSH 抽象的是子 Agent 跑在哪。Grok 的角色、上下文继承与树形谱系,站内已有三课展开:子 Agent 的四种角色、上下文继承与深度控制、子 Agent 的会话谱系。
推演一次超纲委派
部署启用了 subagent_claude_code 工具,模型发起委派时带上了 outputSchema(要求结构化输出)。请按本课能力门闩的代码推演:错误在哪一行抛出、错误码是什么、Claude Code 的 CLI 进程有没有被启动过?再回答:如果 DSH 选择接受请求但忽略 schema,父 Agent 拿到的工具结果会出什么问题,为什么这比当场报错更难排查?
「交互演示 · 委派切换台」的能力藏在每次交接里
「同一个子任务「调研这个模块并汇报」,切换四个 provider 分别委派一次。观察两件事:接缝两端哪些东西完全不变,哪些东西随实现而变。再打开「附带 persona 要求」开关,看能力门闩怎么把超纲请求拦在启动之前」说明,Agent 的表现不只由模型决定。模型、上下文、工具、状态、权限和人之间的每次交接,都会改变任务能否继续以及出了问题能否恢复。
先写清状态,再增加能力
从「先说结论。DSH 没有做一个单独的子 Agent 功能,它做的是一个叫 ctx.subagents 的注册表:任何实现了 SubagentProvider 约定的传输层,都可以按名字注册进来。官方发行版注册了六个: spawn (进程内新开)、 fork (进程内带上下文)、 acp (协议桥)、 codex 、 claude-code (各起一个真实的产品…」出发,可以把流程拆成起始状态、下一步动作、工具返回、状态更新和停止条件。这样调试时找的是第一处丢失信息或权限的位置,而不是笼统地说“模型变笨了”。
成功路径不能代表系统可靠
用「部署启用了 subagent_claude_code 工具,模型发起委派时带上了 outputSchema (要求结构化输出)。请按本课能力门闩的代码推演:错误在哪一行抛出、错误码是什么、Claude Code 的 CLI 进程有没有被启动过?再回答:如果 DSH 选择接受请求但忽略 schema,父 Agent 拿到的工具结果会出什么问题,为什么这比当场报…」重放一次成功和一次失败,记录每一轮真正传入的上下文、工具结果和负责人;只要第二个人能复述这条链,系统才有可维护性。
从「交互演示 · 委派切换台」走到「逻辑拆解 · seam 定义在哪一层」
「交互演示 · 委派切换台」先把问题落在「同一个子任务「调研这个模块并汇报」,切换四个 provider 分别委派一次。观察两件事:接缝两端哪些东西完全不变,哪些东西随实现而变。再打开「附带 persona 要求」开关,看能力门闩怎么把超纲请求拦在启动之前」上;到了「逻辑拆解 · seam 定义在哪一层」,讨论继续推进到「先说结论。DSH 没有做一个单独的子 Agent 功能,它做的是一个叫 ctx.subagents 的注册表:任何实现了 SubagentProvider 约定的传输层,都可以按名字注册进来。官方发行版注册了六个: spawn (进程内新开)、 fork (进程内带上下文)、 acp (协议桥)、 codex 、 claude-code (各起一个真实的产品 CLI 进程)、 sdk (远程 DSH 实例)。出处在…」。两段连起来,重点就不只是记住一个结论,而是看清它成立所依赖的条件。
把这条判断带到下一个场景
分析 Agent 时,沿着状态、动作、工具结果和下一步的顺序走一遍;每次交接都要能说明信息从哪里来、由谁确认、失败时停在哪里。
- 「交互演示 · 委派切换台」:同一个子任务「调研这个模块并汇报」,切换四个 provider 分别委派一次。观察两件事:接缝两端哪些东西完全不变,哪些东西随实现而变。再打开「附带 persona 要求」开关,看能力门闩怎么把超纲请求拦在启动之前
- 「逻辑拆解 · seam 定义在哪一层」:先说结论。DSH 没有做一个单独的子 Agent 功能,它做的是一个叫 ctx.subagents 的注册表:任何实现了 SubagentProvider 约定的传输层,都可以按名字注册进来。官方发行版注册了六个: spawn (进程内新开)、 fork (进程内带上下文)、 acp (协议桥)、 codex 、 claude-code (各起一个真实的产品 CLI 进程)、 sdk (远程 DSH 实例)。出处在…
- 「最后的要点」:第一段证据是能力门闩本体,逻辑不贴代码也说得清。 assertCapabilities 把请求里的四个可选项排成一张需求清单:请求带了 outputSchema,就要求 provider 能力表里 outputSchema 为真;带了 maxDepth 就要求 depthLimit;toolFilter 和 persona 同理。然后逐项对照,第一个对不上的当场抛 SubagentError ,错误文案直说哪个 pr…
最后的「最后的要点」把讨论落到「第一段证据是能力门闩本体,逻辑不贴代码也说得清。 assertCapabilities 把请求里的四个可选项排成一张需求清单:请求带了 outputSchema,就要求 provider 能力表里 outputSchema 为真;带了 maxDepth 就要求 depthLimit;toolFilter 和 persona 同理。然后逐项对照,第一个对不上的当场抛 SubagentError ,错误文案直说哪个 pr…」。回看这条线索时,最值得保留的是:当输入、规模或风险改变,哪些判断需要重新做一遍。
我把这篇文章里的一个判断改写成了今天可以验证的小实验。比记住结论更有用的是,知道下一步要观察什么。
读完以后我先回头找它成立的条件,而不是直接把方法搬进项目。这个顺序让后面的取舍清楚很多。
如果把这个判断放到真实工作里,最先需要补的约束是什么?我想知道从阅读到第一次实践之间,哪一步最值得先做。
还没有这篇文章的讨论。