Skip to content

    AI Architect

    Cost and Unit Economics for AI Systems

    How AI architects measure cost per successful task, forecast with ranges and pick the first lever: caching, batching, routing and cascades, with break-evens.

    By Published 28 min read

    Share

    This lesson is part of AI Architect: From AI Engineer to Expert. Enroll for free, then sign in, to read it in full.

    What you'll learn: how to turn "the bill is too high" into a number you can design against: cost per successful task, split into terms (fresh input, cached input, output, retrieval, tools, retries, people, infrastructure), each with its lever and its break-even. Four "Under the hood" sections give the formulas for decomposition, cache hit rate, batch versus real-time and cascades. No prices appear on this page. Every cost is a ratio or a placeholder cost unit (CU); for real figures use the LLM cost estimator.

    At a glance

    Figure 1. Where each unit of cost goes and which lever moves it

    flowchart LR accTitle: Cost terms of one task and the levers that reduce them accDescr: One user task produces five cost terms: model calls, retrieval and tool calls, retries and failed attempts, human review and an infrastructure share. All five add up to the cost per successful task. Dotted arrows show which levers reduce which term, for example shortening and caching reduce model calls and better escalation rules reduce human review. T["One user task"] M["Model calls: fresh input, cached input, output"] R["Retrieval and tool calls"] X["Retries and failed attempts"] H["Human review"] I["Infrastructure share"] C["Cost per successful task"] L1["Shorten, cache the prefix"] L2["Route, cascade, batch, smaller model"] L3["Fix the cause of retries"] L4["Better escalation rules"] T --> M T --> R T --> X T --> H T --> I M --> C R --> C X --> C H --> C I --> C L1 -.->|"cuts"| M L2 -.->|"cuts"| M L3 -.->|"cuts"| X L4 -.->|"cuts"| H

    How to read this: solid arrows add cost, dotted arrows are levers, and the right-hand node is the only number worth forecasting.

    • Budget cost per successful task, not cost per call: failures, retries and reviewers are part of the bill.
    • The largest term is not always the biggest lever. Measure the terms before choosing.
    • Caching pays only above a break-even hit rate. Batching trades time for a fixed ratio. A cascade pays only below a break-even escalation rate.
    • Cost grows by multiplication: users, tasks, calls per task, tokens per call. Forecast three scenarios, not one number.
    • Every cheaper path must pass the golden set first. A saving that lowers the success rate is not a saving.

    The real-world problem

    Composite scenario, not a real company. A pilot assistant looked cheap per call, so the forecast multiplied that figure by expected traffic. Production cost came in far above it. Users sent more tasks, each task made several model calls, and people reviewed the failures. Nobody had a number for cost per successful task.

    After this page you will be able to

    • Compute cost per successful task from usage records and say which term dominates it.
    • Decompose one request or one agent loop into fresh input, cached input and output, and recompute it when a lever changes.
    • Compute the break-even cache hit rate, the effective batch ratio with a cutoff, and the break-even escalation rate of a cascade.
    • Pick the first lever for a given cost profile and defend the order.
    • Forecast a monthly range and set budgets and per-task caps that fail in a defined way.

    Read models, routing and inference first: it explains prefix caching, cascades and token mechanics, and this page builds on them without repeating them. Also useful:

    Terms used on this page: model cascade, prompt caching, model gateway, token, retry budget, cost per successful task, cost unit, cache hit rate, batch window, escalation rate and unit economics.

    Core concepts

    Cost per successful task, not per call

    Figure 2. Every attempt is paid for, only some succeed

    flowchart LR accTitle: Cost per successful task accDescr: One thousand attempts each cost 3 cost units, 3,000 in total. Eight hundred are accepted, 120 fail validation and 80 are abandoned or sent to a person. All attempts are paid for, so cost per successful task is 3,000 divided by 800, which is 3.75 cost units. A["1,000 attempts at 3 CU each = 3,000 CU"] S["800 accepted: successful tasks"] F["120 fail validation"] U["80 abandoned or sent to a person"] K["Cost per successful task = 3,000 / 800 = 3.75 CU"] A --> S A --> F A --> U S --> K F -->|"still paid for"| K U -->|"still paid for"| K

    How to read this: the three outcomes share one bill, so the divisor is only the accepted tasks.

    The general form is cost per successful task = (model + retrieval + tools + retries + review + infrastructure) / share of tasks that succeed. It lets you compare options that differ in both price and quality. A model one third the cost per attempt (1 CU against 3) that succeeds 25% of the time costs 1 / 0.25 = 4 CU per success, more than the 3.75 above.

    Check yourself: with Figure 2's numbers, what is cost per successful task if accepted tasks rise from 800 to 900 and the attempts cost the same?

    3,000 / 900 = 3.33 CU, an 11% drop with no change to the per-call cost. Raising the success rate is a cost lever, which is why quality work and cost work are not separate.

    Decomposing a task into cost terms

    Term Unit What drives it First lever
    Fresh input tokens x input rate Prompt length, history, retrieved text Shorten, trim history
    Cached input tokens x cached rate Stable prefix reused within the cache lifetime Prefix layout, hit rate
    Output tokens x output rate Answer length, reasoning tokens Tighter format, max_tokens
    Retrieval CU per query Embedding, vector search, reranking Fewer, smaller candidate sets
    Tool calls CU per call Calls per task, external API fees Fewer round trips
    Retries share of repeated work Timeouts, malformed output, rate limits Fix the cause, one retry owner
    People in the loop CU per review Review rate x time per review Escalation rules and thresholds
    Infrastructure CU per task, allocated Gateway, stores, tracing, idle capacity Right-size, share

    How to read this: the first three rows are what a provider bills, the rest are yours.

    FrugalGPT's cost model matches the first three rows: a part proportional to prompt length, a part proportional to the length of the generated answer and, for some services, a fixed cost per query. The remaining rows are what teams forget, and the next sections show how large they get.

    How cost grows from pilot to production

    Figure 3. Cost is a product of factors

    flowchart LR accTitle: Cost grows by multiplication accDescr: Active users multiply by tasks per user to give tasks per month, multiply by model calls per task to give calls per month, multiply by tokens per call to give tokens per month, and multiply by cost per token to give the monthly cost. Agent loops raise calls per task and growing context raises tokens per call. U["Active users"] T["Tasks per month"] C["Model calls per month"] K["Tokens per month"] B["Monthly cost"] U -->|"x tasks per user"| T T -->|"x calls per task: agent loops"| C C -->|"x tokens per call: context grows"| K K -->|"x cost per token: mix, cache, batch"| B

    How to read this: a pilot samples each factor at its friendliest value, and production moves all of them at once.

    The tail matters as much as the average. Take a task mix where most tasks are short and a few loop.

    Share of tasks Model calls per task Contribution to mean calls
    90% 2 1.80
    9% 6 0.54
    1% 40 0.40
    All tasks median 2 mean 2.74

    How to read this: the rows add up to the mean, and a small share of tasks carries a large share of the calls.

    The mean is 37% above the median, and the 1% tail makes 14.6% of all calls. Budget from the mean and cap the tail.

    Check yourself: if the 1% tail is capped at 10 calls, what is the new mean number of calls per task?

    0.9 x 2 + 0.09 x 6 + 0.01 x 10 = 2.44, which is 10.9% lower than 2.74. A cap costs the rare long task its completeness, so give it a defined partial-result behaviour.

    Budgets, quotas and per-team guardrails

    Figure 4. Budget enforcement at the Model gateway

    sequenceDiagram accTitle: Budget check before a model call accDescr: The Orchestrator sends a request with a team tag and a maximum output size to the Model gateway. The gateway asks the Policy engine to reserve the worst-case cost. Under the soft limit the call goes ahead. Between the soft and hard limit the call is degraded to a smaller model and shorter output. Over the hard limit it is rejected with a defined error. After the call the gateway settles the actual cost and writes a tagged usage event to the Trace store. autonumber participant O as Orchestrator participant G as Model gateway participant P as Policy engine participant L as LLM provider participant T as Trace store O->>G: request, team and feature tags, max output 500 tokens G->>P: reserve worst case, estimated input plus max output alt Under the soft limit P-->>G: allow G->>L: model call as requested else Between soft and hard limit P-->>G: degrade Note over G: Smaller model, shorter max output, no optional tools G->>L: degraded model call else Over the hard limit P-->>G: reject G-->>O: error budget exceeded, queue or show a message end L-->>G: answer and usage G->>P: settle actual cost, release the reservation G-)T: usage event with team, feature, version, route, tokens

    How to read this: the reservation uses the worst case, the settlement uses the real usage, and the tags written at the end make the bill explainable.

    Three details make it work. Reserve max_tokens times the output rate plus the estimated input, because the output cap is the only bound you control before the call. Alert at the soft limit, not at the hard one. Cap calls and tokens per task as well as per month, so one runaway loop cannot spend a team's budget.

    Forecasting with ranges

    Driver Low Base High
    Active users x tasks per user per month 3,000 x 20 5,000 x 30 8,000 x 50
    Tasks per month 60,000 150,000 400,000
    Model calls per task 2 3 5
    Cost per call (CU) 0.0031 0.0031 0.0031
    Monthly model cost (CU) 372 1,395 6,200

    How to read this: each column is one coherent story, not a mix of extremes, and the high case is 16.7 times the low case.

    Report the three columns, name the driver with the widest range (tasks per month, 6.7 times from low to high) and re-forecast monthly from usage records. A single number hides the range that a budget has to survive. The table covers model cost only: add the other terms from the decomposition above.

    The lever ladder

    Lever What it cuts Break-even or condition
    Shorten (prompt, history, output) Fresh input and output None; saves in proportion, verify quality
    Cache the prefix Fresh input Hit rate above (w - 1) / (w - r), 21.7% at multipliers 1.25 and 0.1
    Route by rule or classifier Calls to the large model A share of traffic provably easy on the golden set
    Batch All usage of work nobody waits for, by the batch ratio Deadline longer than window plus fallback time
    Cascade Large-model calls Escalation rate below 1 - (small + scorer) / large
    Smaller or tuned model Cost per token Success rate on the golden set holds
    Self-host Cost per token at high load Utilisation above r (next section)

    How to read this: the order is roughly cheapest and most reversible first, and the last column is what you must measure before adopting a lever.

    Try the ladder on five cost profiles.

    Which lever first? (interactive when JavaScript is on)

    A support assistant wraps a short JSON answer in long prose. Output tokens are 65% of the model bill and nothing else is large.

    • Shorten the output with a tighter format and max_tokens
    • Self-host a model
    • Add a semantic cache
    • Move the traffic to batch
    Answer

    Shorten the output with a tighter format and max_tokens. Output tokens weigh more per token and add sequential latency. Cutting them needs no new infrastructure, and the other options fix terms that are not the problem.

    A 6,000-token system prompt is identical across 30 interactive requests a minute.

    • Batch the traffic
    • Cache the prefix
    • Cascade to a small model
    • Self-host
    Answer

    Cache the prefix. A stable prefix with steady traffic gives a hit rate far above the 21.7% break-even at multipliers of 1.25 and 0.1, and no answer changes. Users are waiting, so batch is out.

    Half a million documents must be enriched by tomorrow morning and nobody is waiting for any single result.

    • Batch with a cutoff and a real-time fallback
    • Stream the responses
    • Add a semantic cache
    • Switch to a larger model
    Answer

    Batch with a cutoff and a real-time fallback. Nobody waits, so time can be traded for a fixed discount ratio. The cutoff and fallback protect the morning deadline.

    Rules show that 60% of requests are order-status lookups that the small model answers correctly on the golden set.

    • Route those requests to the small model by rule
    • Run every request through a cascade
    • Self-host the large model
    • Shorten the system prompt
    Answer

    Route those requests to the small model by rule. Difficulty is visible before the call, so a free, deterministic rule beats a cascade that would pay for a scorer on every request.

    The model bill is small, but human review of failed answers is the largest term in cost per successful task.

    • Switch to a cheaper model
    • Fix escalation thresholds and the causes of failure
    • Enable batch
    • Add more caching
    Answer

    Fix escalation thresholds and the causes of failure. A cheaper model that fails more often sends more work to people. The term to cut is the review rate, through better thresholds and fewer failures.

    Build versus buy: the cost view

    Cost line over three years Build Buy Configure
    Initial engineering Highest Integration only Settings and glue
    Running cost Tokens, infrastructure, tracing Licence or usage fee Existing platform cost
    Re-testing after model changes Yours Vendor's, plus your acceptance test Yours, smaller
    Maintenance and on-call Yours Mostly vendor Yours, smaller
    Exit cost Low if layered Contract and data export Low
    Time to first value Slowest Medium Fastest

    How to read this: the table lists lines to price, not winners; fill it with your own figures and total each column over 36 months.

    Do not let token cost stand in for total cost: page 2 and the contract search case show why. For self-hosting versus an API the arithmetic reduces to one ratio. Page 4 gives cost per million tokens = hourly cost / (million tokens per hour at full load x utilisation). With r = self-hosted cost per million tokens at 100% utilisation divided by the API cost per million tokens, self-hosting breaks even at utilisation r.

    r Break-even utilisation Reading
    0.25 25% Wins above a quarter of full load, before operating staff
    0.5 50% Needs a busy fleet
    1.2 never Loses even at full load

    Operating staff, evaluation and on-call add a fixed monthly cost that raises these thresholds, so measure with your own traffic.

    Case files

    Three composite case files apply this page. All are illustrative scenarios, not real companies.

    • Pilot to production: a cost shock. Problem: a 50-user pilot became 5,000 users and an agent loop multiplied the calls. Pattern: decompose the cost per successful task, then fix the largest term first.
    • Batch versus real-time: a redesign. Problem: a nightly enrichment job ran on the real-time path. Pattern: batch with a window, a cutoff for stragglers and retries on the batch.
    • Cascade break-even in legal review. Problem: a legal-review triage used the large model for every document. Pattern: a cascade with a calibrated threshold and a break-even check.

    Under the hood: Decomposing the cost of a request

    Intuition

    A bill is a sum of terms that scale with different things: tokens in, tokens out, calls, people. In a single call the output usually weighs most. In an agent loop the input does, because every call re-sends the history.

    Mechanism

    Figure 5. One task, four model calls, growing input

    sequenceDiagram accTitle: Input grows across an agent loop accDescr: The Orchestrator sends call 1 with 3,000 input tokens to the Model gateway, which forwards it to the LLM provider. The model answers with a tool call and 300 output tokens. The Tool server returns a 200-token result. Each later call re-sends the full history, so input grows by 500 tokens per call: 3,500, 4,000 and 4,500. participant O as Orchestrator participant G as Model gateway participant L as LLM provider participant T as Tool server O->>G: call 1, input 3,000 tokens G->>L: model request L-->>G: tool call, output 300 tokens G-->>O: answer with tool call O->>T: run the tool T-->>O: result, 200 tokens loop calls 2 to 4 O->>G: full history again, plus 300 output and 200 tool result G->>L: model request L-->>G: output 300 tokens end Note over O,L: Inputs are 3,000, 3,500, 4,000 and 4,500 tokens

    How to read this: the loop repeats the whole history, so cost grows faster than the number of calls.

    1. The provider's Messages interface is stateless: Anthropic's documentation says you send the full conversational history each time, and its tool-use loop sends the earlier messages plus the tool result in each new request (as of 2026-10-22, Messages, tool use).
    2. Calls 1 to 3 each end in a tool call and call 4 is the answer. Input per call is the previous input plus the previous output plus the tool result: 3,000 + 300 + 200 = 3,500.
    3. Without caching every input token is fresh. With caching, assume each call reads the previous call's prefix (read at 0.1) and writes only the new 500 tokens (write at 1.25).
    4. Output weighs 4 per token in the placeholder rates.
    Call Input tokens Uncached input (token-equivalents) Cached input (token-equivalents)
    1 3,000 3,000 3,750
    2 3,500 3,500 925
    3 4,000 4,000 975
    4 4,500 4,500 1,025
    Total 15,000 15,000 6,675

    Add output, 4 x 300 x 4 = 4,800 token-equivalents. The totals are 19,800 uncached and 11,475 cached, a 42.0% saving. Input is 75.8% of the uncached total. Growth alone costs 17.9% more than four flat 3,000-token calls with the same output (16,800).

    Use the widget to see the single-call case, where output dominates.

    Cost of one request in placeholder cost units (interactive when JavaScript is on)
    Cost per request (units)0.003100
    Cost per 1,000 requests (units)3.1

    The start values give 0.0031 CU per request, 3.1 CU per 1,000: fresh input 1,000, cached input 100, output 2,000 token-equivalents, so output is 64.5% of the cost. Set cachedShare to 0 and the cost per 1,000 rises to 4; set it to 0.9 and it falls to 2.38. The widget bills cached tokens at the cached rate and ignores the write premium; the next section adds it.

    What this means for architects

    What changes What you do
    Loop depth multiplies input, not only calls Budget tokens per task and alert on the 95th percentile per task, not the mean per call
    Output dominates a single call, input dominates a loop Decompose from your own usage records before choosing a lever
    Retrieval, tools, people and infrastructure sit outside the model bill Put them in the same unit and track them per task
    History re-sent each call is cacheable if it only grows at the end Keep prompts append-only so each call extends the cached prefix
    A rare long loop can carry a visible share of calls Cap calls and tokens per task and define the partial-result behaviour

    Under the hood: Cache hit-rate math

    Intuition

    A cache write costs more than a normal read, and a read costs far less. So caching a prefix saves money only when enough requests reuse it before it expires. The question is not "is caching on" but "what is the hit rate".

    Mechanism

    Figure 6. Life of a cached prefix

    stateDiagram-v2 accTitle: Life of a cached prefix accDescr: A prefix starts cold. The first request writes it and pays the write multiplier, which makes it warm. Each request inside the lifetime reads it at the read multiplier and refreshes the lifetime. If no request arrives for the whole lifetime, or if the prefix text changes, the next request misses and the prefix is written again. [*] --> Cold Cold --> Warm: first request writes the prefix and pays the write multiplier Warm --> Warm: request inside the lifetime reads at the read multiplier and refreshes it Warm --> Cold: no request for the whole lifetime Warm --> Cold: prefix text changed so the next request misses

    How to read this: only the self-loop on Warm is cheap, and both arrows back to Cold cost a full write.

    Provider terms as of 2026-10-22 (page 4 explains the mechanism):

    • Anthropic documents 5-minute cache writes at 1.25 times the base input rate and reads at 0.1 times, lower read multipliers on some models, a default 5-minute lifetime refreshed at no extra cost on each use, and 2 times for writes with the 1-hour option (prompt caching).
    • OpenAI says caching is on by default for supported models, discounts cached input (its page says up to 95%) and reports cached_tokens in usage (prompt caching).
    • Check your own provider and model: the numbers below use 1.25 and 0.1 as illustrative inputs.
    1. Let P be the stable prefix tokens, V the variable tokens, w the write multiplier, r the read multiplier and h the hit rate.
    2. A hit costs rP + V. A miss writes the prefix, so it costs wP + V.
    3. Expected input cost per request = V + P x [h r + (1 - h) w]. Uncached it is P + V.
    4. Caching wins when h r + (1 - h) w < 1, so the break-even hit rate is h* = (w - 1) / (w - r) = 0.25 / 1.15 = 21.7% for w = 1.25 and r = 0.1.
    5. With random arrivals at lambda per minute and lifetime T minutes, the chance that the next request finds a warm entry is about 1 - e^(-lambda T). This is this page's own independent-arrivals model, not a provider statement: it ignores routing across machines.

    With P = V = 1,000 and output weighing 2,000 token-equivalents:

    Hit rate Input (token-equivalents) Input vs no cache Total with output Total vs no cache
    0% 2,250 +12.5% 4,250 +6.3%
    21.7% 2,000 0% 4,000 0%
    50% 1,675 -16.3% 3,675 -8.1%
    90% 1,215 -39.3% 3,215 -19.6%
    100% 1,100 -45.0% 3,100 -22.5%

    With T = 5 minutes, break-even needs about one request every 20 minutes (lambda = 0.049). At 0.1 requests a minute the modelled hit rate is 39%, at 0.5 it is 92%.

    Check yourself: with the 1-hour option's write multiplier of 2 and a read multiplier of 0.1, what hit rate breaks even?

    h* = (2 - 1) / (2 - 0.1) = 1 / 1.9 = 52.6%. The longer lifetime raises the hit rate you can reach on slow traffic but also the hit rate you need, so compute both before choosing it.

    Try the request-level view. Set the write multiplier to 2: on which request does caching start to win?

    When does caching a prefix pay back? (interactive when JavaScript is on)
    Break-evenfrom request 2
    After 1 request(s): with cache / without1.25 / 1
    After 2 request(s): with cache / without1.35 / 2
    After 5 request(s): with cache / without1.65 / 5
    After 10 request(s): with cache / without2.15 / 10

    At the start values the cached total after two requests is 1.35 prefix-lengths against 2 uncached, so break-even is request 2. With a write multiplier of 2 it moves to request 3 (2.2 against 3).

    What this means for architects

    What changes What you do
    Savings depend on hit rate, not on caching being enabled Log the cache-read share per route and alert when it nears the break-even
    A write costs more than an uncached read Mark only prefixes reused within the lifetime; skip one-off prompts
    Releases and layout changes reset the cache Ship prompt changes in batches and warm the cache before traffic
    Slow routes expire between requests Compare arrival rate with lifetime before paying for a longer lifetime
    Output tokens are untouched Do not expect caching to cut the output term

    Under the hood: Batch versus real-time pricing models

    Intuition

    A batch interface lets the provider run your work when capacity is free, in exchange for a discount and a loose deadline. You give up latency and order; you get a lower cost ratio. The design question is what happens to the work that does not finish in time.

    Mechanism

    Figure 7. A nightly batch with a cutoff and a real-time fallback

    sequenceDiagram accTitle: Nightly batch with a cutoff and fallback accDescr: The Worker reads the items due tonight from the Document store and submits them to the LLM provider as one batch, each item keyed by an id. It polls the batch status until the cutoff. If everything finished, the Worker matches results by id and writes them idempotently. If the cutoff arrives first, the Worker cancels the batch, which stops requests not yet started from being billed, runs the stragglers on the real-time path within rate limits and writes everything idempotently. autonumber participant W as Worker participant S as Document store participant L as LLM provider W->>S: read the items due tonight W->>L: create one batch, each item keyed by an id L-->>W: batch id, processing starts loop poll until the cutoff W->>L: batch status end alt Everything finished before the cutoff L-->>W: results in any order, matched by id else Cutoff reached with stragglers W->>L: cancel the batch Note over W,L: Requests not yet sent to the model are not billed W->>L: real-time calls for the stragglers, within rate limits end W->>S: write results idempotently, keyed by item id

    How to read this: the alt block is the part a happy-path design leaves out, and the last arrow is safe to repeat because the write is keyed.

    Documented as of 2026-10-22, expressed as ratios and limits only:

    Provider documentation Cost ratio Window Limits On expiry
    Anthropic batches 50% of standard usage Most finish within 1 hour, expire after 24 hours 100,000 requests or 256 MB; results in any order Expired requests are not billed
    OpenAI Batch API 50% discount 24 hours 50,000 requests or 200 MB; separate rate-limit pool Unfinished requests cancelled; completed ones billed

    Anthropic's page adds that processing can be slowed by demand, that prompt-caching discounts stack with the batch discount but cache hits in batches are best effort (it says typical hit rates range from 30 to 98 percent), and that the 1-hour cache lifetime is worth considering for shared context. OpenAI says batches use a separate rate-limit pool from standard limits.

    1. This page's own model: let b be the batch cost ratio (0.5) and f the share of items not finished by the cutoff and rerun in real time at ratio 1.
    2. Effective ratio for the batchable work = (1 - f) b + f = b + (1 - b) f. This assumes unfinished requests are not billed in the batch: both providers document that for expired requests, and Anthropic for cancelled requests not yet sent. Check how yours bills requests in progress when you cancel.
    3. Stack caching: input cost relative to real-time uncached = b x (V + P x [h r + (1 - h) w]) / (P + V), with the same assumptions as before (P = V = 1,000, w = 1.25, r = 0.1).
    Share missing the cutoff, f Effective ratio
    0% 0.50
    10% 0.55
    20% 0.60
    30% 0.65
    Hit rate inside the batch, h Input cost vs real-time uncached
    30% 47.6%
    50% 41.9%
    98% 28.1%

    How to read this: the first table prices the stragglers, the second prices the cache inside the batch, using the 30% and 98% ends of the range Anthropic documents.

    Check yourself: with a batch ratio of 0.5, 20% of items miss the cutoff and run in real time. What is the effective ratio?

    0.5 + 0.5 x 0.2 = 0.6, a 40% saving instead of 50%. The fallback costs less than the discount is worth as long as most items finish in the batch.

    What this means for architects

    What changes What you do
    Batch trades time and order for a fixed ratio Use it only where nobody waits and the deadline exceeds window plus fallback time
    Some items will not finish Set a cutoff, cancel, and run stragglers on the real-time path within rate limits
    Results come back in any order Key every item by an id and write results idempotently
    Cache hits in a batch are best effort Do not plan on a fixed hit rate: model the range and read the real one from usage
    Batch can have its own limits and rate pool Check the provider's size limits and keep a separate queue for the real-time fallback

    Under the hood: Cascade break-even

    Intuition

    A cascade sends every request to a cheap model first and pays for the large model only when the answer is not trusted. So each request pays for the small attempt and the scorer, and the escalated share pays again. It wins only when escalations are rare enough.

    Mechanism

    Figure 8. What each path of a cascade pays

    sequenceDiagram accTitle: Cost paid on each path of a cascade accDescr: The Orchestrator sends a request to the Model gateway. The gateway calls the small model, then asks the Eval service to score the answer. If the score is at or above the threshold, the small answer is returned and the request has paid the small cost plus the scorer cost. If the score is below the threshold, the gateway also calls the large model, and the request has paid all three costs and waited for both models. autonumber participant O as Orchestrator participant M as Model gateway participant S as LLM provider small model participant E as Eval service participant L as LLM provider large model O->>M: request M->>S: call the small model S-->>M: answer M->>E: score the answer E-->>M: score alt Score at or above the threshold M-->>O: small answer Note over M: Paid small plus scorer else Score below the threshold M->>L: call the large model L-->>M: answer M-->>O: large answer Note over M: Paid small plus scorer plus large, waited for both end

    How to read this: the two branches are the two prices a request can pay, and the escalation rate decides the mix.

    FrugalGPT (Chen, Zaharia and Zou) defines a cascade with a generation scoring function that returns a reliability score in [0, 1] for a query and an answer, and a router that queries models in sequence and returns the first answer whose score passes that model's threshold. Its abstract reports matching the best individual model with up to 98% cost reduction (2023 APIs), or higher accuracy at equal cost. The 98% is its best dataset: Table 3 shows 98%, 73% and 59% on its three.

    The paper names these limits:

    • Labelled examples from a similar distribution are needed.
    • Learning the cascade takes resources, which the authors treat as one-time.
    • In its error analysis, an unsure score forced calls down the whole chain, which it calls an open problem.
    • It lists latency among factors left for future work.

    Their scorer is a small DistilBERT regression model, described as cheaper than the language models, with its cost not quantified.

    1. Let c_s, c_g and c_l be the cost per request of the small model, the scorer and the large model, and f the escalation rate.
    2. Cascade cost per request = c_s + c_g + f x c_l. Always-large = c_l.
    3. Break-even escalation rate f* = 1 - (c_s + c_g) / c_l.
    4. With rework (this page's own extension, not from FrugalGPT): let a be the share of requests wrongly accepted from the small model, e the large model's own wrong share and E the cost of one undetected error in the same units. Then f* = 1 - (c_s + c_g) / c_l - (a - e) x E / c_l.
    Case c_s c_g f Cascade cost Always-large cost Break-even f*
    Widget start 0.1 0 30% 0.40 (-60%) 1.00 90%
    Add a scorer 0.1 0.02 30% 0.42 (-58%) 1.00 88%
    Add rework: E = 20, a = 1%, e = 0.5% 0.1 0.02 30% 0.62 (-43.6%) 1.10 78%

    How to read this: each row adds one cost the row above left out, and the break-even escalation rate falls with each.

    At f* = 78% the adjusted cascade costs 0.12 + 0.78 + 0.2 = 1.10, equal to always-large. Raising the threshold lowers a and raises f, so the threshold is the dial: set it on labelled data (see page 8).

    Check yourself: small model 0.1, scorer 0.02, large model 1. What escalation rate makes the cascade as expensive as always using the large model?

    f* = 1 - (0.1 + 0.02) / 1 = 88%. Above that, every request pays for the small attempt, the scorer and a large call, so the cascade is the more expensive option.

    Try it: set the escalation rate to 0.9 and watch the cascade cost reach the always-large cost.

    Cascade against always-large (interactive when JavaScript is on)
    Cascade cost per request0.4
    Always-large cost per request1
    Break-even escalation rate90.0%

    The widget omits the scorer and the rework term, so its break-even (90%) is the upper bound; the table above shows how the other terms pull it down.

    What this means for architects

    What changes What you do
    Every request pays the small attempt and the scorer Route intents that almost always need the large model by rule, outside the cascade
    The scorer is a model that can be wrong Calibrate it on labelled data and set the threshold for a target false-accept rate
    Escalated requests wait for two models Give the large call the remaining deadline, and avoid cascades on latency-critical paths unless escalation is rare
    Break-even falls as the cost of an error rises Use the rework formula where an error costs reviewer time or customer harm
    Escalation drifts with traffic Track f weekly against f* and alert when it passes 80% of f*

    Decision framework

    Figure 9. Which term to attack first

    flowchart TD accTitle: Choosing the first cost lever accDescr: If per-task cost is not measured, instrument first with tags and per-task cost. Once measured, find the largest term. Output tokens lead to shortening output. A stable fresh-input prefix leads to caching above break-even. Large-model calls on easy traffic lead to rules and then a cascade. Work nobody waits for leads to batch with a cutoff. People review leads to better escalation rules. Retries lead to fixing the cause. Q0{"Is cost per task measured from usage records?"} I0["Instrument first: tags and per-task cost"] Q1{"Which term is largest or most reducible?"} A1["Output tokens: shorten format and max output"] A2["Stable prefix: cache above break-even"] A3["Large calls on easy traffic: rules, then cascade"] A4["Work nobody waits for: batch with a cutoff"] A5["People review: fix escalation rules"] A6["Retries: fix the cause, one retry owner"] Q0 -->|"no"| I0 Q0 -->|"yes"| Q1 Q1 --> A1 Q1 --> A2 Q1 --> A3 Q1 --> A4 Q1 --> A5 Q1 --> A6

    How to read this: measure first, then take the branch for the term that is largest or cheapest to reduce.

    If Then Because Revisit when
    Per-task cost is not measured Instrument at the Model gateway before changing anything A lever chosen without the decomposition often hits a small term Two weeks of tagged data exist
    Output tokens dominate Shorten format and max_tokens first Output weighs more per token and adds sequential latency Quality drops on the golden set
    A stable prefix is reused above break-even Lay out for prefix caching It cuts input cost without changing answers Releases outpace the cache lifetime
    Nobody waits and the deadline exceeds the window Batch with a cutoff and a fallback A fixed ratio for a latency you do not need Deadlines tighten or limits bind
    Easy traffic is visible by rule Route by rule, then cascade the rest Rules are free and testable Escalation rate nears break-even
    Sustained load keeps utilisation above r Evaluate self-hosting with operating cost Below r the API is cheaper Volume, price or residency rules change

    Comparison of the main options

    Option Latency effect Quality risk Effort to adopt How to undo
    Shorten prompt or output Lower Truncated or thinner answers Low Restore the prompt version
    Prefix cache Lower first-token time None to answers; cost rises if hit rate is low Low Remove the marker
    Rule routing None Misrouted hard requests Medium Remove the rule
    Cascade Higher on escalations False accepts by the scorer High Send all to the large model
    Batch Hours instead of seconds Stragglers, ordering Medium Return to the real-time path
    Smaller or tuned model Usually lower Regression on your task Medium Pin the previous model
    Self-host You own it Evaluation burden for each model Highest Return to the API behind the gateway

    Failure scenarios to design for

    Failure What the system does Why
    A team exhausts its monthly budget Degrades at the soft limit, rejects at the hard limit with a defined error, queues non-urgent work A hard stop with no defined behaviour becomes an outage
    An agent loop runs away A per-task cap on calls and tokens stops it, returns a partial result and logs the cap One loop must not spend a team's budget
    Cache read share collapses after a prompt release An alert fires on the read share, a warm-up request runs, the layout change is reviewed A silent layout change shows up only in usage
    The cascade's escalation rate climbs An alert fires at 80% of break-even and those intents move to the large model by rule Past break-even the cascade costs more than it saves
    A batch misses its window The cutoff cancels it and the stragglers run in real time within rate limits The deadline outranks the discount
    The provider changes a multiplier, window or tokenizer The cost model reads ratios from config with a review date, and recounts tokens on version changes Provider terms are volatile
    The forecast is off by three times Scenarios were reported, the forecast is refreshed monthly, optional features have a kill switch A range is survivable, a point estimate is not

    Worked example

    Start from the decomposition of a task and apply four levers in order.

    Assumptions: this is a composite scenario and every figure is illustrative, in placeholder cost units.

    • 1,000 task attempts, 3 model calls per task.
    • Each call has 1,000 fresh and 1,000 cached input tokens and 500 output tokens, at 1, 0.1 and 4 CU per million tokens. The prefix write premium is ignored (hit rate near 100%).
    • Retrieval 0.002 CU per task; two tool calls at 0.0005 CU each; infrastructure 1.5 CU per 1,000 tasks.
    • Retries repeat 8% of model and tool work; reviewers check 2% of tasks at 0.15 CU each; 92% of attempts succeed.
    Term CU per 1,000 attempts Share
    Fresh input 3.00 17.0%
    Cached input 0.30 1.7%
    Output 6.00 34.0%
    Retrieval 2.00 11.3%
    Tool calls 1.00 5.7%
    Retries 0.82 4.7%
    People in the loop 3.00 17.0%
    Infrastructure 1.50 8.5%
    Total 17.62 100%

    Cost per 1,000 successful tasks = 17.624 / 0.92 = 19.16 CU. A forecast of calls times cost per call gives 3 x 3.1 = 9.3, so the full figure is 2.06 times higher.

    Step Change Per 1,000 successful tasks Saving, this step Saving, cumulative Saving, alone
    Start none 19.16
    1 Route half of the calls to a small model at 0.1 of the cost 14.24 25.6% 25.6% 25.6%
    2 Shorten output from 500 to 350 tokens 13.08 8.2% 31.7% 11.0%
    3 Halve the review rate (2% to 1%) with better escalation rules 11.45 12.5% 40.2% 8.5%
    4 Cut retries from 8% to 2% 11.12 2.9% 42.0% 3.5%

    Output is the largest term (34%) but routing is the biggest lever (25.6% against 11.0%), because it applies to all model cost. The steps assume the 92% success rate holds. If the final design succeeds 88% of the time, the cost is 11.62 and the cumulative saving falls to 39.3%: a four-point quality loss gives back 2.7 points.

    A cost guard you can run

    The guard prices a call in placeholder units, computes cost per success and decides allow, degrade or reject from the worst-case reservation of Figure 4. The checks at the bottom were executed and pass.

    from dataclasses import dataclass
    
    @dataclass
    class Rates:  # placeholder cost units per million tokens, never real prices
        fresh_in: float = 1.0
        cached_in: float = 0.1
        out: float = 4.0
    
    def call_cost(r: Rates, fresh: int, cached: int, out: int) -> float:
        return (fresh * r.fresh_in + cached * r.cached_in + out * r.out) / 1_000_000
    
    def cost_per_success(total_cost: float, successes: int) -> float:
        if successes == 0:
            return float("inf")
        return total_cost / successes
    
    def decide(spent: float, limit: float, worst_case: float, soft: float = 0.8) -> str:
        """Reserve the worst case (estimated input + max output) before the call."""
        if spent + worst_case > limit:
            return "reject"
        if spent + worst_case > soft * limit:
            return "degrade"  # smaller model, shorter max output, no optional tools
        return "allow"
    
    worst = call_cost(Rates(), 1000, 1000, 500)  # 0.0031 units
    assert abs(worst - 0.0031) < 1e-12
    assert cost_per_success(3000, 800) == 3.75
    assert decide(10.0, 100.0, worst) == "allow"
    assert decide(79.999, 100.0, worst) == "degrade"
    assert decide(99.999, 100.0, worst) == "reject"

    Read it as the three jobs of the gateway's cost layer: price, divide by successes, and gate on the worst case. A production version would settle the real usage after the call, release the reservation and write the tagged usage event.

    Common mistakes

    • Forecasting calls times cost per call. It omits retries, review and tails; forecast cost per successful task.
    • Comparing models on cost per call. Compare cost per success, with the success rate measured on the golden set.
    • Caching a prefix that is reused less than break-even. Compute the hit rate first, and skip one-off prompts.
    • Batching work with a deadline shorter than the window. Add a cutoff and a real-time fallback, or do not batch.
    • Copying a cascade threshold from a paper. Set it on your labelled data for a target false-accept rate.
    • Checking the budget once a month. Use a soft limit, a hard limit and per-task caps enforced at the gateway.

    Try it

    Time: 25 to 40 minutes.

    1. Take one feature and write its eight cost terms from the table in Core concepts, with your own usage counts (calls per task, tokens per call, review share). Use placeholder units or the cost estimator for real rates.
    2. Compute cost per successful task with a stated success rate, and compare it with calls times cost per call.
    3. Compute the break-even cache hit rate for your provider's documented multipliers, and the hit rate your traffic would give.
    4. Apply two levers from the ladder in order, recompute, and state what you would check on the golden set before shipping each.

    Done when you have a one-page table with the eight terms, cost per successful task before and after two levers, the break-even hit rate or escalation rate for the lever you chose, and the golden-set check that must pass.

    Key takeaways

    • Budget cost per successful task: failed attempts, retries and reviewers are paid for.
    • Decompose before you optimise. Output dominates a single call and input dominates an agent loop.
    • Caching wins above h* = (w - 1) / (w - r); batching gives a fixed ratio b + (1 - b) f with a cutoff; a cascade wins below f* = 1 - (small + scorer) / large.
    • Cost grows by multiplication, and the tail of long loops needs a cap.
    • Budgets need a soft limit, a hard limit, per-task caps and a defined degraded mode.
    • Choose levers by measured term and break-even, cheapest and most reversible first, and prove quality on the golden set.
    • Provider multipliers, windows and discounts change: read them from config with a review date.

    Self-check

    1. 1,000 attempts cost 4,000 CU in total and 640 succeed. What is cost per successful task, and what is the per-call view hiding?

    4,000 / 640 = 6.25 CU per successful task. The per-call view (4 CU per attempt) hides the 360 failed attempts that were paid for but produced nothing.

    2. A prefix has a write multiplier of 1.25 and a read multiplier of 0.1. At what hit rate does caching break even, and why can a 10% hit rate cost more than no caching?

    h* = 0.25 / 1.15 = 21.7%. At 10% most requests are misses that pay the 1.25 write rate on the prefix, so the expected prefix cost is 0.1 x 0.1 + 0.9 x 1.25 = 1.135 times the uncached cost.

    3. A nightly job uses a batch interface with a ratio of 0.5. 30% of items miss the cutoff and run in real time. What is the effective ratio, and what must the real-time path be able to do?

    0.5 + 0.5 x 0.3 = 0.65. The real-time path must absorb the stragglers within its rate limits before the deadline, and writes must be idempotent so a repeated item does no harm.

    4. A cascade has a small model at 0.15, a scorer at 0.05 and a large model at 1. What is the break-even escalation rate, and what two things does the formula leave out?

    f* = 1 - (0.15 + 0.05) / 1 = 80%. It leaves out the cost of errors the scorer wrongly accepts and the extra latency of escalated requests, which wait for two models.

    5. The mean calls per task is 2.74 but the median is 2. Which number do you budget from, and what protects the budget from the tail?

    Budget from the mean, because cost adds up over all tasks and the tail raises it. A per-task cap on calls and tokens, with a defined partial-result behaviour, protects the budget from the rare long loop.

    Further reading

    Last verified: 2026-10-22

    Next

    Next is Security and threat modeling for AI systems, which covers trust boundaries, prompt injection, tool abuse and what to log safely.

    Enroll to keep reading

    Lesson 11 of 13 · AI Architect: From AI Engineer to Expert

    How AI architects measure cost per successful task, forecast with ranges and pick the first lever: caching, batching, routing and cascades, with break-evens.

    In the rest of this lesson

    • Case files
    • Under the hood: Decomposing the cost of a request
    • Under the hood: Cache hit-rate math
    • Under the hood: Batch versus real-time pricing models
    • Under the hood: Cascade break-even
    • Decision framework

    Enrolling is free. It unlocks every lesson in AI Architect: From AI Engineer to Expert, tracks your progress across devices and keeps your quiz scores. The newsletter is optional.

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments