Last reviewed: 2026-07-31
An AI agent does not need to crash to create a cost problem. A valid tool result, handoff, or incomplete answer can send the runner around its loop again. Each pass can add another model request and more token usage. The practical control is a per-run budget contract that defines what is counted, where the hard stops sit, and what the system returns when it reaches one.
Direct answer
Set hard ceilings on at least four separate dimensions before an agent run begins:
- Model calls or turns: the maximum number of model invocations permitted during the run.
- Graph steps: the maximum number of workflow steps permitted before a stop condition must be reached.
- Tool calls: the maximum number of successful tool executions permitted across the run.
- Tokens: cumulative input, output, and total-token limits, plus a per-request input limit for unusually large contexts.
AI agent per-run call limits work only when the counters cover the entire run. Handoffs, nested agents, retries, and resumptions must not silently receive fresh budgets unless an operator explicitly approves a new allocation. A wall-clock timeout is useful, but it is not a substitute for counters tied to model requests, workflow steps, and tokens.
When a hard limit is reached, stop before dispatching the next avoidable operation. Return a controlled incomplete result, record the limit that fired, preserve the run state, and route the event to the named owner. Do not automatically retry the same work with reset counters.
Choose the values from observed successful runs and the workload’s approved unit economics, not merely from a framework default. Reconcile the counters with a token-usage evidence workflow so the configured ceiling can be compared with actual usage.
Who this is for
This control is for platform engineers, FinOps practitioners, SREs, and application owners operating agents that can call tools, hand work to another agent, revisit workflow nodes, or continue until a model produces a final answer.
A single-request application still benefits from token and output limits, but it does not need the full run-budget design described here. The highest priority workloads are autonomous or semi-autonomous flows where one user action can produce an uncertain number of model requests.
Key takeaways
- Treat model calls, graph steps, tool executions, and tokens as different accounting units.
- Apply limits to one shared run scope so a handoff or retry cannot erase earlier usage.
- Check request-count limits before dispatch whenever possible.
- Recognize that some token limits are evaluated only after a response, when usage has already occurred.
- Make a limit hit an expected terminal state with a controlled result and a named owner.
- Log counters and policy metadata without recording raw prompts, tool arguments, tool output, or request headers.
Sources checked
The OpenAI Agents SDK running-agents documentation
describes a loop in which the runner calls the model, processes a final result, handoff, or tool call, and runs the loop again when needed. It documents max_turns and a MaxTurnsExceeded exception when the run crosses that ceiling.
The Google Agent Development Kit Runtime Config documentation
defines max_llm_calls as a cap on total model calls per run. It lists a default of 500 and says that zero or negative values allow unlimited calls, which it does not recommend for production.
The Pydantic AI usage reference separates request, tool-call, input-token, output-token, total-token, and per-request input limits. It says request count is checked before each model request, while token totals normally come from model responses and are checked afterward. It also documents optional pre-request token counting and its overhead.
The LangChain GRAPH_RECURSION_LIMIT guidance explains that a graph stops after reaching its maximum number of steps without a stop condition. It identifies an unintended cycle or infinite loop as a common cause while noting that a legitimately complex graph may also need more steps.
Together, these sources support a provider-neutral control: define several counters, map each framework’s terminology to those counters, and stop the run through a deliberate error path.
Contract details to verify
Do not copy one numeric limit across frameworks until the counted units are clear.
| Limit | What the policy should count | Question to settle |
|---|---|---|
| Model-call limit | Every model request attributed to the run | Are retries and nested-agent requests included? |
| Turn limit | The framework’s defined loop iteration | Does one turn always correspond to one model call? |
| Graph-step limit | Every workflow step or node transition | Can several steps occur without a model request? |
| Tool-call limit | Successful tool executions and any separately approved attempts | Are parallel calls counted individually? |
| Token limit | Input, output, and total tokens for the complete run | Is the check performed before or after a request? |
Also verify whether cancellation stops queued work, whether an in-flight request can complete after a local limit fires, and whether resumed runs inherit prior counters. Record those answers in the run policy instead of relying on assumptions.
Happy-path operator workflow
- Classify the workload and attach a versioned policy before the run starts. The policy contains model-call, graph-step, tool-call, cumulative-token, per-request context, and elapsed-time ceilings.
- Load the run’s existing counters. For a new run they begin at zero; for an approved resume they include the retained usage from the earlier attempt.
- Before each model or tool dispatch, test the projected request count and any other limit that can be checked in advance. If pre-request token counting is supported and justified, test context size here too.
- After each event, update the authoritative counters once. Keep framework events idempotent so replaying an event does not count it twice.
- When the agent produces an acceptable final result below every limit, mark the run complete and reconcile provider-reported usage with the internal ledger.
- Review normal distributions periodically. Raise or lower limits through change control rather than allowing the agent to modify its own budget.
Limit-hit operator workflow
- When the next operation would cross a preflight limit, do not dispatch it. If a post-response token check crosses a limit, accept that usage into the ledger but schedule no further work.
- Stop queued tool calls, handoffs, and nested-agent launches that have not begun.
- Return a controlled state such as
incomplete_limit_reached, with a safe reader message that explains the task stopped without exposing internal prompts or tool data. - Persist the counters, policy version, last completed step, stop reason, and accountable service owner.
- Require an explicit decision to abandon, investigate, or resume. A resume receives a documented incremental allowance or continues under the remaining original budget; it must not silently start from zero.
A sanitized limit event can look like this:
run_id: run-042
workflow: invoice-review
policy: agent-budget-v3
status: stopped
stop_reason: model_call_limit
turn_count: 8
model_call_count: 8
graph_step_count: 21
tool_call_count: 5
input_token_count: 18420
output_token_count: 3160
total_token_limit: 24000
elapsed_ms: 48210
Keep raw prompts, user content, tool arguments, tool responses, and request headers out of this cost-control event. If incident analysis needs content, use a separately governed evidence path with appropriate access controls.
Failure modes
- A handoff resets the budget. A parent agent reaches its ceiling, hands off, and the child begins with zeroed counters. Use one run identifier and one inherited budget scope across the chain.
- Graph steps are treated as model calls. Some nodes perform deterministic work, while one node may trigger more than one model operation. Track both counters instead of assuming they are interchangeable.
- The token cap is only reactive. When a framework learns token usage from the response, the request has already occurred. Use request-count preflight and, where supported and worth the overhead, pre-request context counting.
- The error handler retries automatically. A handler catches a limit exception and restarts the same task with a fresh run. The handler should return a terminal controlled state or require explicit authorization for more budget.
- A timeout is the only guardrail. Duration does not reveal how many calls occurred before the timeout. Keep time, request, step, tool, and token ceilings together.
- A framework default becomes the cost policy. A technical default is not evidence that the workload can afford that many calls. Translate proposed limits into a worst-case usage estimate before approval.
- The limit is too low for valid work. A complex graph can reach a step ceiling without looping. Compare limit hits with successful traces and change the policy only after confirming the extra steps are expected.
- Logs contain sensitive payloads but omit counters. Cost triage needs counts, policy version, status, and stop reason. It rarely needs complete prompts or tool output.
Use the usage-anomaly triage playbook when limit-hit frequency, usage per successful run, or the mix of stop reasons changes unexpectedly.
FAQ
Does a turn limit guarantee a dollar ceiling?
No. Calls can carry different input and output sizes, and a workflow may use different models or tools. A turn cap becomes a stronger cost control when paired with cumulative-token, per-request context, tool-call, and model-routing rules. Convert the combined limits into a worst-case estimate using the price snapshot approved for that workload.
Should we use the framework’s default limit?
Treat it as a technical fallback, not an approved budget. Start with observed successful-run data, task criticality, and the maximum acceptable cost of an unsuccessful run. Then test both normal completion and deliberate limit exhaustion.
What if token usage is checked only after the response?
Record the response usage and stop subsequent work. Do not subtract or discard usage simply because it crossed the cap. If the framework supports pre-request counting, decide whether earlier enforcement justifies the extra counting overhead. Keep a request-count cap as an advance check either way.
How do we distinguish a complex workflow from an infinite loop?
Inspect the sequence of completed steps and stop reasons. Repeated transitions with no meaningful state change suggest a cycle; varied, expected steps progressing toward completion may justify a larger graph-step allowance. Raising the limit without examining the path only postpones the same failure.
Can an operator resume a stopped run?
Yes, when policy allows it. Preserve the earlier counters and grant a documented incremental allowance, or continue under the remaining original budget. Record who approved the resume, the reason, and the new ceiling. Never let a user-facing retry button erase the prior run’s usage automatically.
Reader next step
Write a one-page run-budget contract for one agent workflow. Name the counted units, set a hard ceiling for each, define which checks happen before dispatch, and specify the controlled result returned after a limit hit. Assign an owner for exceptions and an approval rule for resumptions.
Then run two tests: one representative task that should finish comfortably inside the contract, and one forced cycle that must stop at the intended boundary. Confirm that the second test launches no extra work after the stop and produces the sanitized evidence needed for triage. Use those results to set the first production limit, then review actual usage rather than increasing the ceiling on demand.