第 35 章

Sub-agents:非阻塞委托执行、跨 Agent 记忆搜索与结构化通信

第35章:Sub-agents——非阻塞委托执行、跨 Agent 记忆搜索与结构化通信

"Sub-agent 让主 Agent 从'亲力亲为'变成'项目经理'——分配任务,监控进度,整合结果。" —— OpenClaw Sub-agents 设计文档


35.1 Sub-agents 的核心设计理念

在第34章,我们探讨了如何通过 Bindings 将不同的传入消息路由到不同的 Agent。Sub-agents 解决的是另一个问题:当一个 Agent 在处理任务的过程中,需要将部分子任务委托给另一个专门的 Agent 来完成

这是两种不同的并发模式:

Multi-Agent 路由(第34章):         Sub-agents(本章):

消息 A ──→ Agent 1                  主 Agent 接收请求
消息 B ──→ Agent 2                       ↓
消息 C ──→ Agent 3                   分解为子任务
                                         ↓
(不同消息,不同 Agent)           ┌────┴────┬─────┐
                                  子Agent1  子Agent2  子Agent3
                                   任务A    任务B    任务C
                                     └────────┴────┘
                                         ↓
                                   汇总结果,主 Agent 回复

35.2 sessions_spawn:非阻塞机制详解

35.2.1 阻塞 vs 非阻塞的本质区别

阻塞调用(传统方式)

主 Agent 开始任务
    ↓
调用子任务,等待完成
    ↓(等待10分钟...)
子任务完成,继续下一步
    ↓
调用另一个子任务,等待完成
    ↓(等待8分钟...)
...
总耗时 = 所有子任务时间之和

非阻塞调用(sessions_spawn)

主 Agent 开始任务
    ↓
spawn 子任务1 → 立即返回 runId1,继续执行
    ↓
spawn 子任务2 → 立即返回 runId2,继续执行
    ↓
spawn 子任务3 → 立即返回 runId3,继续执行
    ↓
(子任务1、2、3 并行运行)
    ↓
等待所有子任务完成(可设置超时)
    ↓
汇总结果
总耗时 ≈ 最长子任务的时间(而非所有之和)

35.2.2 sessions_spawn 的调用接口

在 Agent 的工具调用中,sessions_spawn 的使用方式如下:

// 主 Agent 发出的工具调用
{
  "tool": "sessions_spawn",
  "parameters": {
    "agentId": "research-agent",     // 要委托的 Agent ID
    "task": "搜索过去30天关于 LLM 安全的学术论文,返回前10篇的摘要",
    "context": {                     // 传递给子 Agent 的上下文
      "keywords": ["LLM security", "prompt injection", "jailbreak"],
      "dateRange": "2026-03-01 to 2026-04-01",
      "outputFormat": "markdown-list"
    },
    "timeout": 300,                  // 超时秒数(300秒=5分钟)
    "priority": "normal"             // "high" | "normal" | "low"
  }
}

立即返回的结果

{
  "runId": "sub-7f8a9b2c-4d3e-11ec-81d3-0242ac130003",
  "status": "spawned",
  "estimatedDuration": 240,
  "agentId": "research-agent",
  "createdAt": "2026-04-26T10:00:00Z"
}

主 Agent 收到 runId 后,可以继续处理其他工作,完全不需要等待。

35.2.3 并发 spawn 示例:研究报告生成

以下是一个主 Agent 同时 spawn 多个子 Agent 来并行生成研究报告的完整示例:

主 Agent 收到请求:
"帮我生成一份关于生成式 AI 在医疗领域应用的完整报告,
 包括技术现状、监管环境、市场数据和案例研究四个部分"

主 Agent 的处理逻辑(伪代码):

