AI agents

Signals a Router Can Read Before Any Model Call

Antonio Evans10 min read

A clean side profile of a network router on a desk, with four labeled input cables feeding it: 'token count', 'language', 'intent keyword', 'account tier'.
Key points

Key points

  1. 1The cheapest router is the one that never calls a model, so start with signals you can compute in microseconds from the request itself.
  2. 2Token estimate, detected language, keyword hits and account tier each map to a routing decision you can write as a plain function.
  3. 3A routing table with explicit thresholds and a logged reason code beats a model-driven classifier for the first layer of decisions.
  4. 4Every pre-call rule needs a kill switch and an escalation path so a wrong decision costs latency, not correctness.

Why decide anything before the model call

A model router is usually drawn as a box that picks a model. The more useful drawing puts two boxes side by side: the upstream half that decides using only what is already in the request, and the downstream half that may consult a classifier or sample outputs. The upstream half is where most production savings live, because the decision costs nothing to make. The vLLM Semantic Router paper frames routing as an uncertainty-reduction problem: before any analysis, every candidate model is equally plausible, and each signal you extract from the raw request reduces that uncertainty the vLLM Semantic Router paper. Their own architecture separates a signal extraction layer, which includes sub-millisecond heuristics, from a decision layer that composes those signals into Boolean rules the vLLM Semantic Router paper. That split is the blueprint: cheap signals in code, expensive judgment only when the cheap signals cannot separate the cases.

The economics are well documented. A production system mixing small and frontier models faces a choice on every request, and frontier models cost roughly 10 to 50 times more per token than a tuned small open model; routing 70% of traffic to the cheaper tier while holding quality on easy tasks translates to 60-70% cost reductions at scale the model routing chapter. You do not need a trained router to capture most of that. You need four signals and the discipline to set thresholds honestly.

The other half of the argument is architectural. Orchestration guidance in the agent space is blunt about this: routing can be deterministic, model-driven, rule-based or hybrid, and the best architecture uses the least flexible control mechanism that can reliably solve each part of the workflow the orchestration guide. A keyword rule is the least flexible mechanism there is. When it is reliable, it is also the fastest and the easiest to audit.

Signal one: estimated token count

Token count is the signal most monotonic with cost. Longer inputs cost more, longer outputs cost more, and long contexts are the ones most likely to be truncated. You cannot know the exact token count before tokenizing, but you can get close enough for a routing decision with a character heuristic plus a constant for the response.

def estimate_tokens(messages, chars_per_token=4.0, overhead_per_msg=4, reply_reserve=512):
    chars = sum(len(m.get("content", "")) for m in messages)
    msgs = len(messages) * overhead_per_msg
    return int(chars / chars_per_token) + msgs + reply_reserve

Four characters per token is a rough rule of thumb for English text and should be calibrated against your own traffic: sample a few thousand real requests, tokenize them with the target model's tokenizer, and fit the ratio. Reserve tokens for the reply too. The costliest routing mistake with this signal is ignoring the output side, because a request that looks small on input can generate a large structured payload.

Once you have a number, the decision is a table, not a model:

Estimated tokens (input + reply reserve)RouteReason
Under 300Small, fast modelAlmost certainly a lookup or one-line answer
300 to 4,000Mid-tier modelDefault lane
4,000 to 30,000Large model, explicit max_tokensContext window and cost justify it
Over 30,000Large model plus chunking or retrieval stepReject the naive single call

The upper boundary is not only about cost. Structured output failures cluster around truncation: when max_tokens runs out mid-generation, the model stops producing tokens with a finish reason of length, and most providers guarantee schema conformance only for complete generations the structured output failure taxonomy. A router that sees a large estimate and an output schema should raise the reply reserve rather than let the call fail downstream.

There is a second use for this signal that engineers underrate: protect the input side. If the estimate exceeds the chosen model's context window minus the reserve, route to a path that summarizes or chunks first. That is a code decision, and it should fire before you pay for a call that will be rejected or silently clipped.

Signal two: detected language

Language detection over the first few hundred characters is a small, deterministic computation with a large effect on quality. A model's strengths are uneven across languages, and support queues are uneven too. Two decisions follow:

  1. If the detected language is outside the set you have evaluated, route to the model you have actually tested for that language, or to a human queue with a drafted reply. Do not silently send it to your default model and hope.
  2. If the language differs from the account's locale, flag the conversation for review, since mixed-language tickets are where translation errors compound.

Run detection on the first 300 to 500 characters rather than the whole message. Early text carries the dominant language for most users, and truncating the input keeps the detector cheap and stable when a ticket contains quoted logs in another language. Treat low-confidence detections as unknown, not as a guess: an unknown language should take the conservative route, which is usually the larger model with a translation step, not the fast lane.

Keep the language decision separate from the quality decision. A common pattern is to pick the model for the language first and then apply the token-count table, so a long French request lands on a large model the same way a long English one does. Two independent lookups are easier to reason about than one combined rule, and much easier to log.

Signal three: keyword and pattern hits for intent

