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 时传入的 task 和 context。
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 可以选择:
- 立即回复查询
- 等待用户输入后回复
- 让超时触发默认值
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 从"一个接一个地完成所有工作"转变为"智能地分配和协调多个专家的并行工作"。
核心要点总结:
- 非阻塞是关键:
sessions_spawn立即返回runId,主 Agent 不等待,可以同时管理多个并行任务 - 隔离是安全保障:每个子 Agent 有独立 Session,工具权限只能继承不能扩展,有效防止权限提升
- 结构化通信(2026.2.17):确定性 Agent ID + 标准化 IPC 消息格式,使复杂的 Agent 间协作成为可能
- 跨 Agent 记忆共享:通过
extraCollections配置,子 Agent 可以读取主 Agent 的上下文知识 - 并发上限 8 个:设计任务时考虑分批策略,超过 8 个并行任务需要分批执行
- 错误处理要健壮:配置
retryPolicy、onTimeout和fallback,防止单个子任务失败导致整体任务崩溃
在本书的最后章节,我们将综合前35章的内容,探讨如何设计一个完整的、可投入生产的 OpenClaw 多 Agent 系统架构。
本章关键词:Sub-agents、sessions_spawn、非阻塞执行、runId、隔离 Session、结构化通信、跨 Agent 记忆、ACP Harness、Native Plugin、并发上限、错误处理