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.
Prerequisites and cross-links
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
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.
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
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
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.
- 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
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.
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.
- 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).
- 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.
- 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).
- Output weighs 4 per token in the placeholder rates.
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.
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
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.
- Let P be the stable prefix tokens, V the variable tokens, w the write multiplier, r the read multiplier and h the hit rate.
- A hit costs rP + V. A miss writes the prefix, so it costs wP + V.
- Expected input cost per request = V + P x [h r + (1 - h) w]. Uncached it is P + V.
- 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.
- 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:
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?
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
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:
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.
- 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.
- 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.
- 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).
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
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.
- 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.
- Cascade cost per request = c_s + c_g + f x c_l. Always-large = c_l.
- Break-even escalation rate f* = 1 - (c_s + c_g) / c_l.
- 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.
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.
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
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.
Comparison of the main options
Failure scenarios to design for
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.
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.
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:
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"
return "allow"
worst = call_cost(Rates(), 1000, 1000, 500)
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.
- 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.
- Compute cost per successful task with a stated success rate, and compare it with calls times cost per call.
- Compute the break-even cache hit rate for your provider's documented multipliers, and the hit rate your traffic would give.
- 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.