Intent keywords are the least glamorous signal and often the most decisive, because some intents are known to be low-stakes or structurally constrained. A refund status check, a password reset, an order tracking question: these do not need a frontier model, and you probably have a template for the answer. The orchestration patterns literature describes exactly this split, where an orchestrator decides whether the next step is a deterministic function, a tool call, a specialist agent or a human approval the orchestration guide.

Keyword rules also fit naturally into gateway configuration. LiteLLM's auto routing exposes keyword tier rules that run before any classifier, escalating to the highest matched tier, and it logs a cause marker per decision so you can see which rule fired the LiteLLM auto routing docs. That cause marker is the part to copy even if you build your own router: every decision should record why.

Write the rules in a single file with a priority order and an explicit escalation direction.

keyword_rules:
  - match: ["refund status", "where is my order", "track my package"]
    intent: order_status
    route: template_then_small
    priority: 10
  - match: ["cancel my account", "gdpr", "delete my data"]
    intent: account_deletion
    route: human_review
    priority: 100
  - match: ["regex:^\\s*SELECT\\s", "regex:\\bstack trace\\b"]
    intent: technical_payload
    route: large_code_model
    priority: 50

Two rules govern the design. First, escalation beats specialization: if two rules match, take the one with the higher priority, and make destructive or regulated intents the highest priority of all. Second, patterns beat single words. The word "cancel" appears in "how do I cancel a subscription" and in "don't cancel anything", and a regex with context separates them far better than a substring match. The same logic that makes a deterministic intent check cheap also makes it auditable, which is why it belongs in code rather than in a prompt.

The limit of this signal is the long tail. Keyword rules have poor recall on rephrased requests, and Paragon's evaluation work found that prompt wording and description depth moved tool-calling metrics only on multi-tool tasks, with the effect close to flat on single-tool tasks the Paragon tool-calling guide. Read that as a warning: do not spend weeks tuning keyword lists for a lane where the choice barely matters. Spend that effort on the lanes where the signal separates real cost or risk.

Signal four: account tier and entitlement

The account tier is already in your session context, which makes it the cheapest signal of all. It is also the one with the clearest business logic, because the decision is usually about service level rather than model capability. A free-tier account can be routed to a smaller model with a lower reply reserve; a paid account with a support entitlement can be routed to the larger model and, when the intent rules say so, to a human queue.

There is a good architectural reason to keep tier routing in the gateway rather than scattered through application code. Gateway patterns in the tool-calling literature emphasize centralized controls: unified interfaces across providers, automatic fallbacks, rate limits and budget governance configured in one place the Maxim overview of tool calling. Tier is the signal that most naturally becomes a gateway policy, because it changes on billing events, not on request content.

The engineering discipline here is the same as canary routing on an API: evaluate identity before the routing policy so the user context is populated, and fall back to the default lane when the entitlement lookup fails the canary routing guide. A missing tier should never produce an error page. It should produce the safe default and a logged reason.

Setting thresholds when you have no labeled set

Most teams do not have a labeled dataset of requests tagged with the model that should serve them, and waiting for one stalls the work. You can set defensible thresholds without labels if you accept that the thresholds are hypotheses to be validated in production.

The method is to calibrate against observable behavior rather than a ground-truth label:

  • Log every request's signal values alongside the model actually used, the finish reason, the output length, the latency and any downstream error. This log is your dataset by proxy, and it costs nothing to collect.
  • Set your first thresholds from the distribution of those signals, not from intuition. If 80% of requests fall under a token estimate of 1,200, a cutoff at 1,200 sends the bulk of traffic to the cheap lane, which is exactly the 80/20 split that delivers roughly a 4x cost reduction in the worked example from the routing chapter the model routing chapter.
  • Pick a boundary you can defend on cost alone even if quality is unchanged. Token count gives you this: at a known per-token price difference, moving a lane changes the bill by a computable amount. If the threshold is wrong, the symptom is a bill, not a quality regression.
  • For signals where quality is the risk (language, intent), set the conservative side of the boundary first and widen it only after you have evidence. An unknown language routes to the tested model until you have evaluated the cheap one.

Here is a worked example. Suppose 100,000 requests a day, 80,000 of them short lookups under 300 estimated tokens and 20,000 of them longer. The short lane runs on a model a tenth the price of the long lane, and both lanes average 400 output tokens at a made-up but consistent unit price of one unit per output token for the large model and one tenth of that for the small one. Sending everything to the large model costs about 40,000,000 units a day on output alone. Routing the 80,000 lookups to the small lane costs about 3,200,000 units for those requests plus 8,000,000 for the long lane, or 11,200,000 units total. The threshold at 300 tokens alone is responsible for roughly a 72% reduction in output cost. That arithmetic is the reason token estimate earns its place as the first signal: the threshold's effect is computable before any quality label exists.

The second discipline is reversibility. Every pre-call rule should be defined in configuration, not buried in a deploy, so you can tighten or loosen it during an incident. LiteLLM's auto router keeps tier decisions in YAML with an LLM classifier as an optional fallback, and its docs describe a semantic auto router being deprecated in favor of the newer configuration-driven approach the LiteLLM auto routing docs. Configuration-first routing means the threshold is a knob, not a release.

