Перейти к содержимому
NEWЧат с 15 ИИ-моделями — попробуйте бесплатно / имейте совесть, когда будете делиться или копировать
>AISTUDY_

Модуль 3.4 · Урок 2

Урок 2: Маршрутизация сообщений

35 мин
ТеорияПрактика
3.4 / Урок 2 из 3

Чему вы научитесь

  • Понимать систему маршрутизации (routing) OpenClaw
  • Настраивать bindings для направления сообщений конкретным агентам
  • Знать приоритеты матчинга: от конкретного к общему
  • Создавать сложные правила маршрутизации для мультиканальных сценариев
  • Отлаживать проблемы маршрутизации через логи

Что такое маршрутизация

В предыдущем уроке мы создали несколько агентов. Но пока все сообщения попадают к агенту по умолчанию. Маршрутизация (routing) определяет, какому агенту отправить входящее сообщение на основе правил.

Как это работает

  1. Пользователь отправляет сообщение через канал (Telegram, WhatsApp, Discord)
  2. Gateway получает сообщение и определяет метаданные: канал, аккаунт, отправитель, группа
  3. Gateway проходит по массиву bindings сверху вниз
  4. Первый binding, который совпадает с метаданными, определяет агента
  5. Если ни один binding не совпал — сообщение уходит агенту по умолчанию

Bindings: формат и структура

Bindings — это массив правил маршрутизации в openclaw.json. Каждый binding связывает условие (match) с агентом (agentId).

Базовая структура

Bindings -- правила маршрутизации сообщений

json
Нажмите на строку — увидите объяснение

Поля match

ПолеОписаниеПример
channelНазвание канала"telegram", "whatsapp", "discord", "slack"
accountIdID аккаунта (если несколько аккаунтов в одном канале)"personal", "work"
peer.kindТип собеседника"dm" (личное сообщение), "group" (группа)
peer.idID собеседника или группы"+79991234567", "120363...@g.us"
guildIdID сервера Discord"1234567890"
teamIdID команды Slack"T1234567890"

Пустой match = все сообщения

Если match пустой или отсутствует, binding совпадает со всеми сообщениями:

{
  "agentId": "catchall",
  "match": {}    // Ловит всё, что не поймали предыдущие bindings
}

Приоритеты маршрутизации

Bindings оцениваются детерминистически по принципу «первый совпавший побеждает — поэтому конкретные правила ставьте перед общими»:

Порядок приоритетов (от высшего к низшему)

  1. Peer match — конкретный собеседник (DM) или группа
  2. Guild ID — конкретный сервер Discord
  3. Team ID — конкретная команда Slack
  4. Account ID — конкретный аккаунт канала
  5. Channel — весь канал целиком
  6. 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"
      }
    }
  ]
}

Что произойдёт:

Кто пишетКудаКакой агент
+79995551234WhatsApp DMwork (peer match)
Кто-тоСемейная группаfamily (group match)
Другой контактWhatsApp DMhome (channel match)
Кто-тоTelegramdefault (нет 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)
  ]
}

Попробуйте сами

  1. Создайте два агента (home и work) с разными SOUL.md.

  2. Настройте bindings так, чтобы Slack-сообщения шли к work, а всё остальное — к home.

  3. Проверьте маршрутизацию через тестовую команду:

    openclaw routing test --channel slack --peer U1234567
    openclaw routing test --channel telegram --peer 123456789
  4. Включите логи маршрутизации и отправьте сообщения через разные каналы:

    openclaw logs -f --filter routing
  5. Проверьте порядок 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 и мониторинг.

Скачать урок

Есть идея или нашли ошибку?

// Обсуждение

Можно писать анонимно. Укажите email, чтобы получать уведомления об ответах.