AI coding

Choosing a Model Per Task from Pass Rates and Token Counts

Antonio Evans7 min read

A wide desk scene: a laptop showing a grid of task types down the left side and model names across the top, each cell holding a pass rate and a token count.
Key points

Key points

  1. 1Tag every request with a task type and log three numbers: whether the output passed your check, input tokens, and output tokens.
  2. 2Compute a 95 percent confidence interval on each pass rate before you trust it, because a 78 percent on 40 samples is not evidence of beating 74 percent on 900.
  3. 3Divide total tokens by successful tasks, not total tasks, so failures and retries get charged to the model that produced them.
  4. 4Pick a routing rule that compares cost per successful task against a quality floor you set in advance.

Why one model for everything costs you twice

The most capable model is also the most expensive per token, often more than ten times the rate of a small model for the same work (ByteByteGo). A single model for everything fails in both directions at once. High-volume trivial traffic pays frontier prices for field extraction a cheaper model does identically. The hardest traffic, where a weak model fails silently and needs three retries, ends up costing more than one strong attempt.

The gap is not academic. OckBench research cited by Splunk found two models reaching the same accuracy differed by 25 times in tokens generated, roughly 1,600 against 42,000 for the same correct answer (Splunk). Accuracy alone cannot see that. Cost per outcome can. Token counts are easy to measure; completed correct work is what you are buying.

Logging the numbers that make routing possible

Capture, at request time, the metadata that lets you reconstruct a model-by-task picture later.

FieldWhy it goes in the log
task_typeThe unit you route on, from a fixed list of about a dozen values
modelThe model that actually produced the output
passedTrue or false, or null when the check has not run yet
input_tokensProvider-reported, not estimated
output_tokensProvider-reported, including reasoning tokens where billed
cost_usdComputed at log time from the rate table in force
retry_ofLinks a retry to the attempt that failed

Tag at the call site rather than parsing logs afterward, because post-hoc parsing reconstructs context the application has already discarded, and a routed request shows up in raw logs as an opaque model call with only a provider, model and token count (Braintrust).

Take token numbers from the provider's usage object. On OpenAI endpoints, Chat Completions reports prompt_tokens, completion_tokens and total_tokens, while the Responses API reports input_tokens, output_tokens and total_tokens, and reasoning tokens are billed as output even though they never appear in the answer (OpenAI Help Center). A reasoning model can look cheap on visible output and expensive on the invoice.

import uuid

def call_model(client, model, task_type, prompt, max_out=800):
    resp = client.responses.create(model=model, input=prompt, max_output_tokens=max_out)
    return {
        "request_id": str(uuid.uuid4()),
        "task_type": task_type,
        "model": model,
        "input_tokens": resp.usage.input_tokens,
        "output_tokens": resp.usage.output_tokens,
        "passed": None,   # set by the checker, inline or in a batch job
    }

Write null for passed unless your checker runs inline. For schema-validated output the check can run in the same request. For anything a human reviews, backfill in a batch. The row is cheap; the gap is expensive. Also record the prompt template version next to the model, since a template change moves input tokens across every request in a feature.

Turning an outcome into a pass or fail you can automate

A pass rate is only as good as the check behind it. You want a check that returns true or false without an argument. Three types cover most production work.

  • Executable checks. The output runs: a test suite passes, a query returns rows, an artifact builds. Prefer this wherever the task produces something executable.
  • Schema and constraint checks. The output parses against a JSON Schema, required fields are present, enum values are allowed, cited identifiers exist in your database. Cheap to run, catches many silent failures.
  • Sampled human labels. A person marks a random sample of about 100 outputs per task type per week. The fallback for prose and tone-sensitive replies.

Bound the third type deliberately. Label a random sample, not a convenient one, and label the same sample shape for every model you compare. If you label model A in the morning and model B after a long day, your pass rates encode your fatigue.

For multi-step tasks, judge each step. Decomposition exists so a failure on step three is attributable to step three and can be rerun without replaying everything, which gives you measurable evaluations at each step rather than only at the end (Brightlume). A plan step and an execute step often want different models, so log them as separate task types.

