All articles
October 5, 2025 6 min read

Agent loops: the reason–act–observe cycle at the heart of every agent

Under the branding, every agent is a loop: the model reasons, calls a tool, sees the result, and goes again until it's done. Here's how that loop works, the ways it fails, and the controls that keep it in line.

Written forEngineeringProduct
AgentsAgent LoopFundamentals

Strip the branding off any agent framework and you find the same engine underneath: a loop. The anatomy-of-an-agent-framework post introduces it as one of five parts; this post goes deep on just that part, because how well you understand and bound the loop is most of what separates an agent that finishes from one that spins.

GoalLLM — reason + decideTool call?yesRun toolresultdoneAnswer
The model reasons, decides whether to call a tool, sees the result, and loops — until it produces a final answer or hits a limit.

Reason, act, observe, repeat

Given a goal and a set of tools, the model reasons about what to do next, decides whether to call a tool, the tool runs, its result is appended to the context, and the model reasons again — now with new information. That interleaving of reasoning and acting is the ReAct pattern, and it's what lets an agent gather information and adapt mid-task instead of answering blind. A single LLM call is a guess; a loop is a process.

Each turn is one decision: act or finish

On every iteration the model faces exactly one choice — call another tool, or produce the final answer. Tool calling and structured outputs are how that decision is expressed unambiguously (see those posts): the model emits either a schema-valid tool request or a final response, and your code reads which. If it's a tool call, execute it and loop; if it's an answer, exit. The whole loop hinges on being able to tell those two apart reliably.

How the loop fails

  • Infinite loops — it never decides it's done, quietly burning calls and money.
  • No progress — it repeats the same failed action, or oscillates between two, without advancing.
  • Context bloat — every observation is appended, so a long loop fills the window and costs balloon (see the memory and context-window posts).
  • Compounding errors — a wrong early observation poisons every step that builds on it.
  • Wandering — it chases a tangent the goal never asked for.

The controls that keep it in line

  • Iteration cap — a hard max-steps ceiling; non-negotiable, and the first thing to add.
  • Timeouts and cost limits — bound wall-clock time and spend per run, not just step count.
  • No-progress detection — stop if the last few steps didn't change the state or just repeat a call.
  • Context trimming — summarise or drop old observations so the window doesn't blow (memory design, applied to the loop).
  • Clear exit criteria — define what 'done' means so the model isn't guessing when to stop.
  • Observability — trace every step; a loop you can't see is a loop you can't debug (see the production and LangSmith posts).

Where the loop should stop

A well-designed loop has several exits, not one: the model signals a final answer, the step cap is hit, a guardrail trips, or confidence is low and it hands off to a human (see the guardrails post). Designing those exits — especially the clean human handoff — is exactly what separates an agent that completes the job from one that loops forever, which is the whole argument of the agents-that-finish-the-job post.

An agent isn't smart because it loops. It's useful because someone bounded the loop — capped it, watched it, and told it how to stop.
Building something with LLMs?
I help teams ship GenAI that’s reliable and cost-efficient.
Let’s talk