Module 8 of 12 · Open Source Track
The ReAct Agent Loop

Reason → Act → Observe → Repeat — the thinking/action cycle that turns a one-shot responder into an autonomous problem-solver. Six concepts, from what an agent even is to knowing when it should stop.

Track 4: Agent Architectures ⏱ ~19 min read 8 / 12
Concept 1 of 6

A chatbot answers; an agent acts

A chatbot is one message in, one response out. It uses only what it already knows, finishes in a single call, and takes no actions in the world. Great for Q&A, summaries, and generation.

An agent takes a goal and runs multiple reasoning/action cycles: it calls real tools (search, code, APIs), observes what comes back, adapts its plan, and continues for as many turns as the task needs. That looping autonomy is exactly what the ReAct pattern gives the model.

Concept 1 of 6

Script-reading operator vs. project manager

BEFORE: Picture a help desk where the operator can only answer from a fixed script. You ask "is my order shipped?" and they read back one field. One question in, one answer out, every time.

PAIN: If your order is late, needs rerouting through a different carrier, and the new tracking number needs emailing to you, the operator says "I can't help with that, call three departments." Each has its own single-answer script.

MAPPING: A chatbot is that operator. An agent is a project manager: it takes your request, decides the steps, calls the right departments in sequence, waits for each result, and returns with a complete solution.

Concept 1 of 6

What separates them

  1. TurnsChatbot: exactly one. Agent: as many reasoning/action cycles as the goal requires.
  2. ToolsChatbot has none. An agent calls real tools — search, code, database, APIs.
  3. AdaptationAn agent reads each tool result and adjusts its plan; a chatbot can't observe or react.
  4. FitChatbot for Q&A and generation; agent for research, automation, multi-step workflows.
Concept 1 of 6

Chatbot or agent?

IF task is one-shot Q&A,
   summary, or generation:
  → a plain CHATBOT call is enough

IF task needs live data,
   real actions, or many steps:
  → build an AGENT (ReAct loop)

IF unsure:
  → start simple; add the loop
     only when one call can't finish

# agents cost more tokens + latency —
# don't loop when you don't have to
Concept 1 of 6

Misconceptions

"An agent is just a chatbot with a bigger prompt."
No. The difference is structural: an agent runs a loop, calls tools, and observes results. A longer prompt still gives you one shot with no actions.
"Agents are always better, so use one everywhere."
For a simple lookup or summary, a chatbot is faster and cheaper. Reach for an agent only when a single call genuinely can't complete the task.

A chatbot responds once with no tools; an agent loops through reason/act/observe cycles using real tools. ReAct is the pattern that makes that loop work.

Match the shape to the task: one-shot → chatbot, multi-step with live data → agent.

Concept 2 of 6

Reasoning + Acting, fused

ReAct (Reasoning + Acting) is a pattern where the model alternates between explicit reasoning ("Thought: ...") and tool actions ("Act: ..."), reads the result, and repeats until the task is done.

The key insight from the original research: a model that reasons out loud before acting makes far fewer errors than one that answers blindly or calls tools silently. The thought step isn't decoration — it's the mechanism that improves quality.

Concept 2 of 6

Two broken ways to work a case

BEFORE: One detective solves cases purely in their head, never checking a record — that's pure reasoning with no tools. Another blindly pulls files with no theory of the case — that's silent tool use.

PAIN: The head-only detective invents facts they can't verify. The file-puller grabs the wrong records because nothing guided the choice. Both dead-end constantly.

MAPPING: ReAct is the detective who thinks, then acts, then thinks again: "I suspect X, so I'll pull this record" → reads it → "that rules out X, now check Y." Reasoning picks the action; the action informs the next reason.

Concept 2 of 6

Why fusing both wins

  1. Pure reasoning failsAll thought, no tools — the model hallucinates facts it can't check.
  2. Silent tool use failsTools with no reasoning — the model picks the wrong action with no plan.
  3. Thought narrows the actionThe "Thought:" step plans, spots gaps, and decides which tool fills them.
  4. Observation feeds the next thoughtReading the result updates the plan, then the loop continues — informed, not blind.
Concept 2 of 6

Pseudocode

# one ReAct step, in the model's voice
Thought: what do I know? what's missing?
         which tool fills the gap?
Act:     call_tool(name, arguments)
Observe: read the tool's result
Thought: did that answer it, or do I
         need another action?

# reasoning chooses the action;
# the observation shapes the next thought.

Smaller local models benefit most from an explicit Thought step — it keeps their tool choices grounded.

Concept 2 of 6

Misconceptions

"ReAct is a library I install."
It's a pattern, not a package. You implement it with the chat API and a loop — nothing to pip install named "react."
"The 'Thought:' text is just filler I can drop."
The thought is the mechanism. Cutting it measurably lowers accuracy — studies showed 10–40% better task completion versus silent tool use.

