The Agent Execution Loop
Watch a planner, tools, and memory work together in an animated graph.
Last lesson you learned that an agent is a model in a loop. Now let's slow that loop down until you can see every gear turn: how a goal becomes a plan, a plan becomes tool calls, results become memory, and memory becomes either another lap around the loop — or the final answer.
Why does this exist?
"The agent did something weird" is the least debuggable sentence in AI engineering — unless you can replay its execution step by step. Every agent trace, whatever the framework, decomposes into the same stations: plan, select tool, execute, store, reason, loop-or-answer. Internalize that graph and any agent log becomes readable.
The problem: what actually happens between question and answer?
Ask an agent "What's the weather in Paris and should I pack an umbrella?" and a couple of seconds later an answer appears. In between, the model made several full decisions — each one a separate LLM call with a growing context. If you can't see those intermediate decisions, you can't fix them. So let's watch them.
Watch the loop run
Press Play (or click Step to advance manually — that's the better way to really see it). The graph highlights whichever station is active while the scratchpad below types out exactly what the agent is thinking, calling, and observing. Note the dashed edge from Reasoning back to Tool selection: that's the loop itself, and this scenario takes it twice.
The agent loop
Scenario: “What's the weather in Paris and should I pack an umbrella?”
Agent scratchpad
Press Play or Step to run the scenario…
Things to notice on a second run:
- The scratchpad is the agent's entire mind. Every thought, action, and observation is appended to one growing transcript — and that whole transcript is re-sent to the model on every iteration. There is no other hidden state.
- Tool selection is reading comprehension. The model picks
search_weatherovercalculatorpurely by reading tool names and descriptions. Vague descriptions are the number-one cause of wrong-tool bugs. - The loop decision is itself a model output. After the first observation (14°C, rain), the model chooses to loop again for tomorrow's forecast. Nothing in your code said "call the tool twice."
- Termination is a judgment call. The agent exits when the model decides it has enough information — which is why runaway agents happen, and why your loop needs a cap.
The stations, precisely
- User Goal. The task, stated as an outcome. Goes into the scratchpad first.
- Planner. A model call (or the opening phase of one) that decomposes the goal: what's needed, what order, which capabilities. In simple agents this is just the model's first "Thought."
- Tool selection. The model matches intent to one of the advertised tool schemas and produces a structured call with arguments.
- Tool execution. Your code runs — not the model. HTTP requests, database queries, shell commands. This is also where guardrails live: validation, timeouts, permission checks.
- Memory. The observation is written into the scratchpad (and, for long-lived agents, possibly summarized or stored externally — scratchpads that grow unbounded eventually overflow the context window).
- Reasoning. The model reads the updated scratchpad and decides: enough to answer, or another lap?
- Answer. The final synthesis, grounded in everything observed along the way.
Here's the same structure as code — one honest iteration of the loop with real message shapes:
messages = [
{"role": "system", "content": "You are a helpful assistant. Use tools when needed."},
{"role": "user", "content": "What's the weather in Paris and should I pack an umbrella?"},
]
resp = llm(messages, tools=[search_weather_schema])
# → resp.tool_call = {"name": "search_weather", "arguments": {"city": "Paris"}}
observation = run_tool(resp.tool_call) # your code: '14°C, light rain, 82% precip'
messages.append(assistant_tool_call(resp.tool_call)) # Action → scratchpad
messages.append(tool_result(observation)) # Observation → scratchpad
resp = llm(messages, tools=[search_weather_schema]) # Reasoning: loop or answer?
Run that second llm() call and the model might answer — or emit another tool call for tomorrow's forecast, exactly like the animation. The scratchpad (messages) is the loop's only state.
Failure modes to expect
Watch for these in real traces: loops (the agent calls the same tool with the same arguments repeatedly — deduplicate and cap iterations), error spirals (a failed tool call confuses the plan — return clear, structured error messages from tools), and context bloat (long scratchpads degrade reasoning and cost real money — summarize or truncate old observations).
Build it yourself
- Extend your mini-agent from last lesson to print a formatted trace:
THOUGHT:,ACTION:,OBSERVATION:lines, exactly like the scratchpad above. - Add a second tool (
get_forecast(city, day)) and pose the Paris umbrella question. Does your agent take the loop twice, like the animation? - Add loop detection: if the same tool is called with identical arguments twice in a row, inject the message "You already tried that — try something else or answer with what you have." Watch behavior change.
- Cap the loop at 5 iterations and make the agent return its best-effort answer when the cap hits.
Summary
- The agent loop decomposes into stations: goal → plan → tool selection → tool execution → memory → reasoning → loop or answer.
- The scratchpad transcript is the agent's entire state, re-sent to the model every iteration.
- Tool selection is driven by tool names and descriptions; tool execution is your code and your guardrail point.
- Loop-or-answer is a model judgment — so wrap it in loop detection, iteration caps, and clear tool error messages.
- Being able to read a trace station-by-station is the core skill of debugging agents.