lumo

Module 10 · Lesson 4 of 11Free

Model routing: small models first, large when needed

Level
Intermediate
Topic
Cost, latency and scaling
Reading time
18 min

When you finish this page you can

  1. Compare rules, a routing classifier and a cascade by what each adds to the cost of a task and how each one fails.
  2. Measure a routing choice on an eval set, with quality and daily cost for every option, the check’s own cost included.
  3. Set an escalation threshold and the signals that show when a router starts to drift.
  4. Spot four routing mistakes, from a router that escalates too much to prompt caches split across models.
  1. Choosing a model: capability, cost and speed8 min
  2. Design: a support assistant over company docs30 min
  3. Building an eval set from real examples18 min
  4. LLM-as-judge: grading outputs with a model18 min

Video lesson · coming soon

Malik’s draft answered one order of two

Deskora is a help desk app. A shop’s support agents use it to answer customer emails from one shared inbox. Each customer problem becomes a ticket: one customer’s whole conversation with the support team. Deskora’s drafting assistant writes a reply for every ticket, and an agent edits and sends it.

Priya is a support agent at a kitchenware shop that uses Deskora. This morning she opens a ticket from Malik: 64 messages, two orders, and a customer who is angry now. The draft apologises for order 48213 and never mentions order 48377, the one that arrived broken. Priya rewrites it from scratch.

Tomás, the Deskora engineer who builds the AI features, runs the drafts on Claude Haiku 5.5, the cheapest Claude model. On Deskora’s eval set of 300 labelled tickets, Haiku’s drafts pass 258. Claude Sonnet 5.5, a larger model, passes 282, at about 20 times the price per draft. Most failures sit in three kinds of ticket: long threads, angry customers and tickets about several orders.

Tomás considers sending only those to Sonnet. This lesson answers one question: which drafts should go to the larger model, how does code decide, and what does that decision cost?

Pass counts, the share of each type of ticket, token counts and timings are estimates for this lesson’s scenario. The 300-ticket eval set, 60,000 drafts a day and the draft call’s token counts come from earlier lessons.

Check yourselfAnswer in your head first, then reveal
Long tickets are a quarter of Deskora’s drafts. If every long ticket goes to Sonnet and everything else stays on Haiku, roughly what share of the drafting model bill moves to Sonnet?Show answerHide answer
Almost all of it. Long drafts cost Sonnet about $893 a day, against about $58 for the typical tickets left on Haiku: about 94% of a bill now about nine times larger. Not “a quarter of traffic, so a quarter of the cost”: the long tickets are the most expensive ones on either model.

Diagram not drawn yet: model-routing-eval-slices.svg

Four pairs of horizontal bars, one pair per type of ticket in Deskora’s 300-ticket eval set, showing pass counts for Claude Haiku 5.5 and Claude Sonnet 5.5. Typical tickets, 200 cases: Haiku 192, Sonnet 194. Long threads, 50 cases: Haiku 34, Sonnet 45. Angry customers, 30 cases: Haiku 21, Sonnet 26. Several orders, 20 cases: Haiku 11, Sonnet 17. Totals under the bars: Haiku 258 of 300, Sonnet 282 of 300. The three hard kinds are outlined in red.

Haiku nearly matches Sonnet on typical tickets. The gap is in three kinds of ticket, which is what makes routing worth testing.

Most drafts don’t need the larger model

The eval set is stratified: it holds far more hard tickets than real traffic does, so every kind gets enough cases to measure (Building an eval set from real examples). Each ticket is assigned to one kind only, checked in the order long, angry, several orders.

Type of ticket Cases Haiku passes Sonnet passes
Typical 200 192 194
Long thread 50 34 45
Angry customer 30 21 26
Several orders 20 11 17
Total 300 258 (86%) 282 (94%)

Sonnet fixes 30 of Haiku’s 42 failures, but it also fails 6 tickets that Haiku passed. And 12 failures defeat both models: in most, search returned the wrong article, which no model can fix.

Now the price. The unit of cost is one draft call, the only call that changes model. The query rewrite and the reranker from Design: a support assistant over company docs stay as they are. Prices are as of October 2026 3.

