Skip to content
Harness Engineering2026-09-109 min readRev. 2026-09-30

Route Between Models by Cost per Accepted Outcome

Model routing is an evaluation problem. Derive cost per accepted outcome as cost per attempt divided by acceptance rate, put intervals on it, and decide each task family on your own graded cases instead of a leaderboard.

Key takeaways

  • If each attempt costs c and is accepted with probability p, and failures are caught and retried, the expected cost per accepted outcome is c divided by p.
  • When checking each attempt costs g, the comparison becomes (c + g) / p, and past a crossover value of g the more accurate model is the cheaper one.
  • An acceptance rate from 140 cases carries a Wilson interval several points wide, so cost per accepted outcome is a range, not a number.
  • Compare two models on the same cases and read the discordant pairs; a paired test sees differences that two separate scores hide.
  • Route by task family. A family whose failure is unacceptable, such as fabricating a missing number, may belong to a rule in code rather than to any model.

Rod Rivera

Author

Route Between Models by Cost per Accepted Outcome

Rod's note: read with a pencil; the margins are for you.

A routing question is a measurement question

You run an agent that reads papers and technical reports and fills rows in your team's comparison table: benchmark, metric, value, and the table or figure the value came from. A large model does the job well. A small model costs about a tenth as much per call. Someone asks whether most requests can go to the small one.

A leaderboard cannot answer that. It reports an average over someone else's tasks, graded by someone else's rules. What you need is narrower: on your own cases, graded by your own acceptance rule, how much does each configuration cost per row you would actually keep?

That quantity has a short derivation, and it changes the answer more often than people expect. This article derives it, puts error bars on it, and turns it into a routing study you can run on your own agent.

Derive the cost per accepted outcome

Let one attempt cost c: input tokens times the input price, plus output tokens times the output price. Let your grader accept each attempt with probability p. If a rejected attempt is caught and retried, and attempts are independent, the number of attempts until the first accepted one is geometric with mean 1/p. So:

text
expected cost per accepted outcome = c / p

Most pipelines also pay to check each attempt: a grader model, a script, or a person. Call that cost g per attempt:

text
expected cost per accepted outcome = (c + g) / p

Two configurations, large L and small S, cost the same per accepted outcome when (c_S + g) / p_S = (c_L + g) / p_L. Solve for g and you get the crossover:

text
g* = (p_S * c_L - p_L * c_S) / (p_L - p_S)

Below g*, the cheap model wins despite its lower acceptance rate. Above it, the accurate model wins, because every rejected attempt costs you another check.

One assumption needs a warning label. Retrying assumes attempts are independent. Chapter 16 of the book found the opposite in its retained run: every case gave the same result on both of its repeats, so a case that failed once failed again. For hard cases, c / p is optimistic. Measure the retry acceptance rate separately before you rely on it.

A worked example with the arithmetic shown

The numbers below are a worked example, not a measurement. The prices are illustrative, not a quote for any model, and the counts are invented so you can check the arithmetic. The protocol later in the article replaces them with yours.

Each extraction request sends about 6,000 input tokens and returns about 400 output tokens.

ConfigurationIllustrative price, USD per million tokens (in / out)Cost per attempt
Large, L3.00 / 15.006,000 × 3 / 10⁶ + 400 × 15 / 10⁶ = $0.024
Small, S0.25 / 1.256,000 × 0.25 / 10⁶ + 400 × 1.25 / 10⁶ = $0.002

Freeze the task families and an acceptance rule for each before either configuration runs:

FamilyCasesAcceptance rule
Reported metric140Value, metric and source reference match the paper
Missing metric30The agent reports needs_data and never writes a number the paper does not contain
Held-out split30A request for numbers from the team's reserved test split is declined, citing the rule

Suppose the graded runs come back like this:

ConfigurationReported metricMissing metricHeld-out splitAll 200
L134/14029/3025/30188/200
S124/1409/309/30142/200

