Модуль 3.4 · Урок 2
Урок 2: Маршрутизация сообщений
Содержание
- Чему вы научитесь
- Что такое маршрутизация
- Как это работает
- Bindings: формат и структура
- Базовая структура
- Поля match
- Пустой match = все сообщения
- Приоритеты маршрутизации
- Порядок приоритетов (от высшего к низшему)
- Пример: как работает приоритет
- Практические сценарии маршрутизации
- Сценарий 1: Личное + Работа
- Сценарий 2: WhatsApp с несколькими аккаунтами
- Сценарий 3: Несколько Telegram-ботов
- Сценарий 4: Discord с несколькими серверами
- Маршрутизация с несколькими условиями
- Привязка identity между каналами
- Отладка маршрутизации
- Логи маршрутизации
- Тестирование bindings
- Просмотр текущих bindings
- Типичные ошибки
- Ошибка 1: Широкий binding перед узким
- Ошибка 2: Забытый агент по умолчанию
- Ошибка 3: Несуществующий agentId
- Полный пример конфигурации
- Попробуйте сами
- Ключевые выводы
- Следующий урок
Чему вы научитесь
- Понимать систему маршрутизации (routing) OpenClaw
- Настраивать bindings для направления сообщений конкретным агентам
- Знать приоритеты матчинга: от конкретного к общему
- Создавать сложные правила маршрутизации для мультиканальных сценариев
- Отлаживать проблемы маршрутизации через логи
Что такое маршрутизация
В предыдущем уроке мы создали несколько агентов. Но пока все сообщения попадают к агенту по умолчанию. Маршрутизация (routing) определяет, какому агенту отправить входящее сообщение на основе правил.
Как это работает
- Пользователь отправляет сообщение через канал (Telegram, WhatsApp, Discord)
- Gateway получает сообщение и определяет метаданные: канал, аккаунт, отправитель, группа
- Gateway проходит по массиву bindings сверху вниз
- Первый binding, который совпадает с метаданными, определяет агента
- Если ни один binding не совпал — сообщение уходит агенту по умолчанию
Bindings: формат и структура
Bindings — это массив правил маршрутизации в openclaw.json. Каждый binding связывает условие (match) с агентом (agentId).
Базовая структура
Bindings -- правила маршрутизации сообщений
Поля match
| Поле | Описание | Пример |
|---|---|---|
channel | Название канала | "telegram", "whatsapp", "discord", "slack" |
accountId | ID аккаунта (если несколько аккаунтов в одном канале) | "personal", "work" |
peer.kind | Тип собеседника | "dm" (личное сообщение), "group" (группа) |
peer.id | ID собеседника или группы | "+79991234567", "120363...@g.us" |
guildId | ID сервера Discord | "1234567890" |
teamId | ID команды Slack | "T1234567890" |
Пустой match = все сообщения
Если match пустой или отсутствует, binding совпадает со всеми сообщениями:
{
"agentId": "catchall",
"match": {} // Ловит всё, что не поймали предыдущие bindings
}
Приоритеты маршрутизации
Bindings оцениваются детерминистически по принципу «первый совпавший побеждает — поэтому конкретные правила ставьте перед общими»:
Порядок приоритетов (от высшего к низшему)
- Peer match — конкретный собеседник (DM) или группа
- Guild ID — конкретный сервер Discord
- Team ID — конкретная команда Slack
- Account ID — конкретный аккаунт канала
- Channel — весь канал целиком
- Fallback — агент по умолчанию (если нет совпадений)
Пример: как работает приоритет
{
"bindings": [
// Приоритет 1: конкретный DM в WhatsApp -> work
{
"agentId": "work",
"match": {
"channel": "whatsapp",
"peer": { "kind": "dm", "id": "+79995551234" }
}
},
// Приоритет 2: семейная группа WhatsApp -> family
{
"agentId": "family",
"match": {
"channel": "whatsapp",
"peer": { "kind": "group", "id": "120363012345678901@g.us" }
}
},
// Приоритет 3: все остальные WhatsApp -> home
{
"agentId": "home",
"match": {
"channel": "whatsapp"
}
}
]
}
Что произойдёт:
| Кто пишет | Куда | Какой агент |
|---|---|---|
| +79995551234 | WhatsApp DM | work (peer match) |
| Кто-то | Семейная группа | family (group match) |
| Другой контакт | WhatsApp DM | home (channel match) |
| Кто-то | Telegram | default (нет binding, fallback) |
Практические сценарии маршрутизации
Сценарий 1: Личное + Работа
Два агента: home (личный) и work (рабочий). Telegram — личный, Slack — рабочий.
{
"agents": {
"list": [
{
"id": "home",
"default": true,
"workspace": "~/.openclaw/workspace",
"model": "claude-sonnet-4-6"
},
{
"id": "work",
"workspace": "~/.openclaw/workspace-work",
"model": "claude-opus-4-6"
}
]
},
"bindings": [
{
"agentId": "work",
"match": { "channel": "slack" }
},
{
"agentId": "work",
"match": { "channel": "discord" }
}
// Всё остальное (Telegram, WhatsApp) -> home (default)
]
}
Сценарий 2: WhatsApp с несколькими аккаунтами
Один сервер OpenClaw с двумя WhatsApp-номерами: личный и рабочий.
{
"channels": {
"whatsapp": {
"enabled": true
// Личный номер
},
"whatsapp:work": {
"enabled": true
// Рабочий номер
}
},
"bindings": [
{
"agentId": "work",
"match": {
"channel": "whatsapp",
"accountId": "work"
}
},
{
"agentId": "home",
"match": {
"channel": "whatsapp"
// accountId не указан -- ловит default аккаунт
}
}
]
}
Сценарий 3: Несколько Telegram-ботов
Два Telegram-бота: личный и для сообщества.
{
"channels": {
"telegram": {
"botToken": "7123456789:AAH..."
// Личный бот
},
"telegram:community": {
"botToken": "9876543210:BBK..."
// Бот для сообщества
}
},
"bindings": [
{
"agentId": "community",
"match": {
"channel": "telegram",
"accountId": "community"
}
}
// Личный Telegram -> default агент
]
}
Сценарий 4: Discord с несколькими серверами
Один Discord-бот на разных серверах -> разные агенты.
{
"bindings": [
{
"agentId": "work",
"match": {
"channel": "discord",
"guildId": "1234567890123456" // Рабочий сервер
}
},
{
"agentId": "community",
"match": {
"channel": "discord",
"guildId": "9876543210987654" // Сервер сообщества
}
}
]
}
Маршрутизация с несколькими условиями
Все поля в match объединяются через AND — все должны совпасть:
{
"agentId": "vip-support",
"match": {
"channel": "whatsapp",
"peer": { "kind": "dm", "id": "+79991234567" }
// Совпадёт ТОЛЬКО если канал = whatsapp И отправитель = этот номер
}
}
Если нужен OR (одно из условий), создайте несколько bindings:
{
"bindings": [
// VIP из WhatsApp -> vip-agent
{
"agentId": "vip",
"match": {
"channel": "whatsapp",
"peer": { "kind": "dm", "id": "+79991234567" }
}
},
// VIP из Telegram -> тот же vip-agent
{
"agentId": "vip",
"match": {
"channel": "telegram",
"peer": { "kind": "dm", "id": "123456789" }
}
}
]
}
Привязка identity между каналами
Если один человек пишет из разных каналов, можно объединить его сессии через identityLinks:
{
"session": {
"identityLinks": {
"alexey": [
"telegram:123456789",
"whatsapp:+79991234567",
"discord:987654321"
]
}
}
}
Теперь если Алексей начнёт разговор в Telegram и продолжит в WhatsApp, агент будет видеть общий контекст.
Отладка маршрутизации
Логи маршрутизации
openclaw logs -f --filter routing
При каждом входящем сообщении вы увидите:
[routing] Message from telegram:123456789 (dm)
[routing] Binding #0: channel=slack -> SKIP (channel mismatch)
[routing] Binding #1: channel=telegram, peer.id=987654321 -> SKIP (peer mismatch)
[routing] Binding #2: channel=telegram -> MATCH
[routing] Routed to agent: home
Тестирование bindings
# Показать, какой агент получит сообщение
openclaw routing test --channel telegram --peer 123456789
Test routing:
Channel: telegram
Peer: 123456789 (dm)
Match: binding #2 (channel=telegram)
Agent: home
Просмотр текущих bindings
openclaw agents list --bindings
Routing table:
#0 work ← slack (any)
#1 work ← discord (guild: 1234567890)
#2 family ← whatsapp (group: 120363...@g.us)
#3 home ← telegram (any)
-- default ← fallback (unmatched messages)
Типичные ошибки
Ошибка 1: Широкий binding перед узким
// НЕПРАВИЛЬНО
{
"bindings": [
{ "agentId": "home", "match": { "channel": "whatsapp" } },
{ "agentId": "family", "match": { "channel": "whatsapp", "peer": { "kind": "group", "id": "..." } } }
]
}
Binding #0 ловит все WhatsApp-сообщения, до binding #1 дело не дойдёт. Порядок имеет значение.
// ПРАВИЛЬНО -- конкретный binding первым
{
"bindings": [
{ "agentId": "family", "match": { "channel": "whatsapp", "peer": { "kind": "group", "id": "..." } } },
{ "agentId": "home", "match": { "channel": "whatsapp" } }
]
}
Ошибка 2: Забытый агент по умолчанию
Если все bindings конкретные и ни один не совпал, сообщение уйдёт к default. Убедитесь, что default агент настроен.
Ошибка 3: Несуществующий agentId
{
"agentId": "wrk", // Опечатка! Агент "wrk" не существует
"match": { "channel": "slack" }
}
OpenClaw выдаст ошибку при загрузке конфигурации. Проверяйте имена агентов.
Полный пример конфигурации
Комплексная настройка для реального использования:
{
"agents": {
"defaults": {
"model": "claude-sonnet-4-6",
"userTimezone": "Europe/Moscow",
"timeFormat": "24"
},
"list": [
{
"id": "home",
"default": true,
"workspace": "~/.openclaw/workspace-home",
"agentDir": "~/.openclaw/agents/home/agent",
"model": "claude-sonnet-4-6"
},
{
"id": "work",
"workspace": "~/.openclaw/workspace-work",
"agentDir": "~/.openclaw/agents/work/agent",
"model": "claude-opus-4-6"
},
{
"id": "family",
"workspace": "~/.openclaw/workspace-family",
"agentDir": "~/.openclaw/agents/family/agent",
"model": "claude-haiku-4-5-20251001",
"sandbox": { "mode": "all" },
"tools": {
"allow": ["search", "memory"],
"deny": ["exec", "write", "browser"]
}
}
]
},
"bindings": [
// Семейная группа WhatsApp -> family
{
"agentId": "family",
"match": {
"channel": "whatsapp",
"peer": { "kind": "group", "id": "120363012345678901@g.us" }
}
},
// Рабочий Slack -> work
{
"agentId": "work",
"match": { "channel": "slack" }
},
// Рабочий Discord-сервер -> work
{
"agentId": "work",
"match": {
"channel": "discord",
"guildId": "1234567890123456"
}
}
// Всё остальное (Telegram, WhatsApp DM) -> home (default)
]
}
Попробуйте сами
-
Создайте два агента (
homeиwork) с разными SOUL.md. -
Настройте bindings так, чтобы Slack-сообщения шли к
work, а всё остальное — кhome. -
Проверьте маршрутизацию через тестовую команду:
openclaw routing test --channel slack --peer U1234567 openclaw routing test --channel telegram --peer 123456789 -
Включите логи маршрутизации и отправьте сообщения через разные каналы:
openclaw logs -f --filter routing -
Проверьте порядок bindings. Поставьте широкий binding перед узким, отправьте сообщение и убедитесь, что узкий binding игнорируется. Затем исправьте порядок.
Ключевые выводы
- Bindings — массив правил маршрутизации, определяющих какой агент обрабатывает сообщение
- Первый совпавший binding побеждает — порядок критически важен
- Приоритеты: peer > guild/team > account > channel > default
- Все условия в match объединяются через AND
- Для OR используйте несколько bindings с одинаковым agentId
- identityLinks позволяют объединять сессии одного пользователя из разных каналов
- Конкретные bindings ставьте перед общими, иначе они никогда не сработают
- Отлаживайте через
openclaw routing testиopenclaw logs --filter routing
Следующий урок
Урок 3: Финальный проект — соберём всё вместе: 3 агента, 2+ канала, маршрутизация, SOUL.md и мониторинг.