Skip to content

什么是子代理

子代理(subagent) 是一个拥有自己的会话、自己的 scope、自己的回合循环的子级 Agent;某个代理启动它,以把一件自包含的任务外包出去。在 DeepSeek Harness 中,子代理并非特殊的运行时模式,而是一个能力接缝(capability seam)ctx.subagents,由 packages/subagent/subagent 拥有),背后是按名称注册的 provider 注册表,因此某个代理通过同一个服务 API 委派,而传输层是可替换的。与 shell 接缝(单一执行器)不同,子代理接缝允许多个 provider 按名称共存。与 shell 工具的另一个区别是:子代理不是工具,而是一个 scoped 服务;模型真正调用的工具(subagentsend_messagereport 等)只是它的薄消费者。

领域词汇来自术语表:子代理拥有自己的 scope,它的父子事实作为 谱系(lineage) 数据传递,而创建者在setup 窗口中组合子代理的世界。Scope 刻意保持扁平——scoped 注册不会向下继承给子级;子代理需要的一切都在 setup 期间被显式组合。

父 Agent ──ctx.subagents.start/startContinuable──▶ provider
                                                          │ (spawn | fork | acp | codex | claude-code | dsh-sdk)

                                              子 Agent + 子 Session
                                              (自己的 scope、自己的回合循环)
                                                          │ report / 结算

                                                    父回合流

包家族