The formula, per call

cost = input tokens × input price + (thinking + reply tokens) × output price

Thinking is billed as output; usage.output_tokens_details.thinking_tokens reports the real count.

Typical draft on Haiku 5.5

7,800 × $0.10 ÷ 1M + 500 × $0.50 ÷ 1M = $0.00078 + $0.00025 = $0.00103

Sources plus a 4,000-token thread. Assumed: 350 thinking tokens and a 150-token reply.

Long draft on Haiku 5.5

18,800 × $0.10 ÷ 1M + 1,000 × $0.50 ÷ 1M = $0.00238

A 15,000-token thread. Assumed: 800 thinking tokens and a 200-token reply.

Typical and long on Sonnet 5.5

7,800 × $2 ÷ 1M + 500 × $10 ÷ 1M = $0.0206; 18,800 × $2 ÷ 1M + 1,000 × $10 ÷ 1M = $0.0476

Same tokens, assumed, with effort set to medium on both models. Sonnet costs 20 times Haiku on both sizes.

One day

75,000 draft calls = 56,250 typical + 18,750 long. All Haiku ≈ $103; all Sonnet ≈ $1,159 + $893 = $2,051

75,000 drafts are made to show 60,000, because some are replaced before anyone reads them.

All-Sonnet buys 24 more passes in 300 for about $1,950 a day. Most of that money goes to typical-length tickets, and on typical tickets Sonnet passes only 2 more of 200. A router should send the hard tickets to Sonnet and keep the rest on Haiku. The hard part is knowing which ones are hard.

Three ways to choose the model

Each way answers “Haiku or Sonnet?” at a different moment, with different information. So each has its own cost, and way of failing.

Diagram not drawn yet: model-routing-three-patterns.svg

Three panels, each a small flow from a ticket to a finished draft. Panel 1, rules: the ticket goes to a code box with three tests, more than 40 messages, two or more order numbers, angry words; yes goes to Claude Sonnet 5.5, no to Claude Haiku 5.5. Label: free, decides before any draft, sees only surface features. Panel 2, routing classifier: the ticket goes to a small Claude Haiku 5.5 call that predicts whether Haiku’s draft will fail; above the threshold to Sonnet, below to Haiku. Label: about $13 a day, a guess before the draft exists. Panel 3, cascade: the ticket goes to Haiku, which writes a draft; a check box tests the draft; pass goes to Priya, fail goes to Sonnet for a second draft, drawn with a dashed arrow labelled escalate. Label: pays for the check on every draft and two drafts on escalated tickets, but judges the real draft.

Rules and a classifier pick a model before any draft exists. A cascade lets the small model try first and checks what it wrote.

Rules. Code reads facts it already has: the thread is longer than 40 messages, the thread holds two or more order numbers, the language is one where the eval shows Haiku is weaker, or the text matches a list of angry words. A match goes to Sonnet. Rules cost nothing, and anyone can read why a ticket went where. They fail on what they can’t see. Many angry customers use no angry words, so the word list catches only 12 of the 30 angry tickets. And “long” sends every long ticket, though Haiku already handled 34 of the 50.

A routing classifier. A small model call reads the ticket and predicts one thing: will Haiku’s draft fail? A score above a threshold sends the ticket to Sonnet. RouteLLM trains such routers on human preference data, choosing between a strong and a weak model per request, and reports costs cut by over 2 times in some cases without lower quality 2. For Deskora, a Haiku call reading the latest messages costs about $0.000175, about $13 a day. Its weakness is timing: it guesses before the draft exists. It also needs labelled examples where both models ran.

A cascade. Haiku writes the draft. A check tests it. If the check fails, Sonnet writes a second draft and the first is thrown away. FrugalGPT studied cascades like this over a list of models and reports matching the best single model with up to 98% lower cost on its benchmarks 1. A cascade judges the real output, not a prediction. It pays for that twice: a check on every draft, and two drafts plus extra waiting on every escalated ticket.

The cascade’s cost depends on the check, so the check needs its own price before any comparison.

