AI ENGINEERING · AGENTS · FOUNDATIONS · 3 OF 5

The Agent Loop: Reason, Act, Observe, and Knowing When to Stop

The control loop at the heart of every agent: reason, act, observe and repeat. Stopping rules and budgets, a step-by-step trace of a coding agent, why context grows and how to trim, summarise and offload it, planning and self-checks, catching stuck loops, sub-agents, pausing for people, checkpoints for long runs, and tracing every step.

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
1 / 16

Press play to hear the narration, or step through at your own pace.

Notes

The basic loop

The pattern is often called reason and act. The model reads everything so far and decides what to do next. It acts by requesting a tool call. Then it observes, because the result is added to the context. Back to reasoning, now with new information. The power is that each observation shapes the next decision. If the tests fail, the model reads the failure and changes its plan, which no fixed script could do.

Reason, act, observe
  • reason: read the context,
  • decide what to do next
  • act: request one or more
  • tool calls
  • observe: tool results are
  • added to the context
  • repeat until a final answer
  • or a limit

One turn

Here is one turn in detail. Build the context within its token budget. Call the model with the tool list. Log the reply. If it is a final answer, return it. Otherwise, for each tool request, check it against policy, run it, and add the result to the context under the matching id. Count the step and check the budgets. That is about ten lines of code. Everything else in agent frameworks is helpers around this core.

Inside one turn
ctx = context.build(budget)
reply = model(ctx, tools)
log(reply)
if reply.final: return reply
for call in reply.tool_calls:
  check(call)            # policy
  result = run(call)
  ctx.add(call.id, result)
steps += 1
if over_budget(): stop()

Stopping rules

The hardest part of the loop is stopping. The good ending is a final answer, ideally backed by a check, like the tests now pass. Then there are the safety endings: a step limit, a time limit, a cost limit, or no progress for several turns. The model can also stop to ask the user a question. Whatever the reason, report it clearly. Stopped after twenty five steps, tests still failing, here is what I tried, is far more useful than silence.

Ways a run ends
  • • final answer (success)
  • • goal check passes
  • (tests green)
  • • step limit (e.g. 25)
  • • time or cost limit
  • • no progress for N steps
  • • model asks the user
  • always say WHY it stopped

A trace

Here is a real shaped trace. The user says a test is failing. Turn one, run the tests, one fails. Turn two, read the source file. Turn three, read the test file. Turn four, edit the source to handle an empty list. Turn five, run the tests again, and they all pass. Turn six, the final answer explains the cause and the fix. Notice that the agent checked its own work before claiming success. That habit matters more than raw model strength.

Fix the failing test
1 run_tests       → 1 failing
2 read_file sum.js
3 read_file sum.test.js
4 edit sum.js: handle empty
5 run_tests       → all pass
6 final: "Fixed. sum([])
  returned NaN, now returns 0."

Context growth

The loop has a hidden cost. The model is stateless, so every turn resends the whole history: system prompt, tool list, every reply and every result. The context grows each turn, so total tokens across a run grow roughly with the square of the number of steps. One careless tool result, like a four thousand line log, rides along on every later turn. Long contexts also make models worse at finding the detail that matters.

Why context grows
  • turn 1: system + tools + ask
  • turn 2: + reply + result
  • turn 3: + reply + result
  • ...
  • each turn resends all of it
  • a 4,000-line log in one
  • result stays for every
  • later turn

Managing context

So manage it. Truncate tool output to the first and last lines and say how much was cut. Clear old tool results once the model has used them. When the history gets long, summarise the early turns into a short progress note. Let the agent write its plan and findings to a file and read them back when needed, which is memory outside the window. And keep the system prompt and tools stable at the start, so prompt caching makes them cheap.

Keep it small
  • • truncate tool output: head
  • and tail, plus "N lines cut"
  • • clear old tool results once
  • they have been used
  • • summarise early turns into
  • a short progress note
  • • offload to files: notes.md,
  • plan.md, read back on need
  • • cache the stable prefix

Planning

For short tasks, deciding one step at a time works well. For longer ones, ask the model to write a plan first: a numbered list of steps, saved where it can see it. Then work through the plan, ticking items off and revising it when a result is surprising. A written plan keeps a long run on track after the early turns are summarised away, and it gives a person something to review before any real work starts.

Two styles
  • interleaved: decide one
  • step at a time (ReAct)
  • good for short, uncertain
  • tasks
  • plan then execute: write a
  • numbered plan first, then
  • work through it, revising
  • when results surprise you
  • good for long tasks

Self-checks

Before declaring success, check the result against the goal. The best checks are automatic: tests pass, the type checker is clean, the page returns a success status. Without one, give the model a short rubric to check its own output, or have a second model review it. Either way, the check result goes back into the loop as an observation. A failing check is not the end of the run, it is the next thing to fix.

Verify before done
  • best: an automatic check
  • tests, type check, linter,
  • the API returns 200
  • next: a rubric the model
  • checks itself against
  • or: a second model reviews
  • the check result goes back
  • into the loop as a result

Getting stuck

