第 18 讲:Subagent 委托

DeepSeek Harness 源码解析 · 第 18 讲

DeepSeek Harness 源码解析系列 第 18 讲:Subagent 委托 基于 DeepSeek Harness 源码 · 2026-09-02 💡 本讲一句话: 模型把任务"外包"给子 Agent,本质是 一次带契约的委托 ——不是简单再开一个 LLM。读完整篇你能带走五件事: 一次性 run 与可续会话两条路径为什么必须分开建模 、 递归深度如何用一个单调下界防住"复活后假装顶层" 、 子代权限为什么在委托边界就被收窄且 approval 强制 'never' 、 spawn(全新)vs fork(继承父历史)两个 provider 的取舍 ,以及 结果回收时"部分输出也算数"的选择规则 。 一、能力缝:SubagentRuntime 服务定义 第 9/10 讲拆过 Agent Loop 和工具注册表,都是"能力缝 + provider"的形态。Subagent(子代委托)是同一范式的又一次落地: packages/subagent/subagent/src/index.ts 只拥有 Service Definition( ctx.subagents )——它 不决定子代怎么跑 ,只负责维护一个"按名字索引的 provider 注册表"、校验能力、发布生命周期事件。先看它的类声明和构造器: 📄 packages/subagent/subagent/src/index.ts(第 170-201 行) /** Named provider registry with one-shot runs, durable discovery, and continuable-child operations. */ export class SubagentRuntime extends Service { private providers = new Map<string, SubagentProvider>() private continuations: SubagentContinuationManager | undefined /** Deployment contributions composed into unpublished continuable children. */ private readonly setupRegistry = new SubagentActivationSetupRegistry() private readonly emitLifecycle: LifecycleEmitter constructor(ctx: Context) { super(ctx, 'subagents') this.emitLifecycle = createLifecycleEmitter(this.ctx, parent => scopeTarget(this, parent)) ctx.inject(['agents'], (childCtx: Context) => { const manager = new SubagentContinuationManager(childCtx, { prepareContinuable: (name, request) => this.prepareContinuable(name, request), observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent), }, this.setupRegistry) this.continuations = manager childCtx.effect(() => () => { if (this.continuations === manager) this.continuations = undefined }, 'subagents.continuationBinding()') }) } 为什么这样设计: 三个细节值得逐行看。其一, providers 是一个 Map<string, SubagentProvider> —— 多个 provider 共存,按名字索引…

阅读全文:DeepSeek Harness 源码解析 第 18 讲