The check is a second bill

A check that runs on 75,000 drafts a day must be cheap. Tomás has three kinds to choose from.

  • Code checks, free. The draft stopped early (stop_reason is max_tokens), an order number from the thread is missing from the draft, or the marks from the drafting assistant’s existing checks fire: an invalid citation, a possible promise of a refund.
  • A small model check: one Claude Haiku 5.5 call at low effort that grades the draft on four criteria and quotes its evidence.
  • The Sonnet judge from LLM-as-judge, the one that grades the eval set.

Haiku check, typical draft

(7,800 + 450) × $0.10 ÷ 1M + 200 × $0.50 ÷ 1M = $0.000825 + $0.0001 ≈ $0.00093

It reads the same sources and thread, plus the draft and a short rubric. Assumed: 200 output tokens, thinking included.

Haiku check, long draft

19,300 × $0.10 ÷ 1M + 300 × $0.50 ÷ 1M ≈ $0.00208

Haiku check, one day

56,250 × $0.00093 + 18,750 × $0.00208 ≈ $52 + $39 = $91

Almost as much as the $103 the drafts themselves cost.

Sonnet judge as the check, one day

56,250 × $0.0259 + 18,750 × $0.0519 ≈ $2,430

The judge’s per-draft costs are from the LLM-as-judge lesson. That is more than writing every draft on Sonnet.

So code checks run first, and a draft that fails one escalates without a model check. Everything else gets the Haiku check. The Sonnet judge stays on 300 eval tickets, not 75,000 drafts.

The real drafting call sends sources as document blocks with citations; plain text keeps this sketch short.

python
import json
from pathlib import Path

import anthropic

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from the environment
SMALL, LARGE = "claude-haiku-5-5", "claude-sonnet-5-5"
CRITERIA = ["answers_latest_message", "covers_every_order", "tone_fits_customer", "stays_within_sources"]
GRADE = {"type": "object", "additionalProperties": False, "required": ["verdict", "evidence"],
         "properties": {"verdict": {"type": "string", "enum": ["pass", "fail"]}, "evidence": {"type": "string"}}}
CHECK = {"type": "json_schema", "schema": {"type": "object", "additionalProperties": False,
         "required": CRITERIA, "properties": {c: GRADE for c in CRITERIA}}}


def call(model: str, prompt_file: str, user: str, output_config: dict) -> anthropic.types.Message:
    return client.messages.create(model=model, max_tokens=4000, system=Path(prompt_file).read_text(),
                                  output_config=output_config, timeout=30,
                                  messages=[{"role": "user", "content": user}])


def text_of(message: anthropic.types.Message) -> str:
    return "".join(block.text for block in message.content if block.type == "text")


def failed_checks(draft: anthropic.types.Message, case: str, orders: list[str]) -> list[str]:
    if draft.stop_reason == "max_tokens":
        return ["cut_off"]  # code checks first: free, and they skip the model check
    if any(number not in text_of(draft) for number in orders):
        return ["missing_order"]
    grades = call(SMALL, "prompts/check-v3.txt", f"{case}\n<draft>\n{text_of(draft)}\n</draft>",
                  {"effort": "low", "format": CHECK})
    if grades.stop_reason != "end_turn":
        return ["no_verdict"]  # a check that can't answer counts as a fail
    verdicts = json.loads(text_of(grades))
    return [c for c in CRITERIA if verdicts[c]["verdict"] == "fail"]


def draft_reply(ticket: str, sources: str, orders: list[str]) -> dict:
    case = f"<sources>\n{sources}\n</sources>\n<ticket>\n{ticket}\n</ticket>"
    ask = case + "\nDraft a reply to the customer's latest messages."
    first = call(SMALL, "prompts/draft-v9-haiku.txt", ask, {"effort": "medium"})
    failed = failed_checks(first, case, orders)
    if not failed:
        return {"model": SMALL, "text": text_of(first), "escalated_for": []}
    second = call(LARGE, "prompts/draft-v9-sonnet.txt", ask, {"effort": "medium"})  # own prompt, effort set
    return {"model": LARGE, "text": text_of(second), "escalated_for": failed}

