Agent Cost in Production
Per-request cost is a variable the system chooses at runtime, not a constant you can capacity-plan around — so budget ceilings become an operational control.
The question, the obvious approach, and why it breaks
Every lesson starts where the work starts: an operational problem, a first attempt that is entirely reasonable, and the way production disagrees with it.
Why can an agent's spend rise by an order of magnitude without any deploy, and what stops it?
For an ordinary service, cost per request is roughly constant and capacity planning is about volume. For an agent, one request may take a single model call and another may take dozens with tool calls between them, and nothing in the deployment determines which.
Track total spend against a monthly budget, review it at the end of the month, and set an alert if the bill looks unusual. It is how every other infrastructure cost is managed.
Monthly review is far too slow. A runaway loop shipped on a Friday spends for a weekend before anyone opens a report.
- Monthly review is far too slow. A runaway loop shipped on a Friday spends for a weekend before anyone opens a report.
- Total spend hides everything that matters. A stable total can conceal that a small share of requests now costs a large multiple of the rest.
- Provider bills lag and aggregate. By the time the number is authoritative, the behaviour that produced it is days old (Cost Per Request).
- There is no natural ceiling. An ordinary service saturates — CPU, connections, a pool — and starts failing, which is itself a signal. An agent loop just keeps spending successfully.
- A prompt or model change with no code change can move cost per request substantially, and it will not appear in any capacity model that was built around request volume (Prompts and Models Are Deployables).
What is actually happening
Underneath the tooling, which is the part that survives a change of tool.
- Cost per request is a distribution, not a value. Its shape is set by input size, output length, how many loop iterations the model chooses, how many tool calls it makes, how much context is resent on each iteration, and whether retries occur (The Agent Loop).
- Context growth within a loop is the dominant driver for many agents: each iteration typically resends the accumulated conversation and tool results, so cost within a single request grows superlinearly with iteration count (Token Budgets).
- Long-tail requests therefore dominate spend. A small share of requests can account for a large share of cost, which means the mean is close to useless as a control signal and the tail is where the money is (Tail Latency: Why p50 Being Fine Does Not Help describes the same shape for time).
- The failure mode with no ordinary-service equivalent is the runaway loop: an agent that retries a failing tool, or oscillates between two actions, or re-plans indefinitely. Every iteration is a successful, well-formed, billable request.
- Because there is no saturation point, the ceiling has to be imposed: per-request iteration and token caps, per-user or per-tenant budgets, and a global spend rate limit (Budgets, Limits and Termination).
- A budget ceiling is a load-shedding decision made in cost terms. When it is hit, something must happen — degrade, queue, escalate to a human, or refuse — and choosing that behaviour is a product decision, not an infrastructure one (Load Shedding).
- Cost, latency and quality are coupled: fewer iterations is cheaper and faster and sometimes worse. That is the trade the ceiling is actually making, and it should be made deliberately rather than by an unbounded loop.
What makes one request cost a multiple of another
Each driver is chosen at runtime by the system rather than fixed by the deployment, which is what separates this from ordinary capacity planning. The rightmost column is what you can actually do about each.
| Driver | Why it varies per request | Where it hides | The control |
|---|---|---|---|
| Loop iterations | The model decides how many steps a task needs, and hard tasks take more | Averages: a small share of long runs dominates total spend | A hard iteration cap, with the cap event logged (Budgets, Limits and Termination) |
| Context resent per iteration | Accumulated history and tool results are resent each step, so cost grows superlinearly with iterations | Invisible unless per-iteration input size is recorded | Trim, summarise or window the context (Context Selection & Compression) |
| Output length | Response length is a model choice unless bounded | Output is usually the more expensive direction; totals hide the split | An explicit output limit, plus prompt guidance on length |
| Tool call count and cost | Each tool call is a backend call with its own cost and load (A Tool Call Is a Backend Call) | Charged to the downstream service's budget, not the agent's | Per-request tool-call caps, and attribution of downstream cost back to the agent |
| Retries | Tool failures and parse failures trigger retries inside the loop, on top of client retries outside it | Retries look like ordinary calls in aggregate metrics | Bounded retries with backoff, counted against the same request budget (Tool Errors, Retries and Timeouts) |
| Model choice | Routing may send a request to a more capable, more expensive path | A routing change moves cost with no code change | Route deliberately, and treat routing as a versioned input (Prompts and Models Are Deployables) |
| Input size | User-supplied documents and retrieved context vary enormously | One large upload can cost a multiple of a typical request | Limit input size and retrieved context volume explicitly |
How runaway spend actually happens
Every row below is a successful request from the system's point of view. Nothing errors, nothing times out, nothing saturates — which is exactly why cost needs an explicit ceiling rather than a monitor.
| Trigger | Symptom | Cause | Response |
|---|---|---|---|
| A tool starts returning an error the model treats as retryable | Requests run to the maximum iteration count; cost per request jumps and latency follows | The loop has no termination condition other than success or an iteration cap | Cap iterations, classify tool errors as terminal versus retryable, and surface cap-hits as an alert (Tool Errors, Retries and Timeouts) |
| A prompt change encourages more thorough tool use | Cost per request rises steadily with no deploy of application code | A behaviour input changed and cost was not a canary signal | Include cost distribution in every behaviour-change canary (Canarying a Model or Prompt Change) |
| One tenant automates a workflow against your agent | Total spend rises sharply; other tenants see provider rate limiting | No per-tenant budget, so one actor consumes shared capacity | Per-tenant budgets and rate limits, with a defined exhaustion behaviour (Budgets, Deadlines and Step Limits) |
| Two agents in a multi-agent setup delegate to each other | Cost per task grows without bound while both agents report progress | No global depth or budget limit across the delegation graph (When Not to Use Multi-Agent) | A budget that travels with the task across agents, not per agent |
| Client retries a slow request while the loop is still running | Spend multiplies; duplicate side effects appear downstream | Retries at two layers with no idempotency key (Idempotency) | Idempotency keys and a single retry authority; never retry at both layers |
| Cheaper model adopted to reduce spend | Per-call cost drops, total cost rises, quality drops | More iterations and more retries needed per completed task | Measure cost per completed task, not per call, and evaluate quality alongside (Eval Metrics: What to Measure and How) |
A ceiling that degrades rather than fails
A budget check is only a control if something specific happens when it is exhausted. The interesting part is not the arithmetic; it is that the exhaustion path is a deliberate product behaviour rather than an exception, and that partial results are marked as partial.
The check belongs at the same place as the kill switch guard — immediately before each model or tool call — so a request already running stops at its next step rather than at its next request.
1type Budget = { maxIterations: number; maxCostUnits: number; tenantId: string }2 3async function runAgent(input: Input, budget: Budget): Promise<Result> {4 let iterations = 05 let spent = 06 const trail: Step[] = []7 8 while (true) {9 // Checked before every step, not once at request entry.10 if (iterations >= budget.maxIterations || spent >= budget.maxCostUnits) {11 emit('agent.budget_exhausted', { tenantId: budget.tenantId, iterations, spent, reason: iterations >= budget.maxIterations ? 'iterations' : 'cost' })12 return degrade(trail) // partial result, escalation, or handoff — never a silent truncation13 }14 if (!await tenantBudget.reserve(budget.tenantId, ESTIMATED_STEP_COST)) {15 emit('agent.tenant_budget_exhausted', { tenantId: budget.tenantId })16 return degrade(trail)17 }18 19 const step = await model.step(trail, input)20 spent += step.costUnits // measured from the response, not estimated21 iterations += 122 trail.push(step)23 24 if (step.kind === 'answer') return complete(trail)25 if (!toolGuard.allows(step.tool, budget.tenantId)) { // same guard as the kill switch26 trail.push(unavailable(step.tool))27 continue28 }29 const result = await callTool(step.tool, step.args)30 spent += result.costUnits // tool cost counts against the same budget31 trail.push(result)32 }33}Three things carry the lesson. The check is inside the loop, so an in-flight request is bounded. Tool cost counts against the same budget as model cost, because a loop over an expensive internal API is the same problem. And exhaustion calls degrade, which returns a marked partial result or a handoff — a truncated answer presented as a complete one is a quality regression that will be blamed on the model (In-the-Loop vs On-the-Loop and Escalation).
How to do it properly
Most important first.
- Attribute cost per request and record it with the trace: tokens in and out, model identifier, tool calls, iterations (Tracing Agents).
- Alert on the distribution rather than the total — p95 and p99 cost per request, and the share of requests above a defined multiple of the median.
- Cap iterations and total tokens per request as a hard limit, and treat hitting the cap as an event worth logging and reviewing rather than a silent truncation.
- Set per-user and per-tenant budgets so one actor cannot consume the system's capacity, and decide explicitly what happens on exhaustion (Budgets, Deadlines and Step Limits).
- Enforce a global spend rate limit as a backstop, wired to the kill switch, so a systemic runaway has a ceiling that does not depend on anyone noticing (The Agent Kill Switch).
- Include cost as a canary signal on every behaviour change, since prompt and model changes move it without touching code (Canarying a Model or Prompt Change).
- Reduce cost structurally before reducing it by limits: trim resent context, cache what is stable, and route simple requests to a cheaper path (Context Selection & Compression, Fallbacks, Caching and Model Routing).
- Bound tool-call cost too — an agent calling an expensive internal API in a loop can cost more in downstream load than in model usage (A Tool Call Is a Backend Call).
How much can this affect
Every production change has a blast radius. Stated as a scale so it is comparable between changes rather than adjectival — and paired with what actually contains it, because a wide scope with a real containment mechanism is a different situation from a wide scope with none.
An uncapped runaway consumes shared provider capacity and shared budget, so it degrades every tenant at once. Per-request caps, per-tenant budgets and a global rate limit are the three layers that contain it.
What can go wrong
- A budget enforced only at request start, so a single request can run far past the ceiling before anything checks again.
- Caps applied per model call rather than per request, so an agent with many small calls never trips anything.
- Silent truncation at the cap, producing subtly incomplete answers that look like a quality regression with no obvious cause.
- Cost attributed to the service rather than to the tenant or feature, so nobody can say what the expensive thing is (Cost Drivers).
- A retry storm across the loop and the client at once, multiplying spend precisely when the system is already unhealthy.
- A cheaper model adopted to control cost, which then needs more iterations to complete the same task and costs more overall.
- Cost dashboards built from provider billing exports, which lag by hours or days and cannot support an operational decision.
- "Cost is a finance problem." It is an availability problem: an exhausted budget or a rate-limited provider account is an outage, and a runaway loop is an incident.
- "Average cost per request is what to watch." The mean is dominated by the tail and moves too little to be an alarm. Watch the tail and the share above a threshold.
- "A cheaper model is cheaper." Per call, yes. Per completed task, only if it does not need more attempts, more iterations or more human correction.
- "Set a monthly budget alert and you are covered." An alert is a notification; a ceiling is a control. During a runaway loop you need the second one (Load Shedding).
- "Caching solves it." Caching helps where inputs repeat. It does nothing for the long-tail, many-iteration requests that dominate spend (Operating a Cache).
Operating it
Evidence is the signal, not the intention. Rollback is sometimes 'you cannot, and that is the point'.
- Cost per request is visible as a distribution with a tail, in near real time, broken down by tenant and by feature.
- A cap has actually been hit in production and you can point at what happened to those requests.
- The cost impact of the last prompt or model change was measured during its canary, not discovered in a bill.
- A budget exhaustion event produces a defined user-visible behaviour that someone has seen.
- Cost controls are configuration and reverse immediately, which is the argument for setting them somewhat tight: a limit that is too aggressive is visible in minutes and cheap to relax.
- Spend already incurred is not recoverable. Unlike most production mistakes, this one has a direct, permanent financial cost, which is why the ceilings exist rather than only the alerts.
- If a cap turns out to be cutting off legitimate work, raise it deliberately and record why. A cap quietly raised each time it trips is not a control.
- Automate enforcement of hard ceilings — per-request tokens and iterations, per-tenant budgets, global spend rate. These are unambiguous and must not depend on a human noticing.
- Automate attribution and anomaly detection on the cost distribution, particularly the share of requests in the expensive tail.
- Keep the budget-setting human. What a tenant is allowed to spend is a commercial decision, and an automatically raised ceiling is not a ceiling (FinOps).
- Keep the decision to spend more during an incident human: sometimes the right answer is to raise the limit and pay, and no automation should make that call.
- Every ceiling trades completeness for predictability. A capped request may return a partial answer, and that is a real product cost you are choosing.
- Fine-grained attribution costs telemetry volume and adds work to the request path.
- Aggressive caching and context trimming reduce cost and can reduce quality, and the effect is only measurable through evaluation (Evaluating Agents: Testing Probabilistic Systems).
- Per-tenant budgets require a billing or entitlement model to exist, which is often organisational work far outside the agent team.
Where this applies
This domain is unusually tool- and organisation-dependent. These labels say what each claim is specific to, and what a different platform, provider or organisation does instead.
- GENERALThe mechanism — variable per-request cost, superlinear growth within a loop, no saturation point — holds for any agent architecture on any provider. Specific pricing does not transfer and is not taught here; measure your own.
- SIMULATEDAny figure used in this lesson is a scenario premise for reasoning about shape, not a measurement. Ratios between your own model calls, tool calls and iterations must be measured in your own system before any of this becomes a number you can plan with.
- SCALE-SPECIFICBelow meaningful volume, ceilings matter more than attribution — one runaway loop is the entire risk. Above it, attribution by tenant and feature becomes the dominant need, because the question changes from "is something wrong" to "what is expensive".
Where the depth lives
This domain teaches delivery and operation, and hands the mechanism off to the domain that owns it.