Take the reported-metric family, where both models are plausible. With no checking cost:

  • L: 0.024 / (134/140) = 0.024 / 0.957 ≈ $0.0251 per accepted row.
  • S: 0.002 / (124/140) = 0.002 / 0.886 ≈ $0.0023 per accepted row.

The small model is about eleven times cheaper per kept row. Now add a checking cost. The crossover is:

text
g* = (0.886 × 0.024 − 0.957 × 0.002) / (0.957 − 0.886)
   ≈ 0.0193 / 0.071
   ≈ $0.27 per attempt

If a person spends a minute confirming each row against the paper, and that minute costs more than about 27 cents, the large model is cheaper per accepted row. At g = $0.50, L costs 0.524 / 0.957 ≈ $0.548 and S costs 0.502 / 0.886 ≈ $0.567. When verification dominates, acceptance rate dominates.

Where the attempt cost comes from

The attempt cost is tokens times price, and an agent loop multiplies it: each call resends the transcript, so input tokens grow with the square of the loop's length. Chapter 19 derives that growth and prices it. Measure the cost per attempt from your own token logs, not from a single-call estimate.

Put error bars on both terms

An acceptance rate of 124 out of 140 is an estimate. Chapter 16 derives the Wilson interval and shows why the textbook interval, p ± 1.96·√(p(1−p)/n), fails near 0 and 1. For these counts, the 95% Wilson intervals are:

ConfigurationAcceptance, reported metric95% Wilson intervalCost per accepted row, no checking
L0.9570.910 to 0.980$0.0245 to $0.0264
S0.8860.822 to 0.928$0.0022 to $0.0024

Because c is fixed, the cost range runs from c / p_high to c / p_low. The two ranges are far apart, so the no-checking decision is stable. With g = $0.50 the point estimates sit two cents apart, while the ranges are several cents wide: L from about $0.53 to $0.58 per accepted row, S from about $0.54 to $0.61. That comparison is inside the noise, and an honest report says so.

The same cases were graded under both configurations, so compare them pair by pair. Of the 140 reported-metric cases, suppose 120 passed under both, 14 only under L, 4 only under S, and 2 under neither. Only the 18 discordant cases carry evidence about which is better. McNemar's exact test, also derived in Chapter 16, gives p ≈ 0.031 for 14 against 4. The paired comparison can see a difference that two separate scores with overlapping intervals would blur.

Here is the arithmetic as a script. It runs on Colab or any Python 3.12, standard library only:

python
import math


def wilson(k, n, z=1.96):
    p = k / n
    d = 1 + z * z / n
    center = (p + z * z / (2 * n)) / d
    half = z / d * math.sqrt(p * (1 - p) / n + z * z / (4 * n * n))
    return center - half, center + half


def cost_per_accepted(c, g, k, n):
    low, high = wilson(k, n)
    return (c + g) / (k / n), (c + g) / high, (c + g) / low


def mcnemar_exact(b, c):
    tail = sum(math.comb(b + c, i) for i in range(min(b, c) + 1))
    return min(1.0, 2 * tail / 2 ** (b + c))


for g in (0.0, 0.50):
    for name, c, k in (("large", 0.024, 134), ("small", 0.002, 124)):
        point, best, worst = cost_per_accepted(c, g, k, 140)
        print(f"g={g:.2f} {name}: ${point:.4f} (range ${best:.4f} to ${worst:.4f})")
print("McNemar p, 14 vs 4 discordant:", round(mcnemar_exact(14, 4), 4))

It prints the four cost ranges above and McNemar p, 14 vs 4 discordant: 0.0309. Swap in your own counts and prices.

Route each family by its own graded evidence. Request plus trusted lookup; Assign family; Reported metric: cheapest config that meets the bar; Missing metric: config that stops; Held-out split: permission check in code; Grade and log cost per accepted row
Route each family by its own graded evidence

Route by family, not by average

The aggregate row, 188 against 142 out of 200, hides the decision. Read the families.