ReAct interleaves explicit reasoning with tool actions. Thinking before acting is what makes the difference, not any framework.

A few hundred tokens of thought buys fewer dead-end calls, fewer hallucinations, and traces you can debug.

Concept 3 of 6

Four phases that repeat

The loop has four phases. Reason: the model thinks out loud about what it needs next. Act: it calls a tool. Observe: it reads the tool result. Repeat until it has enough to give a final answer.

Understanding each phase is what lets you debug and extend an agent. The single most important detail: the loop continues or ends based on a structured signal from the API (finish_reason), not on guessing from the model's words.

Concept 3 of 6

A cook tasting between steps

BEFORE: Imagine a cook who dumps every ingredient in at once and serves without ever tasting — no checking, no adjusting along the way.

PAIN: If the sauce is too salty or the pasta underdone, they only find out when the plate comes back. There's no chance to course-correct mid-cook.

MAPPING: The ReAct loop is a cook who tastes between steps: decide what's needed (Reason), add it (Act), taste (Observe), then decide again (Repeat) — stopping only when the dish is right.

Concept 3 of 6

One trip around the loop

  1. ReasonThe model writes a Thought: what it knows, what's missing, which action helps.
  2. ActIt calls one tool with specific arguments — the moment it touches the real world.
  3. ObserveYour code appends the tool result as a tool message; the model reads it next turn.
  4. Repeat or stopIf finish_reason == "tool_calls" keep going; if "stop", return the final answer.
Reasonplan the next move
Actcall a tool
Observeread the result
Concept 3 of 6

Pseudocode

history = [ user_goal ]

LOOP:
  reply = model.call(history, tools)

  IF reply.finish_reason == "stop":
    RETURN reply.text        # done

  history.append(reply)         # the Act
  FOR each tool_call IN reply:
    result = run_tool(tool_call)
    history.append(result)      # the Observe
  # loop back → next Reason
Concept 3 of 6

Misconceptions

"The model runs the tools itself."
It doesn't. The model requests a tool; your code executes it and hands back the result as a tool-role message. The Observe phase is the model reading that.
"I detect 'done' by reading the model's text."
Fragile. Check finish_reason: tool_calls means continue, stop means finished. Text phrasing changes every run.

Reason → Act → Observe → Repeat, driven by finish_reason. Your code runs tools and appends results; the model reads them.

Let the structured signal control the loop — never parse natural language for "task complete."

Concept 4 of 6

A while-loop over the chat API

ReAct isn't a framework — you implement it directly with the chat API you already know. The core is the agentic loop: a while loop that keeps calling the model until finish_reason == "stop".

Each iteration appends both the model's tool calls and your tool results to a growing history list. That list is the agent's memory: the stateless API becomes stateful because your code keeps the conversation.

Concept 4 of 6

A dispatcher with a running logbook

BEFORE: Imagine a dispatcher whose radio wipes its memory after every single call — each transmission arrives with zero context about what came before.

PAIN: If the dispatcher keeps no record, every driver has to re-explain the whole job each time. Nothing accumulates; coordination collapses.

MAPPING: The agent loop is the dispatcher's logbook. Every request and every reply gets written down, and the full log is read aloud before each new call — so the "forgetful radio" (the model) always has the whole story in front of it.

Concept 4 of 6

Building the loop

  1. Define toolsGive each a name, a clear description (drives tool choice), and an input schema.
  2. Call the modelSend the history plus the tool list; the model reasons and maybe requests tools.
  3. Check finish_reasonIf stop, return the answer. Otherwise append the assistant message and continue.
  4. Execute & appendRun each tool, append results as tool-role messages, loop again.
Concept 4 of 6

Pseudocode

history = [ user_question ]
turns = 0

WHILE turns < max_turns:      # safety net
  turns += 1
  reply = model.call(history, tools)

  IF reply.finish_reason == "stop":
    RETURN reply.text

  history.append(reply)
  FOR call IN reply.tool_calls:
    TRY: out = run_tool(call)
    CATCH err: out = {error: err}  # never crash
    history.append(tool_result(out))
Concept 4 of 6

Misconceptions

"A tool error should throw and stop the agent."
Return the error as a structured tool result instead. The model can then read it and try a different approach — crashing kills that chance.
"Tool descriptions are just documentation."
The model reads descriptions to decide when to call each tool. Vague descriptions cause wrong tool selection — they're part of the logic.

The agentic loop is a while-loop that appends every tool call and result to a growing history. That history is the memory that makes reasoning work.

Write sharp tool descriptions and always return errors as structured results, never exceptions.

Concept 5 of 6

Notes the agent leaves itself