角色ctx key传输层
subagentService Definition:provider 注册表、契约、描述符、延续ctx.subagents
subagent-in-process-driver共享的一次性运行驱动(深度、组合、结构化输出)进程内
subagent-spawn-in-process全新子级,无父历史注册到 ctx.subagents进程内
subagent-fork-in-process以父代理已完成的 turn 为种子的子级注册到 ctx.subagents进程内
subagent-dsh-sdk经 TS SDK 的跨进程 Harness 子级注册到 ctx.subagents外部进程
subagent-acp经 Agent Client Protocol 的跨进程子级注册到 ctx.subagents外部进程
subagent-claude-code真实 Claude Code 子级(官方 SDK)注册到 ctx.subagents外部进程
subagent-codex真实 Codex app-server 子级注册到 ctx.subagents外部进程
tool-subagent面向模型的委派工具(subagent注册到 ctx.tools
tool-subagent-control面向模型的 send_message / interrupt_agent / list_agents注册到 ctx.tools
tool-subagent-report子级作用域的 report 回传通道注册到子作用域
client/ui-subagentWeb 目录树、@ 引用、composer 控制ctx 事件 + ctx.inputTriggers

服务契约(dsh-subagent

接缝的核心是 packages/subagent/subagent/src/index.ts 中的 SubagentRuntime 服务,而类型化契约位于 src/types.ts

成员含义
registerProvider(provider)按名称注册可信的进程内 provider;重名会 loudly 失败。
start(name, request)一次性前台委派;子代理发布后以持有者拥有的 SubagentRun 兑现。
startContinuable(spec)建立一个持久的可延续子级并投递其初始 prompt;返回 { childId, messageId }
followup(parent, childId, content, …)从确切的存活直接父代理投递一条后续消息,作为子级的下一 FIFO turn。
interrupt(targetSessionId, authority)停止某个存活可延续子级的当前 turn(keepInbox: true)。
reportFrom(child, content, …)从确切的存活可延续子级向它的直接父代理投递一条精选消息。
listChildren(parentSessionId) / listDescendants(rootSessionId)枚举持久化的子代理目录。

Provider 契约

SubagentProvider(在 src/types.ts 中)是各传输层实现的接口:{ name, capabilities, inheritsParentContext, start(request), prepareContinuable?() }capabilities 是四个布尔值,服务会在委派之前验证——outputSchemadepthLimittoolFilterpersona——因此需要未支持特性的请求会 loudly 被拒,而非接受后再忽略。inheritsParentContext描述性的,不可执行:它只说明子级是否可见已完成的父历史(fork 是;spawn 和外部 provider 否),绝不说明是否继承工具、服务或权限。

prepareContinuable?() 是那个可选方法,其存在本身就是延续能力。它只返回一个分离的 ContinuableCreateSpec{ seed? })——是数据,绝非能力——因为延续管理器在准备之后拥有身份预留、组合、Agent 创建、prompt 投递、冷恢复、所有权与销毁。

创建选项

SubagentStartRequest 携带一次性委派选项。面向模型的工具根据模型的 { description, prompt } 加上自身配置来构造它:

字段含义
prompt作为子代理用户消息投递的内容。
parent派生的 Agent;进程内 provider 从中推导工作目录、谱系与委派深度。
signal规范的取消通道,启动前后都是。
agentOptions可选的子级 providermodelmaxTokens 覆盖。
outputSchema用于结构化最终结果的对象根 JSON Schema(需 outputSchema 能力)。
maxDepth子级的绝对委派深度上限(需 depthLimit)。
toolFilter子级工具限制,作为 scoped tools.restrict() 应用(需 toolFilter)。
persona每子级 persona,遮蔽部署 persona(需 persona)。
label会话支持子级的可选持久显示标签。

Setup 窗口与委派策略

组合发生在子代理的 setup 窗口内——在 scope 与 agent 存在之后、agent/session 发布之前。对于进程内子级,applyChildComposition(childCtx, parent, composition)(在 packages/subagent/subagent/src/child-agent.ts 中)在应用子级自己的 persona 与工具过滤器之前先合并父代理的 agent-preset 组合——正是这个合并让面向模型的子级拥有可用的工具注册表。childSessionMeta() 把已合并的 preset id 记录到持久 header 上,以使冷读重建同样的组合。

委派还会在边界上固定子级的权限范围(src/child-agent.ts 助手):captureDelegatedPolicyOverrides(parent) 快照父会话的显式 sandbox 覆盖,并在组合了审批能力时把子级的审批策略钉为 'never',从而每次 sandbox_permissions 询问都被确定性地拒绝。appendDelegatedPolicyOverrides() 把每个值写成子级自己日志中的 source: 'delegation' sandbox/modeapproval/policy 事件。每个进程内子级还会收到一条 scoped 运行时上下文声明(subagent:delegation),告知它范围已被固定。

进程内 vs 派生态 vs 外部后端

进程内 provider 共享一个运行驱动(在 subagent-in-process-driver 中)。Spawnfork 只在它们的 seed 上不同:

方面spawnfork
Session 种子平衡的已完成 turn 前缀(直到最后一个 turn/end
父历史可见是(一次性快照)
能力全四项全四项
模型继承父模型,除非覆盖复制继承前缀字节以复用缓存
可延续路径可用已实现但无生产组合使用(绑定 backgroundMode: one-shot

共享驱动的 startInProcessRun() 校验父深度、直接调用 parent.ctx.agents.create、在未发布的 setup 窗口中安装 persona/工具过滤/结构化输出、以 child.followup(prompt) + child.whenIdle() 驱动一个任务,然后读取子级输出。

外部 provider 在独立进程中运行子级,并返回 localAgent: undefined,因此它们的一次性运行不属于基于 trace 的枚举:

  • subagent-acp 在子进程上通过 Agent Client Protocol@agentclientprotocol/sdk)通信,并为子级自己的权限提示提供可配置的 permission 策略(allow/reject)。
  • subagent-codex 驱动真实的 Codex app-server 子级。
  • subagent-claude-code 通过官方 Claude Agent SDK 驱动 Claude Code。
  • subagent-dsh-sdk 通过 TypeScript SDK 运行跨进程的 Harness 子级。

委派谱系与深度

父子事实作为谱系数据传递,而绝不依赖 scope 结构:子级的 SessionHeader 记录 parentSession、持久化的 delegationDepth 以及身份 origin: 'subagent'。深度核算位于 packages/subagent/subagent/src/depth.ts

ts
export function delegationDepthOf(agent: Agent): number {
  const runtime = agent.options.subagentDepth
  if (runtime !== undefined && (!Number.isSafeInteger(runtime) || runtime < 0 || Object.is(runtime, -0)))
    throw new TypeError('agent subagentDepth must be a non-negative safe integer')
  // 头部值已在会话边界(创建与加载)校验过。
  return Math.max(agent.session.header.delegationDepth ?? 0, runtime ?? 0)
}

持久化的 SessionHeader.delegationDepth 是权威且单调的——运行时选项可加深但绝不降低——因此被恢复的子级不能被重算为顶层。assertSubagentMaxDepth() 校验记录的深度上限。模型工具的 maxDepth 默认值为 30 禁止委派)。

结果契约

一次性 SubagentRun{ id, localAgent, result, dispose() }resultSubagentResult = { output, structured?, stopReason } 兑现。stop reason 镜像 harness 的 turn 词汇——completedabortederrormax-tokensrefusal。关键点:result 在子级失败时不会 reject(它以 stopReason: 'error' 兑现,以便消费者映射为 errored 工具结果);它只在接缝无法表达的基础设施故障时 reject。dispose() 是幂等的并取消剩余工作。AssistantOutputFold/finalAssistantOutput 助手选择子级的最后一条非空 assistant 消息(跳过仅 usage 的消息),否则使用累积的 assistant 文本。

可延续子级(Activation)

可延续子级拥有一个持久 Session 与至多一个进程本地 Activation——一个被重建的子级 Agent 的驻留纪元。Agent inbox 是唯一的 turn 队列,因此延续管理器(在 src/continuation.ts 中)拥有驻留,而 Agent 循环拥有 turn 排序与执行。每条延续消息都是 Agent.followup() 并成为一个 FIFO turn。路由只依赖驻留状态:运行中入队、等待中唤醒同一 Agent、无 Activation 则从持久化的 subagent/descriptor 冷恢复一个新实例。管理器的冷恢复绝不经过 provider 分发——折叠的描述符就是整个重建输入。结算时,子级的持久直接父代理会收到一条结算通知(source kind subagent-settled),在所有权释放之前投递,作为一条普通后续 turn(唤醒)或注入排空中的谱系。

三个面向模型的工具

dsh-tool-subagent —— 委派

每个插件实例把一个 provider 绑定到一个 toolName(默认 subagent)。模型收到 { description, prompt } 以及可选的 run_in_background

配置默认含义
provider(必填)Provider 名(spawnforkacp、…)
toolNamesubagent面向模型的名称,每实例各异
enableRunInBackgroundtrue暴露后台模式
backgroundModeone-shotone-shot(Task 支持的任务)或 continuable(持久子级 id)
agentOptions / persona / toolFilter / maxDepth传给 start() 的子级定制

前台会等待 run.result 并总是 dispose()。一次性后台注册一个普通父拥有的 Task 并返回 { kind: 'background', jobId }。可延续后台调用 ctx.subagents.startContinuable() 并返回 { kind: 'continuable', subagentId }

dsh-tool-subagent-control —— send_message / interrupt_agent / list_agents

一组可选、全局命名的控制工具,作为 ctx.subagents 之上的适配器,只注册一次,因此多个委派工具不会重复注册全局控件。send_message(subagent_id, message) 成为子级的下一 FIFO turn 且不返回回复。interrupt_agent(agent_id) 只停止当前 turn(keepInbox),让排队的消息停驻。list_agents(scope: 'children' | 'descendants') 把持久目录投影为可延续子级,状态为 running / idle / ready(仅存储,可恢复而非终结)。

dsh-tool-subagent-report —— 子级→父级 report

一个子级作用域工具,经 registerContinuableSetup() 安装到可延续子级作用域,而非全局。report(output: string) 精确到达子级的存活直接父代理(从持久化 parentSession 推导);reportDelivery 选择 wakeup(默认,一个普通父 turn)或 quietparent.inject(),注入上下文而不发起模型请求)。report 成功返回已被父级接受的稳定 MessageId,而非投递回执。