# 主 Agent 分解任务并并发 spawn 子 Agent
tasks = [
    {
        "agentId": "tech-researcher",
        "task": "分析生成式 AI 在医疗领域的技术现状,重点包括:\n"
                "1. 当前主要应用场景(诊断辅助、药物发现、影像分析)\n"
                "2. 各场景的技术成熟度\n"
                "3. 主要技术挑战和突破"
    },
    {
        "agentId": "legal-researcher", 
        "task": "分析生成式 AI 在医疗领域的全球监管环境,重点包括:\n"
                "1. FDA、EMA、NMPA 的 AI 医疗器械监管框架\n"
                "2. 2025-2026年的重要监管变化\n"
                "3. 合规要求对 AI 开发的影响"
    },
    {
        "agentId": "market-analyst",
        "task": "收集生成式 AI 医疗市场数据,包括:\n"
                "1. 市场规模(2024-2026年)\n"
                "2. 主要参与者和市场份额\n"
                "3. 投融资趋势"
    },
    {
        "agentId": "case-researcher",
        "task": "收集3-5个生成式 AI 医疗应用的真实案例,每个案例包括:\n"
                "公司名称、应用场景、技术方案、效果数据、经验教训"
    }
]

# 并发 spawn
run_ids = []
for task in tasks:
    result = await sessions_spawn(
        agentId=task["agentId"],
        task=task["task"],
        timeout=300
    )
    run_ids.append(result["runId"])

# 主 Agent 现在可以做其他工作(比如告知用户正在处理)
await send_message("正在并行研究四个维度,预计5分钟后完成报告...")

# 等待所有子任务完成(超时设为360秒)
results = await wait_for_runs(run_ids, timeout=360)

# 汇总结果
final_report = synthesize_report(results)
await send_message(final_report)

时间效率对比

串行执行(阻塞):
  技术研究:240秒
  监管分析:180秒
  市场数据:150秒
  案例研究:200秒
  总计:770秒(约13分钟)

并行执行(sessions_spawn):
  四个任务并行运行
  总计:约240秒(取最长任务)
  效率提升:约3.2倍

35.3 子 Agent 的隔离 Session 与受限工具访问

35.3.1 子 Agent 的 Session 隔离

每个通过 sessions_spawn 创建的子 Agent 实例,都有自己独立的 Session:

主 Agent Session(session-main-abc123)
  ├── 对话历史
  ├── 工具调用记录
  └── 当前任务状态
  
子 Agent Session(session-sub-def456)  ← 独立 Session
  ├── spawn 时传入的 task 和 context
  ├── 子 Agent 的执行历史
  └── 子任务结果
  
子 Agent Session(session-sub-ghi789)  ← 另一个独立 Session
  ├── 不同的 task 和 context
  └── 独立的执行历史

子 Agent 无法访问主 Agent 的对话历史,只能看到 sessions_spawn 时传入的 taskcontext

35.3.2 子 Agent 的工具权限继承

子 Agent 默认继承主 Agent 的工具权限,但可以进一步限制(不能扩展):

// 主 Agent 配置
{
  "tools": {
    "allow": ["read", "write", "browser.search", "python"]
  }
}

// spawn 子 Agent 时限制工具权限
{
  "tool": "sessions_spawn",
  "parameters": {
    "agentId": "research-agent",
    "task": "...",
    "toolRestrictions": {
      "allow": ["browser.search"],  // 只允许 browser.search
      "deny": ["read", "write", "python"]  // 显式拒绝其他工具
    }
  }
}

这种设计的意义:即使子 Agent 被恶意 Skill 感染,它能造成的破坏也被限制在其工具权限范围内。


35.4 结果自动公告机制

35.4.1 子 Agent 完成时的通知流程

当子 Agent 完成任务后,结果会通过以下机制自动传递给主 Agent:

子 Agent 完成任务
    ↓
将结果写入共享结果缓冲区(Result Buffer)
    ↓
通知主 Agent(事件推送)
    ↓
主 Agent 从 Result Buffer 读取结果
    ↓
继续后续处理

35.4.2 主 Agent 处理结果的方式

主 Agent 可以以两种方式接收子 Agent 的结果:

方式一:轮询等待(推荐用于已知数量的子任务)

{
  "tool": "sessions_wait",
  "parameters": {
    "runIds": ["sub-7f8a9b2c", "sub-8a9b3c4d", "sub-9b0c4d5e"],
    "waitMode": "all",       // "all"=等全部完成, "any"=等任意一个完成
    "timeout": 360,
    "onTimeout": "return-partial"  // "fail" | "return-partial"
  }
}

方式二:事件驱动(推荐用于不确定数量的子任务)