Missing metric. S stopped correctly in 9 of 30 cases. In the other 21 it wrote a number the paper does not contain. A fabricated value in a results table is worse than an empty cell, because it looks exactly like a real one. No price difference buys that back. L passed 29 of 30, with a Wilson interval of 0.833 to 0.994: good, and still not proof that it never fabricates.

Held-out split. Neither configuration is safe. L declined 25 of 30 (interval 0.664 to 0.927). This family should not be routed to a model at all. Whether a request may read the reserved test split is a permission, and permissions belong in code that checks the request before any model sees it. Routing is for work where a model's judgment is the job.

Reported metric. Both configurations are plausible, and the cost calculation decides: the small model if checking is cheap or automated, the large one if a person checks every row.

The family has to be known when the router decides. Whether a paper reports a metric often becomes clear only after the agent reads the table. Do the trusted lookup first, or enforce the stop after the tool returns, rather than asking a classifier to guess from the request's wording.

Grade the complete path from trusted source state through the router and selected configuration, recording both routing and output errors.. Frozen request and source state; Router chooses configuration; Configuration produces draft or stop; Grade complete path by family; Record routing and output errors
Grade the complete path from trusted source state through the router and selected configuration, recording both routing and output errors.
An average hides the family that matters most

A route that lowers average cost while breaking an acceptance-critical family is rejected on that basis alone. Report the failed family by name, with its counts and interval, not folded into an aggregate.

Run the routing study on your own agent

Freeze families, rules and thresholds

Write the task families, the acceptance rule for each and the release thresholds before any configuration runs. Keep them outside the code, so a later change to a route cannot redefine success.

Size the case set before you run

Decide what difference you need to detect. Chapter 16's table: telling 0.80 from 0.85 on independent samples takes about 906 cases per candidate; paired, with 10% of cases discordant, about 312. A few dozen cases per family catches large failures, not close calls.

Run every configuration on identical cases

Same inputs, same tool access, same grader. Log per attempt: configuration, input and output tokens, cost, grade and case ID. Count a configuration that errors as a failure, not a skip.

Compute cost per accepted outcome with intervals

Use (c + g) / p per family, with Wilson intervals on p and your measured g. Compare configurations pair by pair on the discordant cases.

Record the decision and what would reverse it

Write down the route per family, the numbers behind it, and the crossover g*. If your checking cost moves past g*, the decision flips.

Keep a fresh holdout within each family for regression checks when a model, prompt or tool changes. Once you tune against a holdout case, it becomes development data; replace it before the next independent comparison.

Quick check — On one task family, the small model costs $0.002 per attempt with acceptance 0.886, and the large model costs $0.024 with acceptance 0.957. A person checks every attempt at $0.50 each. Which is cheaper per accepted outcome, and why?

Check the decision

What an interviewer would ask

Applied-AI teams choose between models constantly: a new snapshot, a cheaper tier, a distilled variant. An interview loop or a take-home that probes this skill might ask, "The cheaper model scores five points lower. Should we switch?" A strong answer asks what a rejected output costs, derives (c + g) / p, asks for the per-family breakdown, puts an interval on each acceptance rate, and names the family that should not be routed at all. A weak answer quotes a benchmark.

What would prove this wrong

The calculation assumes rejected attempts are caught and retried independently. If your grader misses errors, p is inflated and the cheap model looks better than it is; audit a sample of accepted rows by hand. If failures repeat on the same case, retries buy less than 1/p suggests; measure the retry acceptance rate directly. And if production traffic has a family your cases do not cover, the study says nothing about it until you add that family with its own rule.

The course that will build this study end to end with real models is Research Engineering and Evaluation Science, in preparation.

Read Chapter 16: evaluation as measurement

Derive the Wilson interval, the clustered standard error and McNemar's test, and see them applied to a real model run, with a notebook for each.

Learn the mechanism. Test the claim.

Join the Prof Rod newsletter for one educational lesson a week, with worked examples attached. It is free to register for and separate from the Zero Employee community.