Day 5 | OpenClaw 多 Agent 路由:一个 Gateway 托管多个 AI 大脑
Day 5 | OpenClaw 多 Agent 路由:一个 Gateway 托管多个 AI 大脑
系列:《从 0 到 1 拆解 AI Agent 框架:OpenClaw 技术深度解析》
前言
想象一个场景:你有一个个人助手 Agent,同时你还部署了一个专门处理代码审查的 Agent,以及一个管理家庭自动化的 Agent。它们需要接入同一个 Telegram 账号,但各自有独立的"大脑"和记忆。
这就是 多 Agent 路由 要解决的问题:一个 Gateway,多个 AI 大脑,消息如何精准投递?
路由看起来简单,但实现起来有不少细节:怎么区分消息属于哪个 Agent?跨 Agent 的消息怎么传递?不同 Agent 如何共享同一个渠道账号却互不干扰?本文将逐一拆解。
一、架构概览:Gateway 与 Agent 的关系
先明确核心概念。
1.1 Gateway vs Agent
在 OpenClaw 中:
-
Gateway 是消息的"交换机"——它负责接收来自各渠道(Telegram/Discord/WhatsApp…)的消息,并将其路由到正确的 Agent。它不做 AI 推理,只做路由。
-
Agent 是消息的"处理器"——每个 Agent 有自己的配置(使用什么模型、什么系统提示词、什么工具),负责接收消息、调用 LLM、返回回复。
Telegram ─┐
Discord ──┤ Gateway ──┬── Agent A (个人助手)
WhatsApp ─┘ ├── Agent B (代码审查)
└── Agent C (家庭自动化)
1.2 一对多 vs 多对多
最简单的部署是一个 Gateway + 一个 Agent,这是大多数人的起点。
但 OpenClaw 支持更复杂的拓扑:
- 一个 Gateway + 多个 Agent:共享渠道账号,按规则分发消息
- 多个 Gateway + 多个 Agent:完全隔离的部署,适合多用户场景
本文聚焦最有意思的场景:一个 Gateway,多个 Agent。
二、Binding:路由规则的核心
消息从 Telegram 进来,Gateway 怎么知道该交给哪个 Agent?答案是 Binding(绑定)。
2.1 什么是 Binding?
Binding 是一条路由规则,定义了:
“来自渠道 X、用户 Y 的消息,交给 Agent Z 处理”
配置示例:
agents:
- id: personal-assistant
bindings:
- channel: telegram
userId: "123456789" # 我自己的 Telegram ID
- id: code-reviewer
bindings:
- channel: discord
guildId: "my-work-server"
channelId: "code-review"
- id: home-automation
bindings:
- channel: telegram
userId: "987654321" # 家里另一个账号
2.2 Binding 的匹配优先级
当一条消息可能匹配多个 Binding 时(比如同一个用户在不同场景),需要有清晰的优先级规则:
精确匹配 > 通配匹配 > 默认 Agent
具体来说:
userId + channelId都匹配 → 优先级最高- 只有
guildId匹配 → 次之 - 没有任何精确匹配 → 走默认 Agent(如果配置了的话)
2.3 动态 Binding vs 静态 Binding
除了配置文件里的静态 Binding,OpenClaw 还支持运行时动态创建 Binding。
典型场景:用户发 /start 命令,Gateway 动态创建一个新 Session 并绑定到指定 Agent:
// 用户发送 /start code-review
// Gateway 解析命令,动态创建 Binding
await bindingManager.create({
agentId: 'code-reviewer',
channel: 'telegram',
userId: message.userId,
sessionKey: `code-reviewer:telegram:${message.userId}`,
ttl: 3600 * 24, // 24小时后过期
});
这让用户可以在运行时"切换"使用不同的 Agent,而不需要重新配置。
三、Session 隔离:多 Agent 的记忆边界
多 Agent 场景下,Session 的隔离尤为重要。
3.1 Session Key 的 Agent 前缀
还记得 Day 3 讲的 Session Key 格式吗?
{agentId}:{channelId}:{userId}
agentId 作为前缀,天然保证了不同 Agent 的 Session 完全隔离:
personal-assistant:telegram:user_123 ← 个人助手的记忆
code-reviewer:telegram:user_123 ← 代码审查的记忆
同一个用户(user_123)和不同 Agent 的对话,存储在不同的 JSONL 文件中,互不干扰。
3.2 上下文不跨 Agent 共享
这是一个重要的安全和隐私设计:Agent 之间默认不共享上下文。
你告诉个人助手的私人信息,不会出现在代码审查 Agent 的上下文里。每个 Agent 只看得到自己 Session 里的历史。
当然,如果你明确需要跨 Agent 共享信息,可以通过 Workspace 文件(比如 MEMORY.md)来传递——这是一种受控的、显式的信息共享机制。
四、消息分发:从 Gateway 到 Agent
一条消息进来,Gateway 的分发流程如下:
1. 接收消息
↓
2. 解析来源(channel + userId + groupId…)
↓
3. 查询 Binding 规则,确定目标 Agent
↓
4. 构造投递包(消息内容 + 元数据 + sessionKey)
↓
5. 将投递包发送到 Agent 的消息队列
↓
6. Agent 异步处理,回复通过 Gateway 发回
4.1 消息队列:解耦 Gateway 和 Agent
Gateway 和 Agent 之间通过消息队列通信,而不是直接调用。这个设计有几个好处:
好处一:解耦。Gateway 不需要知道 Agent 在哪里、是否在线。只要把消息放进队列,就完成了职责。
好处二:背压。如果 Agent 处理速度跟不上消息速度,队列可以缓冲,避免丢消息。
好处三:可观测。队列里的消息可以被监控,方便排查问题。
4.2 本地部署的简化实现
对于单机部署,OpenClaw 用内存中的事件总线替代正式的消息队列:
// Gateway 发送消息
eventBus.emit(`agent:${agentId}:message`, {
sessionKey,
content: message.content,
role: 'user',
metadata: { channel, userId, messageId }
});
// Agent 监听消息
eventBus.on(`agent:${agentId}:message`, async (payload) => {
await agent.processMessage(payload);
});
这在单机上运行得很好,扩展到多机时可以把 EventBus 替换成 Redis Pub/Sub 或 RabbitMQ,上层逻辑不变。
五、多账号支持:同一 Agent,多个渠道身份
一个 Agent 可以同时出现在多个渠道——比如同一个助手,在 Telegram 用一个号,在 Discord 用另一个号。
5.1 Channel Account 管理
每个渠道账号在 OpenClaw 中叫做 Channel Account:
channelAccounts:
- id: tg-personal
provider: telegram
token: "BOT_TOKEN_1"
- id: discord-work
provider: discord
token: "BOT_TOKEN_2"
- id: tg-family
provider: telegram
token: "BOT_TOKEN_3"
Agent 通过 Binding 关联到一个或多个 Channel Account:
agents:
- id: personal-assistant
channelAccounts: [tg-personal, discord-work] # 在两个渠道都活跃
5.2 回复时的渠道感知
Agent 处理完消息后,回复需要发回原来的渠道,而不是随便选一个。
OpenClaw 在 Session 中记录了消息来源:
// 每条消息都携带来源信息
interface InboundMessage {
content: string;
source: {
channelAccountId: string; // 从哪个渠道账号来的
channelType: string; // telegram/discord/...
userId: string;
messageId: string;
}
}
// 回复时,使用相同的 channelAccountId 发送
async function reply(sessionKey: string, content: string) {
const source = getLastMessageSource(sessionKey);
await channelManager.send(source.channelAccountId, source.userId, content);
}
这确保了用户在哪个渠道问,就在哪个渠道收到回答。
六、Sub-Agent 路由:Agent 内部的任务分发
除了 Gateway 层面的多 Agent 路由,OpenClaw 还支持 Agent 内部的 Sub-Agent 路由。
6.1 什么是 Sub-Agent?
当主 Agent 遇到需要"专注处理"的任务时,可以召唤一个 Sub-Agent:
// 主 Agent 调用 sessions_spawn 创建子 Agent
const subSession = await sessions.spawn({
agentId: 'code-reviewer', // 使用专门的代码审查 Agent
task: '请审查这段 Python 代码:\n' + code,
mode: 'run', // 一次性任务
cleanup: 'delete', // 完成后删除 Session
});
Sub-Agent 有完全独立的执行环境:独立的 Session、独立的上下文、独立的工具调用历史。
6.2 Sub-Agent 的结果返回
Sub-Agent 完成任务后,结果会推送回主 Session:
主 Agent Session
→ 调用 sessions_spawn
→ 创建 Sub-Agent Session
→ Sub-Agent 执行任务(可能有多轮对话)
→ Sub-Agent 完成,生成最终结果
→ 结果推送到主 Session
→ 主 Agent 接收结果,继续处理
主 Agent 不需要等待(阻塞),Sub-Agent 完成后会主动通知。这是异步编排的典型模式。
6.3 Agent 间的信任边界
Sub-Agent 的权限由父 Agent 决定,不能超越父 Agent 的权限范围:
- 父 Agent 可以给 Sub-Agent 更少的工具权限(沙箱化)
- 父 Agent 不能给 Sub-Agent 自己没有的权限(防权限提升)
这个设计参考了 Unix 的进程权限模型:子进程不能拥有比父进程更高的权限。
七、路由的安全性:防止越权访问
多 Agent 场景下,安全性是个重要议题。
7.1 Agent 隔离的安全价值
不同 Agent 的 Session 完全隔离,这不只是功能设计,也是安全设计:
- 防止信息泄露:代码审查 Agent 无法读取个人助手的对话历史
- 防止权限提升:低权限 Agent 无法调用高权限 Agent 的工具
- 故障隔离:一个 Agent 崩溃不影响其他 Agent
7.2 消息来源验证
Gateway 在路由消息前,会验证消息来源的合法性:
async function routeMessage(message: InboundMessage): Promise<void> {
// 1. 验证消息签名(防止伪造)
if (!verifyMessageSignature(message)) {
logger.warn('Invalid message signature', message);
return;
}
// 2. 检查发送者是否有权限访问目标 Agent
const binding = findBinding(message);
if (!binding) {
logger.info('No binding found, using default agent or dropping');
return;
}
// 3. 路由到目标 Agent
await dispatch(binding.agentId, message);
}
7.3 频率限制
为了防止滥用,每个 (channelAccount, userId) 对都有独立的频率限制:
- 单用户每分钟最多 N 条消息
- 超过限制的消息被静默丢弃(或返回提示)
- 不同 Agent 的频率限制独立计算
八、实战:配置一个多 Agent 部署
理论讲完了,来看一个实际的配置示例。
假设你想部署:
- 个人助手:接 Telegram 私聊,使用 Claude Sonnet,有记忆功能
- 代码审查机器人:接 Discord 某个频道,使用 GPT-4o,专注代码质量
# openclaw.config.yaml
gateway:
port: 3000
token: "your-gateway-token"
channelAccounts:
- id: tg-me
provider: telegram
token: "${TELEGRAM_BOT_TOKEN}"
- id: discord-work
provider: discord
token: "${DISCORD_BOT_TOKEN}"
agents:
- id: personal-assistant
model: anthropic/claude-sonnet-4
systemPrompt: |
你是一个私人助手。你有访问用户文件和工具的权限。
workspace: ~/assistant-workspace
bindings:
- channelAccountId: tg-me
# 不限制 userId,接受所有私聊
tools:
- read
- write
- web_search
- memory_search
- id: code-reviewer
model: openai/gpt-4o
systemPrompt: |
你是一个专业的代码审查助手。只关注代码质量、安全性和最佳实践。
bindings:
- channelAccountId: discord-work
channelId: "1234567890" # 只监听特定频道
tools:
- read # 只允许读文件,不允许写
这个配置实现了:
- 两个 Agent 使用不同的模型
- 两个 Agent 有不同的工具权限(代码审查只读)
- 消息来源不同,互不干扰
九、设计哲学:显式优于隐式
回顾 OpenClaw 的多 Agent 路由设计,有一个贯穿始终的原则:
路由规则要显式、可读、可调试。
- Binding 是配置文件中的 YAML,而不是隐藏在代码里的逻辑
- Session Key 把路由信息编码进 ID 本身,一眼可见
- Agent 之间的信息共享通过 Workspace 文件显式进行,而不是隐式传递
这让整个系统的行为可预测、可审计。当出现问题时,你可以通过查看配置文件和日志,快速定位是哪个 Agent 处理了哪条消息。
小结
本文拆解了 OpenClaw 的多 Agent 路由机制:
| 机制 | 实现方案 | 核心思想 |
|---|---|---|
| 消息路由 | Binding 规则匹配 | 显式配置,精确投递 |
| Session 隔离 | agentId 作为 Key 前缀 | 身份即边界 |
| 多渠道支持 | Channel Account 管理 | 一 Agent 多身份 |
| Sub-Agent | 异步任务分发 + 结果推送 | 关注点分离 |
| 安全性 | 消息验证 + 权限隔离 | 最小权限原则 |
下一篇,我们将进入工具系统与 Skills——AI 是怎么"动手"的?工具调用的完整生命周期是什么样的?
作者:一个在折腾 AI Agent 框架的工程师
系列索引:[Day 1 架构概览] | [Day 2 Gateway] | [Day 3 Agent 运行时] | [Day 4 流式输出] | Day 5 多 Agent 路由 | Day 6 工具系统 → 敬请期待
如果这篇文章对你有帮助,欢迎点赞收藏 🎯
更多推荐



所有评论(0)