{
  "tool": "sessions_on_complete",
  "parameters": {
    "runId": "sub-7f8a9b2c",
    "callback": {
      "action": "notify_main",
      "message": "research-task-1-done"
    }
  }
}

35.5 结构化 Agent 间通信(2026.2.17 新特性)

2026年2月17日发布的 OpenClaw 版本带来了确定性子 Agent 生成和结构化 Agent 间通信。

35.5.1 确定性子 Agent 生成

在此版本之前,sessions_spawn 每次调用都会创建一个新的子 Agent 实例,Agent ID 不固定。2026.2.17 引入了确定性 agentId 绑定

{
  "tool": "sessions_spawn",
  "parameters": {
    "agentId": "research-agent",
    "deterministicId": "report-2026-q1-research",  // 新增:确定性 ID
    "task": "...",
    "deduplication": true  // 如果同 ID 任务已在运行,不重复创建
  }
}

这解决了幂等性问题:如果主 Agent 因为网络问题重试,不会导致同一任务被执行两次。

35.5.2 结构化消息传递协议

新版本定义了 Agent 间的结构化消息格式:

// Agent 间消息格式(IPC Message)
{
  "messageId": "msg-a1b2c3d4",
  "from": {
    "agentId": "research-agent",
    "runId": "sub-7f8a9b2c"
  },
  "to": {
    "agentId": "main",
    "sessionId": "session-main-abc123"
  },
  "type": "task_result",          // "task_result" | "progress" | "error" | "query"
  "payload": {
    "status": "completed",
    "data": {
      "summary": "找到47篇相关论文...",
      "papers": [...]
    },
    "metadata": {
      "duration": 218,
      "toolCallCount": 12,
      "tokensUsed": 8432
    }
  },
  "timestamp": "2026-04-26T10:04:58Z"
}

35.5.3 双向通信:子 Agent 向主 Agent 查询

新特性允许子 Agent 在执行过程中向主 Agent 发出查询,请求澄清或额外信息:

// 子 Agent 发送给主 Agent 的 query 消息
{
  "type": "query",
  "payload": {
    "question": "在分析监管框架时,您是否希望我重点关注某个特定地区(美国/欧盟/中国)?",
    "options": ["仅美国", "仅欧盟", "仅中国", "全球(耗时更长)"],
    "timeout": 30,        // 30秒内未回复则使用默认值
    "default": "全球(耗时更长)"
  }
}

主 Agent 可以选择:

  1. 立即回复查询
  2. 等待用户输入后回复
  3. 让超时触发默认值

35.6 跨 Agent 记忆搜索配置

35.6.1 memorySearch.qmd.extraCollections 配置

子 Agent 默认只能访问其自身的记忆集合。通过配置 memorySearch.qmd.extraCollections,可以让子 Agent 在执行记忆搜索时跨越自身边界,访问其他 Agent 的记忆:

// 在子 Agent 的配置中(或 spawn 时传入)
{
  "agents": {
    "list": [
      {
        "id": "research-agent",
        "memorySearch": {
          "qmd": {
            "enabled": true,
            "extraCollections": [
              {
                "agentId": "main",
                "collections": ["user-profile", "research-history"],
                "permission": "read-only"
              },
              {
                "agentId": "work",
                "collections": ["business-knowledge"],
                "permission": "read-only"
              }
            ]
          }
        }
      }
    ]
  }
}

35.6.2 跨 Agent 记忆搜索的实际应用

场景:用户之前告诉主 Agent "我在研究医疗 AI 时特别关注中国市场"
主 Agent 将这个偏好存储在记忆中。

当主 Agent spawn 一个研究子 Agent 时,
子 Agent 通过 extraCollections 访问主 Agent 的记忆,
发现了这个偏好,
在搜索时自动重点关注中国市场数据。

用户不需要重复说明自己的偏好——跨 Agent 记忆共享自动传递了这个上下文。

35.6.3 记忆隔离与共享的平衡

记忆访问权限矩阵(含 Sub-agents)

              主 Agent  子Agent-研究  子Agent-法律  子Agent-市场
主 Agent         ✓          ✓R            ✓R           ✓R
子Agent-研究    ✗           ✓             ✗             ✗
子Agent-法律   ✗            ✗             ✓             ✗
子Agent-市场   ✗            ✗             ✗             ✓

