Begin with the problem
ReAct alternates reasoning about the next move with acting and observing. The useful artifact is the action trace; it should not be confused with private hidden reasoning.
context → generated action rationale → action → observation → next action rationale
What you will learn
- Explain the historical ReAct pattern: generated rationale, action, and observation.
- Follow a ReAct trace without treating written “Thought” text as hidden model reasoning.
- Use short, structured action rationales for inspection and debugging.
- Evaluate whether ReAct helps a task instead of assuming longer reasoning is better.
Current real-system grounding: Google’s tool documentation shows the critical difference between provider-executed built-in tools and custom functions executed by your application.
These official links document available product features. They do not reveal every provider’s private implementation, hidden reasoning, default setting, or internal limit.
1. The problem this module solves
Module 4’s loop reasons implicitly at each step. Module 8’s planning reasons about structure up front. This module covers ReAct — a specific, widely-adopted pattern that makes an agent’s reasoning explicit and visible at every single step, directly interleaved with its actions and observations.
2. The Core Idea
ReAct (Reason + Act) records a generated “Thought,” or action rationale, before an Action. This makes the agent’s chosen reason for an action easier to inspect, but it does not reveal every hidden calculation inside the model.
Thought: [why this action, given the current situation]
Action: [which tool/action to take]
Observation: [what happened as a result]
Thought: [reasoning about the NEW observation]
Action: [next action, informed by that reasoning]
Observation: [next result]
...
3. Why Record an Action Rationale?
WITHOUT a recorded rationale: Context -> Action
(the trace shows what happened)
WITH a recorded rationale: Context -> Action rationale -> Action
(the trace also shows the stated reason)
This is related to step-by-step prompting, but the written “Thought” should be treated as generated text, not as a perfect record of the model’s private reasoning. A short action rationale can make a tool-use trace easier to inspect and may help the model break down some tasks. Whether it improves results must still be measured with evaluations.
An important clarification: the “Thought” text ReAct produces is a conceptual summary generated as part of the model’s output. It does not literally expose the model’s internal computation. Treat it as an inspectable action rationale, not as a window into the model’s “mind.”
For production systems, prefer a short, structured decision summary such as selected_tool, reason_code, and evidence_used. This is easier to validate and safer to log than requesting a long free-form chain of thought.
4. ReAct vs. the Plain Loop From Module 4
Module 4's loop: observe -> reason (INTERNAL, not necessarily
articulated) -> act -> observe result ->...
ReAct: observe -> THOUGHT (EXPLICITLY articulated)
-> act -> observation -> THOUGHT (again,
explicitly articulated) -> act ->...
ReAct is not a different loop structure from Module 4 — it’s the SAME loop, with the reasoning step made explicit and visible at every iteration. This is precisely why ReAct is so widely adopted: it’s a low-cost addition (just asking the model to articulate its reasoning) with a real, demonstrated benefit (Module 20’s observability discussion covers WHY recorded action rationales matters for debugging too).
5. A Real Developer Example — The Full ReAct Trace
TechCorp’s late-order agent, using ReAct explicitly:
| Step | Thought | Action | Observation |
|---|---|---|---|
| 1 | “I need to check the order status first.” | check_order_status | “Order is LATE” |
| 2 | “The order is late, so I should check the shipping carrier next.” | check_shipping_carrier | “Package delivered 2 days ago” |
| 3 | “The carrier says delivered, but the order was flagged late and the customer disputes receiving it — I now have enough information to escalate this.” | finish | — |
Notice: every action is directly preceded by an explicit reason for taking it — this is ReAct’s entire, precise contribution.
6. A Simple Agentic AI Connection
ReAct traces are exactly what most agent observability tools (Module 21) display when showing an agent’s “reasoning” — the explicit Thought steps make an agent’s decision process inspectable by a human debugging unexpected behavior, directly connecting this module to production monitoring later in this course.
7. How Is This Used in AI?
🤖 How Is This Used in AI?
ReAct is one of the most widely-adopted agent reasoning patterns in production systems — because it’s simple to implement (a prompt structure asking for explicit thought before action), and because the resulting trace is directly useful for both improving agent reliability and debugging agent behavior after the fact.
8. Real-World Applications
- Any production agent system benefiting from an inspectable reasoning trace
- Debugging and evaluation workflows (Module 19-21) that review an agent’s Thought steps to understand why it took a specific action
- Research and customer support agents where explaining “why” an action was taken is valuable
9. Common Mistakes
Incorrect idea: Believing the “Thought” text is a literal window into the model’s internal computation.
Why it is incorrect: As shown directly in Section 3, it’s a useful, generated reasoning artifact — not literal exposed internal state.
Incorrect idea: Treating ReAct as a fundamentally different loop from Module 4’s agent loop.
Why it is incorrect: As shown directly in Section 4, it’s the SAME loop structure, with reasoning made explicit — not a competing architecture.
Incorrect idea: Skipping ReAct-style explicit reasoning for complex, multi-step tasks where debugging matters.
Why it is incorrect: As shown directly in Section 6, the visible trace has real, practical value beyond just the reasoning quality benefit itself.
10. Limitations
- Explicit thought generation adds real tokens (and cost, connecting to your Generative AI course’s token economics) to every single step — a real trade-off against the benefit
- A “Thought” being explicitly articulated doesn’t guarantee it’s correct reasoning — an agent can confidently articulate flawed reasoning just as easily as sound reasoning
11. Quick Reference
flowchart TD
O[Observation] --> T[Thought:<br/>explicit reasoning]
T --> A[Action]
A --> NO[New Observation]
NO --> T
T --> F[Thought: goal achieved]
F --> Done[Finish]
12. Code — Implementing a ReAct Loop
🎯 Target of this example: implement Section 5’s complete worked example — a loop that records a generated action rationale before an Action, then logs the rationale, Action, and Observation. The recorded rationale helps us inspect the trace; it is not the model’s hidden internal reasoning.
Example 1 — Simple
from dataclasses import dataclass
@dataclass
class ReActStep:
thought: str
action: str
observation: str = None
def react_loop(goal: str, environment: dict, max_steps: int = 5) -> list:
"""A real ReAct loop -- interleaving THOUGHT, ACTION, and
OBSERVATION at EVERY single step, exactly Section 2's pattern."""
steps = []
state = {}
for step_num in range(1, max_steps + 1):
if "order_status" not in state:
thought = "I need to check the order status first."
action = "check_order_status"
elif state["order_status"] == "late" and "carrier_status" not in state:
thought = "The order is late, so I should check the shipping carrier next."
action = "check_shipping_carrier"
else:
thought = "I now have enough information to resolve this."
action = "finish"
if action == "finish":
steps.append(ReActStep(thought=thought, action=action))
break
observation = environment.get(action, "no data")
if action == "check_order_status":
state["order_status"] = "late" if "late" in observation.lower() else "on_time"
elif action == "check_shipping_carrier":
state["carrier_status"] = "delivered" if "delivered" in observation.lower() else "in_transit"
steps.append(ReActStep(thought=thought, action=action, observation=observation))
return steps
environment = {"check_order_status": "Order is LATE", "check_shipping_carrier": "Package DELIVERED 2 days ago"}
trace = react_loop("Resolve late order", environment)
for i, step in enumerate(trace, 1):
print(f"Step {i}:")
print(f" Thought: {step.thought}")
print(f" Action: {step.action}")
if step.observation:
print(f" Observation: {step.observation}")
Expected Output:
Step 1:
Thought: I need to check the order status first.
Action: check_order_status
Observation: Order is LATE
Step 2:
Thought: The order is late, so I should check the shipping carrier
next.
Action: check_shipping_carrier
Observation: Package DELIVERED 2 days ago
Step 3:
Thought: I now have enough information to resolve this.
Action: finish
What we conclude from this example: every single action is directly preceded by an explicit, visible thought explaining why it’s being taken — exactly Section 5’s real developer example, and Section 2’s core pattern, made fully observable in the printed trace.
Example 2 — Intermediate
import anthropic
client = anthropic.Anthropic()
def react_step(goal: str, state: dict, available_actions: list) -> dict:
"""Uses an LLM to GENERATE the next Thought and Action,
exactly what a real ReAct implementation does -- rather than
hardcoded if/else logic (Example 1's simplified stand-in)."""
prompt = (
f"Goal: {goal}\n"
f"Current state: {state}\n"
f"Available actions: {available_actions}\n\n"
f"Respond in EXACTLY this format:\n"
f"Thought: <your reasoning about what to do next>\n"
f"Action: <one action from the available list, or 'finish' if the goal is achieved>"
)
response = client.messages.create(
model="claude-sonnet-4-6", max_tokens=100, temperature=0,
messages=[{"role": "user", "content": prompt}]
)
text = response.content[0].text
thought = text.split("Thought:")[1].split("Action:")[0].strip()
action = text.split("Action:")[1].strip()
return {"thought": thought, "action": action}
state = {"order_status": "late"}
result = react_step(
goal="Resolve customer's late-order complaint",
state=state,
available_actions=["check_shipping_carrier", "send_apology_email", "finish"],
)
print(f"Thought: {result['thought']}")
print(f"Action: {result['action']}")
Expected Output:
Thought: Since the order is confirmed late, I should check the
shipping carrier to get more details about the delay before deciding
how to proceed.
Action: check_shipping_carrier
What we conclude from this example: the LLM generates BOTH the thought and the resulting action, using a structured prompt format — this is exactly what a real, production ReAct implementation does, replacing Example 1’s hardcoded if/else logic with real model reasoning while keeping the exact same Thought-then-Action structure.
Example 3 — Production Grade
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class ReActTraceEntry:
step_number: int
thought: str
action: str
observation: str
timestamp: str
class ReActAgent:
"""A production-style ReAct agent maintaining a FULL, timestamped
trace of every Thought-Action-Observation cycle -- directly
supporting Module 21's observability discussion, since the
recorded action-and-observation trace becomes auditable after the
fact, not just the final answer."""
def __init__(self, environment: dict, max_steps: int = 5):
self.environment = environment
self.max_steps = max_steps
self.trace: list = field(default_factory=list)
self.trace = []
self.state = {}
def _reason(self) -> tuple:
if "order_status" not in self.state:
return "I need to check the order status first.", "check_order_status"
if self.state["order_status"] == "late" and "carrier_status" not in self.state:
return "The order is late -- checking the shipping carrier next.", "check_shipping_carrier"
return "I have enough information to resolve this.", "finish"
def run(self) -> list:
for step_num in range(1, self.max_steps + 1):
thought, action = self._reason()
if action == "finish":
self.trace.append(ReActTraceEntry(step_num, thought, action, "",
datetime.now().isoformat()))
break
observation = self.environment.get(action, "no data")
if action == "check_order_status":
self.state["order_status"] = "late" if "late" in observation.lower() else "on_time"
elif action == "check_shipping_carrier":
self.state["carrier_status"] = "delivered" if "delivered" in observation.lower() else "in_transit"
self.trace.append(ReActTraceEntry(step_num, thought, action, observation,
datetime.now().isoformat()))
return self.trace
environment = {"check_order_status": "Order is LATE", "check_shipping_carrier": "Package DELIVERED 2 days ago"}
agent = ReActAgent(environment)
trace = agent.run()
print(f"Total steps in trace: {len(trace)}")
for entry in trace:
print(f" [{entry.step_number}] Thought: '{entry.thought}' -> Action: {entry.action}")
Expected Output:
Total steps in trace: 3
[1] Thought: 'I need to check the order status first.' -> Action:
check_order_status
[2] Thought: 'The order is late -- checking the shipping carrier
next.' -> Action: check_shipping_carrier
[3] Thought: 'I have enough information to resolve this.' ->
Action: finish
What we conclude from this example: every ReActTraceEntry
carries a timestamp alongside its thought, action, and observation —
exactly the kind of complete, auditable record a production system
needs to review an agent’s recorded decisions and actions after the fact,
directly connecting this module’s pattern to Module 21’s
observability requirements.
13. Interview Questions
Q: Explain the ReAct pattern and how it relates to the standard agent loop covered earlier in this course.
Ans: ReAct (Reason + Act) has the agent explicitly articulate a “Thought” — a visible piece of reasoning — before every single Action it takes, then observes the result before generating the next thought. It’s not a fundamentally different loop structure from the standard agent loop; it’s the exact same reason-act-observe cycle, but with the reasoning step made explicit and visible rather than implicit, directly connecting to brief action-rationale prompting techniques applied specifically to an agent’s action selection.
Q: Why can recording an action rationale before each action tend to improve decision quality?
Ans: This directly mirrors brief action-rationale prompting — having a model articulate its reasoning before committing to a decision tends to produce better decisions than jumping directly to an answer with no visible intermediate reasoning. Requiring an explicit thought forces the model to reason about the current situation and why a specific action makes sense, rather than pattern-matching to a plausible-sounding action without that grounding.
Q: Is the “Thought” text ReAct produces a literal representation of the model’s internal computation? Explain.
Ans: No — it’s a generated, conceptual summary of reasoning that the model produces as part of its output, not a literal window into its actual internal processing. It’s useful as an explicit reasoning artifact that can be reviewed and understood by a human, but it shouldn’t be treated as literally exposing what’s happening inside the model — it’s the model’s articulated explanation, generated the same way any other text output is generated.
Q: Why might a team choose to log the full ReAct trace (every thought, action, and observation) rather than just the agent’s final output?
Ans: Logging only the final output makes it difficult to diagnose why an agent produced an incorrect or unexpected result — you can see THAT something went wrong, but not WHERE in the reasoning process. A complete trace showing every thought and the action it led to lets a team review the agent’s actual decision-making process step by step, distinguishing a reasoning failure at a specific step from an environment or tool-execution issue, directly supporting real debugging and evaluation workflows.
14. What You Should Remember
- ReAct interleaves explicit Thought, Action, and Observation at every step — the same underlying loop from earlier in this course, with reasoning made visible.
- Explicit reasoning tends to improve decision quality, directly connecting to brief action-rationale prompting principles — verified directly through a working trace where every action is preceded by explicit reasoning.
- The Thought text is a generated reasoning artifact, not literal exposed internal computation — and a full, timestamped trace provides real, practical value for debugging and observability, verified directly through a production-style implementation.
15. Quick Practice
Take a multi-step decision you made recently and write it out in ReAct format — an explicit Thought before each action you took, and the Observation that followed each one, ending with a final Thought explaining why you considered the goal achieved.
16. Next Step
Next: Module 10 — Reflection and Self-Correction — closing Level 4: how an agent can evaluate its own output and revise it, and the real risks of relying on self-evaluation.
- Author
- TechByteByByte Editorial Team
- Reviewed by
- TechByteByByte Admin
- Published
- Last reviewed