escalated_for goes into the draft log, so Tomás can count why drafts escalate. The Sonnet draft gets no second model check; Priya still reads every draft before sending.

Five options on the same 300 tickets

Tomás runs every option on the eval set, with the same judge and code checks. Daily costs use production volume, where hard tickets are rarer.

Option Sent to Sonnet: eval set Sent to Sonnet: production Eval passes Draft calls and checks, per day
All Haiku 0 0% 258 (86%) about $103
All Sonnet 300 100% 282 (94%) about $2,051
Rules 89 about 34% 277 (92%) about $1,078
Routing classifier 50 about 15% 276 (92%) about $480
Cascade, escalate on any failed check 50 about 15% 280 (93%) about $577

Rules

18,750 long × $0.0476 + 6,500 typical-length × $0.0206 + 49,750 × $0.00103 ≈ $893 + $134 + $51 = $1,078

Every long ticket, plus about 6,500 typical-length tickets a day that match a rule (assumed). The long ones are most of the bill.

Routing classifier

$13 classifier + 5,625 × $0.0476 + 5,625 × $0.0206 + 13,125 × $0.00238 + 50,625 × $0.00103 ≈ $13 + $384 + $83 = $480

Assumed: 15% of drafts sent to Sonnet, half of them long.

Cascade

$103 Haiku drafts + $91 checks + 11,250 escalated × $0.0341 average ≈ $194 + $384 = $577

Assumed: 15% escalate, half of them long, so an escalated Sonnet draft averages ($0.0206 + $0.0476) ÷ 2 = $0.0341.

The cascade passes the most of the routed options: its check caught 34 of Haiku’s 42 failures, and Sonnet fixed 23 of those. The check also escalated 16 good drafts, and Sonnet failed 1 of them. Rules spend almost twice as much for fewer passes, because they send drafts Haiku had already done well.

Diagram not drawn yet: model-routing-options-cost-quality.svg

A scatter chart. Horizontal axis: draft calls and checks per day, from $0 to $2,100. Vertical axis: eval passes out of 300, from 255 to 285. Five labelled points: All Haiku at $103 and 258; Routing classifier at $480 and 276; Cascade at $577 and 280, highlighted as the choice; Rules at $1,078 and 277; All Sonnet at $2,051 and 282. A dashed line joins All Haiku, Classifier, Cascade and All Sonnet as the best options at each cost. Rules sits below that line, labelled: pays for long tickets Haiku already handled.

The cascade gets within 2 passes of all-Sonnet for about 28% of its cost. Rules cost more and pass fewer.

Decision: a cascade, escalating on any failed check

How should Deskora choose the model for each draft?

Keep every draft on Claude Haiku 5.5.

About $103 a day. Malik’s type of ticket keeps failing: about 1 in 3 of the long, angry and several-order tickets in the eval set, rewritten by hand.

Move every draft to Claude Sonnet 5.5.

About $2,051 a day for 2 more passes than the cascade on 300 tickets. Nearly all the extra money goes to typical tickets that Haiku already drafts well.

Rules on length, order count and angry words.

Free to run and easy to explain, but about $1,078 a day: every long ticket goes to Sonnet, and angry customers without angry words stay on Haiku.

A routing classifier before the draft.

About $480 a day and 276 passes. It guesses before any draft exists, needs a labelled set where both models ran, and drifts as tickets change.

Why this choice

The cascade decides with the most information: the draft itself. Malik’s missing order is visible in a draft and invisible in a guess. Drafts are written in the background, so the extra wait rarely reaches Priya. When she opens a ticket before its draft is ready, an escalated draft takes about 14 seconds instead of 7 (an estimate).

The threshold. Tomás tested two rules on the eval set. Escalating when two or more checks fail sends 28 of 300 and passes 274, at about $399 a day. Escalating on any failure sends 50 and passes 280, at about $577. That is about $180 a day for 6 more passes in 300. If production behaves like the eval set, about 1,200 better drafts a day, about 15 cents each: less than the minutes an agent spends rewriting one. Hard tickets are rarer in production, so the real gain is smaller.

