🏠 Home ← Docs Index
Open Beta 📖 Interactive Reader Raw .md llms.txt

Multi-Agent Routing System

How GoClaw decides which agent answers an inbound message: ResolveAgent matches the channel, peer, thread, tenant, roles and account against the routing rules in eight priority tiers, then builds the session key.

internal/routing/routing.go + internal/routing/identity_links.go + internal/routing/routing_test.go

Architecture

flowchart TD
    IN["<b>Inbound Message → RouteInput</b><br/>• Channel, AccountID, PeerKind (direct/group), PeerID<br/>• ParentPeerKind, ParentPeerID (thread inheritance)<br/>• TenantID, PlatformRoles[]"]

    RES["<b>ResolveAgent(input) — 8-tier priority matching</b><br/>1. Peer match (exact channel + peer)<br/>2. Parent peer match (thread inherits parent binding)<br/>3. Tenant + Roles (tenant + role intersection)<br/>4. Roles match (channel role/dept without tenant)<br/>5. Tenant match (tenant without role filter)<br/>6. Account match (account ID)<br/>7. Channel match (channel-wide, no peer filter)<br/>8. Default (config.ResolveDefaultAgentID)"]

    KEY["<b>buildSessionKey(agentID, input)</b><br/>• ResolveIdentity() → cross-channel session sharing<br/>• Multi-agent: agent:channel:peer<br/>• Single-agent: channel:peer (backward compat)<br/>• Output: AgentID + SessionKey + MatchedBy"]

    IN --> RES --> KEY

Feature Comparison

# Feature OpenClaw GoClaw Status
R.1 Multi-agent routing ✅ ✅ ResolveAgent() — 8-tier priority Done
R.2 Peer binding (channel+peer) ✅ ✅ Exact peer match (kind + ID) Done
R.3 Thread inheritance — ✅ ParentPeerID → inherits parent binding GoClaw only
R.4 Tenant + Role matching — ✅ Tenant + platform role intersection (Discord guild and role IDs; App API) GoClaw only
R.5 Tenant-wide binding ✅ ✅ TenantID match (Discord guild, Teams group conversation) Done
R.6 Account binding ✅ ✅ Account ID → specific agent (the bot on Teams, Telegram and Facebook; the sender on Discord) Done
R.7 Channel-wide binding ✅ ✅ All messages on channel → agent Done
R.8 Default agent fallback ✅ ✅ config.ResolveDefaultAgentID() Done
R.9 Identity-linked sessions — ✅ ResolveIdentity() — cross-channel session sharing GoClaw only
R.10 Multi-agent session keys — ✅ agent:channel:peer format GoClaw only
R.11 Peer kind normalization — ✅ "channel" → "group" alias GoClaw only
R.12 Debug logging ✅ ✅ debug.Log("routing", ...) Done

What each channel supplies

A binding can only match what the channel puts on the message:

Channel Account ID Peer ID Tenant ID Platform roles
Discord the sender's user ID dm:<user ID> in a DM; <channel ID>:<user ID> in a guild channel, for messages, reactions and slash commands alike; the parent is the channel itself, or for a thread the channel it belongs to guild ID the member's role IDs
MS Teams the bot's app ID conversation ID (;messageid=… removed) the conversation ID in a group chat or channel; empty in a 1:1 chat none
Telegram the bot's ID chat ID none none
Facebook the page ID sender's page-scoped ID none none
App API app_api the employee's user ID from the caller's identity from the caller's identity

A Discord guild message's peer ID includes the user, so a peer binding with a bare channel ID matches through the parent: every message in that channel and in its threads. Route a whole Discord server with tenant_id. Teams never supplies roles, so a Teams binding with platform_roles never matches.

Config Example

agents:
  default: support-bot
  list:
    - id: support-bot
      model: gemini/gemini-2.5-flash
    - id: dev-bot
      model: claude/claude-sonnet-4-5-20250929

bindings:
  - match:
      channel: discord
      peer:
        kind: group
        id: "123456789012345678"           # a Discord channel and its threads
    agent_id: dev-bot

  - match:
      channel: discord
      tenant_id: "112233445566778899"      # a Discord server (guild ID)
      platform_roles: ["998877665544332211"]  # Discord role ID
    agent_id: dev-bot

  - match:
      channel: msteams
      tenant_id: "19:0123456789abcdef0123456789abcdef@thread.v2"  # one Teams group chat or channel
    agent_id: dev-bot

  - match:
      channel: discord                     # the rest of Discord
    agent_id: support-bot

Coverage: every feature in the table: routing with 8 priorities, identity links, thread inheritance.