Agents & Agentic AI· Lesson 3 of 6
- 1What Is an Agent?8 min
- 2The Agent Loop9 min
- 11The ReAct Pattern — The Agent That Thinks Before It Acts8 min
- 20Reflection — The Agent That Reviews Its Own Work9 min
- 21The Evaluator–Optimizer Pattern — One Builds, One Judges9 min
- 22What Do You Build Agents With? — No Framework, LangGraph, CrewAI, or AutoGen?10 min
The ReAct Pattern — The Agent That Thinks Before It Acts
In one line · **ReAct makes the agent write a short reasoning step before every tool call and read the result before deciding the next one — so instead of acting blindly, it course-corrects as it goes.**
- An agent that fires off tools without thinking fails in three ways: wrong tool, wrong order, and completely ignoring the results coming back.
- ReAct = Reasoning + Acting: think ("what do I need right now?") → execute a tool call → read the result (Observation) and adjust → think again... until the answer.
- The thinking step isn't a luxury: when the model writes down what it needs before deciding, its choice of the next tool is grounded in information — not a guess.
- ReAct is not a new invention layered on top of the agent loop — it's a prompt-level recipe for the same loop you already know, just out loud.
- The cost is real: every Thought is billed in tokens. On multi-step tasks it pays for itself; on a single obvious tool call — save your money.
Picture this: an agent receives a two-part question and has 6 tools available. Without a thinking step, it does one of three things — all seen in real project reviews: it calls a tool that needs a parameter it hasn't fetched yet and gets an error, it answers half the question and ignores the other half, or — worst of all — it takes the error and retries the exact same call with the exact same inputs, because there is no moment where it asks itself "so what did that result change?"
Multi-step tasks are precisely the difference between a real agent and a chatbot with decorations. ReAct is the best-known and simplest pattern that makes those steps work — which is why "explain ReAct" has become a near-standard question in agent engineering interviews.
Before each step, the agent writes itself a line: "What do I need right now, and why?" After each tool, it reads the result and asks: "How does this change my plan?"
That thinking happens inside the conversation itself — ordinary text the model generates — not an external component or magic. Simply forcing the model to think in writing improves its decisions.
A rushed repair technician opens the machine and swaps parts on hope: maybe the fan, maybe the power supply. Every wrong part costs time and money.
The skilled technician works differently: "The noise is coming from the fan area — I'll measure the temperature first" → measures → "Temperature is normal? Then it's not a cooling problem; let me check the bearings" → every step builds on the previous result.
Both have the same toolbox. The difference is that the first one just executes, while the second thinks → acts → observes → adjusts. ReAct turns your agent from the first into the second.
A real example
The question: "What's the price of product X, and is it in stock at the Riyadh branch?"
Without ReAct, the typical failure looks like this:
Action: get_inventory(product_id=???, branch="Riyadh")
Observation: Error: product_id is required
Action: get_inventory(product_id=???, branch="Riyadh") ← same mistake againWith ReAct — same model, same tools:
Thought: The question has two parts: the price, and stock at the Riyadh branch.
Both need the product ID first — I'll search for the product.
Action: search_products(query="X")
Observation: [{id: "P-1042", name: "X", price: 249 SAR}]
Thought: I got both in nearly one shot: the price is 249 SAR,
and I have the ID. All that's left is Riyadh stock.
Action: get_inventory(product_id="P-1042", branch="Riyadh")
Observation: {branch: "Riyadh", in_stock: true, quantity: 7}
Thought: I have both parts of the answer. I can respond.
Answer: Product X costs 249 SAR and is currently in stock at the Riyadh branch (7 units).Note the second Thought: the model noticed that the price came back with the search result, so it didn't make a second tool call for the price. That is exactly the money the thinking saves.
The cycle has three beats, repeated until the model decides it's ready to answer:
1. Thought — think: the model writes a line or two: what's required, what I have, what's missing. 2. Action — act: based on the thought, it requests a specific tool call — and your code is what actually executes it. 3. Observation — observe: the tool result returns to the conversation and feeds raw into the next Thought.
Why does this move work at all? Two reasons:
- Thinking improves tool selection. When the model writes "I'm missing the product ID" before choosing, the choice is conditioned on that sentence — so it picks
search_products, notget_inventory. Without it, it picks whichever tool looks most like the question. - The Observation corrects the course. An empty result, an error, a surprising number — these are all information. In ReAct they have an official place in the cycle, so the plan adjusts instead of the agent barreling down the wrong track.
Diagram
┌──────────────────────────────────────────────┐
│ │
▼ │
┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ Thought │ ───► │ Action │ ───► │ Observation │ ──┘
│ "what │ │ (tool │ │ (read the │
│ next?" │ │ call) │ │ result) │
└─────────┘ └─────────┘ └─────────────┘
│
│ "I have everything I need"
▼
┌─────────┐
│ Answer │
└─────────┘The arrow looping back from Observation to Thought is the whole pattern: the result is read before the next decision. If that arrow is broken in your system, you have an agent that executes — not an agent that thinks.
First thing to nail down: ReAct is the agent loop you already know — perceive → decide → act → observe — with one difference: the decide step becomes explicit text the model writes instead of happening implicitly inside the model. So ReAct isn't a new architecture to build — it's a prompting style for the same loop.
Second, for historical context: the original paper (2022) predates native tool calling in the APIs — they wrote Action: search[...] as plain text and parsed it. Today the Action comes out structured from the API itself, and the Thought is text preceding the call. The syntax changed — the idea is intact.
Third: Thoughts are an internal channel. Their natural home is the trace and the logs — and they are the best debugging gift you'll get: when the agent goes wrong, you read its reasoning and learn why it went wrong, not just that it did.
The code
The whole loop, with any provider and any SDK — read it as runnable pseudocode:
SYSTEM = """You are a store assistant. Solve the task step by step.
Before every tool call, write ONE short "Thought:" line stating
what you need next and why. After each tool result, reconsider
your plan before acting again. Thoughts are internal — never
show them to the user. Answer only when you have everything."""
steps = 0
while steps < MAX_STEPS: # always cap the loop!
response = llm.generate(messages, tools=TOOLS)
messages.append(response) # keep the Thought in context
if not response.tool_calls:
break # model produced the Answer
for call in response.tool_calls:
result = execute(call) # your code does the acting
messages.append(tool_result(call, result)) # the Observation
steps += 1Three things to watch:
- `MAX_STEPS` is not a detail — it's the guardrail that stops "The agent that never stops" challenge from happening to you in production.
- The Thought is kept in context (
messages.append(response)) — so earlier reasoning stays in front of the model and gets built on. - The Observation comes back in full, including error messages — the model won't fix a mistake it can't see.
Exercise (20 minutes): Take a simple agent you have — or your Research Agent project's agent if you've started it — and run it on a two-part compound question, twice: once with no thinking instructions, and once after adding the line "Before every tool call, write one short Thought line" to the system prompt. Compare the traces: number of calls, wrong calls, and total tokens. The difference you'll see with your own eyes beats any explanation.
| # | ❌ Wrong | ✅ Right |
|---|---|---|
| 1 | Thoughts leak to the user: "Thought: the user probably means..." shows up in chat | Separate the channels: reasoning goes to the trace; the user sees a short status ("Checking inventory...") |
| 2 | A loop with no cap: the agent spins and spins while the bill counts up | MAX_STEPS + a per-task cost cap — from day one, not after the first incident |
| 3 | Novelistic thinking: the model writes an essay before every call and you pay for it | Specify in the system prompt: "ONE short Thought line" — a line that steers the decision, not a diary entry |
| 4 | Truncating Observations: you send the model "Error" with no details | Pass the full error message — it is the raw material for course correction |
| 5 | ReAct on everything: even "what's the weather?" gets a Thought and an Observation | One obvious tool = direct call. Save the ceremony for tasks that earn it (see "When not to") |
Misconceptions
1) "ReAct is the same thing as reasoning models" — ❌ Reasoning models think internally before responding. ReAct is a pattern at your application level: it structures thinking between tool calls and makes it part of the trace. They complement each other — and if you're running a strong reasoning model, you can lighten the explicit Thought format and leave the thinking internal. Experiment and measure.
2) "If it's thinking, the answer must be right" — ❌ A Thought can be beautifully argued — and wrong. Written reasoning improves the odds; it doesn't guarantee the outcome. That's why evaluation is still mandatory, and a trace with convincing reasoning needs review like any other trace.
Cost considerations
Every Thought is billed twice: as output when written, and as input on every subsequent call — because the full context is sent each time. Let's run the numbers on our inventory question (3 cycles + answer):
> 💸 Numbers subject to change *(Volatile fragment — illustrative pricing; check your provider's current rates)*: $3/million input tokens and $15/million output tokens.
| Item | Calculation | Cost |
|---|---|---|
| One direct call (if the task had been simple) | Input 900 + Output 150 | ~$0.005 |
| ReAct with four calls | Cumulative input: 900 + 1,150 + 1,400 + 1,650 ≈ 5,100 → $0.015; Output: 3 cycles × ~80 + answer ~150 ≈ 390 → $0.006 | ~$0.021 |
So ReAct here is roughly 4× the cost of a single call. At 20,000 questions a month: ~$420 instead of ~$105.
But make the right comparison: a multi-step task doesn't get solved at all in one call — so that 4× is the price of the job getting done, not a luxury. The correct engineering question isn't "is ReAct expensive?" — it's "does this task actually need multiple steps?" If not, go back to mistake #5 above.
- A compound question with parts: each part needs a different tool, and the order matters.
- Tools that depend on each other's results: the search result is the input to the next query.
- An environment with unpredictable results: web search, external APIs, possibly incomplete data — someone has to "read and adjust."
- You want intelligible traces: written reasoning = dramatically easier debugging — you see why the agent chose what it chose.
- One obvious tool: "How many vacation days do I have left?" = one known call. A Thought here is tokens for nothing — skip the ceremony.
- Steps known in advance and fixed: if you can draw the flow on paper before running it, build a plain workflow — cheaper, more reliable, easier to test.
- Latency-critical paths: every ReAct cycle = a full LLM call. On a hot user path, every Thought is felt.
- No tools at all: plain conversation or direct text generation — no actions means no ReAct.
💡 Tip: The practical rule: are the steps decided by the results? ReAct. Are the steps known before running? Workflow. Not sure? Start with a workflow — and revisit the "Agents vs. workflows vs. chains" concept if you get stuck.
- Inside BuildAI (in order): Planning intro → Agent reliability → Agent cost management.
- The original paper: *"ReAct: Synergizing Reasoning and Acting in Language Models"* — Yao et al., 2022. Read it as the history of the idea, not as an implementation guide — it predates native tool calling, and you'll notice how much of the pattern today's APIs have absorbed.