✓ = 读写,✓R = 只读,✗ = 无访问

原则:子 Agent 可以读取主 Agent 的记忆(通过显式配置),但不能写入。子 Agent 的任务结果由主 Agent 决定是否写入记忆。


35.7 Sub-agents vs ACP Harness vs Native Plugin:三方对比

当需要扩展 OpenClaw 的能力时,有三种不同的机制可供选择。理解它们的区别对于做出正确的架构决策至关重要。

35.7.1 三方对比表

维度 Sub-agents ACP Harness Native Plugin
执行位置 OpenClaw 内部 外部 Runtime OpenClaw 进程内
沙箱约束 受 OpenClaw 沙箱约束 不受 OpenClaw 沙箱约束 部分约束
工具访问 受主 Agent 工具权限限制 独立工具访问 直接访问 Pi 框架
记忆访问 可配置跨 Agent 记忆读取 独立记忆系统 直接访问记忆 API
性能开销 低(进程内通信) 高(跨进程/网络) 极低(直接调用)
开发复杂度 低(JSON 配置) 高(实现 ACP 协议) 高(需要 Pi 框架知识)
适用场景 任务分解 / 并行处理 复杂外部集成 核心功能扩展
安全边界 OpenClaw 安全边界 外部安全边界 最小安全边界

35.7.2 详细分析

Sub-agents

适合:
✓ 并行执行多个相关子任务
✓ 专家分工(不同 Agent 专注不同领域)
✓ 任务隔离(防止子任务污染主 Agent 上下文)
✓ 渐进式结果公告

不适合:
✗ 需要访问外部系统(数据库、消息队列)的深度集成
✗ 需要持久化进程的任务(后台服务)
✗ 需要超出 OpenClaw 沙箱权限的操作

ACP Harness(外部 Harness Runtime)

适合:
✓ 与企业内部系统深度集成(ERP、CRM、数据库)
✓ 需要运行 OpenClaw 沙箱不允许的操作(如直接文件系统访问)
✓ 多框架混用(同时使用 OpenClaw 和其他 AI 框架)
✓ 构建独立的 AI 服务,通过 ACP 协议与 OpenClaw 协作

不适合:
✗ 简单的任务分解(Sub-agents 足够)
✗ 对延迟敏感的场景(跨进程开销高)

Native Plugin(嵌入最深)

适合:
✓ 扩展 OpenClaw 的核心能力(新的记忆后端、新的渠道适配器)
✓ 性能敏感的功能(需要直接内存访问)
✓ 深度定制 OpenClaw 的行为(修改 Pi 框架的核心逻辑)

不适合:
✗ 大多数业务逻辑扩展(Sub-agents 更合适)
✗ 团队没有 OpenClaw 内部开发经验的情况

35.7.3 选择决策树

需要扩展 OpenClaw 功能?
    ↓
需要完整的操作系统权限或深度外部系统集成?
    ├── 是 → 考虑 ACP Harness
    └── 否 ↓
        需要修改 OpenClaw 核心行为或性能极限优化?
        ├── 是 → 考虑 Native Plugin
        └── 否 → 使用 Sub-agents

35.8 典型多 Agent 架构:主 Agent 调度 + 子 Agent 并行处理

35.8.1 架构图

用户 / 外部渠道
        ↓
   主调度 Agent(main)
   ├── 接收用户请求
   ├── 分解任务
   ├── spawn 子 Agent
   ├── 监控进度
   ├── 汇总结果
   └── 回复用户
        ↓
   ┌────┬────┬────┬────┐
   子A  子B  子C  子D    ← 并行执行,互不感知
   (研究)(分析)(写作)(校对)
     └────┴────┴────┘
           ↓
     结果汇总(主 Agent)
           ↓
     最终报告 → 用户

35.8.2 完整的 YAML 任务配置示例

# 任务:生成竞品分析报告
name: competitive-analysis-workflow
version: "1.0"

main_agent:
  id: coordinator
  model: claude-sonnet-4
  
