Pattern · last reviewed 2026-07-13
The bounded agentic loop: how to stop an agent from running away
A bounded agentic loop is an autonomous tool-using loop with hard, explicit limits on how far it can go before it must stop or hand off. You wrap the send → check stop_reason → run tool → feed result back → repeat loop in budgets (turns, tokens, cost), explicit stop conditions, a kill switch, a timeout, and progress detection — so the loop always terminates on a reason you chose, never on the model deciding it is finished (or never deciding at all).
Reach for it the moment a loop can call tools more than once without a human between iterations. That is exactly when an unbounded loop turns a bad turn into a runaway: a spend blowup, an infinite tool cycle, or a slow drift off the task. The bound is not an optimization you add after it misbehaves; it is a design requirement of shipping the loop at all.
Context & problem
The autonomous agent is the last building block in Anthropic's Building Effective Agents: an LLM that runs tools in a loop against environmental feedback, for as many turns as the task needs, with no path hardcoded. That open-endedness is the whole point — you use an agent precisely when you cannot predict the number of steps. It is also the whole danger.
A naive loop looks harmless in the demo. You call the model, it asks for a tool, you run the tool, you append the result, you call the model again, and you keep going while (true) until the model returns end_turn. In a notebook it terminates in three turns. In production, against a real user, a flaky tool, or an adversarial input, the same loop has three standard failure modes:
- Cost blowup. Each turn re-sends the whole growing transcript. A loop that should take four turns but takes forty doesn't cost 10× more — because the context grows every turn, it costs far more than that. One stuck agent can spend a month's LLM budget in an afternoon. OWASP tracks this as its own risk: LLM10, Unbounded Consumption.
- Infinite tool loop. The model calls a search, doesn't like the result, calls the same search with a trivially different argument, doesn't like that either, and repeats — a two-step cycle with no exit. Nothing in a
while (true)loop notices that no progress is being made. - Drift. Over many turns the model wanders from the original objective, chasing a sub-goal it invented, taking actions the user never asked for. This is excessive autonomy — the third root cause OWASP names under LLM06, Excessive Agency: high-impact action proceeding with no human in the loop.
The through-line: an agent's autonomy is only safe in a trusted, bounded environment. The bound is what makes the trust rational. Without it, "the model decides when it's done" is the only stop condition — and that is not a control, it's a hope.
Forces
- Autonomy vs. control. You chose an agent because you can't hardcode the path. But total autonomy means no ceiling on cost, time, or actions. The bound is where you draw the line between "let it decide" and "but not past here."
- Task completion vs. runaway cost. Set budgets too tight and legitimate tasks get cut off mid-work; too loose and a stuck loop bankrupts the turn. The caps are a real tuning knob, not a constant you can guess once.
- Progress vs. termination. A loop that always terminates is safe but may quit on good work; a loop that runs until "done" may never stop. Progress detection is how you tell a working loop from a spinning one.
- Graceful stop vs. hard kill. Hitting a budget should ideally hand back a partial, useful answer — but some conditions (a kill switch, a hard spend cap) must stop the loop dead regardless of how gracefully.
The pattern
Keep the loop exactly as Anthropic describes it, then put a guard in front of the one place autonomy compounds: the decision to take another action. Every iteration passes through the guard before it runs a tool or asks the model for another turn. The guard owns five bounds, and the loop has exactly three exits.
The five bounds the guard enforces:
- Turn budget. A hard cap on model iterations (
MAX_TURNS). The coarsest bound and the one that alone prevents the true infinite loop. - Token / cost budget. A running total of tokens or spend, checked against a cap. This is the bound that catches the loop that stays under the turn cap but each turn re-sends a huge context. Cost is the honest unit for a per-request ceiling; tokens are its proxy when you're pre-flight.
- Tool-call budget. A cap on total tool invocations, separate from turns — one turn can request several tools. This bounds the blast radius of side effects, not just the length of the loop.
- Timeout. A wall-clock deadline. Budgets bound work; a timeout bounds latency and catches a tool that hangs rather than a loop that spins.
- Progress detection. The bound that distinguishes a working loop from a stuck one: track whether each iteration changes state — a new tool argument, a new fact retrieved, a shrinking to-do. Repeated identical tool calls, or N turns with no state change, is a no-progress signal that should stop the loop before it exhausts its other budgets.
And on top of the budgets, a kill switch: an out-of-band way to halt the loop immediately — a hard spend cap enforced at the gateway, a feature flag, a cancel signal from the user. Budgets stop the loop at the next guard check; the kill switch stops it now.
When a bound trips, the loop takes one of three explicit exits, never a fourth silent one: the model finished (end_turn), a budget is spent (stop with a partial answer and an annotation saying why), or the work must go to a human (escalate). Every exit is a reason you chose in advance.
Reference implementation notes
The pattern is stack-agnostic; the bounds live at different layers on each platform.
Anthropic
The loop branches on the Messages API stop_reason: tool_use means run the requested tools and loop; end_turn is the model's natural exit. Branch on stop_reason, not on the model's prose — the structured signal is the contract. Two API-level knobs feed the budgets: max_tokens caps a single response, and a token counter over the request/response pair drives your running cost budget. Anthropic's own guidance is explicit that autonomous loops should carry "stopping conditions (such as a maximum number of iterations) to maintain control." The Claude Agent SDK runs this same gather-context / act / verify / repeat loop and exposes the iteration bound as a first-class setting rather than something you bolt on.
AWS
On Bedrock, the agent runtime exposes a maximum-iterations bound directly, so the turn budget is configuration rather than hand-rolled control flow. Pair it with Bedrock Guardrails for the content bound — the denied-topics and filter checks are a separate, policy-level stop condition from your numeric budgets. Keep the two distinct: a guardrail block and a turn-budget exhaustion are different exits that your loop should annotate differently.
Cloudflare
Two platform features do budget work for free. The AI Gateway enforces a hard spend cap server-side: past the cap it returns HTTP 429, which your loop should catch and convert into a graceful in-band stop rather than an unhandled error — the gateway is your kill switch for cost. And the Workers runtime imposes its own ceilings (CPU time, subrequest count) that act as a backstop timeout on any single request; a loop that would run forever is bounded by the platform even if your own timeout has a bug. For a loop that must outlive one request, a Durable Object alarm gives you a durable wall-clock deadline.
Trade-offs
- The caps are a tuning problem, not a set-and-forget constant. Too tight cuts off legitimate long tasks; too loose defeats the purpose. Expect to set them from real transcripts and revisit them as the task mix changes.
- Partial answers become a first-class output. A budget exit hands back incomplete work. Your loop, your UI, and your callers all have to handle "stopped early, here's what I have" — which is more surface than "success or exception."
- Progress detection is genuinely hard. Turn and cost caps are trivial counters. Deciding that an iteration made "no progress" is a heuristic that can false-positive on a legitimately slow step. Start with the cheap bounds; add progress detection only where infinite tool cycles actually bite.
- An extra invariant on history. A budget exit can produce a turn that is only an annotation. If you persist conversation history, that turn still has to be recorded as a non-empty assistant turn — an annotation-only turn can otherwise leave two consecutive user roles, which the Anthropic API rejects on the next call. The bound forces a concrete storage invariant, not just a control-flow one.
When not to use this
Don't reach for a bounded agentic loop when there is no loop to bound. If the flow is a fixed sequence of steps you can hardcode — prompt chaining, routing, a single tool call and done — that is a workflow, not an agent, and it already terminates by construction. Adding turn budgets and progress detection to a straight-line pipeline is overhead guarding against a runaway that cannot happen. Anthropic's own advice is to prefer the workflow until the task genuinely needs an agent's open-endedness.
Equally, the budgets are a containment control, not an authorization control. If the real risk is what the agent can do — a tool that can delete records or move money — bounding the number of turns does nothing about the one destructive call. That is a least-privilege and human-in-the-loop problem; see human-in-the-loop approval and layer it with defense in depth. Budgets bound how far a loop runs; they do not bound how much damage a single action can do. You need both.
As-built evidence
This platform runs the pattern in production. The aiArch coach is a real bounded agentic loop, not a chat wrapper — and it is bounded because the module it teaches (The Agentic Loop, Track B) makes bounding the loop a graded objective. The build is the curriculum, so the safeguard the lesson preaches is the safeguard the code enforces.
Concretely, in src/lib/coach.ts:
- Hard caps as named constants —
MAX_TURNS = 6andMAX_TOOL_CALLS = 8, with the OWASP LLM06 rationale in the comment beside them. The loop runswhile (turns < MAX_TURNS)and checks the tool count before every dispatch. - Three explicit exits — the model finishing (
end_turn), the tool budget spent (tool_budget, which emits an escalation annotation and stops), or the turn budget spent (max_turns). There is no fourth path where the loop keeps going. - A per-turn cost log — spend is recorded turn by turn, the running-total bound the abstract pattern calls a cost budget.
- Cost as a kill switch — the AI Gateway enforces a hard monthly spend cap; on exhaustion it returns 429, which the loop catches as a
BudgetExhaustedErrorand turns into a graceful in-band coach message rather than a crash. - Least-privilege tools — the coach can read learner state, retrieve lesson context, and grade attempts, but has no write access to content or billing, and grades only through the one shared grader. Budgets bound the loop; least privilege bounds the blast radius.
- Termination is tested, not asserted — a dependency-injection seam lets a test inject a model that always asks for another tool and prove the loop still terminates within budget.
The full build-in-public write-up, grounded entirely in the repo, is Why the coach runs a bounded agentic loop.
Changelog
- 2026-07-13 — Initial publication.
- Autonomous loop shape, "stopping conditions such as a maximum number of iterations," workflow-vs-agent guidance: Anthropic engineering, Building Effective Agents, checked 2026-07-13.
- Excessive Agency and its three root causes (excessive functionality / permissions / autonomy); mitigation via least privilege and human-in-the-loop: OWASP LLM06:2025 Excessive Agency. Cost blowup as a distinct risk: OWASP LLM10:2025 Unbounded Consumption, checked 2026-07-13.
stop_reason/tool_use/end_turnbranching andmax_tokens: Anthropic Messages API tool-use docs, checked 2026-07-13.- Cloudflare AI Gateway spend caps returning 429, and Workers runtime limits as a backstop: Cloudflare AI Gateway docs, checked 2026-07-13.
- The production loop, its budgets, the three exits, the per-turn cost log, and the 429→graceful-message path are aiArch's own, drawn from this repository (
src/lib/coach.ts) and documented at the architecture note — stated as first-person practice.
The API-level knobs (Bedrock max-iterations, Anthropic SDK loop settings) and OWASP's numbering are the facts here most likely to drift — re-verify before relying on a specific name. Corrections: hello@aiarch.dev.
Learn to build the loop, then bound it.
aiArch teaches the agentic loop, its budgets and stop conditions, evals, and production agent architecture by building — on a platform whose own coach runs the bounded loop live in production. The build is the curriculum.
See how aiArch helps senior engineers become AI-native, or compare Professional Membership pricing.
Free sample — no signup · every claim cited · full curriculum is waitlist-only