That extra money competes with the cut planned in Token economics: the cascade spends about $474 a day more than all-Haiku, about two thirds of what that plan saves with Resolve on Haiku. So Tomás takes it to the finance lead as a quality decision with its price, not as part of the savings.

When the choice changes. If escalation rises above about 35%, the cascade costs more than rules; above about 73%, more than all-Sonnet. If a feature can’t wait for two drafts, such as live chat, a classifier or rules decide before the first token. If a prompt fix closes the gap on a hard type of ticket, route less. If prices or models change, run all five options again: the 20× price gap is what makes routing pay.

Including the rewrite and the reranker, drafting goes from about $267 to about $741 a day. Running a small open model yourself is another way to cut this bill; Self-hosting open models asks when they pay.

Watching the router after launch

A router drifts quietly as tickets, prompts and models change. Tomás launches the cascade in for a week: Sonnet drafts are made for escalated tickets but not shown, so he can measure the real escalation rate before paying for it in front of Priya.

Signal Where it comes from Expected Alert
Escalation rate, hourly model and escalated_for in the draft log about 15% above 25% (draft calls about $833 a day) or below 8%
Escalation reasons escalated_for a stable mix one criterion doubles in a day
Check’s false pass rate the weekly 50 drafts Priya and two colleagues grade, 25 of them drafts the check passed 8 of 42 failures missed on the eval set, about 19% 5 points above the launch figure
Sent with small or no edits Priya’s actions, split by path Sonnet drafts close to Haiku drafts that passed Haiku-passed drafts fall behind
Cost per draft shown usage in the draft log about $0.0124 the drafting budget

Any change to a prompt, a model or the check reruns the eval set on the whole cascade.

Diagram not drawn yet: model-routing-cascade-in-production.svg

Left to right. A draft job takes a quiet ticket. 1: Claude Haiku 5.5 writes a draft with prompt draft-v9-haiku. 2: code checks, free; a failed check jumps straight to step 4. 3: the Haiku check grades four criteria, about $0.00093 a draft. 4: on any failure, Claude Sonnet 5.5 redrafts with its own prompt, draft-v9-sonnet, on about 15% of drafts. 5: the draft and its escalation reasons are saved and shown to Priya. Below the flow, a monitoring strip with four gauges: escalation rate 15%, alert above 25% or below 8%; false pass rate from the weekly human sample; sent-as-is rate by path; cost per draft about $0.0124.

The cascade in production. The escalation rate is the number to watch: it moves the bill and shows when the check changes.

Try it yourself

Send one hard message to both models and compare what each bills. You need a Claude API key in ANTHROPIC_API_KEY; without one, skip this.

bash
for model in claude-haiku-5-5 claude-sonnet-5-5; do
  curl -s https://api.anthropic.com/v1/messages \
    -H "content-type: application/json" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -d '{"model": "'"$model"'", "max_tokens": 1500, "system": "Draft a short reply for a support agent. Never promise a refund.", "messages": [{"role": "user", "content": "Customer: This is the third time I write. Order 48213 is late and order 48377 came broken. Fix both today."}]}'
  echo
done

An example of the end of each response, from one run; yours will differ:

json
{"usage": {"input_tokens": 61, "output_tokens": 412, "output_tokens_details": {"thinking_tokens": 270}}}
{"usage": {"input_tokens": 61, "output_tokens": 735, "output_tokens_details": {"thinking_tokens": 560}}}

Check whether each reply covers both orders, and compare thinking_tokens: with no effort set, Sonnet runs at its default, high, and Haiku at medium. Both calls together cost under 2 cents, as of October 2026.

Where this shows up

Escalated drafts still run as soon as a ticket goes quiet. Some of Deskora’s model work could wait hours, and the next lesson asks what that is worth.

Common mistakes

A router that sends too much to the large model

Routing by what looks hard sends whole groups that the small model already handles, as the length rule did. A stricter check prompt that escalates 40% of drafts raises the cascade’s cost from about $577 to about $1,217 a day. Alert on the escalation rate, not only on the total bill.

