subagent-spawn.ts — 多 Agent 调度中枢

subagent-spawn.ts 是sessions_spawn 工具的完整实现。当 Manager Agent 调用 sessions_spawn 派任务给 Worker 时,就是这个文件在跑。

  • 所属层级:Agent 运行时 → 多 Agent 协作层。

  • 核心职责:接收来自用户或父 Agent 的“生成子代理”请求,校验权限和资源,创建独立的子会话,配置模型、提示词、附件、线程绑定等,最后触发子 Agent 的执行。

  • 调用方式:通过网关的 "agent" 方法(内部调用 callSubagentGateway)触发。

总体结构

spawnSubagentDirect(params, ctx)  ← 唯一入口 L1046-1140
  │
  ├── ① 校验层
  │   ├── 参数校验:task/label/agentId/model L144-167
  │   ├── 模式解析:run vs session, fork vs isolated
  │   ├── 深度限制:callerDepth >= maxDepth → 拒绝 L1132-1140
  │   ├── 并发限制:activeChildren >= maxChildren → 拒绝 L1142-1150
  │   └── 沙箱检查:sandboxed parent 不能 spawn unsandboxed child L1225-1248
  │
  ├── ② 目标解析
  │   ├── resolveSubagentTargetPolicy:允许 spawn 到这个 agent 吗?L1201-1209
  │   ├── resolveSubagentCapabilities:子 Agent 的角色 + 控制范围 L1251-1254
  │   └── 生成 childSessionKey = "agent:{target}:subagent:{uuid}" L1216
  │
  ├── ③ Model/Thinking 决策
  │   ├── readRequesterThinkingLevel:继承父的 thinking 配置 L379-436
  │   └── resolveSubagentModelAndThinkingPlan:最终模型+thinking L1263-1278
  │
  ├── ④ Session 准备
  │   ├── patchChildSession:写 spawnDepth/subagentRole/工具白名单 L1280-1298, L290-343
  │   ├── prepareSubagentSessionContext:fork vs isolated  L456-554
  │   └── persistInitialChildSessionRuntimeModel:写入初始模型 L1336-1359
  │
  ├── ⑤ Thread 绑定 [可选]
  │   └── bindThreadForSubagentSpawn:Discord/Slack 线程绑定 L714-727, L834-948,  L1361-1395
  │
  ├── ⑥ 附件物化
  │   └── materializeSubagentAttachments:写入文件系统 L1427-1443
  │
  ├── ⑦ 启动 Agent (L760-L830) ★ 核心
  │   └── callSubagentGateway({ method: "agent", ... }) L1530-1560
  │       发送 childTaskMessage → run-loop 启动
  │
  ├── ⑧ 注册追踪
  │   └── registerSubagentRun:登记到 subagent-registry L1636-1689
  │
  └── ⑨ 生命周期事件
      ├── subagent_progress hook  L1691-1709
      ├── subagent_spawned hook   L1711-1738
      └── emitSessionLifecycleEvent  L1741-1746

核心概念:子agent(Subagent)

子agent是 OpenClaw 中一种特殊的 Agent 运行实例,它:

  • 拥有独立的会话childSessionKey),可持久化对话历史。

  • 可以继承父会话的上下文(通过 fork 模式复制历史消息),也可以隔离(isolated 模式)。

  • 可以绑定到特定通信线程(如 Discord/Slack 线程),让用户在同一个线程中与子代理交互。

  • 受深度和并发限制,防止资源滥用。

  • 支持附件注入,允许在启动时传入文件内容(如代码片段、文档),供子代理分析。

工作流程(10 步详解)

1️⃣ 校验层:参数解析与校验
  • 校验 taskName 合法性(normalizeSubagentTaskName)。L1051-1057

  • 校验 agentId 是否符合命名规范(isValidAgentId)。L1066-1071

  • 根据 mode 和 thread 确定生成模式(run 或 session)。 L1076-1087, L670-679, L714-727, 

  • 解析上下文模式(fork 或 isolated),并处理线程绑定请求。L438-454, L456-554

  • 获取当前会话的深度(getSubagentDepthFromSessionStore),确保不超过配置的 maxSpawnDepth。L1132-1140

  深度限制 → ClawForge 的 4 Agent 架构天然满足
// L480
const callerDepth = getSubagentDepthFromSessionStore(requesterInternalKey);
const maxSpawnDepth = cfg.agents?.defaults?.subagents?.maxSpawnDepth 
  ?? DEFAULT_SUBAGENT_MAX_SPAWN_DEPTH;  // 默认值
if (callerDepth >= maxSpawnDepth) {
  return { status: "forbidden", error: "..." };
}

ClawForge 是单层 spawn(Manager → Worker),depth 最多到 1,完全在限制内。

  • 获取当前会话已激活的子代理数量(countActiveRunsForSession),确保不超过 maxChildrenPerAgent。L1142-1150

 并发控制 → 并发锁方案的底层机制
// L490
const maxChildren = cfg.agents?.defaults?.subagents?.maxChildrenPerAgent 
  ?? DEFAULT_SUBAGENT_MAX_CHILDREN_PER_AGENT;
const activeChildren = countActiveRunsForSession(requesterInternalKey);
if (activeChildren >= maxChildren) {
  return { status: "forbidden", error: "..." };
}

这就是"lane 并发控制" 的底层实现。countActiveRunsForSession 来自 subagent-registry.ts

2️⃣ 目标解析:权限与资源检查
  • 如果配置了 requireAgentId,则必须显式指定 agentId。L1152-1165

  • 检查目标 Agent 是否允许被生成(resolveSubagentTargetPolicy)。L1201-1215

  • 格式:agent:{targetAgentId}:subagent:{UUID}。确保全局唯一性。 L1216 

3️⃣ Model/Thinking决策:模型与思考级别解析
  • 根据父会话的模型、目标 Agent 的配置、显式传入的 model/thinking 参数,通过 resolveSubagentModelAndThinkingPlan 确定最终使用的模型和思考级别。L1263-1278

4️⃣ 准备子会话上下文(fork 或 isolated)
  • 调用 prepareSubagentSessionContext

    • 如果 contextMode === "fork",通过 forkSessionEntryFromParent 从父会话克隆历史消息到子会话,实现上下文继承。

    • 如果 contextMode === "isolated",子会话从零开始。

 上下文模式:fork vs isolated
// L335 prepareSubagentSessionContext
if (params.contextMode === "isolated") {
  return { status: "ok", mode: "isolated" };  // 空白新 Session
}
// fork 模式:复制父 Session 的对话历史
const forkedResult = await forkSessionEntryFromParent({...});

ClawForge 用 isolated(Worker 不需要 Manager 的对话历史),但如果 Worker 需要参考前面的分析结果,应该用 fork

5️⃣ 线程绑定(如果 thread=true
  • 调用 bindThreadForSubagentSpawn,通过 getSessionBindingService() 将子会话绑定到当前通信平台的线程(如 Discord 线程)。

  • 绑定成功后,deliveryOrigin 会更新,子代理的回复将路由到该线程。

6️⃣ 处理附件与系统提示
  • 调用 materializeSubagentAttachments 将传入的附件(文件内容)写入磁盘,并生成系统提示后缀(告知 Agent 附件位置)。

  • 构建完整的 childSystemPrompt,包含任务描述、上下文、附件信息等。

7️⃣ 注册并启动子agent :核心启动调用→ 这就是"派任务"
  • 通过 callSubagentGateway 调用 "agent" 方法,传递消息、会话键、系统提示等,触发子代理的 Agent 循环执行。

// L790
const response = await callSubagentGateway({
  method: "agent",           // ← 触发 agent-run-handler 的 9 阶段流水线!
  params: {
    message: childTaskMessage,  // 子 Agent 收到的第一条消息
    sessionKey: childSessionKey,
    lane: AGENT_LANE_SUBAGENT,  // 标记为子 Agent 车道
    disableMessageTool: true,   // 子 Agent 不能用 messaging tool
    cleanupBundleMcpOnRunEnd: spawnMode !== "session",
    extraSystemPrompt: childSystemPrompt,  // 额外系统提示
    thinking: thinkingOverride,
    timeout: runTimeoutSeconds,
  },
  timeoutMs: resolveSubagentAgentGatewayTimeoutMs(runTimeoutSeconds),
});

调用 method: "agent" 后,整个 agent-run-handler 9 阶段流水线重新走一遍,然后进入 run-loop这就是子 Agent 的完整执行链路。agent-run-handler 9阶段过程见:openclaw源码解读(9)—— Agent执行链路2:agent-run-handler.ts 核心 Pipeline 每个 Agent 的执行生命周期-CSDN博客

8️⃣ 注册追踪:注册并启动子代理
  • 调用 registerSubagentRun 将子代理运行信息注册到全局 registry(用于后续清理、状态跟踪)。

registerSubagentRun 的追踪机制,请见下一篇博客:openclaw源码解读之 subagent-registry.ts

9️⃣ 处理附件与系统提示
  • 调用 materializeSubagentAttachments 将传入的附件(文件内容)写入磁盘,并生成系统提示后缀(告知 Agent 附件位置)。

  • 构建完整的 childSystemPrompt,包含任务描述、上下文、附件信息等。

 附件传递 → AgentTeams 的 MinIO 替代方案
// L730 materializeSubagentAttachments
// 把 attachments: [{name, content, encoding, mimeType}] 
// 写入子 Agent 的工作空间
const materializedAttachments = await materializeSubagentAttachments({
  config: cfg,
  targetAgentId,
  workspaceDir: spawnedCwd ?? spawnedWorkspaceDir,
  attachments: params.attachments,
  mountPathHint,
});

这就是文件产物的原生传递机制!AgentTeams 用 MinIO shared/tasks/{task-id}/,而 OpenClaw 原生通过 attachments 参数直接传文件。

🔟 注册并启动子代理
  • 调用 registerSubagentRun 将子代理运行信息注册到全局 registry(用于后续清理、状态跟踪)。

  • 通过 callSubagentGateway 调用 "agent" 方法,传递消息、会话键、系统提示等,触发子代理的 Agent 循环执行。L1530-1560

 ClawForge 映射

spawnSubagentDirect 步骤 ClawForge 对应
targetPolicy 检查 AgentTeams 的 Worker CRD 允许列表
spawnDepth 限制 4 Agent 单层结构,depth≤1
maxChildren 并发 .processing 分布式锁的底层原理
contextMode Worker 用 isolated(不继承对话)
attachments 传递 替代 MinIO 的原生文件传递
method: "agent" 触发完整 run-loop,Worker 开始干活
registerSubagentRun AgentTeams 的 task 状态追踪
subagent_spawned hook 通知 Manager "任务已下发"

一句话总结

subagent-spawn.ts 就是一条流水线校验 → 准备 → 下发 → 追踪。ClawForge 的 Manager 调用 sessions_spawn 时,这个文件负责确保子 Agent 在正确的上下文、正确的权限、正确的并发限制下启动,并全程追踪其生命周期。

注:本文中提到的ClawForge是我做的多agent协同的研发全流程代码自愈与审查系统,详见:openclaw源码解读——高阶与淬炼:1. 面向软件研发全流程的多Agent 协同代码审查与自愈系统设计(初稿)-CSDN博客

更多推荐