Files
picoclaw/docs/guides/routing-guide.md
T
lxowalle 77b0c43392 refactor: support explicit provider field in model list entries (#2609)
* refactor: support explicit model list providers

* fix(web): preserve explicit model providers

* fix(web): preserve legacy provider prefixes on model updates

fix(models): normalize explicit provider-prefixed ids

fix(api): preserve legacy model updates across providers

fix(agent): preserve config identity for explicit provider refs

* fix ci
2026-04-22 11:28:47 +08:00

7.0 KiB

Routing Guide

Back to README

In PicoClaw, routing has two user-facing parts:

  • agent routing: choose which agent should handle a message
  • model routing: choose whether a turn should use the primary model or the configured light model

This guide explains how to configure both for real deployments.

Quick Start

Route one Telegram group to a support agent

{
  "agents": {
    "list": [
      { "id": "main", "default": true },
      { "id": "support" }
    ],
    "dispatch": {
      "rules": [
        {
          "name": "telegram support group",
          "agent": "support",
          "when": {
            "channel": "telegram",
            "chat": "group:-1001234567890"
          }
        }
      ]
    }
  }
}

Route only Slack mentions in one workspace

{
  "agents": {
    "list": [
      { "id": "main", "default": true },
      { "id": "support" }
    ],
    "dispatch": {
      "rules": [
        {
          "name": "slack mentions",
          "agent": "support",
          "when": {
            "channel": "slack",
            "space": "workspace:t001",
            "mentioned": true
          }
        }
      ]
    }
  }
}

Use a light model for simple turns

{
  "model_list": [
    {
      "model_name": "gpt-main",
      "provider": "openai",
      "model": "gpt-5.4",
      "api_keys": ["sk-main"]
    },
    {
      "model_name": "flash-light",
      "provider": "gemini",
      "model": "gemini-2.0-flash-exp",
      "api_keys": ["sk-light"]
    }
  ],
  "agents": {
    "defaults": {
      "model_name": "gpt-main",
      "routing": {
        "enabled": true,
        "light_model": "flash-light",
        "threshold": 0.35
      }
    }
  }
}

Agent Routing

Agent routing is configured with:

agents.dispatch.rules

Rules are evaluated from top to bottom. The first matching rule wins. If no rule matches, PicoClaw falls back to the default agent.

Supported Match Fields

Field Meaning Example
channel Channel name telegram, slack, discord
account Normalized account ID default, bot2
space Workspace, guild, or similar container workspace:t001, guild:123456
chat Direct chat, group, or channel direct:user123, group:-100123, channel:c123
topic Thread or topic topic:42
sender Normalized sender identity 12345, john
mentioned Whether the bot was explicitly mentioned true

Values must match the normalized runtime shape, not the raw incoming payload.

Rule Ordering

Put more specific rules before broader rules.

Good:

  1. VIP sender inside one group
  2. all traffic for that group
  3. channel-wide fallback

Bad:

  1. all traffic for that group
  2. VIP sender inside the same group

In the bad ordering, the broad rule wins first and the VIP rule never runs.

Session Interaction

Routing and sessions are related but different.

  • routing decides which agent handles the message
  • session settings decide which messages share memory

You can override the global session.dimensions value for one matched rule with session_dimensions.

Example:

{
  "agents": {
    "list": [
      { "id": "main", "default": true },
      { "id": "support" },
      { "id": "sales" }
    ],
    "dispatch": {
      "rules": [
        {
          "name": "vip in support group",
          "agent": "sales",
          "when": {
            "channel": "telegram",
            "chat": "group:-1001234567890",
            "sender": "12345"
          },
          "session_dimensions": ["chat", "sender"]
        },
        {
          "name": "support group",
          "agent": "support",
          "when": {
            "channel": "telegram",
            "chat": "group:-1001234567890"
          },
          "session_dimensions": ["chat"]
        }
      ]
    }
  },
  "session": {
    "dimensions": ["chat"]
  }
}

In this configuration:

  • the VIP gets routed to sales
  • everyone else in the group goes to support
  • the VIP route also gets per-user session isolation

session.identity_links also affects routing when you match on sender. Use it when the same real user may appear under multiple raw sender IDs.

Example:

{
  "session": {
    "identity_links": {
      "john": ["slack:u123", "legacy-user-42"]
    }
  },
  "agents": {
    "dispatch": {
      "rules": [
        {
          "name": "john goes to sales",
          "agent": "sales",
          "when": {
            "sender": "john"
          }
        }
      ]
    }
  }
}

Model Routing

Model routing is configured under:

agents.defaults.routing

Current fields:

Field Meaning
enabled Turn model routing on or off
light_model model_name from model_list used for simple turns
threshold Complexity cutoff in [0, 1]

Important behavior:

  • the light model must exist in model_list
  • PicoClaw resolves the light model at startup; if it is invalid, routing is disabled
  • one turn stays on one model tier, even if it later calls tools

What Affects The Complexity Score

The current model router looks at structural signals such as:

  • message length
  • fenced code blocks
  • recent tool calls in the same session
  • conversation depth
  • media or attachments

This means a "simple" turn may still go to the primary model if it includes:

  • code
  • images or audio
  • a very long prompt
  • a tool-heavy ongoing workflow

Choosing A Threshold

Recommended starting point:

{
  "agents": {
    "defaults": {
      "routing": {
        "enabled": true,
        "light_model": "flash-light",
        "threshold": 0.35
      }
    }
  }
}

General rule:

  • lower threshold: use the primary model more often
  • higher threshold: use the light model more aggressively

Practical suggestions:

  • 0.25 if you want safer routing with fewer light-model turns
  • 0.35 as the default starting point
  • 0.50+ only if your light model is already strong enough for most chat traffic

Troubleshooting

A rule is not matching

Check:

  • rule order
  • normalized value shape such as group:-100123 instead of just -100123
  • whether the channel actually provides space, topic, or mentioned

The wrong agent handles a message

The most common cause is ordering. Remember: first match wins.

The light model is never used

Check:

  • agents.defaults.routing.enabled is true
  • light_model exists in model_list
  • the light model can actually initialize
  • your threshold is not too low

The primary model is still chosen for short messages

That can still happen when the turn includes:

  • a code block
  • media or attachments
  • recent tool-heavy history

Routing works, but the conversation memory is still too shared

Adjust session.dimensions globally or session_dimensions on the specific route. Routing chooses the agent, but sessions decide context sharing.