AAI Agentic Handbook
Agents

The agent loop (ReAct)

Budding levelReviewed Oct 2026In 4 interview questionsIn 8 posts

The core cycle of every agent: model decides, host runs a tool, result is appended, repeat until a final answer or a stop condition.

In plain English

The agent loop is the repeating cycle that turns a language model into an AI agent. Each pass, the model looks at the goal and everything so far and picks an action, usually a tool call. The program runs it and adds the result to what the model will see next.

The loop ends when the model says it is finished, or when a safety limit trips — a maximum number of steps, a time limit or a budget. Those limits are the Stop conditions & step budgets.

Analogy

Driving to an unfamiliar address with a map app: check where you are, take the next turn, check again — and pull over to ask after too many wrong turns.

Common pitfall

Letting the model be the only thing that ends the loop. A confused agent can retry the same failing call for hundreds of turns; caps must be enforced by the host.

How it actually works

The bare loop is ten lines: while True: r = model(ctx); if not r.tool_calls: return r; ctx += [r, run(r.tool_calls)]. It descends from ReAct (Yao et al., 2022), which interleaved reasoning traces and actions; current models emit reasoning and tool calls natively, including several parallel calls per turn.

Production loops add what the bare version lacks: caps on turns, tokens, wall-clock and $; detection of repeated identical calls; tool errors returned as results so the model can recover; context management (trim or compact tool output as the Context window fills); a checkpoint per turn for Durable execution & checkpointing; and pauses for Human in the loop approval. Every turn re-sends the whole history, so turn t costs prefix + (t−1) × Δ input tokens and the run total grows quadratically — Prompt caching with a moving breakpoint turns most of that into 0.1× reads.

What I'd ship on day one

Host-enforced caps: 25 turns, 300k cumulative tokens, 10 min, $1. Abort after the same tool+arguments repeats 3 times or 3 consecutive tool errors. Tool errors returned as is_error results with a fix hint. Cache breakpoint moved to the latest turn each call. Checkpoint after each turn. Distinct stop_reason logged on every exit.

Worked example

Internal-wiki agent answering "Which on-call rotation owns the payments DB?" Turn 1 search_wiki("payments database owner") → 5 hits (1.8k tokens). Turn 2 get_page("svc-catalog/payments-db") → owner team pay-infra (2.2k tokens). Turn 3 get_oncall(team="pay-infra") → this week's primary. Turn 4 answers. Input per turn grew 6k → 7.9k → 10.2k → 11.1k; total 35k tokens, stop_reason final_answer at 4 of a 20-turn cap.

In the interview

If asked to sketch the loop, write it in five lines and then add, unprompted, the four things that make it production-grade: caps, repeat detection, errors-as-results, and a checkpoint per turn. Say why: "the model deciding to stop is the success path, never the safety path."

The likely follow-up is cost: show the per-turn input formula and that caching the stable prefix plus history is the first lever.

01Observegoal + history
02Decidetool call or answer
03Check capsturns, tokens, $
04Acthost runs tool
05Appendresult to context
↺ repeat until final answer or a cap
Observe, decide, check, act, append; the host enforces the exits.
Turn 1
8 k input tokens
Turn 5
14 k input tokens
Turn 10
21.5 k input tokens
Turn 20
36.5 k input tokens
PrefixHistory
Each turn re-sends the prefix plus all history (8k prefix, +1.5k per turn).

Numbers worth knowing

Input at turn t
prefix + (t−1) × Δhistory kept verbatim; Δ = tokens added per turn
Cumulative input over T turns
T·prefix + Δ·T(T−1)/28k prefix, Δ 1.5k, T 20 → 445k tokens
Default turn cap in OpenAI Agents SDK
max_turns = 10as of Oct 2026; raises MaxTurnsExceeded

How to decide

WhenDo this
Successful runs need ≤3 tool calls in a known orderDon't loop; write a chain.
Runs routinely exceed ~20 turnsAdd compaction and trim tool outputs before raising caps.
A turn can trigger an irreversible actionPause the loop for approval and checkpoint before resuming.

Cost maths

Cumulative input = T·P + Δ·T(T−1)/2. With P = 8k, Δ = 1.5k, T = 20: 160k + 285k = 445k tokens → $1.34 at $3 / M (illustrative, Oct 2026), plus 20 × 300 output tokens × $15 / M = $0.09. With a moving cache breakpoint, ≈ 36.5k tokens are cache writes (1.25×) and ≈ 408.5k are reads (0.1×): $0.137 + $0.123 = $0.26 input, ≈ 5× cheaper. Doubling T to 40 roughly triples uncached input (≈ 1.49M), which is why loops need caps on tokens, not just turns.

Operating it

Time to scale up when…
  • Median turns per successful run rises 50% after a model or prompt change
  • Cumulative tokens per run > 50% of the context window
  • Repeat-call aborts above 2% of runs
What to monitor
  • Turns per run p50/p95 (alert when p95 > 2× baseline)
  • Repeat-call aborts (alert > 2% of runs)
  • Cumulative input tokens per run p95 (alert at 60% of context window)
  • Cache-read share of input tokens (alert if it drops below 70%)

When it goes wrong

What users saw

A developer-docs agent's monthly bill tripled with no traffic increase.

How it was caught

Cost-per-run p99 alert; traces showed runs of 200+ turns all calling fetch_url on the same URL.

Root cause

A docs page started returning 429s; the tool returned an empty string instead of an error, so the model kept retrying, and the loop's only cap was a 300-turn limit.

Fix

Tool returns is_error with "rate limited, retry after 60 s"; repeat detector aborts after 3 identical calls; added a $1 and 10-minute cap per run.

Sources

  1. ReAct: Synergizing Reasoning and Acting in Language Models — Yao et al., 2022paper
  2. Building effective agents — Anthropic (Schluntz & Zhang), 2024blog
  3. Prompt caching — Anthropic, 2026docs
  4. Running agents (max_turns) — OpenAI Agents SDK, 2025docs

Check yourself

Three questions. Every option is explained once you answer.

1ConceptWhat should normally end a *successful* agent loop?
2ScenarioA tool starts returning HTTP 429 and your wrapper converts that to an empty string. What will the loop most likely do, and what's the fix?
3CalculationPrefix 8k, +1.5k tokens per turn, 20 turns, no caching. About how many cumulative input tokens?