A check that passes bad drafts

Haiku checking Haiku can share its blind spots, and a missed failure never escalates. A check that never fails still costs $91 a day. Measure its false pass rate against people, as Judge calibration does for the judge, and again after every change to the check.

Sending a prompt tuned for one model to another

The drafting prompt was tuned on Haiku for months. On Sonnet the same request runs at a different default effort, high instead of medium, and Sonnet 5.5’s levels are recalibrated, so a level doesn’t give the same thinking it did on earlier Sonnet models 4. A forced tool_choice that Haiku accepts returns a 400 on Sonnet, as Function calling shows. Give each model its own prompt version and effort setting, and run both on the eval set.

Splitting prompt caches across models

A cache hit needs an identical prompt prefix 5, and the cache holds work one model did on that prefix, so Sonnet can’t read what Haiku cached. An escalated draft pays full input price, plus the cache write if it is marked. When Priya presses “Draft again” on an escalated ticket, send it to Sonnet again, or both the cache and the escalation are lost.

Going deeper

Cheaper checks, longer cascades and one-model routing
  • A check that reads the draft call’s cache. If the check sends the draft call’s exact system prompt, sources and thread first, then the draft and rubric in a later turn, it can read that prefix from Haiku’s cache at 0.1 times the input price 3. Every draft call then pays the 1.25 times cache write, so measure both before switching.
  • One model, two settings. Higher effort on hard tickets is routing too, with one prompt and one cache. Test it as a sixth option.
  • Token economics combines routing with caching, batch and effort.

Check your understanding

Check yourselfAnswer in your head first, then reveal
After a check prompt edit, the escalation rate jumps from 15% to 32% overnight, and eval passes stay at 280. What happened, and what does it cost?Show answerHide answer
The check got stricter, not the drafts worse: it now fails good drafts, which Sonnet rewrites at 20 times the price. At 32% the draft calls cost about $1,012 a day instead of $577. Not “tickets got harder”: a change in tickets would also move the eval score or the reasons mix.
Check yourselfAnswer in your head first, then reveal
Tomás wants to make the check cheaper by asking it only “pass or fail?” with no criteria. What does he lose?Show answerHide answer
The escalation reasons, so he can no longer see which criterion drives escalation or catch one that drifts. The output saving is a few hundred tokens of a call that is mostly input. Not a big saving: the check’s cost is the sources and thread it reads.
Check yourselfAnswer in your head first, then reveal
Deskora Chat answers customers live, and they wait for each reply. Should it reuse the drafting cascade?Show answerHide answer
Probably not. An escalated reply means two answers and a check while a customer watches, about double the wait. Rules or a classifier decide before the first token, at the cost of guessing; measure them on Chat’s own eval set.
Check yourselfAnswer in your head first, then reveal
A teammate proposes the Sonnet judge as the cascade’s check, since it is already calibrated. Why does Tomás say no?Show answerHide answer
On 75,000 drafts a day it would cost about $2,430, more than writing every draft on Sonnet at $2,051. A check must cost much less than the large model it saves you from calling. The judge stays on the 300-ticket eval set.

Summary

  1. Deskora’s drafts go to Claude Sonnet 5.5 only when Claude Haiku 5.5’s draft fails a check: code checks first, then a Haiku check, escalating on any failure. That is about 15% of drafts, 280 of 300 eval passes and about $577 a day, against 258 and $103 for all-Haiku and 282 and $2,051 for all-Sonnet (estimates, October 2026 prices).
  2. Rules are free but see only surface features; a classifier guesses before the draft; a cascade checks the real draft and pays for a check on every draft plus two drafts when it escalates.
  3. Count the check as part of the task: a Haiku check costs about $91 a day, and a Sonnet judge as the check would cost more than all-Sonnet drafting.
  4. Measure every option on the same eval set, then price it at production volume, where long tickets are the most expensive ones on either model.
  5. Watch the escalation rate and the check’s false pass rate, and give each model its own prompt, effort setting and cache.

Report a mistake

Tell us what’s wrong in this lesson and we’ll fix it.