第 1 卷
16|委派任务:Subagent 的创建、协作与回收
张嘉熙 · DeepSeek Harness 前沿工程实践




你好,我是张嘉熙。
第 15 讲我们让一个 agent 能自己续跑长任务,但它分身乏术,任务里那些能独立、能并行的子任务,比如查资料、数一整个仓库的文件,还得亲力亲为。这一讲我们给它加一个能力:委
派,把活交给子 agent(Subagent)。这跟续跑正好相反:续跑是纵向把一件事做深做透,委派是横向把活分给子 agent 干。委派分成三个问题:
怎么创建一个子 agent?
父 agent 和子 agent 怎么协作?
最后怎么回收?
关于环境:本讲新增五个包:
@deepseek-ai/dsh-subagent(委派服务,ctx.subagents)
@deepseek-ai/dsh-subagent-spawn-in-process(spawn 提供方)
@deepseek-ai/dsh-subagent-fork-in-process(fork 提供方)
@deepseek-ai/dsh-subagent-in-process-driver(共享的进程内驱动器)
@deepseek-ai/dsh-tool-subagent(面向模型的委派工具)
进程外提供方 acp / codex / claude-code / dsh-sdk(对应 dsh-subagent-acp / dsh-subagent-codex / dsh-subagent-claude-code / dsh-subagent-dsh-
sdk)正文点到即止,示例用不上,本讲不展开。面向模型的 dsh-tool-subagentcontrol(子级消息、中断、列举)和 dsh-tool-subagent-report(子级上报)本
讲不展开。另有 peer 依赖 @deepseek-ai/dsh-jobs、@deepseek-ai/dshsession-projection-cache(本讲不展开)。npm install --legacy-peer-
deps 装上即可。
关于示例:npm run start:ch16 让父 agent 用 spawn 委派一个子 agent 去“数文件”,子 agent 跑完返回结果,父 agent 拿到后把它释放掉。示例用假模型,不管 prompt
问什么,都固定回答“仓库里一共 42 个文件”,所以不联网、也不需要 API Key。
怎么创建:提供方、委派方式、深度上限
创建看着简单,但要落地,有几个关键点你要想清楚:用哪个提供方、怎么把活派出去、会不会无限往下委派。这一节我们就按这个顺序走。
先看第一个关键点:用哪个提供方。委派统一走 ctx.subagents 这个 seam,也就是05 讲那个“可替换能力”的组织方式:一个入口、多个实现,这些“实现”就是提供方,每个提供方
决定子 agent 跑在哪。父 agent 只认 ctx.subagents 这一个入口,不认具体提供方,所以换提供方、调用它的代码不用改。提供方可以多个共存,具体有这么几个:
spawn(本讲示例)进程内,空历史起步
选哪个,看需求:要子 agent 独立从头干,用 spawn(空历史);要它接着父 agent 的进度往下干,用 fork(带父历史);要它在进程外跑,才用 acp / codex / claude-code /
dsh-sdk 那几个,让子 agent 在独立进程里由外部实现来跑:Codex、Claude Code、另一个 dsh,或任意 ACP 兼容的 agent。
示例用的是 spawn,所以子会话是全新的:
复制代码 1--- 委派:父 agent 把活交给子 agent ---
2父会话:sess-parent 3子会话是新会话:true(spawn 看不到父的历史)
4子 agent 的委派深度:1
我们逐行看这段日志:父会话:sess-parent 是父 agent 的会话 id;子会话是新会话:
true 说明子 agent 的会话独立于父 agent;子 agent 的委派深度:1 (delegationDepth)是子 agent 在委派树里的深度,比父 agent 深一层:父 agent 是顶
层(0)。
第二个关键点:怎么把活派出去。我们先看一段最小调用(parent 是父 agent):
复制代码
| 1 | construn =awaitctx.subagents.start('spawn', { |
|---|---|
| 2 | parent, |
| 3 | prompt: [{type:'text',text:'数一下这个仓库里有多少个文件,直接回答。'}], |
| 4 | signal:newAbortController().signal, |
| 5 | }) |
| 6 | constresult =awaitrun.result //等它跑完,读result里的output / stopReason |
| 7 | awaitrun.dispose() //父agent用完子agent的句柄后,释放资源 |