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: 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.
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.
- • 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.
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.
- 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.
- • 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.
- 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.
- 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.
- • 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.
- 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.
- 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.
- 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.
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.
- "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."