4 min read

The agent loop: plan, act, observe

Most agent failures look the same: stuck calling the same tool forever. The plan-act-observe loop, why it gets stuck, and the fixes that actually work.

The agent loop: plan, act, observe

Watch an agent go wrong and it usually looks the same way. It calls a tool, gets a result back, calls the exact same tool again with the exact same arguments, gets the exact same result again, and keeps doing that until something times out or your bill notices. Nothing crashed. It's just stuck in a circle, very politely, forever.

The local agents and tool use post covered function calling itself, the mechanism where a model outputs a structured request to run something. This post is about the loop that wraps around that mechanism, the part that decides what to call, when to stop, and what to do when a call doesn't go as planned.

The three steps

The pattern, usually called plan, act, observe or ReAct after the paper that popularized it, is simpler than it sounds:

  1. Plan. Given the task and everything that's happened so far, decide what to do next, answer directly, or call a tool.
  2. Act. If it decided to call a tool, run that tool with whatever arguments it chose.
  3. Observe. Feed the tool's result back into the model's context, and go back to step one.

That's the whole loop. Repeat until the model decides it has enough to answer, or you hit a limit you set on purpose.

Why loops get stuck

A few specific, repeatable failure shapes:

  • No termination condition. If nothing tells the loop when to stop, it doesn't stop on its own. The model isn't counting turns unless you're counting for it.
  • Repetition with no memory of trying. The model calls a tool, the result doesn't help, and on the next turn it has no signal that this exact call already failed, so it tries again, identically. Without an explicit "you already tried this" nudge, the model has no way to notice it's repeating itself.
  • Bad observation formatting. If a tool's result comes back as a wall of raw JSON or a stack trace, the model may not parse out the actually useful part, and it plans the next step off a misread of what just happened.
  • The task genuinely can't be done with the available tools. Sometimes the loop isn't broken. It's correctly failing at something impossible, and the bug is that nothing catches that and reports it instead of retrying forever.

The fixes, in the order I'd apply them

Set a hard iteration cap. Simplest fix, and it should exist regardless of the others: after N loop iterations, stop and report what's been tried, don't let it run forever on your dime.

Detect repetition explicitly. Track the tool calls made this run. If the model is about to make an identical call to one that already ran, inject a message into its context: "you already tried this exact call and got this result, don't repeat it." This alone fixes a large share of stuck loops, because the model usually just didn't know it was repeating itself.

Return clean, parseable observations. Summarize a tool's result into the specific fact the model needs, not the entire raw payload. A 2,000-line API response with the one relevant field buried in it is worse than a three-line summary with just that field.

Give the model a way to say "I can't." An agent that's only ever allowed to keep trying will keep trying. One that's explicitly allowed to report "this isn't achievable with what I have" can actually stop instead of looping until the cap kicks in.

What this doesn't fix

None of this makes the model better at deciding what to do, only better at not getting stuck once it's decided. A model that consistently picks the wrong tool, or calls the right tool with badly-formed arguments, needs a different kind of fix: better tool descriptions, better examples in the prompt, or in the worst case, a bigger model. The loop mechanics and the model's judgment are two separate problems, and it's worth knowing which one you're actually debugging before you start changing code.

Once a loop behaves, the next question is what it remembers between one run and the next. Right now, nothing. That's next.

From the dictionary

Terms used in this post

Quick reference for the 7 terms you met above. Each one comes from the AI dictionary.

Agent LoopAI
The plan, act, observe cycle an agent runs: decide what to do, do it (usually by calling a tool), read the result, and decide the next step.
AgentAI
A system that uses a model to decide its own next action instead of just answering in one shot: it can call tools, read the result, and decide what to do next, looping until the task is done or it gives up.
APIGeneral
Application Programming Interface. In LLM context: the HTTP endpoint a hosted model exposes (api.openai.com, api.anthropic.com). You send JSON, you get tokens back. The cloud-inference contract.
Context WindowNLP
The maximum number of tokens an LLM can take in for a single forward pass. Everything the model knows about your current conversation has to fit inside this window — anything outside is invisible.
ModelML
In ML, a model is a file of learned numbers (parameters or weights) plus an architecture that tells the program how to use them. Loading a model means reading those numbers; running it means doing arithmetic with them.
PromptNLP
The text you send to an LLM. Includes any system prompt, conversation history, retrieved context, and your actual question. The prompt is the only thing you can change without retraining.
Tool CallingAI
A model's ability to output a structured request to run a specific function, then use that function's result to continue answering.

Rate this article

How helpful did you find this?

Newsletter

Get new articles in your inbox

AI engineering, LLM systems, and software architecture — no filler.

No spam. Unsubscribe any time.

Discussion

Comments

Leave a note about the article, architecture choices, or what you would build next.

Comments are stored in Supabase and fetched per post slug.

Loading comments...