Content updated: September 28, 2026
On this page
- What the Loop Actually Is
- What the Loop Controls
- The Control Flow: Plan, Act, Observe
- Stopping Conditions — the Hard Part
- Budgets: Steps, Tokens, Time, Cost
- Verification: Closing the Loop
- Error Recovery and Doom Loops
- Loop Topologies
- Context Across Iterations
- Tuning the Loop Empirically
- Pitfalls and Tradeoffs
- Frequently Asked Questions
- Worked Example: Fixing a Failing Test
- Sources and Next Steps
An AI agent loop repeatedly calls a model, executes permitted tool calls, and feeds the results back until a task is verified or a limit is reached. In this guide, loop engineering means designing that control flow: stopping conditions, budgets, verification, and recovery. It is part of harness engineering, which also covers tools, permissions, and the surrounding execution environment.
- The loop is what turns a one-shot model into an agent. Call → act → observe → repeat is the cycle where exploration, action, and self-correction happen.
- Stopping is the hardest part. Knowing when the task is actually done — and not stopping early or looping forever — is the central control-flow problem.
- Every loop needs budgets. Hard caps on steps, tokens, time, and cost are non-negotiable: they're the backstop against a confused agent burning unbounded resources.
- Verification closes the loop. Running tests/builds and feeding failures back supplies evidence of progress. Passing incomplete tests does not prove the whole task is correct.
- Recovery is most of the engineering. Tool errors, malformed output, and the agent repeating a failing action ("doom loops") need retries, loop detection, and escape hatches.
- Topology is a design choice: single loop, reflection (act → critique → retry), plan-then-execute, and orchestrator + sub-agents each fit different tasks.
- You tune the loop empirically — small changes to stop conditions, budgets, or context handling swing real success rates, so you measure with evals and trajectory analysis.
The model proposes the next step; the loop controls whether it can run, records the result, and checks progress. Start with one loop and explicit exit reasons. Add reflection or multiple agents only when evaluation shows a benefit.
What the Loop Actually Is
The following is Python-style pseudocode, not a drop-in SDK implementation. It shows message ordering and exit paths. The helpers represent runtime responsibilities described below; their exact implementation depends on the model and tool APIs.
def run(state, ledger, deadline):
recent_failures = deque(maxlen=3) # per-task state, not shared globally
try:
for step in range(MAX_STEPS):
reply = metered_model(state, ledger, deadline)
state.append(reply) # preserve calls before their results
if reply.tool_calls:
for call in reply.tool_calls:
result = authorized_tool(call, ledger, deadline)
state.append(tool_result(call.id, result))
if guard(call, result, recent_failures):
return incomplete(state, reason="repeated_failure")
else:
check = metered_verify(state, reply, ledger, deadline)
if check.passed:
return completed(reply, check, ledger.spent)
state.append(verification_feedback(check))
state = compact_preserving_call_pairs(state)
except LimitReached as limit:
return incomplete(state, reason=limit.reason)
return incomplete(state, reason="step_limit")
metered_model, authorized_tool, and metered_verify must reserve a bounded allowance before starting, enforce per-call limits and the shared deadline, and settle actual usage before returning, including a successful final call. A rejected reservation or deadline expiry raises LimitReached with a reason; the loop returns incomplete without another paid attempt. Uncertain usage remains reserved until reconciled. Every tool result must reference its matching call ID, and compaction must preserve unresolved call/result pairs. Authorization is checked before tool execution. See tool calling and MCP for the interface boundary.
What the Loop Controls
Model choice and loop design both affect results. A useful comparison holds the model and task set fixed, then changes one loop decision: stopping rules, verification, retry limits, or context handling. Measure task completion, incorrect success claims, latency, and cost. This separates an engineering hypothesis from an unsupported claim that all frontier models behave alike.
The Control Flow: Plan, Act, Observe
The canonical loop body is a three-beat cycle, often called ReAct (reason + act): the model reasons about what to do next, emits an action (a tool call), and the harness returns an observation that goes back into context for the next reason step. The power is in the feedback: the agent isn't predicting a whole solution up front, it's taking one grounded step at a time, adapting to what each action reveals.
The first real design choice lives here: how much to do per turn. A single tool call per step is simplest and easiest to recover from, but slow and chatty. Allowing the model to request several tool calls at once (parallel reads, say) cuts latency and token cost — at the price of harder error handling when one of them fails. A conservative starting point is to batch independent reads and serialize dependent writes. Parallel mutations need explicit ownership or conflict handling; the right choice depends on the tools and their side effects.
Stopping Conditions — the Hard Part
The deceptively hard question in any loop is "are we done?" Stop too early and the agent hands back a half-finished task; never stop and it loops forever, or keeps "improving" a fine answer until it breaks it. There is no single right answer — you compose several signals:
- Model-declared completion — the model stops calling tools and emits a final answer. Necessary but not sufficient: models declare victory prematurely all the time.
- Verification-gated — "done" only counts if a check passes (tests green, build succeeds, output matches a schema). This is the strongest signal because it's grounded in the environment, not the model's opinion.
- Goal/exit criteria — an explicit predicate ("the function exists and all tests pass") the loop evaluates, rather than trusting prose.
- Budget exhaustion — a hard stop regardless of state; the backstop, not the goal.
Never let the model be the sole judge of "done." A model declaring success is a request to stop, not proof of completion — gate it on a verification check whenever the task has one. "It said it was finished" is the agent equivalent of "it compiles on my machine."
Budgets: Steps, Tokens, Time, Cost
An LLM in a loop is an unbounded spender by default — a confused agent will happily take 200 steps and burn dollars going nowhere. Budgets are the hard limits that make a loop safe to run unattended, and a serious loop tracks several at once:
| Budget | What it caps / why |
|---|---|
| Step / iteration count | Caps model turns; also bound tool calls per turn and nested work, since one reply can request multiple actions. |
| Token budget | Track billed input/output and cached-token categories across calls. Token count alone misses different model prices and tool fees. |
| Wall-clock / timeout | Caps a single tool call (a hung command) and the whole task; protects latency-sensitive callers. |
| Dollar cost | A per-task or per-user ceiling; what you actually bill against and the limit ops cares about. |
The subtlety isn't having a budget, it's what to do as you approach it. A good loop degrades gracefully: as the step budget runs low it can switch from exploring to wrapping up ("you have 2 steps left — summarize what you found and stop"), rather than getting cut off mid-thought. Budgets aren't just a kill switch; they're a signal the loop can reason about.
Verification: Closing the Loop
Verification gives the loop external evidence: test results, build output, or a check of the requested artifact. It improves feedback but does not guarantee convergence or correctness. Define checks from the task’s acceptance criteria; a weak check can approve the wrong result. A browser feature may need an interaction check in addition to unit tests.
Feed a failed check back with a concise error and the evidence needed to act on it. Retain full logs outside the prompt when useful. Evaluate whether another attempt improves completion enough to justify the extra latency and cost; LLM evals should measure both successful work and false claims of success.
Error Recovery and Doom Loops
Agents fail in ways a single call never does, and most of the engineering in a production loop is the recovery paths. The big ones:
- Tool errors — a command fails or a file is missing. The fix is to return a readable, actionable error as the observation ("file not found: did you mean X?"), not a raw stack trace — the error text is feedback the model acts on.
- Malformed output — the model emits invalid JSON for a tool call. Re-prompt with the parse error, optionally with constrained decoding, rather than crashing the loop.
- The doom loop — the agent repeats the same failing action over and over, convinced it'll work this time. This is the signature agent failure, and it needs explicit loop detection.
For a minimal guard, track the last three consecutive failed attempts in a buffer owned by the current task. Normalize arguments and use a stable error category. Clear the buffer after success so legitimate repeated reads are not classified as failure. Three identical failures are a reason to change strategy or stop, not proof that every retry is unsafe.
from collections import deque
import json
def guard(action, result, recent):
if result.ok:
recent.clear() # successful repetitions are not failures
return False
args = json.dumps(action.args, sort_keys=True, separators=(",", ":"))
signature = (action.name, args, result.error_code)
recent.append(signature) # use a stable error category, not a raw log
return len(recent) == 3 and len(set(recent)) == 1
recent_failures = deque(maxlen=3) # create one buffer per task
Loop Topologies
The plain while-loop is the baseline, but "loop engineering" increasingly means choosing the right shape of loop for the task. Four common topologies:
- Single loop — the default ReAct cycle. Best for open-ended tasks where the next step depends on the last observation; simplest to build and debug.
- Reflection — after producing an answer, a separate critique step looks for flaws and triggers a revision. Adds a self-review beat that catches errors the first pass missed, at the cost of extra model calls.
- Plan-then-execute — the agent drafts a plan first, then executes the steps (each potentially its own small loop). Keeps long tasks coherent and gives you a checkpoint to inspect the plan before spending on execution.
- Orchestrator + sub-agents — a main loop delegates scoped jobs (search the codebase, review a diff) to fresh sub-agents with their own clean context, which return just a conclusion. This bounds how much any one context window must hold and enables parallelism ; shared writes still need coordination.
Context Across Iterations
Every loop iteration appends observations to the state, so a long task inevitably bloats toward the context-window ceiling. Managing that growth is a core loop-engineering job — the loop has to keep the goal and the key facts in view while shedding noise. The standard move is compaction: when the state approaches the window, summarize or drop older turns, preserving the goal, the plan, and recent results. Related tactics include offloading detail to an external scratchpad the agent can re-read on demand, and the sub-agent pattern above, which keeps the main loop's context lean by sending heavy work to a child. Get this wrong and the agent "forgets" what it was doing mid-task; get it right and it can work on problems far longer than any single window.
Tuning the Loop Empirically
You cannot tune a loop by intuition, because the changes that matter — a different stop condition, a tighter budget, when to compact, whether to add a reflection beat — interact in ways that swing real-world success rates surprisingly far. Serious loop work is empirical: run the agent against benchmark tasks, measure success rate, steps, and cost, and inspect trajectories (the full sequence of calls and observations) to see where runs go wrong — premature stops, doom loops, budget blowouts. Then change one thing and re-measure. The loop is a system you tune against data, the same way you'd tune any control system.
If asked to "design an agent," resist jumping to the model. Lead with the loop: "It's a call-act-observe cycle; the interesting decisions are the stopping condition, the budgets, in-loop verification, and recovery from doom loops." That framing shows you understand where agent reliability actually comes from.
Pitfalls and Tradeoffs
- Trusting model-declared "done." Without a verification gate, the agent stops on its own say-so and ships half-finished work. Gate on a real check.
- No loop detection. Relying on the budget alone means a doom loop wastes the entire budget on one mistake; detect repetition early.
- Budgets with no graceful degradation. A hard cut-off mid-thought wastes the work done so far; let the loop wind down when it's near the limit.
- Over-engineering the topology. Reflection and multi-agent add model calls, latency, and cost; many tasks are better served by a tight single loop. Add structure only when a simpler loop demonstrably fails.
- Context rot. Skipping compaction lets the window fill with stale observations until quality silently degrades; manage state every iteration.
- Tuning blind. Without trajectory analysis you fix the wrong thing — you see a low success rate but not whether it's early stops, loops, or bad recovery.
A useful loop makes its completion evidence and exit reason inspectable. Keep the model, tools, and task constant when comparing loop changes, and retain the simpler design unless the measured benefit justifies more calls or coordination.
Frequently Asked Questions
What is loop engineering for AI agents?
It is the design of repeated model and tool execution: how state is updated, actions are authorized, results are verified, budgets are enforced, and the run stops. It is one part of the surrounding agent harness.
When should an AI agent stop?
Stop successfully when task-specific verification passes. Otherwise return an explicit incomplete result for exhausted budgets, repeated failures, cancellation, or missing required input. A model saying “done” is not sufficient evidence.
How do you prevent an agent doom loop?
Track repeated failed attempts using normalized arguments and stable error categories. A successful action should reset a consecutive-failure guard. Combine this signal with step, time, and cost limits.
Can an agent safely retry a timed-out write?
A timeout does not prove the write failed. Check operation status or use a stable idempotency key before retrying. Replaying a mutation with a fresh key can duplicate its side effects.
Worked Example: Fixing a Failing Test
Give a coding agent a bug report and a failing test. Require the final result to include the patch and the relevant test output. The following outcomes need different exit reasons:
| Observation | Loop response |
|---|---|
| The targeted check passes, and the change meets the request | Record verification evidence, account for all calls, and finish. |
| The same missing-file error repeats three times | Stop that approach; recheck the path or return incomplete. |
| A tool times out after submitting a write | Treat the result as unknown. Query its status or reuse the operation’s idempotency key before replaying it. |
| No budget remains for verification | Save the patch as unverified. Do not label the task completed. |
The guard above intentionally does not detect alternating failures, successful polling with no progress, or semantically equivalent calls. Add a progress signal for the task, such as newly passing checks, and bound polling separately. Log the task ID, step, tool name, outcome, and exit reason; omit secrets and raw tool arguments.
Sources and Next Steps
Anthropic’s Building Effective Agents describes simple agent patterns and environmental feedback. The pseudocode and failure cases on this page are our explanatory examples. Continue with harness design, tool calling and MCP, and agent evaluation.