> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nasiko.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing engine

> How Nasiko picks an agent for a query, the knobs to tune, and how to inspect decisions.

The routing engine is a three-stage pipeline that picks one agent from a candidate set. It makes one decision, then hands off — it's not an agentic loop. For several agents collaborating on one task, see [multi-agent workflows](/platform/maf).

## When routing runs

<Warning>
  This pipeline does **not** run on every chat message that omits an agent. Read this section before assuming what powers your unrouted chat traffic.
</Warning>

Chat requests go to `POST /api/orchestrator/a2a`. What happens depends on `agent_id`:

| Request                            | What runs                                                                                                                                                                                                                                                |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Explicit `agent_id`                | Proxies straight to that container. No selection.                                                                                                                                                                                                        |
| No `agent_id`, or `"orchestrator"` | A multi-turn **orchestrator** — every accessible agent becomes a callable tool and an LLM decides across up to ten turns which to call. No shortlist or rerank. See [how Nasiko dispatches to agents](/adlc/a2a-agents#how-nasiko-dispatches-to-agents). |

The pipeline below is invoked in one place today: **auto-assigning an agent to a [MAF workflow](/platform/maf) step** that doesn't name one. Tuning it shapes MAF step assignment, not live chat routing. Full detail: [where the pipeline actually runs](/platform/orchestrator#where-the-pipeline-actually-runs).

## The three stages

```mermaid theme={null}
flowchart LR
    Q(["User query"]) --> S1["① Shortlist<br/>semantic similarity"]
    S1 --> S2["② Rerank<br/>conversation context"]
    S2 --> S3["③ Select<br/>LLM decision"]
    S3 --> A(["Chosen agent"])
```

1. **Shortlist** — ranks agents by semantic similarity between the query and each agent's description, keeping the top candidates. Skipped for small fleets (`ROUTER_SHORTLIST_THRESHOLD`) or if the embedding provider is unavailable; either way every agent advances.
2. **Rerank** — re-scores candidates using conversation context, so a follow-up is more likely to stay with the agent already handling the session. On failure, the shortlist order is kept.
3. **Select** — an LLM makes the final call. On failure, the top-ranked candidate is chosen and the decision is marked `fallback_used: true`.

Every stage degrades gracefully — a query is never rejected because a routing sub-step had trouble. Decisions are logged asynchronously, off the response path.

## Tuning

Environment variables on the control plane:

| Env var                      | Default          | What it controls                                |
| ---------------------------- | ---------------- | ----------------------------------------------- |
| `ROUTER_SHORTLIST_THRESHOLD` | `15`             | Fleet size above which shortlisting runs at all |
| `ROUTER_SHORTLIST_SIZE`      | `10`             | Max candidates kept for reranking               |
| `ROUTER_MODEL`               | `gpt-4o`         | Model for the selection stage                   |
| `EMBEDDING_MODEL`            | provider default | Embedding model for the shortlist stage         |
| `ROUTER_AGENT_TIMEOUT_SECS`  | `60`             | How long to wait on the chosen agent's response |

These tune the engine's *own* decision-making, separate from which model an agent calls once selected — see [reusable LLM configs](#reusable-llm-configs).

## Inspecting decisions

`GET /api/orchestrator/stats` returns aggregated stats from this pipeline's decisions — today, MAF step auto-assignments. One row per agent per day, newest and most-selected first, capped at 200 rows.

```sh theme={null}
curl -H "Authorization: Bearer $TOKEN" https://<control-plane>/api/orchestrator/stats
```

```json theme={null}
{
  "data": [
    {
      "agent_name": "weather-bot",
      "selection_count": 142,
      "successful_calls": 138,
      "failed_calls": 4,
      "avg_agent_latency_ms": "812.4",
      "avg_selection_latency_ms": "310.2",
      "avg_stage1_candidates": "6.0",
      "avg_stage2_candidates": "3.0",
      "date": "2026-07-30"
    }
  ],
  "total": 1
}
```

There's no CLI command for this yet — call the route directly.

To see which agent handled a *live chat* query, look at its session and trace instead: `GET /api/observability/session/{id}` and `GET /api/observability/trace/{id}`.

## Reusable LLM configs

Separate from engine tuning, you can control which LLM an *individual agent* calls, through a per-user library of named configs:

```sh theme={null}
# available providers, models, price per 1M tokens
nasiko llm-config providers

# create a named config
nasiko llm-config create --name support-cheap --provider openai --model gpt-4o-mini

# attach it to an agent you own
nasiko llm-config attach support-bot support-cheap

# check what an agent will use, and where that resolved from
nasiko llm-config get support-bot
```

A config carries a provider, model, optional fallback models, tuning (temperature, max tokens), and optionally your own API key. `--pin` locks an agent to one model — the engine still picks *which agent* answers, but that agent's LLM calls stay fixed.

`nasiko model-registry ls` / `set` manage a platform-wide strength-level→model table (level 1 strongest, level 3 smallest) that a smart-routing config can draw from. Changing it requires superuser.

Full command set: `nasiko llm-config --help`.

<CardGroup cols={2}>
  <Card title="LLM router dashboard" href="/product/llm-router">
    Configure providers and per-level models in the web app.
  </Card>

  <Card title="TokenOps" href="/platform/tokenops">
    What your routing and model choices cost.
  </Card>

  <Card title="A2A agents and frameworks" href="/adlc/a2a-agents">
    What makes an agent discoverable and routable.
  </Card>

  <Card title="Orchestrator reference" href="/platform/orchestrator">
    Failure behavior and the flow guard.
  </Card>
</CardGroup>