workflow:
  - step: intake
    agent: coordinator
    action: |
      1. 解析用户提供的竞品列表
      2. 确认分析维度(功能/定价/市场/技术)
      3. 估算完成时间
      
  - step: parallel_research
    parallel: true
    sub_tasks:
      - agent: research-agent
        task: "深度搜索竞品A的公开信息:官网/博客/GitHub/财报"
        timeout: 240
        
      - agent: research-agent
        task: "深度搜索竞品B的公开信息:官网/博客/GitHub/财报"
        timeout: 240
        
      - agent: market-agent
        task: "获取竞品A和B的市场数据:下载量/用户数/融资情况"
        timeout: 180
        
  - step: synthesis
    agent: coordinator
    depends_on: parallel_research
    action: |
      1. 汇总所有子 Agent 的研究结果
      2. 识别关键差异和共同点
      3. 生成结构化对比报告
      
  - step: quality_check
    agent: critic-agent
    depends_on: synthesis
    action: |
      审查报告的准确性、客观性和完整性
      返回修改建议
      
  - step: final_output
    agent: coordinator
    depends_on: quality_check
    action: |
      根据审查意见修正报告
      格式化为 Markdown/PDF
      回复用户

35.9 Sub-agents 的并发上限与任务设计

35.9.1 并发上限:8 个

OpenClaw 的 Sub-agents 最多支持 8 个并发子 Agent 实例(单个主 Agent 可 spawn 的最大数量)。

这个限制的设计考量:

因素 说明
LLM API 并发 大多数 LLM 提供商对并发请求有速率限制
内存消耗 每个子 Agent 实例维护独立上下文,内存开销线性增长
结果汇总复杂度 超过8个并行结果,主 Agent 的汇总质量会下降
任务分解粒度 8个通常已经足够实现有效的任务分解

35.9.2 超过 8 个任务的处理策略

# 策略:分批执行(Batching)
async def process_large_task_list(tasks):
    BATCH_SIZE = 8
    all_results = []
    
    for i in range(0, len(tasks), BATCH_SIZE):
        batch = tasks[i:i + BATCH_SIZE]
        
        # spawn 当前批次
        run_ids = [await sessions_spawn(task) for task in batch]
        
        # 等待当前批次完成
        batch_results = await sessions_wait(run_ids, timeout=300)
        all_results.extend(batch_results)
        
        # 可选:向用户报告进度
        progress = min(i + BATCH_SIZE, len(tasks))
        await report_progress(f"已完成 {progress}/{len(tasks)} 个子任务")
    
    return all_results

35.9.3 任务设计原则

好的任务设计

✓ 子任务之间没有依赖关系(可以真正并行)
✓ 每个子任务有明确的输入和输出格式
✓ 子任务的预期运行时间相近(避免木桶效应)
✓ 子任务输出可以被主 Agent 直接整合

不好的任务设计

✗ 子任务 B 依赖子任务 A 的输出(应该串行,而非并行)
✗ 子任务 A 需要10秒,子任务 B 需要300秒(浪费并行效率)
✗ 子任务输出格式不统一(主 Agent 整合困难)
✗ 同一类型的任务 spawn 了太多子 Agent(超过实际必要数量)

35.10 错误处理与超时配置

35.10.1 子 Agent 的错误类型

错误类型:
1. timeout(超时)    → 子任务在指定时间内未完成
2. tool_error(工具错误)→ 子 Agent 调用的工具返回错误
3. llm_error(LLM错误) → LLM API 调用失败(限流/网络)
4. context_overflow(上下文溢出)→ 子任务内容超出模型上下文窗口
5. spawn_limit(超出并发限制)→ 尝试 spawn 第9个子 Agent

35.10.2 完整的错误处理配置

{
  "tool": "sessions_spawn",
  "parameters": {
    "agentId": "research-agent",
    "task": "...",
    "timeout": 300,
    "retryPolicy": {
      "maxRetries": 2,
      "retryOn": ["llm_error", "tool_error"],
      "backoffSeconds": 5
    },
    "onError": {
      "action": "return-partial",   // "fail" | "return-partial" | "use-fallback"
      "fallback": {
        "agentId": "simple-search-agent",  // 失败时用更简单的 Agent 重试
        "task": "..."
      }
    },
    "onTimeout": {
      "action": "return-partial",   // 超时时返回已完成的部分结果
      "includePartialResult": true
    }
  }
}

35.10.3 主 Agent 的容错处理模式