Computing a pass rate with an interval you can defend

The textbook Wald interval collapses at 0 and 100 percent and misbehaves on small samples. The Wilson score interval behaves correctly across the range and is a few lines of code.

import math

def wilson(passes, n, z=1.96):
    if n == 0:
        return (0.0, 1.0)
    p = passes / n
    denom = 1 + z*z/n
    centre = (p + z*z/(2*n)) / denom
    half = (z * math.sqrt(p*(1-p)/n + z*z/(4*n*n))) / denom
    return (max(0.0, centre-half), min(1.0, centre+half))

Now apply it to four model and task pairs on the same invoice_extract task.

ModelPassesTriesPass rate95 percent interval
A (small)74490082.7%80.1% to 85.0%
B (mid)314077.5%62.5% to 87.7%
C (frontier)89090098.9%98.0% to 99.4%
D (new)1212100%75.7% to 100%

B never beat A: its interval sits almost entirely below A's point estimate, and 40 samples cannot argue otherwise. The 5 point gap is noise. D's perfect record means nothing yet, because 12 clean runs give a lower bound under A's point estimate. C is genuinely better and tight enough to prove it.

Set a minimum sample size per pair before you allow a comparison. A working figure is 200 tries per pair, with 30 or more recorded failures. Below that, print "insufficient" rather than a number, because a dashboard that shows a rate for twelve samples teaches people to trust twelve samples. Treat the interval as a floor on your uncertainty, too: retries of the same input and documents from the same customer cluster, so true uncertainty is wider than the arithmetic suggests.

From tokens to cost per successful task

Token counts are input to the number you steer by. The arithmetic has three parts: cost per attempt, expected attempts per success, and the cost of the check itself.

cost_per_attempt = (input_tokens/1e6) * input_rate + (output_tokens/1e6) * output_rate
cost_per_success = total_cost_for_pair / count_of_passed_tasks

Use measured attempts per success from retry_of chains rather than 1/pass_rate, because failures correlate: a hard document stays hard.

Work the invoice example with plausible rates. Suppose the small model costs 0.30 dollars per million input and 1.20 per million output, the frontier model costs 5 and 25, and both consume 1,800 input and 400 output tokens per attempt.

  • Small model: about 0.00054 dollars per attempt. Over 900 attempts that is about 0.486 dollars for 744 successes, so about 0.000653 dollars per success.
  • Frontier model: about 0.019 dollars per attempt. Over 900 attempts that is about 17.10 dollars for 890 successes, so about 0.01922 dollars per success.

The frontier model costs about 29 times more per successful task. If a failed extraction triggers a manual review costing well under 3 cents, the small model wins outright. If the failure is a rejected invoice and a support ticket, the frontier model wins.

Correct twice before trusting the figure. Budget for overhead: retries, system prompts and context often warrant a 1.7 to 2.0 times multiplier on base estimates (iternal.ai). And check tokenization, because a lower price per million does not guarantee lower total cost, since models tokenize the same text differently and produce different amounts of output and reasoning (OpenAI Help Center).

Setting the selection rule

You now have two coordinates per model and task pair: a pass rate with an interval, and a cost per successful task. Turn them into a rule anyone can apply without a meeting.

Set a quality floor per task from what the downstream consumer tolerates, not from what the best model achieves. A code_fix task checked by a test suite might need 95 percent because a broken merge is expensive; a tag_suggestion task might accept 85 percent because a human reviews every tag.

The rule: among models whose lower confidence bound clears the floor, choose the lowest cost per successful task. If none clears the floor, the task is not ready to automate. Fix the checker, split the task, or keep a human in the loop. The rule refuses to switch on a point estimate and refuses to compare a well-measured model against a thinly measured one.

When two models clear the floor and the cost difference is small, prefer the tighter interval over the higher point estimate. Recheck monthly rather than continuously, on a stable task taxonomy and checker. Continuous reshuffling on partial data produces unstable routes and makes a cost regression impossible to attribute.

