OpenClaw / 06
Multi-Agent:子进程隔离与多渠道路由
OpenClaw 的多 Agent 架构有两个维度:
- SubAgent:主 Agent 派生子 Agent 处理独立子任务(上下文隔离)
- Multi-Channel:同一个 Agent 通过 20+ 消息平台接入(统一路由)
SubAgent:上下文隔离的正确姿势
为什么需要 SubAgent
单 Agent 处理复杂任务时,上下文会被中间过程污染:
flowchart TD
A["任务: 审查 3 个模块的安全问题"] --> B["读 auth.py<br>500 行进入上下文"]
B --> C["读 api.py<br>800 行进入上下文"]
C --> D["读 db.py<br>上下文溢出,早期分析被压缩"]
D --> E["最终报告质量很差"]
SubAgent 做法:每个模块由独立的子 Agent 处理,各自维护独立上下文:
flowchart TD
M["主 Agent: 拆任务 + 收结果"] --> S1["SubAgent 1<br>审查 auth.py"]
M --> S2["SubAgent 2<br>审查 api.py"]
M --> S3["SubAgent 3<br>审查 db.py"]
S1 -->|报告| R["主 Agent: 合并 3 份报告"]
S2 -->|报告| R
S3 -->|报告| R
OpenClaw 的 SubAgent 实现
// src/context-engine/index.ts
export interface ContextEngine {
// SubAgent 生命周期
prepareSubagentSpawn(task: SubagentTask): SpawnConfig
onSubagentEnded(result: SubagentResult): void
}
// 主 Agent 中派生子 Agent
const subagentResult = await spawnSubagent({
task: '审查 src/auth.py 的安全问题,返回发现的漏洞列表',
tools: ['Read', 'Grep', 'Bash'], // 子 Agent 可用的工具子集
budget: { maxTokens: 32000 }, // 独立的 token 预算
systemPrompt: '你是安全审计专家...' // 可以有专用指令
})
// subagentResult 只包含最终输出,不包含中间过程
// 主 Agent 的上下文不会被子 Agent 的工具调用记录污染
关键设计决策
| 决策 | pi-mono / OpenClaw 的选择 | 原因 |
|---|---|---|
| 子 Agent 进程模型 | 同进程异步(非 fork) | 避免进程间通信开销 |
| 上下文共享 | 不共享(完全隔离) | 防止上下文污染 |
| 工具权限 | 子集(主 Agent 指定) | 最小权限原则 |
| 结果格式 | 纯文本摘要 | 主 Agent 只需结论,不需过程 |
Worktree 隔离:文件系统级并行
当多个 SubAgent 需要同时修改代码时,上下文隔离不够——还需要文件系统隔离:
// Git Worktree:每个子 Agent 在独立的工作目录中操作
async function spawnWithWorktree(task: SubagentTask): Promise<SubagentResult> {
// 创建临时 worktree
const worktreePath = await git.worktree.add(`/tmp/agent-${uuid()}`, 'HEAD')
const result = await spawnSubagent({
...task,
workingDirectory: worktreePath // 子 Agent 在 worktree 中工作
})
if (result.hasChanges) {
// 子 Agent 有代码修改:返回 worktree 路径和分支
return { ...result, worktreePath, branch: result.branch }
} else {
// 无修改:自动清理 worktree
await git.worktree.remove(worktreePath)
return result
}
}
这解决了并行 Agent 的”踩踏”问题——两个 Agent 同时改同一个文件不会冲突。
Multi-Channel 路由:20+ 平台统一接入
OpenClaw 的另一个多 Agent 维度是渠道——同一个 Agent 能力通过不同消息平台接入:
// src/channels/index.ts
export interface Channel {
name: string
connect(): Promise<void>
onMessage(handler: MessageHandler): void
sendMessage(userId: string, content: string): Promise<void>
}
src/channels/
telegram/ ← Telegram Bot API
slack/ ← Slack Bolt
discord/ ← Discord.js
whatsapp/ ← WhatsApp Business API
feishu/ ← 飞书开放平台
dingtalk/ ← 钉钉机器人
wechat/ ← 微信公众号/企业微信
matrix/ ← Matrix 协议
irc/ ← IRC
email/ ← IMAP/SMTP
web/ ← WebSocket
cli/ ← 终端标准输入输出
... ← 共 20+ 渠道
统一网关架构
flowchart LR
A[Telegram] --> G[Gateway]
B[Slack] --> G
C[飞书] --> G
D[Web] --> G
E[CLI] --> G
G --> H[Session Router]
H --> I[Agent Instance\n+ Context Engine]
I --> J[Tools / MCP]
// src/gateway/router.ts
export class SessionRouter {
private sessions: Map<string, AgentSession> = new Map()
async route(channelMessage: ChannelMessage): Promise<string> {
// 用 channel + userId 唯一标识一个 session
const sessionKey = `${channelMessage.channel}:${channelMessage.userId}`
let session = this.sessions.get(sessionKey)
if (!session) {
session = await this.createSession(sessionKey)
this.sessions.set(sessionKey, session)
}
// 将消息路由到对应 session 的 Agent
return session.agent.processMessage(channelMessage.text)
}
}
每个用户在每个渠道有独立的 Session,互不干扰。同一用户跨渠道可以选择共享 Session 或独立 Session。
Binding-based 消息路由
当消息到达时,OpenClaw 用级联匹配策略决定路由到哪个 Agent 实例:
1. Exact Peer ID → 指定了具体 Agent ID,直接路由
2. Thread Inherit → 在已有会话线程中,继承该线程的 Agent
3. Role Routing → 根据消息内容匹配 Agent 角色(如 @security-bot)
4. Account Fallback → 用户的默认 Agent
5. Default → 系统兜底 Agent
Session 重置策略
session:
resetPolicy: "daily" // 每天 4 AM 重置上下文
// 或
resetPolicy: "idle" // 5 分钟无活动后重置
// 或
resetPolicy: "manual" // 只有用户手动 /reset 时重置
isolationScope: "channel" // 按渠道隔离
// 或
isolationScope: "user" // 按用户隔离(跨渠道共享)
跨 Agent 记忆搜索
多 Agent 场景下,Agent A 可以搜索 Agent B 的记忆(需要配置授权):
// 配置跨 Agent 记忆访问
memorySearch:
qmd:
extraCollections:
- agentId: "security-bot"
scope: "readonly"
- agentId: "docs-bot"
scope: "readonly"
这让专业化 Agent(安全审计、文档助手)的知识可以被通用 Agent 引用,而不需要重复存储。
Agent Teams:协作模式
超越简单的 SubAgent 派生,OpenClaw 支持多种协作模式:
模式 1:Fan-out / Fan-in(并行)
// 任务可并行拆分时
const tasks = [
{ task: '分析 auth 模块', agent: 'security-reviewer' },
{ task: '分析 api 模块', agent: 'security-reviewer' },
{ task: '检查测试覆盖', agent: 'test-analyst' }
]
// 并行执行所有子任务
const results = await Promise.all(
tasks.map(t => spawnSubagent(t))
)
// 合并结果
const report = await synthesize(results)
模式 2:Pipeline(串行依赖)
// 任务有先后依赖时
const analysis = await spawnSubagent({ task: '分析代码结构' })
const plan = await spawnSubagent({ task: `基于以下分析制定重构计划:${analysis}` })
const implementation = await spawnSubagent({ task: `执行以下重构计划:${plan}` })
模式 3:Debate(对抗验证)
// 需要减少幻觉时:一个生成,一个验证
const proposal = await spawnSubagent({
task: '给出修复方案',
systemPrompt: '你是修复专家...'
})
const review = await spawnSubagent({
task: `审查以下修复方案是否正确:${proposal}`,
systemPrompt: '你是严格的代码审查者,找出方案中的问题...'
})
什么时候用多 Agent
| 信号 | 单 Agent | 多 Agent |
|---|---|---|
| 上下文会溢出 | ❌ | ✅ 子 Agent 各自维护小上下文 |
| 子任务可并行 | ❌ | ✅ Promise.all 加速 |
| 需要专业化 system prompt | ❌ | ✅ 不同子 Agent 不同人设 |
| 需要减少幻觉 | ❌ | ✅ 生成 + 验证对抗 |
| 需要同时改多个文件 | ❌ | ✅ Worktree 隔离 |
| 任务简单、上下文够用 | ✅ | ❌ 不要过度设计 |
核心原则:如果单 Agent 能搞定,不要引入多 Agent。多 Agent 增加的复杂度必须被明确的收益覆盖。
面试高频题
Q:主从调度(Orchestrator + Worker)架构的问题是什么?
核心问题是上下文污染。Worker 的执行细节(大量工具调用记录、中间结果)全部流回 Orchestrator,它的上下文迅速膨胀,判断力下降。正确做法是 Worker 只返回最终结论,不返回过程。
Q:SubAgent 和 MCP Server 的区别?
SubAgent 是一个完整的 Agent(有 LLM 推理能力、有独立上下文),处理需要思考和决策的子任务。MCP Server 是纯工具(无 LLM 调用),执行确定性操作(查数据库、调 API)。选择依据:子任务是否需要”思考”。
Q:如何设计多 Agent 间的通信协议?
OpenClaw 的做法是不通信——子 Agent 之间完全隔离,只通过主 Agent 的任务描述和结果汇总间接交互。避免了消息格式协商、死锁、竞态等分布式系统经典问题。如果确实需要通信(如 Debate 模式),用结构化文本(Markdown)作为接口,而不是自定义协议。
下一篇:读懂 pi-mono 源码