Build an AI agent from scratch: the ReAct loop explained, plus planning, reflection and the failure modes that sink real agents.
Intro
A Model Inside a Loop
Ask a chatbot a question that needs three lookups and a calculation, and it will often answer confidently and wrongly. Ask an agent the same thing, and it searches, reads, calculates, checks itself, and then answers. What changed is not the model. It is the loop wrapped around it.
- One shot versus a loop
- Same model, different wrapper
From One Call to a Loop
In the last lesson we taught a model to call tools: it emits a structured request, your code runs it, and the result goes back in. That was a single step. Today we chain those steps into a loop, and then add planning and reflection so the loop is smart instead of just busy.
- Lesson 27: single tool call
- Today: loop, plan, reflect
Today's Roadmap
Here is the roadmap. First the intuition and the ReAct pattern. Then a worked trace. Then planning, reflection, and the ways agents fail, with real numbers. Finally we write a working agent from scratch in plain Python, about sixty lines, and add a reflection step.
- ReAct, trace, planning
- Reflection and failures
- Code from scratch
The ReAct Pattern
Think, Act, Observe, Repeat
Think about how you fix a leaking tap. You do not plan every move in advance. You look, you guess the washer is worn, you take the tap apart, you see what you find, and you adjust. Think, act, observe, repeat. Agents work the same way, because the world often surprises you after the first step.
- Look, guess, try, adjust
- The world surprises you
Thought, Action, Observation
ReAct, short for reasoning and acting, formalises this. At each step the model writes a Thought, which is free text reasoning about what to do next. Then an Action, which names a tool and its arguments. Your code runs the tool and appends an Observation, the tool's output. The three lines go back into the prompt, and the model continues.
- Thought: free-text reasoning
- Action: tool name plus arguments
- Observation: tool output
Why It Works
Why does writing the thought help? As in the chain of thought lesson, the model computes better when it spells out intermediate steps, and here those steps also decide which tool to call. The observation matters just as much: it injects fresh, real information, so the model stops guessing and starts grounding itself in facts.
- Thoughts steer tool choice
- Observations ground the model
Stop Conditions
A loop needs a way to stop. There are three stop conditions. First, the model emits a final answer, usually through a special finish action. Second, a step limit is reached, say ten steps. Third, a budget runs out, such as tokens, dollars, or wall clock time. Without at least the second and third, a confused agent will happily run forever.
- Final answer action
- Step limit
- Token, cost or time budget
The Loop Is Just a Transcript
Under the hood, the loop is just string and message management. The model sees the system prompt, the task, and the growing history. It produces one step. You parse it, run the tool, append the result, and call the model again. The model has no hidden state. Everything it knows about the run is in that history.
- History grows each step
- No hidden state
A Worked Trace
A Multi-Step Question
Let us trace a real question. Take: what is the population of the capital of the country that hosted the 2016 Summer Olympics, divided by one thousand? No single lookup answers it. The agent needs to find the country, then the capital, then the population, then do arithmetic.
- Population of the capital
- of the 2016 Olympic host
- divided by one thousand
Step 1: Find the Country
Step one. Thought: I need the host country of the 2016 Summer Olympics. Action: search for 2016 Summer Olympics host. Observation: Rio de Janeiro, Brazil. The agent now knows something it could not have planned around, and the first box lights up.
- Search: 2016 Olympics host
- Observation: Brazil
Step 2: Check the Capital
Step two. Thought: the capital of Brazil is not Rio, so I should check rather than assume. Action: search for capital of Brazil. Observation: Brasilia. Notice the thought caught a tempting mistake. This is the value of reasoning written out in the open.
- Rio is not the capital
- Observation: Brasilia
Steps 3 and 4: Look Up, Compute
Step three. Thought: now I need the population of Brasilia. Action: search for Brasilia population. Observation: roughly two point eight million people. Step four. Thought: divide by one thousand. Action: calculator with two million eight hundred thousand over one thousand. Observation: two thousand eight hundred.
- Population about 2.8 million
- 2,800,000 / 1,000 = 2,800
Step 5: Finish
Step five. Thought: I have everything I need. Action: finish, with the answer about two thousand eight hundred. Five model calls, three tool types, one grounded answer. Notice the arithmetic went to a calculator, not the model, because models are unreliable at exact arithmetic on larger numbers.
- Finish action ends the loop
- Use tools for exact arithmetic
Planning
Two Planning Styles
So far the agent decided one step at a time. That is the interleaved style. The alternative is plan-then-execute: the model first writes a complete plan, a numbered list of steps, and then the system executes it. Each style has a place, and the choice is a real design decision.
- Interleaved: decide each step
- Plan-then-execute: plan once
Plan-Then-Execute Tradeoffs
Plan-then-execute has real advantages. The plan is inspectable, so a human or a checker can approve it before anything runs. It is cheaper, because the model does not have to re-reason from scratch at every step. And it keeps a long task organised. The weakness is brittleness: if step two returns something unexpected, steps three to six may be wrong.
- Inspectable and cheaper
- Brittle when surprised
Interleaved and Hybrid
Interleaved planning adapts after every observation, which is why it handles exploration well, like debugging or research. Its weakness is myopia. Without a global plan, the agent can wander, repeat itself, or forget the original goal. Many good systems combine the two: write a short plan, then execute it interleaved, updating the plan as the evidence comes in.
- Adapts to surprises
- Can wander without a plan
- Hybrid: plan, then adjust
Task Decomposition
Underneath both is task decomposition: break a big goal into sub-goals that are small enough for one tool call or one reasoning step. Write a report on competitor pricing becomes: list competitors, fetch each pricing page, extract numbers, build a table, write the summary. Good sub-goals are concrete, verifiable, and ordered by their dependencies.
- Split goals into sub-goals
- Concrete and verifiable
- Order by dependencies
Replanning on Failure
Plans fail, so you need replanning. When a tool errors or an observation contradicts the plan, feed that failure back and ask the model to revise the remaining steps. A simple rule works well: after any failed step, show the model the original goal, the current plan, and the failure, and request an updated plan. Do not let it blindly continue.
- Detect a failed step
- Show goal, plan and error
- Request a revised plan
Reflection
Self-Critique
Planning looks forward. Reflection looks back. The simplest form is self-critique: after producing a draft or an answer, ask the model to review it. Does it answer the question? Are the numbers consistent with the observations? Then revise. This catches careless slips, but a model critiquing its own work with no new information has limits, since the same blind spots produce the same errors.
- Draft, critique, revise
- Catches careless slips
- Limited without new information
Feedback From Verifiers
The stronger form brings in external feedback. A coding agent writes a function, runs the tests, and reads the failures. A SQL agent runs the query and reads the error message. A verifier, whether a test suite, a compiler, or a schema checker, gives a signal the model cannot fake. Reflection with ground truth feedback is far more reliable than reflection alone.
- Tests, compilers, schema checks
- Ground truth beats opinion
Reflexion-Style Retries
Reflexion is a well known pattern built on this idea. After a failed attempt, the agent writes a short verbal lesson, such as: I forgot to handle an empty list. That note is stored and placed in the prompt for the next attempt. No weights change. The model improves across retries purely because it can read what went wrong last time.
- Attempt, fail, write a lesson
- Lesson goes into next prompt
- No weights change
Reflection Rules of Thumb
A few practical rules. Keep the reflection short and specific, not a vague apology. Cap retries, often at two or three, because gains shrink while cost keeps growing. And always judge by the verifier, not by the model's own confidence, since a model can sincerely declare success on code that fails the tests.
- Short, specific lessons
- Cap the retries
- Trust the verifier
Failure Modes
Failure One: Loops
Now the part tutorials skip: agents fail, and in predictable ways. The first is looping. The agent calls the same search with the same arguments, gets the same useless result, and tries again. It looks busy, but it makes no progress. A cheap defence is to detect repeated identical actions and either stop or force a different approach.
- Same action, same result
- Detect repeated calls
Compounding Errors: 0.95 to the 10
The second failure is compounding errors, and the math is sobering. Suppose each step is correct ninety five percent of the time. Over ten independent steps, the chance everything is right is zero point nine five to the power of ten, which is about zero point six. Only sixty percent. At twenty steps it is about thirty six percent.
- 0.95^10 is about 0.60
- 0.95^20 is about 0.36
Small Gains Compound Too
Raise per-step reliability to ninety nine percent and ten steps succeed about ninety percent of the time. Drop to ninety percent per step and ten steps succeed only about thirty five percent. Small per-step gains matter enormously. The real situation is a little kinder, since agents can recover from some errors, but the lesson stands: shorter, more verifiable trajectories win.
- 0.99^10 is about 0.90
- 0.90^10 is about 0.35
- Shorter paths win
Failure Three: Tool Misuse
The third failure is tool misuse. The model invents an argument that does not exist, passes a string where a number is required, or picks the wrong tool entirely. Defences include strict argument schemas, clear tool descriptions with examples, and returning readable error messages as observations, so the model can correct itself on the next step.
- Hallucinated or bad arguments
- Validate against a schema
- Return readable errors
Failure Four: Cost Blowups
The fourth is cost blowup. Every step resends the entire history. Suppose each step adds one thousand tokens. Step one sends about one thousand, step twenty sends about twenty thousand. Summed across twenty steps, that is one thousand times twenty times twenty one over two, so two hundred and ten thousand input tokens, from what felt like twenty small calls.
- History is re-sent every step
- 20 steps: 210,000 input tokens
- Cost grows quadratically
Python From Scratch
The Four Pieces
Time to build it. We need four pieces: a tool registry, a prompt that teaches the format, the loop with a step limit, and trace logging. Everything is provider neutral. We assume a function called call underscore llm that takes a list of messages and returns text. Swap in any API you like.
- Tool registry
- Prompt format
- Loop with step limit
- Trace logging
A Tool Registry
First the registry. It is just a dictionary mapping a tool name to a Python function. We register a search function, which returns a short string, and a calculator that evaluates arithmetic safely. Notice every tool returns a string, and we will truncate it, because the model reads text, and text has a token cost.
- Dict from name to function
- Every tool returns a string
- Truncate outputs
The System Prompt
Now the prompt. The system message lists the tools, then demands a strict format: a line starting with Thought, then a line starting with Action holding a tool name and a JSON argument, or the word finish with the answer. We also tell the model to stop after the action line and wait. Your code supplies the observation, never the model.
- List tools and arguments
- Strict Thought/Action format
- Model must not invent observations
The Agent Loop
Here is the loop. For step in range of max steps: call the model, log the text, parse the action. If the action is finish, return the answer. Otherwise look the tool up in the registry, run it inside a try block, truncate the output, append it as an Observation message, and go around again. If the loop ends without finishing, return a clear failure, not silence.
- for step in range(max_steps)
- try/except around tools
- Return failure if out of steps
Trace Logging
Trace logging is three lines but saves hours. At each step, record the step number, the thought, the action, the observation length, and the elapsed time and token estimate. Write it as JSON lines. When an agent misbehaves in production, this log is the only way to see where it went wrong, so treat it as a required feature.
- Log every step as JSON lines
- Step, action, size, time
- Your debugging lifeline
Adding a Reflection Step
Now add reflection. After the agent returns an answer, run a verifier. For a coding task, that is the test suite. For a factual task, it can be a second model call that checks the answer against the logged observations. If the check fails, build a short lesson from the failure, add it to the messages, and run the loop again, with a retry cap of two.
- Verify the answer
- Write a short lesson
- Retry with cap of two
Common Mistakes
Mistake: No Termination Guard
The first mistake is shipping without a termination guard. No step limit, no budget, no repeat detection. It works in the demo, then one odd input sends it spinning overnight and you wake up to a bill. Always set a maximum number of steps and a spending ceiling, and make the failure message tell the user what happened.
- Always set max steps
- Add a spending ceiling
- Report why it stopped
Mistake: Giant Tool Outputs
The second mistake is giant tool outputs. Imagine a fetch tool that returns an entire web page of fifty thousand tokens. It floods the context, buries the original question, and since history is resent each step, you pay for those tokens again and again. Truncate, summarise, or return the top few results, and tell the model how to ask for more.
- Floods the context window
- Paid again every step
- Truncate or summarise
More Traps to Avoid
A few more traps. Letting the model do arithmetic instead of using a calculator. Trusting the model's claim of success without a verifier. Giving the agent twenty overlapping tools when three would do. And building an agent at all when a fixed pipeline would solve the task. If the steps are known in advance, a plain workflow is cheaper and more reliable.
- Use tools for exact math
- Fewer, clearer tools
- Pipeline beats agent when steps are known
Recap and Next
Recap
Let us recap. ReAct is a loop of Thought, Action, Observation, which grounds the model in real results. Planning can be upfront, interleaved, or hybrid, with replanning when things break. Reflection works best with external feedback like tests, as in Reflexion. And remember the failure modes: loops, compounding errors, tool misuse, and cost blowups.
- ReAct: think, act, observe
- Plan, then replan
- Reflect with verifiers
- Guard against failures
Next: Agent Memory
Our agent has a weakness, though. Its only memory is the transcript, which grows, costs more, and eventually overflows the context window. In the next lesson we tackle agent memory: short term, long term, and how to manage context so long running agents stay sharp.
- Transcript growth is a problem
- Next lesson: memory
- Subscribe for more