One case the rule handles badly: task types whose difficulty varies wildly inside them. If support_reply contains both "where is my order" and "explain this charge", split it into two task types and route separately. Granularity detailed enough to execute but not so fine that the system becomes brittle is the right target (promptessor). Routing cannot fix a taxonomy that is too coarse.

Worked example: a three-way split on support tickets

Define task types. ticket_intent classifies the request, ticket_reply_draft writes the first draft, ticket_escalation_check decides whether a human takes over. Three fixed labels, no free-text categories.

Instrument. Every call logs task type, model, both token counts, cost, and passed. ticket_intent checks against a closed intent list and sets passed inline. The other two backfill from a weekly sample of 100 labelled outputs, plus a rule that an escalation check is wrong if a human later reclassified the ticket.

Measure. After three weeks, ticket_intent has thousands of tries with tight intervals around 91 percent and 94 percent, the frontier model costing an order of magnitude more per success. ticket_reply_draft has 220 labelled samples per model and a wide interval for the small model, which labelers mark as too terse. ticket_escalation_check has a 98 percent floor because a missed escalation is a service failure.

Route. The rule picks the small model for ticket_intent. Neither model clears the floor for ticket_reply_draft at a confidence people trust, so drafts stay on the frontier model while the team collects labels and adds a terse-is-a-fail rule. Only the frontier model's lower bound clears 98 percent on escalation, so it keeps the frontier model despite the cost.

The insight: good routing produces a mixed fleet, not a single winner. Anyone can audit each choice by reading the log.

When the fleet spans several providers and you want token and cost figures in one shape, Quanta Inference puts text, image, video, audio and embedding models behind one OpenAI-compatible key, prepaid and priced in US dollars. The routing rule above does not change; only the billing plumbing gets simpler.

FAQ

FAQ

How many samples do I need before a pass rate means anything?

A working rule is 200 or more tries per model and task pair, plus 30 or more recorded failures, so precision-oriented tasks need larger samples. Below that, report "insufficient" rather than a percentage. The Wilson interval shows why: 12 of 12 tries gives a lower bound under 76 percent.

Should I log tokens myself or trust the provider's usage numbers?

Trust the provider and store its reported usage verbatim. Estimates from word counts are fine for planning before you have traffic, since one token is roughly three quarters of a word or four characters of English (iternal.ai), but they are not a billing record. Reasoning tokens are billed as output even when hidden (OpenAI Help Center).

What if two models sit within each other's confidence interval?

Treat them as tied and choose on cost per successful task, preferring the tighter interval when cost is close. Do not pick the higher point estimate, because a one or two point difference on a few hundred samples is not a signal. If the tie persists and the cost gap is small, keep the current route to avoid churn.

Does routing by task type work for agentic multi-step work?

It works better if you route per step rather than per whole task. Long-horizon agents fail non-linearly as duration grows, and failure modes shift from environment and instruction errors on short tasks to planning breakdown and accumulated history errors on long ones (tianpan.co). Judge and route the plan, retrieval and execution steps separately, each with its own pass rate and cost per success.

How often should I rerun the comparison?

Monthly is common, plus after any deliberate change to a prompt template, model or task definition. Recomputing continuously on partial data produces unstable routes and makes cost regressions impossible to attribute. Keep the taxonomy and checker stable between runs, or you will be comparing different measurements rather than different models.

Sources

Sources

  1. Token Spend Out of Control? The Case for Smarter Routing
  2. Measure AI by Cost-Per-Outcome
  3. How to track LLM costs (2026): A playbook for per-user, per ...
  4. Understanding and counting tokens | OpenAI Help Center
  5. Task Decomposition for AI Agents: How to Break Down ...
  6. Token Usage Guide 2026: How Many Tokens AI Really Uses
  7. AI Agent Planning: Task Decomposition, Execution & ...
  8. Agentic Task Complexity Estimation: Budget Tokens Before ...
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