OpenClaw 子智能体结果传递机制详解
本文基于 OpenClaw 源码分析
源码仓库:https://github.com/openclaw/openclaw
概述
在 OpenClaw 的多智能体架构中,主智能体通过 sessions_spawn 工具调用创建子智能体执行任务。子智能体完成后,其结果通过事件驱动的推送机制传递给主智能体,而不是主智能体主动轮询获取。
一、子智能体如何将结果传递给主智能体
核心机制:自动通知(Auto-Announce)
子智能体执行完成后,结果通过 runSubagentAnnounceFlow 函数自动通知主智能体:
// src/agents/subagent-announce.ts
export async function runSubagentAnnounceFlow(params: {
childSessionKey: string;
childRunId: string;
requesterSessionKey: string;
// ...
}): Promise<boolean>
传递流程
-
子智能体完成执行:子智能体运行结束,触发生命周期事件
phase: "end" -
等待子智能体最终回复:
runSubagentAnnounceFlow调用agent.wait等待子智能体产生最终输出 -
读取执行结果:从子智能体会话中读取最新的 assistant 回复
-
构建通知消息:将结果封装为内部事件消息
-
发送通知:通过
deliverSubagentAnnouncement发送通知给主智能体的会话
代码实现细节
// 读取子智能体的最终输出
let reply = params.roundOneReply;
if (!reply) {
reply = await readLatestSubagentOutput(params.childSessionKey);
}
// 构建内部事件
const internalEvents: AgentInternalEvent[] = [
{
type: "task_completion",
source: "subagent",
childSessionKey: params.childSessionKey,
status: outcome.status,
result: findings,
statsLine,
replyInstruction,
},
];
// 发送通知给主智能体
await deliverSubagentAnnouncement({
requesterSessionKey: targetRequesterSessionKey,
triggerMessage: buildAnnounceSteerMessage(internalEvents),
// ...
});
传递内容
- 执行结果:子智能体的最终输出内容
- 执行状态:
ok、timeout、error或unknown - 统计信息:
- 运行时长(runtime)
- Token 消耗(input/output/total)
- 缓存 token(prompt/cache)
- 格式示例:
Stats: runtime 45s • tokens 12.5K (in 3.2K / out 9.3K) • prompt/cache 15.1K
- 元数据:会话键、任务标签、执行时间
- announceType:通知类型,
"subagent task"或"cron job",影响事件 source 字段 - cleanup:会话清理策略,
"delete"删除会话 或"keep"保留会话
关键参数详解
| 参数 | 类型 | 说明 |
|---|---|---|
childSessionKey | string | 子智能体会话键 |
childRunId | string | 子智能体运行 ID |
requesterSessionKey | string | 请求者会话键 |
announceType | "subagent task" | "cron job" | 通知类型,影响事件 source |
cleanup | "delete" | "keep" | 完成后是否删除子会话 |
roundOneReply | string? | 预先获取的回复(可选) |
fallbackReply | string? | 兜底回复,当无输出时使用 |
expectsCompletionMessage | boolean? | 是否期望向用户发送完成消息 |
wakeOnDescendantSettle | boolean? | 子智能体子孙完成后是否唤醒继续执行 |
Embedded PI 机制
当子智能体是嵌入式 PI (Protocol Interpreter) 时,系统会等待其真正完成:
// 如果子会话是嵌入式 PI 运行,等待其结束
if (childSessionId && isEmbeddedPiRunActive(childSessionId)) {
const settled = await waitForEmbeddedPiRunEnd(childSessionId, settleTimeoutMs);
if (!settled) {
shouldDeleteChildSession = false;
return false; // 延迟通知
}
}
二、主智能体如何处理子智能体结果
当主智能体收到子智能体的完成通知时,系统会:
1. 作为内部消息注入
通知被作为用户消息注入到主智能体的会话中,触发主智能体的新一轮执行:
// 通知被注入为主智能体的用户消息
message: buildAnnounceSteerMessage(internalEvents),
2. 回复指令
通知消息中包含 replyInstruction,指导主智能体如何处理:
- 如果主智能体本身就是子智能体:回复
SILENT_REPLY_TOKEN(静默跳过) - 如果期望用户消息:将结果转换为正常的助手回复发送给用户
- 如果结果已发送:回复
NO_REPLY(静默跳过)
3. 投递重试机制
通知投递具有重试逻辑,针对瞬态错误会进行自动重试:
const DIRECT_ANNOUNCE_TRANSIENT_RETRY_DELAYS_MS = [5_000, 10_000, 20_000]; // 5s, 10s, 20s
// 瞬态错误(可重试)
const TRANSIENT_ERRORS = [
/unavailable/i,
/no active .* listener/i,
/gateway not connected/i,
/gateway closed/i,
/timeout/i,
/network error/i,
];
// 永久错误(不重试)
const PERMANENT_ERRORS = [
/unsupported channel/i,
/chat not found/i,
/user not found/i,
/bot was blocked/i,
/bot was kicked/i,
/recipient is not a valid/i,
];
4. 幂等性保证
使用 announceId 防止重复通知:
const announceId = buildAnnounceIdFromChildRun({
childSessionKey: params.childSessionKey,
childRunId: params.childRunId,
});
const directIdempotencyKey = buildAnnounceIdempotencyKey(announceId);
// 投递时携带幂等性 key,避免重复投递
5. 系统提示中的指导
主智能体收到系统提示中的明确指导:
Default workflow: spawn work, continue orchestrating, and wait for auto-announced completions.
等待:生成工作,继续编排,等待自动通知的完成。
Wait for completion events to arrive as user messages.
等待:等待完成事件作为用户消息到达。
Track expected child session keys and only send your final answer after completion events for ALL expected children arrive.
跟踪:跟踪预期的子智能体会话键,只有在所有预期的子智能体完成事件到达后才发送最终答案。
If a child completion event arrives AFTER you already sent your final answer, reply ONLY with NO_REPLY.
注意:如果子智能体完成事件在你的最终答案之后到达,只回复 NO_REPLY。
6. ACP/Harness 会话支持
系统提示中包含对 ACP (Codex/ClaudeCode/Gemini) 外置智能体会话的处理指导:
For ACP harness sessions (codex/claudecode/gemini), use `sessions_spawn` with `runtime: "acp"`.
`agents_list` and `subagents` apply to OpenClaw sub-agents (`runtime: "subagent"`).
Subagent results auto-announce back to you; ACP sessions continue in their bound thread.
ACP 会话与 OpenClaw 子智能体的区别:
- ACP 会话绑定到外部线程,结果不会自动通知给主智能体
- 使用
runtime: "acp"参数创建 ACP 会话
三、子智能体的调用执行方式
通过工具调用创建
子智能体通过 sessions_spawn 工具调用被创建:
// src/agents/tools/sessions-spawn-tool.ts
export function createSessionsSpawnTool() {
return {
name: "sessions_spawn",
execute: async (_toolCallId, args) => {
const result = await spawnSubagentDirect({...});
return jsonResult(result);
},
};
}
执行方式
- 同步创建,异步执行:
sessions_spawn调用立即返回结果(包含childSessionKey和runId) - 子智能体在独立会话中异步执行任务
- 主智能体可以继续其他操作,无需阻塞等待
// src/agents/subagent-spawn.ts
const response = await callGateway<{ runId: string }>({
method: "agent",
params: {
message: childTaskMessage,
sessionKey: childSessionKey,
// ...
},
timeoutMs: 10_000,
});
四、主智能体如何决定等待还是继续执行
关键点:基于 LLM 的自主决策
主智能体(基于 LLM)根据当前任务的上下文和系统提示,自主决定策略选择。
系统提示提供的指导
// src/agents/subagent-announce.ts - buildSubagentSystemPrompt
const lines = [
"Default workflow: spawn work, continue orchestrating, and wait for auto-announced completions.",
"默认工作流:生成工作,继续编排,等待自动通知的完成。",
"自动通知是基于推送的。生成子智能体后,不要调用 sessions_list、sessions_history、exec sleep 或任何轮询工具。",
"Wait for completion events to arrive as user messages.",
"等待完成事件作为用户消息到达。",
"Track expected child session keys and only send your final answer after completion events for ALL expected children arrive.",
"跟踪预期的子智能体会话键,只有在所有预期的子智能体完成事件到达后才发送最终答案。",
"If a child completion event arrives AFTER you already sent your final answer, reply ONLY with NO_REPLY.",
"如果子智能体完成事件在你的最终答案之后到达,只回复 NO_REPLY。",
// ...
];
可能的策略选择
| 场景 | 策略 | 说明 |
|---|---|---|
| 需要子智能体结果才能继续 | 等待 | 结束当前轮次,等待事件唤醒 |
| 有其他独立任务可处理 | 继续执行 | 启动多个子智能体,并行处理 |
| 嵌套子智能体 | 层级等待 | 等待所有层级的子智能体完成 |
嵌套子智能体特殊处理
待处理子孙检测
当子智能体本身还有未完成的子智能体时,会延迟通知:
const pendingChildDescendantRuns = Math.max(
0,
subagentRegistryRuntime.countPendingDescendantRuns(params.childSessionKey)
);
if (pendingChildDescendantRuns > 0 && announceType !== "cron job") {
shouldDeleteChildSession = false;
return false; // 延迟通知,等所有子孙完成
}
Wake 继续机制
子智能体可以通过 wakeOnDescendantSettle 参数配置,在其子孙完成后被"唤醒"继续执行:
if (
params.wakeOnDescendantSettle === true &&
childCompletionFindings?.trim() &&
!childRunAlreadyWoken
) {
// 唤醒子智能体 run,允许其继续处理子孙结果
await wakeSubagentRunAfterDescendants({...});
}
代码层面的非阻塞设计
// sessions_spawn 工具立即返回
const result = await spawnSubagentDirect({...});
return jsonResult(result); // 立即返回,主智能体继续执行
// 子智能体完成后,通过事件通知唤醒主智能体
// 主智能体收到新消息触发新一轮执行
五、主智能体等待时的状态
执行周期结束,等待事件唤醒
当主智能体决定等待子智能体时:
- 主智能体结束当前执行周期
- 不阻塞代码:主智能体不执行任何操作,不轮询子智能体状态
- 等待新的消息触发:子智能体完成后,通知被注入为主智能体的用户消息
- 被唤醒:主智能体收到新消息,触发新一轮执行周期
事件驱动机制
// src/infra/agent-events.ts
export function emitAgentEvent(event: Omit<AgentEventPayload, "seq" | "ts">) {
const nextSeq = (seqByRun.get(event.runId) ?? 0) + 1;
seqByRun.set(event.runId, nextSeq);
for (const listener of listeners) {
listener(enriched); // 通知所有监听器
}
}
六、调用 sessions_spawn 后的决策流程
┌─────────────────────────────────────────────────────────────┐
│ 主智能体调用 sessions_spawn │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 工具立即返回 { childSessionKey, runId, status } │
└─────────────────────────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 需要等待结果 │ │ 有其他任务 │ │ 需要多智能体 │
│ 才能继续 │ │ 可并行执行 │ │ 协调 │
└──────────────┘ └──────────────┘ └──────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 结束执行轮次 │ │ 继续执行 │ │ 记录会话键 │
│ 等待事件唤醒 │ │ 其他工具调用 │ │ 跟踪进度 │
└──────────────┘ └──────────────┘ └──────────────┘
决策依据
- 任务依赖关系:是否需要子智能体结果才能继续
- 系统提示指导:“不要轮询、等待事件”
- 会话键跟踪:记录所有创建的子智能体会话键
七、结束执行时的输出方式
当主智能体选择结束执行等待子智能体时:
可能的输出方式
- 静默结束:不输出任何内容,直接结束当前轮次
- 等待提示:输出简短提示如"正在处理中…"
- NO_REPLY:如果已经发送过最终答案,收到延迟通知时回复此标记
代码层面的处理
// src/agents/subagent-announce.ts
function buildAnnounceReplyInstruction(params: {
requesterIsSubagent: boolean;
expectsCompletionMessage?: boolean;
}): string {
if (params.requesterIsSubagent) {
return `... reply ONLY: ${SILENT_REPLY_TOKEN}.`;
}
if (params.expectsCompletionMessage) {
return `... send that user-facing update now.`;
}
return `... Reply ONLY: ${SILENT_REPLY_TOKEN} if this exact result was already delivered.`;
}
八、子智能体与工具调用的区别
| 特性 | 工具调用 | 子智能体 |
|---|---|---|
| 执行方式 | 同步执行,等待结果 | 异步执行,立即返回 |
| 执行环境 | 在主智能体环境中 | 在独立会话中 |
| 结果传递 | 通过返回值直接获取 | 通过事件通知获取 |
| 复杂性 | 简单任务 | 复杂、多步骤任务 |
| 并行能力 | 串行执行 | 可并行管理多个 |
| 生命周期 | 随主智能体结束 | 独立生命周期 |
总结
OpenClaw 的子智能体机制核心特点:
- 工具调用创建:通过
sessions_spawn工具同步创建 - 异步执行:子智能体在独立会话中异步执行
- 推送通知:子智能体完成后自动推送结果给主智能体
- 事件驱动:基于消息注入机制触发主智能体的新执行周期
- LLM 自主决策:主智能体根据系统提示和任务上下文自主决定等待或继续策略
- 投递重试:瞬态错误自动重试(5s → 10s → 20s),永久错误直接失败
- 幂等性保证:使用 announceId 防止重复通知
- 嵌套支持:支持多层子智能体嵌套,待子孙完成后再通知
- ACP 集成:支持外置智能体 (Codex/ClaudeCode/Gemini) 会话
- 会话清理:完成后可选择删除或保留子智能体会话
更多推荐



所有评论(0)