Docs · Routing groups
Intent routing
Send a turn to a model by what the user asks for: tests to the strong model, quick questions to the fast cheap one. You describe each kind in a few words. When a turn begins, a small model you choose picks which kind the message is.
Intent is one of the conditions a routing group’s rule can have, next to length, images, reasoning and agent. This page describes exactly what magpie does with it, as of v0.1.99, so you can predict every decision and check it in the Routing tab.
Set it up
You need a routing group with at least two models, and one more model to classify with. The classifier can be any model magpie has, in the group or not.
- Open the groupGo to Routing, find the group under Routing groups and click Edit.
- Add a rule with an intentClick Add a rule, choose the model under send to, click asks for and type what the message asks for, such as writing or fixing tests. Other conditions on the same rule must hold too.
- Pick the classifierIntent told by appears once a rule has an intent. Choose a small, fast model without reasoning. A group can’t be saved with an intent and no classifier.
- Save, and send a messageChanges apply from the next turn, with no restart. The Routing tab shows what the classifier said and where the turn went.
The same from a terminal:
magpie group rule add coder use=deepseek/deepseek-v4-flash intent="a quick question" classifier=groq/llama-3.1-8b-instant magpie group rule add coder use=deepseek/deepseek-v4-pro intent="writing or fixing tests" magpie group rule coder # the rules, numbered, and the classifier magpie group rule classifier coder groq/llama-3.1-8b-instant # change the classifier magpie group rule mv coder 3 1 # order matters: top first magpie group rule rm coder 2
intent= also takes the spellings asks= and about=. classifier= also takes classify= and by=. classifier= on rule add sets the group’s classifier and is needed only the first time. The classifier is saved in the group’s record in ~/.config/magpie/providers.json as "classifier", and each rule as {"use": …, "intent": …}.
How a turn is routed
Every request an agent sends to a group with rules goes through these steps, in this order:
- Is this a new turn?magpie looks at the request’s last user message. If it carries text or an image and no tool result, you just spoke, so a new turn begins. If it carries tool results, the agent is continuing a turn already under way (see The rest of the turn). Turns are counted per conversation. A subagent is its own conversation: it shares its agent’s session but starts with different words.
- Which intents could decide?magpie walks the rules top first and ignores intent for now: a rule is a candidate if its other conditions (tokens, images, reasoning, agents) hold for this request. Each candidate’s intent is collected, and duplicates spelled the same apart from case count once. The walk stops at the first candidate without an intent, because that rule matches outright and nothing below it could come first. If no intents were collected, the classifier isn’t asked and costs nothing.
- Ask the classifiermagpie sends the classifier the collected intents and the text of your message. The exact prompt is shown below. The call goes through magpie’s own gateway, like a request from an agent, so it uses that model’s provider with its keys or accounts and their failover. It has 8 seconds to answer.
- Read the answermagpie takes the first whole number in the reply.
1tonnames that intent, and0means none of them. Anything else, such as no number or one out of range, counts as a failure. The answer is kept for 10 minutes for the same message and intents. - Match the rulesThe rules are checked top first with the answer. A rule with an intent matches only when the classifier named its intent, compared without case, and all its other conditions hold. The first rule that matches wins. If none matches, the group routes the turn as it always does.
- Order the modelsThe winning rule’s model goes first, and the group’s other models follow in their usual order as failover. This holds even if another model answered the conversation’s last turn: a rule outranks the Stays setting as a turn begins. If the rule’s model has nothing ready right now (every key resting, the provider off), the group’s usual order stands and the trace says so.
- Remember the decisionmagpie keeps the decision for this conversation (the group, the agent’s session and the conversation’s first words) with the classifier’s answer, so the rest of the turn follows it.
An example
The Coder group above has three rules:
| # | When | Send to |
|---|---|---|
| 1 | ≥ 128000 tokens | kimi/kimi-k2.5 |
| 2 | asks for “a quick question” | deepseek/deepseek-v4-flash |
| 3 | asks for “writing or fixing tests” | deepseek/deepseek-v4-pro |
- A 20k-token turn: rule 1 doesn’t hold, so both intents are candidates. The classifier is asked to choose between “a quick question” and “writing or fixing tests”.
- A 150k-token turn: rule 1 has no intent and holds, so the walk stops there. The classifier isn’t asked, and the turn goes to Kimi.
- The classifier says
0(none): no rule matches, and the turn goes by the group’s routing (here In order, so deepseek-v4-pro first).
In the app
A real request through a group with two intent rules (“a quick question or explanation, with no code to write or change” and “writing or fixing tests”), sent from Claude Code: “Add unit tests for the LRU cache in cache.go, covering eviction and concurrent reads and writes”. The Routing tab shows it as it happens.
What the classifier sees
This is the whole request, and nothing else is sent. The system message is fixed. The user message lists your intents in rule order, as they were collected in step 2:
You route a user's message to a coding assistant by what it asks for. Given numbered kinds of request and the user's message, answer with the number of the kind the message is, or 0 if it is none of them. Answer with the number only.
Kinds: 1. a quick question 2. writing or fixing tests The user's message: <message> Write unit tests for the parseTokens function and fix any that fail. </message> The number of its kind (0 for none):
- Only your latest message. magpie sends the text parts of the last user message and nothing else: no earlier turns, no system prompt, no tool definitions, no files.
- Images aren’t sent. A message that is only an image has no words to classify, so no intent matches. To route by images, use the rule’s has an image condition.
- Agent reminders are removed.
<system-reminder>…</system-reminder>blocks, which Claude Code adds to your message, are stripped. Other text an agent puts in the same message is kept. - Long messages are shortened. Over 4,000 characters, magpie keeps the first 3,000 and the last 1,000, joined by
…. The start and the end usually say what is being asked. - The request settings. An OpenAI-style chat completion, not streamed, with
temperature 0,max_tokens 2048and User-Agentmagpie-router/1. When magpie knows the classifier’s reasoning levels, it asks for the least:noneif the model has it, else its lowest level (for examplelow). When the levels aren’t known, nothing is sent and the vendor’s default applies. magpie translates it for the classifier’s provider, like any other request, so an Anthropic, Gemini or other model works too.
When it can’t say
A classifier is a helper, not a gate. Whatever happens to it, the turn is still answered: the intent rules just don’t match.
| What happens | What magpie does |
|---|---|
It answers 0 | No intent matches. Rules without an intent and the group’s usual order decide. |
| No answer within 8 s | Treated as a failure: no intent matches, and the turn goes on at once. |
| An error (quota, 5xx, bad key) | Its provider’s own failover is tried first, within the 8 s. If it still fails, no intent matches. |
| An answer that isn’t a number from 0 to n | No intent matches this turn. The classifier did answer, so it isn’t rested: the next turn asks it again. |
| An error or the 8 s timeout | That classifier model is left alone for 30 s: turns in that time don’t wait on it, and the trace says it wasn’t asked. The first good answer clears this. |
| The message has no words | Not asked. No intent matches. |
Each case is written in the turn’s trace with the reason. An intent rule can therefore make a turn go to a different model than you hoped, but it can’t make it fail.
The rest of the turn
A turn is often dozens of requests: the agent calls a tool, sends the result back, calls another, and so on. magpie deals with them like this:
- Held. Every request within the turn goes to the model the turn began on, and the classifier isn’t asked again. Switching models mid-turn would throw away what the vendor cached of the conversation (prompt caching), and some vendors reject reasoning that another one wrote.
- Failover sticks. If the rule’s model failed and another member answered, that member keeps the rest of the turn (with Stays on Auto, Session or Within a turn). The rule decides again at your next message.
- Outgrown. The one exception: if the conversation grows within the turn to 95% of the current model’s context, magpie checks the rules again, keeping the classifier’s earlier answer. If a rule sends it to a model with more context, it moves. It never moves to a model with less.
- Waits. If magpie didn’t see the turn begin (it was started or restarted in the middle of one), or the rule that decided it was removed or now sends to another model, no rule moves the turn. The rules decide again at your next message.
- Subagents. A subagent’s turns are decided on their own, from the subagent’s own messages. A rule can send a subagent elsewhere while its agent stays where it is.
Decisions are kept in memory for 24 hours after a conversation’s last request. Restarting magpie clears them, and the next message decides afresh.
Reading the trace
The Routing tab shows each group’s latest request: who answered it, and under How the last request was routed, why.
The lines you can see about intent:
| The trace says | Meaning |
|---|---|
| … was asked which of “A”, “B” turn 1 is, and said “B” (in 323 ms). | The classifier was asked, with these candidates, and named B. |
| … and said none (…). | It answered 0. |
| … (said before, for the same message). | The answer came from the 10-minute cache, so no call was made. |
| … was to tell which … but couldn’t — reason — so no rule with an intent matches it. | It failed. The reason is the error, the timeout, what it answered, or that it is resting after a failure. |
| Turn N begins and rule R matches — asks for “B” — so M goes first … | The rule that won, and the model put first. |
| Rule R matches, but M has nothing ready now, so the group’s order stands. | The rule’s model has no key or account available right now. |
| Rule R (…) sent turn N to M as it began; the turn stays there. | A tool round within the turn, held on its decision. |
| This turn began before magpie saw it, so the rules wait for the next one. | See Waits above. |
The classifier’s own calls appear in Usage under the agent magpie, with their tokens and cost, so you can see what classifying costs.
Latency, cost, privacy
- Latency. Only the first request of a turn waits for the classifier, typically a few hundred milliseconds on a fast provider (323 ms in the screenshot) and never more than 8 seconds. Tool rounds within the turn, turns where no intent could decide, and cached answers add nothing.
- Cost. One short call per turn: roughly the size of your message plus the intents in, and one or two tokens out. In the example above that was 212 tokens in and 1 out. With a subscription model as the classifier, these calls count against that subscription’s limits.
- Privacy. The text of your message (up to about 4,000 characters) goes to the classifier’s provider. To keep it on your machine, use a local model from Ollama or LM Studio as the classifier. magpie keeps only a hash of the message and the answer, in memory, for 10 minutes. The message itself isn’t stored or logged.
Writing good intents
- Describe the request, not the model. Write “writing or fixing tests”, not “hard tasks for Opus”. The classifier sees only your words and the message.
- Keep kinds apart. If two intents overlap (“a question”, “a question about the code”), a small model will choose between them unpredictably. Merge them, or make the difference plain.
- Few and broad beats many and narrow. Two to five intents are easy for an 8B model. Up to 200 characters each is allowed, but a few words are usually enough.
- Order still matters. Only the first matching rule counts, so put a rule that must win (such as a long-context rule) above the intent rules. The classifier is then skipped whenever that rule holds.
- “None” is a real answer. A turn that fits no intent goes to the group’s usual order, so let that order be your default choice.
- Any language. Intents and messages can be in any language the classifier understands. The fixed prompt is in English, and small models read mixed languages well.
- Choosing a classifier. Pick a small, fast model without reasoning: Llama 3.1 8B on Groq, a Flash-Lite, nano or mini model, Claude Haiku, DeepSeek’s non-reasoning chat model, or a local model. A reasoning model is slower; magpie asks it for its least reasoning and leaves it 2048 tokens, but a slow one can still miss the 8 s. Check the trace for a few turns: if it guesses wrong, rewrite the intents before switching models.
Numbers
| What | Value |
|---|---|
| Classifier timeout | 8 s, including its provider’s failover |
| Rest after a failure | 30 s, per classifier model |
| Answer cache | 10 min, per classifier + intents + message; in memory |
| Turn decisions kept | 24 h after the conversation’s last request; in memory |
| Message sent | Up to 4,000 characters; longer is first 3,000 + last 1,000 |
| Classifier request | temperature 0, max_tokens 2048, the least reasoning the model has, not streamed |
| Intent length | Up to 200 characters; spaces collapsed, compared without case |
| Outgrown | At 95% of the current model’s context, only to a model with more |
FAQ
Is this embeddings or a vector search?
No. There is no embedding model, no similarity threshold, no keyword list and nothing to train. The classifier is an ordinary chat model that reads your intents and your message and answers with a number. It is only as good as that model and your wording, and the trace shows every answer.
Does it look at the whole conversation?
No, only the message you just sent. So a follow-up like “now do the same for the other file” is judged on those words alone. Give that turn a clearer message, or add an intent that fits follow-ups.
My agent made 40 requests for one message. Was the classifier asked 40 times?
No, once. The other 39 carried tool results, so they belong to the same turn and follow its decision. You can see this in the trace (“the turn stays there”) and in Usage (one magpie call).
Can a turn switch models halfway?
Only in two cases: the model it is on fails and another member takes over, or the conversation outgrows the model’s context and a rule sends it to one with more room. Intent is never re-asked mid-turn.
What if the classifier is down or slow?
The turn still goes through. After up to 8 s, no intent matches and the group’s other rules and order decide. For the next 30 s magpie doesn’t ask that classifier again, so turns don’t each wait out the timeout. The trace gives the reason.
Why did the turn not go to the rule’s model when the classifier named its intent?
Check the trace. The usual causes are:
- a rule above it matched first;
- another condition on the rule didn’t hold (tokens, images, reasoning or agent);
- the rule’s model had nothing ready (“has nothing ready now”);
- it was a tool round in a turn that began elsewhere;
- magpie didn’t see the turn begin (“the rules wait”).
Two rules have the same intent. What happens?
The intent is offered to the classifier once. If it is chosen, the first of those rules whose other conditions hold wins. Use this to send, for example, “writing or fixing tests” from Codex to one model and from Claude Code to another.
Does changing a rule affect a turn already running?
No. Saving applies from the next turn. A turn whose rule you removed, or pointed at another model, keeps where it is until then; the trace says the rules wait.
Can the classifier be a routing group, or one of the group’s own models?
It can’t be a group: it must be one model, provider/model, which magpie checks when you save. It can be any model magpie has, including one of the group’s members, a subscription model or a local model.
Can I use a reasoning model?
Yes, if it answers within 8 s. magpie asks it for the least reasoning it has (none, else its lowest level) and leaves 2048 tokens, so thinking doesn’t crowd out the answer. A small, fast model is still the better choice: cheaper, and every turn waits for it.
How well does it work with real models?
We ran 20 real coding messages (English and Chinese: quick questions, test work, and ordinary coding tasks, including “Fix the bug in parseTokens and add a test for it”) through a group with two intent rules. DeepSeek V4 Flash, GLM-5.3 Flash and MiMo v2.6 Flash each classified all 20 correctly; the median classifier time was 0.75 s, 1.3 s and 1.9 s. Gemini 3.8 Flash through one reseller took 11 s even at low reasoning, so every turn timed out and routed as if there were no intents — the trace said so each time. That is the pattern to expect: a wrong classifier costs you the routing, never the turn.
Does retrying the same message ask again?
Not within 10 minutes: the same message with the same intents and the same classifier is answered from the cache, and the trace says “said before”. Changing an intent or the classifier makes it ask again.
Which agents does it work with?
Any agent that sends to a routing group through magpie: Claude Code, Codex, OpenCode, Gemini CLI and the rest. Turns are detected from the request itself (a message with text and no tool results), whatever API the agent speaks.
Is my message stored anywhere?
magpie keeps a hash of it (for the cache) and the classifier’s answer, in memory only. The text goes to the classifier’s provider, like any request, under that provider’s terms. The classifier’s usage record holds its token counts, not the text.
How accurate is it?
That depends on the classifier and on how distinct your intents are. With a few clearly different kinds, a small model picks well; with overlapping ones it will sometimes guess. Since a wrong or missing answer only changes which model answers, start with two or three broad intents, watch the trace for a day and adjust the wording.
Questions or a setup worth sharing? Ask on Discord. Routing groups in general are covered in Get started.