Keeping the decision cheap, auditable and reversible

Cheapness is measurable. Aim for a pre-call decision budget in the low microseconds: string length, a regex, a lookup table, a language detector over a few hundred characters. Compare that with a classifier call, which the same LiteLLM comparison prices at roughly 100 to 500 milliseconds for an embedding match, versus sub-millisecond for heuristic or keyword rules the LiteLLM auto routing docs. The heuristic layer is two to five orders of magnitude cheaper than the classifier layer, which is why the heuristic layer should capture as many requests as it can.

Auditability is a logging contract. For every request, emit the signal vector, the matched rule, the route chosen, and a reason code. Structured outputs matter here for the same reason they matter anywhere a program consumes model text: valid JSON is not a contract, and validation belongs after generation the TrueFoundry guide to structured outputs. Apply that to your own router's decision record: require the reason code field, validate it against an enum, and log the raw signal values next to it.

PropertyWhat good looks likeHow to check it
CostSub-millisecond decision, no network hopMeasure decision latency separately from model latency
DeterminismSame input, same routeReplay logged requests and diff the decisions
TransparencyReason code plus signal values on every callQuery the log for one route and inspect the distribution
ReversibilityThreshold changeable without a deployChange a value in config and confirm the next request obeys
Blast radiusOne lane can be disabled by a flagToggle the flag in staging and watch the route change

Reversibility also means having an escalation path. When a routing decision has no confidence, or when a rule is disabled during an incident, the request should still be served correctly. The orchestration guidance on determinism and fallbacks is written for agents but applies verbatim to routers: prefer known-good routes when uncertainty is high, and bound responses with latency targets that trigger fallbacks if exceeded the Maxim overview of tool calling.

A final piece of discipline: keep the downstream half of routing optional. The pre-call layer should produce a default route for every request, so that a failure in the classifier layer degrades to a reasonable lane instead of an error. That is how cascades are supposed to work, escalating only when a cheap route provably fails, and it keeps the expensive decision from becoming a single point of failure the model routing chapter.

When you want to apply these signals across several providers without rewriting the plumbing, a single API in front of the fleet removes a whole class of variation. Quanta Inference gives every model type one OpenAI-compatible endpoint and prepaid billing in US dollars, which makes the token-estimate threshold the same arithmetic regardless of which vendor answers: https://quantaintelligence.ai/inference.

FAQ

FAQ

Do I need a labeled dataset to start routing?

No. Start with the token-count table, which you can justify on cost arithmetic alone, and log the signal values and outcomes of every request. That log becomes your dataset over a few weeks, and you can then check whether routing decisions correlate with finish reasons, errors and retries. Routing 70% of traffic to a cheaper tier can produce 60-70% cost reductions without any learned classifier the model routing chapter.

How accurate does a pre-call token estimate need to be?

Accurate enough to separate your lanes, not exact. A character-based estimate with a per-message overhead and a reply reserve will misclassify requests near the thresholds, and that is acceptable if the boundaries are set with margin. Calibrate the characters-per-token ratio on a sample of your own traffic, and always add the reply reserve, because truncation is the failure mode that hurts most: a finish reason of length means an incomplete payload the structured output failure taxonomy.

Should the intent keyword rules live in code or in the gateway?

Either works if the rules are configuration, versioned, and carry a priority order and a reason code. The gateway option centralizes tier and entitlement policies with rate limits and fallbacks, which the tool-calling infrastructure guidance recommends for cost and governance the Maxim overview of tool calling. The important property is that a rule change does not require a code deploy, so you can react during an incident.

What happens when two signals disagree?

Resolve disagreement with an explicit precedence order, and make the most conservative route win when the disagreement involves risk. Destructive or regulated intents outrank cost signals; a paid account with a short message still goes to the fast lane if a deletion intent matched. Write the precedence in the config file next to the rules, log which rule won, and test the order with a small set of replay cases before you widen any threshold.

How do I know the router is not quietly degrading quality?

Watch the outcomes of each route separately: finish reasons, retries, downstream validation failures and human escalations, all split by route and reason code. The structured output literature is explicit that schema-valid output is not correct output, so track business validators alongside parse success the TrueFoundry guide to structured outputs. If a lane's escalation rate drifts upward, tighten its threshold or retire the rule.

Sources

Sources

  1. vLLM Semantic Router: Signal Driven Decision Routing for ...
  2. Model Routing: Selection, A/B Testing, Cascades & ...
  3. AI Agent Orchestration: Patterns, Workflows & Best Practices
  4. Structured Outputs Are Not a Solved Problem: JSON Mode ...
  5. Beta Auto Routing
  6. How to Optimize Tool Calling for AI Agents - Paragon
  7. Understanding Tool Calling Mechanisms in AI Agents
  8. What Is Canary Routing and How Do You Implement It?
  9. LLM Structured Outputs with JSON Schema
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles