conceptAgents & Tools~3 min readUpdated 2026-06-10#agents#react#agent-loop#reasoning

The ReAct loop: reason + act

Mental model: strip any agent to its skeleton and you find one loop — reason about the goal and current state, take an action (a tool call), observe the result, repeat until done. This is ReAct (Yao et al. 2022, arXiv:2210.03629). The paper's insight is that interleaving reasoning traces with actions beats either alone: reasoning grounds the next action in the latest evidence; observations ground the reasoning in reality.

From paper to production (what changed)

The original ReAct was a prompting pattern: few-shot examples teaching the model to emit Thought: / Action: / Observation: text that a parser executed. In 2026 the pattern is native: models are trained for tool use, "Thought" is the model's extended/adaptive thinking, "Action" is a structured tool_use block, and the loop is ~30 lines around the API. The conceptual structure survived; the brittle text parsing did not.

export async function agentLoop(task: string, opts = { maxTurns: 15, maxTokens: 150_000 }) {
  const messages: Anthropic.MessageParam[] = [{ role: "user", content: task }];
  let spent = 0;

  for (let turn = 0; turn < opts.maxTurns; turn++) {
    const res = await anthropic.messages.create({
      model: "claude-opus-4-8",
      max_tokens: 4096,
      thinking: { type: "adaptive" },   // the "Reason" half, handled by the model
      system: SYSTEM,                    // goal framing + stop criteria live here
      tools,
      messages,
    });
    spent += res.usage.input_tokens + res.usage.output_tokens;
    if (spent > opts.maxTokens) throw new AgentBudgetError(messages);

    if (res.stop_reason !== "tool_use") {
      return res.content.filter((b) => b.type === "text").map((b) => b.text).join("");
    }
    messages.push({ role: "assistant", content: res.content });
    messages.push({ role: "user", content: await executeAll(res.content) }); // the "Observe" half
  }
  throw new AgentTurnLimitError(messages); // surface the trace, don't swallow it
}

Three things make this production-shaped rather than demo-shaped: a turn cap, a token budget, and errors that carry the trace (you will need it — debugging is trace reading).

Why interleaving beats plan-then-execute alone

Pure upfront planning is brittle — the world rarely matches the plan (the file isn't where the plan assumed; the API returns an error the plan didn't anticipate). Pure acting is blind — without a reasoning step, the model pattern-matches its way into repeated failing actions. ReAct's per-step adaptation is why it became the default; planning survives as a layer on top of the loop (a revisable todo list), not a replacement for it.

The empirical caveat from the original paper still holds: ReAct's gains come mostly from grounding (fewer hallucinated facts, because the model checks), at some cost in flexibility — the model can over-anchor on an uninformative observation. Reflexion (Shinn et al. 2023, arXiv:2303.11366) adds verbal self-critique between attempts; modern models do a weaker version of this natively inside their thinking blocks.

What makes the loop work or fail

  • Observation quality is everything. The model decides the next step from what the tool returned — noisy, huge, or silent results derail the loop (ACI design).
  • Stop criteria must be explicit. Tell the model in the system prompt what "done" looks like ("when the test passes, reply with SUMMARY: ..."), and enforce caps in code. A loop with neither runs until the budget does.
  • Context grows every turn — thought + action + observation, accumulated. Past ~20 turns, memory management (compaction, external scratchpads) stops being optional.
  • Verification closes the loop. The strongest agents end with a checking action (run the tests, re-fetch the record) rather than trusting their own claim of success — cheap insurance against false-victory declarations.

Cost & latency lens

Each turn re-sends the whole accumulated conversation: token spend grows roughly quadratically in turn count, and per-task agent usage runs ~4× a chat interaction on Anthropic's production numbers. Defenses: prompt caching (append-only messages + byte-stable system/tools make every prior turn a ~0.1× cache read), a small model for mechanical turns if you route, and per-turn token telemetry in tracing — the loop is one span per turn, with tool spans nested, or you're blind. Latency is turns × (model latency + tool latency); the lever is fewer/parallel tool calls per turn, not faster prose.

Failure modes

  • Repetition loops — same action, same failing result, again. Detect programmatically (hash recent actions; two identical consecutive calls → inject a "this exact call already failed, change approach" message or abort).
  • Unbounded cost — the cap-less loop is a billing incident in waiting.
  • Context rot at long horizons — quality decays as the window fills with stale observations; see agent memory.
  • Anchoring on a bad observation — one misleading tool result steers everything after; actionable errors and verification steps are the antidote.

Connects to: tool calling · planning · loops · context management

Sources