Thought traces are the "Thought: ..." paragraphs the model writes before each tool call. They look optional, but they're what keeps multi-step reasoning coherent.

Because each thought becomes part of the conversation history, later steps can reference earlier ones. The model is leaving notes for itself — anchoring every action to the original goal instead of acting in a vacuum.

Concept 5 of 6

The researcher's notebook

BEFORE: Picture a researcher who opens a paper, reads a paragraph, closes it, opens the next, reads, closes — taking no notes and drawing no connections between sources.

PAIN: By the third paper they've forgotten the first. They can't synthesize, because nothing they read got linked to anything else. The report is fragments.

MAPPING: Thought traces are the notebook: "After paper 1, I understand X and still need Y." That note travels into every later step, so the final answer can synthesize across everything read.

Concept 5 of 6

What thought traces do

  1. Anchor to intentEach thought restates the goal, so the model doesn't drift across many tool calls.
  2. Carry context forwardBecause thoughts live in history, step 3 can build on what step 1 concluded.
  3. Enable debuggingWhen an answer is wrong, the trace shows exactly where — bad tool, bad query, or misread result.
  4. Provide an audit trailEvery action has a recorded "why" right before it — essential in production.
Concept 5 of 6

Pseudocode

# the system prompt asks for this shape:
Turn 1:
  Thought: goal = compare LLMs; I need
           current benchmark scores first
  Act: web_search("LLM leaderboard 2025")

Turn 2:
  Observe: got scores, missing pricing
  Thought: goal was a COMPARISON, so I
           still need pricing → search it
  Act: web_search("LLM pricing 2025")

# each thought re-anchors to the goal.
Concept 5 of 6

Misconceptions

"Thought traces just waste tokens."
They cost a few hundred tokens but prevent dead-end calls and hallucinations, and give you a debuggable trail. The reliability gain usually outweighs the cost.
"Traces are only useful during development."
In production they're your audit log. When someone asks "why did the agent delete that file?", the thought before the action is your answer.

Thought traces are the agent's working memory and audit trail. They keep reasoning anchored to the goal across many turns.

Instruct the model to write a Thought before each act and after each observation — then keep those traces in your logs.

Concept 6 of 6

Let the model decide, keep a safety net

The correct primary stop signal is finish_reason == "stop": the model decides it has enough and stops requesting tools. That's the natural terminator of a ReAct loop.

An iteration cap (max_turns) is a safety net, not the control mechanism. Set it high enough that real tasks finish (often 20–50), and handle the cap-exceeded case explicitly instead of silently returning half an answer.

Concept 6 of 6

The circuit breaker in your house

BEFORE: Imagine wiring your kitchen so appliances only turn off when the breaker trips — using the emergency cutoff as the everyday on/off switch.

PAIN: You either trip it too early (dinner half-cooked) or the wiring runs hot before it ever trips. The safety device was never meant to run the kitchen.

MAPPING: finish_reason is the normal light switch — the agent stops when the job's done. max_turns is the breaker: it exists for when something goes wrong, not to run the loop day to day.

Concept 6 of 6

Stopping, done right

  1. Primary: finish_reasonWhen it's stop, the model chose to finish — return its text.
  2. Safety net: max_turnsA high cap that only fires when something is wrong, never as routine control.
  3. Handle the cap loudlyIf you hit it, log it and notify — don't silently return partial results.
  4. Investigate repeatsHitting the cap often signals a design flaw: no "enough info" signal or too-open goals.
Concept 6 of 6

How should I stop the loop?

IF finish_reason == "stop":
  → done — return the final answer

IF finish_reason == "tool_calls":
  → run tools, then loop again

IF turns >= max_turns:
  → SAFETY trip — log + surface it,
     do NOT return silent partial data

# never: scan text for "I'm done"
# never: use a small cap as main control
Concept 6 of 6

Misconceptions

"Stop when the text says 'task complete.'"
Parsing natural language is fragile — phrasing varies every run, and the words can appear mid-thought. Use finish_reason instead.
"Cap iterations at 10 and call it done."
A small cap is too few for complex tasks and wasteful for simple ones. Worse, hitting it returns a half-finished result with no flag.

Stop on finish_reason == "stop"; treat max_turns as a breaker, not a switch. The model decides completion; the cap only catches runaways.

When the cap trips, surface it — regular trips mean your agent design needs fixing.

Test the core insight

One question per concept. Tap to reveal the answer.

Open the full module on desktop

The desktop version has you implement the full ReAct loop by hand against a local model — tool definitions, the agentic while loop, thought-trace prompting, and correct stop logic — then compare it to the Agent SDK, in Python and TypeScript.

Open M12 on Desktop → Chatbot vs agent · ReAct loop · tool use · thought traces · stop conditions

Module 8 of 12 · Open Source Track · Agent Architectures