生命周期事件

服务会就每次一次性运行和每个驻留的可延续 Activation 纪元发出 subagent/start / subagent/end(scoped 到委派父代理,共享一个 runId,带 local 标志),以及 subagent/provider-added / subagent/provider-removed。Provider 记录持久的 subagent/descriptor 会话事件(版本化,snapshotSubagentDescriptor() / foldSubagentDescriptor()),它是仅日志的:不出现在模型历史中,且跨压缩保留。

UI(简述)

client/ui-subagentconversation.session.header.actions 贡献可惰性展开的子代理目录树、按原因区分的只读 composer 替换,以及向 ctx.inputTriggers 贡献既有的 @ 引用源。它通过标准的 useSessions hook 读取 subagentsByParent 与会话摘要;选择一行会以确切的 { parentSessionId, childSessionId, mode } 地址调用 SessionRuntime.openSubagent()。目录与模型无关:子代理来源的行被从侧边栏中省略,因此 header 目录就是它们的导航入口。

已知限制

  • ACP 子级保持一次性且不可 trace 枚举——它们在父级语料中没有本地子会话。
  • 无 host 用户延续——followup() 要求确切的存活直接父代理;只有 interrupt() 接受持久的父地址。
  • 无当前 turn 转向——可延续消息与唤醒 report 入队后续 turn。
  • 进程本地驻留——Activation 不协调两个 harness 进程;需要持久的 mailbox 与租约协议。

延伸阅读

  • 会话查询与日志导出工作流与 Ralph——ctx.subagents 所驱动的兄弟编排家族。
  • 术语表——scopesetup windowlineagegoal round 等词条。
  • 仓库内的子系统参考:docs/subsystems/subagent.md
  • 接缝自带的 README:packages/subagent/subagent/README.md,以及 packages/subagent/<provider>/* 下每个 provider 的 README.md
  • 承载决策的 Agent Note:.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md2026-07-21-continuable-background-subagents.md
  • 完整契约类型:packages/subagent/subagent/src/types.ts