# 主 Agent 的健壮任务处理模式
async def robust_parallel_task(sub_tasks):
    run_ids = []
    for task in sub_tasks:
        run_id = await sessions_spawn(task, timeout=300)
        run_ids.append(run_id)
    
    # 等待结果,超时后继续(不阻塞整个流程)
    results = await sessions_wait(
        run_ids,
        timeout=360,
        on_timeout="return-partial"
    )
    
    successful = [r for r in results if r["status"] == "completed"]
    failed = [r for r in results if r["status"] != "completed"]
    
    if len(successful) < len(sub_tasks) * 0.5:
        # 超过一半失败,整体任务失败
        return {"status": "failed", "reason": "Too many sub-tasks failed"}
    
    # 汇总成功结果,对失败的子任务用简单回退
    final = synthesize(
        successful_results=successful,
        failed_tasks=failed,
        fallback_note="部分数据由于超时未能收集,报告可能不完整"
    )
    return final

35.11 Sub-agents 调试与监控

35.11.1 查看子 Agent 运行状态

# 查看所有活跃的子 Agent 运行
openclaw subagents list --active

# 输出:
# RunID                                  Agent           Status      Duration  Progress
# ─────────────────────────────────────────────────────────────────────────────────────
# sub-7f8a9b2c-4d3e-11ec-81d3          research-agent  running     1m 23s    ~60%
# sub-8a9b3c4d-5e4f-22fd-92e4          legal-agent     running     1m 23s    ~40%
# sub-9b0c4d5e-6f5g-33ge-a3f5          market-agent    completed   58s       100%

# 查看特定子 Agent 的执行日志
openclaw subagents logs sub-7f8a9b2c-4d3e-11ec-81d3

# 手动取消子 Agent
openclaw subagents cancel sub-7f8a9b2c-4d3e-11ec-81d3

35.11.2 Sub-agents 性能分析

# 生成任务执行性能报告
openclaw subagents report --session session-main-abc123

# 输出:
# Sub-agent Performance Report
# ═══════════════════════════════════════════
# Session: session-main-abc123
# Main Agent: coordinator
# 
# Sub-task Summary:
#   Total tasks: 4
#   Completed: 3
#   Timed out: 1
#   Total elapsed: 248s
#   Serial equivalent: 712s
#   Speedup factor: 2.87x
# 
# Token Usage:
#   Main agent: 3,241 tokens
#   Sub-agents: 22,847 tokens (avg 7,615/agent)
#   Total: 26,088 tokens
#   Estimated cost: $0.42
# 
# Recommendations:
#   - sub-9b0c4d5e timed out: consider increasing timeout to 360s
#   - sub-market-agent ran in 58s (fastest): good task design

35.12 小结

Sub-agents 是 OpenClaw 架构中最能体现"Agent 作为工作协调者"理念的特性。通过 sessions_spawn 的非阻塞机制,主 Agent 从"一个接一个地完成所有工作"转变为"智能地分配和协调多个专家的并行工作"。

核心要点总结:

  1. 非阻塞是关键sessions_spawn 立即返回 runId,主 Agent 不等待,可以同时管理多个并行任务
  2. 隔离是安全保障:每个子 Agent 有独立 Session,工具权限只能继承不能扩展,有效防止权限提升
  3. 结构化通信(2026.2.17):确定性 Agent ID + 标准化 IPC 消息格式,使复杂的 Agent 间协作成为可能
  4. 跨 Agent 记忆共享:通过 extraCollections 配置,子 Agent 可以读取主 Agent 的上下文知识
  5. 并发上限 8 个:设计任务时考虑分批策略,超过 8 个并行任务需要分批执行
  6. 错误处理要健壮:配置 retryPolicyonTimeoutfallback,防止单个子任务失败导致整体任务崩溃

在本书的最后章节,我们将综合前35章的内容,探讨如何设计一个完整的、可投入生产的 OpenClaw 多 Agent 系统架构。


本章关键词:Sub-agents、sessions_spawn、非阻塞执行、runId、隔离 Session、结构化通信、跨 Agent 记忆、ACP Harness、Native Plugin、并发上限、错误处理

本章评分
4.8  / 5  (3 评分)

💬 留言讨论