Agents get stuck in recognisable ways. The same tool call with the same arguments and the same result, over and over. Edit a file, revert it, edit it again. Lots of turns, no new facts. The loop controller can spot these. Hash recent calls and count repeats. When it sees one, tell the model plainly that it is repeating itself and must try a different approach, or stop the run and ask a person for help.

Stuck patterns
  • • same call, same args,
  • same result, again
  • • edit, revert, edit, revert
  • • growing context, no new
  • facts
  • responses: tell the model it
  • is repeating, force a new
  • plan, or stop and ask

Sub-agents

When one subtask would flood the context, hand it to a sub-agent. A sub-agent is just another loop, with a fresh context, its own tools and a narrow goal, like find where authentication is handled. It might read forty files, but it returns a summary of a few hundred words, and only that enters the main context. Several sub-agents can run in parallel. The catch is cost, because total tokens multiply quickly.

Orchestrator and workers
  • main loop hands a subtask
  • to a sub-agent with its own
  • fresh context and tools
  • it may read 40 files, but
  • returns a 200-word summary
  • good for search and research
  • costly: tokens multiply

People in the loop

Real agents pause for people. Before a risky tool runs, when the model needs an answer only the user has, or when a plan should be reviewed before work starts. A pause can last seconds or a day, so you cannot hold the loop in memory. Save the full state, notify the person, and when they reply, resume from exactly the same turn with their answer added as the next observation.

Human in the loop
  • pause points:
  • • before risky tools
  • • when the model asks a
  • question
  • • at plan review
  • save state, notify the
  • person, resume from the same
  • turn when they reply

Checkpoints and traces

Save a checkpoint after every turn: the messages, the plan, the step count and the budgets used. A crash or a deploy then resumes the run instead of starting over. Separately, trace every step: the context size, the model's reply, the tool and its arguments, the result, the latency, the tokens and the cost. When an agent fails, the trace is how you find the exact turn where it went wrong, and those failures become your test cases.

Record everything
  • checkpoint per turn: messages,
  • plan, step count, budgets
  • → resume after a crash or
  • a long human pause
  • trace per step: prompt size,
  • model reply, tool, args,
  • result, latency, tokens, cost
  • debug failures by replaying

Numbers

Some ballpark numbers. A task takes anywhere from three to fifty turns. Each model call takes half a second to a few seconds. Typical step caps sit between fifteen and fifty. Cap a single tool result at a few thousand tokens. Start summarising when the context is around two thirds full. Treat three identical calls in a row as stuck. And keep sub-agent fan out small, two to five, because each one has its own full cost.

Numbers worth knowing
turns per task          3 - 50
model call              0.5 - 5 s
step cap (typical)      15 - 50
tool output cap         ~2-10k tokens
summarise when ctx      ~60-80% full
repeat detection        3 same calls
sub-agent fan-out       2 - 5

Say it like this

How to say it. A reason and act loop with a thirty step cap and a per task cost cap. Tool output is truncated and old turns are summarised when the context reaches seventy percent. Repeated calls are detected and trigger a re-plan. Risky tools pause for approval, state is checkpointed every turn so runs can resume, and every step is traced. That covers termination, cost, reliability and debugging in one breath.

In the interview
  • "A reason-act loop with a
  • 30-step cap and a cost cap.
  • Tool output is truncated and
  • old turns summarised at 70%
  • of the window. Repeats are
  • detected. Risky tools pause
  • for approval; state is
  • checkpointed every turn and
  • every step is traced."

Interview questions

What is the ReAct pattern?

Reason and act: the model alternates between reasoning about what to do, taking an action through a tool call, and observing the result, which is appended to the context before the next round. It lets each observation shape the next decision.

How do you decide when an agent loop should stop?

Stop on a final answer, ideally confirmed by an automatic check, and also on hard limits for steps, time and cost, on detected lack of progress, or when the model needs input from a person. Always return the reason it stopped.

Why does the cost of an agent run grow faster than the number of steps?

The model is stateless, so each turn resends the whole history, which grows every turn. Total tokens grow roughly with the square of the step count unless you trim results, clear old ones, summarise and use prompt caching.

How do you keep the context window under control in a long run?

Truncate large tool outputs, drop tool results once they have been used, summarise early turns into a progress note, keep notes and plans in files the agent can reread, and keep the stable prefix unchanged so it can be cached.

How can an agent detect that it is stuck?

Track recent tool calls and results; repeated identical calls, edit-and-revert cycles or many turns without new information signal a loop. Respond by telling the model to change approach, forcing a new plan, or stopping and asking a person.

When would you use sub-agents?

When a subtask needs lots of reading or searching that would flood the main context, or when independent subtasks can run in parallel. Each sub-agent has a fresh context and returns a short summary, at the price of more total tokens.

How do you support human approval in the middle of an agent run?

Treat the approval as a pause point: checkpoint the full state, notify the person, and resume from the same turn when they respond, adding their decision as an observation. The loop must not depend on in-memory state that a long pause would lose.

What should an agent trace contain?

For each step: the context size, the model reply, the tool name and arguments, the result or error, latency, tokens and cost, plus the final status and stop reason. Traces let you replay failures and turn them into evaluation cases.