Every AI agent you've ever used — Cursor, Claude Code, Perplexity, Devin — runs the same pattern under the hood.
Think. What should I do next?
Act. Call a tool, execute a function.
Observe. Read the result.
Repeat.
That's ReAct — Reasoning + Acting. Published by Yao et al. in 2022, it became the foundational pattern for AI agents. Before ReAct, LLMs could either reason (chain of thought) or act (tool calling), but not both at the same time. ReAct combined them, and everything changed.
The Problem ReAct Solves
Before ReAct, there were two approaches:
Chain of Thought (Reasoning Only)
Q: How many goals has Messi scored in World Cup finals?
Think: Messi has played in World Cup finals in 2014 and 2022.
Think: In 2014, Argentina lost to Germany. Messi didn't score.
Think: In 2022, Messi scored twice against France.
Answer: 2 goals.
The LLM reasons step by step. But it can't verify anything. If its knowledge is wrong, the reasoning is confidently wrong.
Action Only (Tool Calling Without Reasoning)
Q: How many goals has Messi scored in World Cup finals?
Action: search("Messi World Cup final goals")
Result: "Messi scored 2 goals in the 2022 World Cup final"
Answer: 2 goals.
The LLM calls a tool, but there's no reasoning about why it chose that search query, whether the result is complete, or if it needs to dig deeper. What about the 2014 final?
ReAct: Both Together
Q: How many goals has Messi scored in World Cup finals?
Thought: I need to find Messi's goals across ALL World Cup finals, not just one.
Action: search("Messi goals in all World Cup finals career")
Observation: "2014 final: 0 goals. 2022 final: 2 goals (+ 1 in penalties)."
Thought: So 2 goals in regulation/extra time across both finals.
Penalties are usually counted separately. I should clarify.Action: search("do penalty shootout goals count in World Cup stats")
Observation: "Penalty shootout goals are not counted in a player's tournament goal tally."
Thought: So the answer is 2 goals. Both in the 2022 final. None in 2014.
Answer: Messi scored 2 goals in World Cup finals (both in the 2022 final vs France).
See the difference? The LLM reasons about what to search, evaluates the results, identifies gaps, and does follow-up searches. It's not just calling tools — it's thinking about tool results.
The ReAct Loop
Here's the formal pattern:
Input: User's question or task
Loop:
- THOUGHT → LLM reasons about current state
"I need to...", "The result shows...", "I should check..."- ACTION → LLM chooses a tool and provides arguments
search("query"), calculate("2+2"), read_file("config.py")- OBSERVATION → System executes tool, returns result
The LLM reads this but doesn't generate it→ Back to THOUGHT with new information
Exit: When the LLM decides it has enough information → generates final ANSWER
The key insight: thoughts and actions are interleaved. Every action is preceded by reasoning about why that action is the right choice. Every observation is followed by reasoning about what it means.
🔧
Implementation: The Prompt
The ReAct pattern is implemented primarily through the system prompt. Here's a simplified version:
You are a helpful assistant that solves problems step by step.
For each step, use exactly this format:
Thought: [Your reasoning about what to do next]
Action: [tool_name(arguments)]
After you see the result, reason about it:
Thought: [What the result tells you, what to do next]
Action: [next tool call, or provide final answer]
When you have enough information:
Thought: [Summary of findings]
Answer: [Final response to the user]
Available tools:
- search(query: string) — Search the web
- calculate(expression: string) — Evaluate math
- read_url(url: string) — Read a webpage
That's it. The "pattern" is really a prompting strategy combined with tool calling. The LLM generates the thoughts as regular text and the actions as tool calls.
📊
ReAct in Production (Real Code)
Here's how ReAct looks in actual code, not just theory:
python
import openai
import json
client = openai.OpenAI()
system_prompt = """You solve problems step by step.Before each action, explain your reasoning.
After seeing results, analyze them before your next step.
When done, provide a final answer."""
tools = [
{
"type": "function",
"function": {
"name": "search",
"description": "Search the web for information",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "Evaluate a math expression",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string"}
},
"required": ["expression"]
}
}
}]
def execute_tool(name, args):
if name == "search":
return web_search(args["query"]) # Your search implementation
elif name == "calculate":
return str(eval(args["expression"]))
return "Unknown tool"def react_agent(question, max_steps=10):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": question}
]
for step in range(max_steps):
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools
)
msg = response.choices[0].message
messages.append(msg)
# If the model generated text (thought/answer) AND tool calls
if msg.content:
print(f"💭 {msg.content}")
if msg.tool_calls:
for tc in msg.tool_calls:
name = tc.function.name
args = json.loads(tc.function.arguments)
print(f"🔧 {name}({args})")
result = execute_tool(name, args)
print(f"👁️ {result[:200]}")
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": result
})
else:
# No tool calls → final answer
return msg.content
return "Max steps reached"Run it:
python
answer = react_agent("What's the population of India divided by the area in square km?")💭 I need to find India's current population and total area.
🔧 search({"query": "India population 2025"})
👁️ India's population is estimated at 1.44 billion as of 2025...
🔧 search({"query": "India total area square kilometers"})
👁️ India has a total area of 3,287,263 square kilometers...
💭 Now I can calculate: 1,440,000,000 / 3,287,263
🔧 calculate({"expression": "1440000000 / 3287263"})
👁️ 438.0
💭 The population density is about 438 people per square km.
→ "India's population density is approximately 438 people per square kilometer..."
Three thoughts, three actions, three observations. That's ReAct in action.
ReAct vs Other Patterns
ReAct isn't the only agent pattern, but it's the most fundamental:
Pattern Description When to Use
| ReAct | Think → Act → Observe loop | General-purpose agents, most tasks |
|---|---|---|
| Chain of Thought (CoT) | Think step by step, no tools | Pure reasoning, math, logic |
| Act-Only | Call tools without explicit reasoning | Simple tool-routing, low latency |
| Plan-then-Execute | Make a full plan first, then execute all steps | Well-defined tasks with clear steps |
| Reflexion | ReAct + self-critique after each attempt | Complex tasks needing error recovery |
| LATS | Tree search over ReAct trajectories | When you need to explore multiple paths |
Most production agents use ReAct or a variant of it. The "plan-then-execute" pattern is popular for coding agents — plan the changes, then make them all.
Why Thoughts Matter
You might wonder: why not skip the "Thought" step and just let the model call tools directly? Two reasons:
- Better Tool Selection
Without reasoning:
User: "Is my flight on time?"
Action: search("flight status") ← Too vague
With reasoning:
User: "Is my flight on time?"
Thought: I need the user's flight number to check status.
I should ask, or if they mentioned it before, check context.
They mentioned flight AI-204 earlier.Action: search("AI-204 flight status today") ← Much better
- Error Recovery
Without reasoning:
Action: search("population of Gondor")
Result: "Gondor is a fictional kingdom in Lord of the Rings"
Action: search("Gondor population census") ← Going down a rabbit hole
With reasoning:
Action: search("population of Gondor")
Result: "Gondor is a fictional kingdom in Lord of the Rings"
Thought: Gondor is fictional — it doesn't have a real population.
I should tell the user this instead of searching further.Answer: "Gondor is a fictional kingdom from Lord of the Rings —
it doesn't have a real population figure."The thought step acts as a circuit breaker — it lets the model realize when it's going down the wrong path and course-correct.
🔬
Common Failure Modes
ReAct isn't perfect. Watch out for:
Thought loops: The model thinks the same thought repeatedly without making progress.
Thought: I should search for more information.
Action: search("topic")
Observation: Same information as before.
Thought: I should search for more information. ← Loop!
Over-thinking: The model generates long, unnecessary thoughts that waste tokens.
Thought: Let me carefully consider all the possible approaches. First,
I could search for X. Or I could search for Y. The advantage of X is...
[500 tokens of deliberation]
Action: search("X") ← Could have just done this immediately
Premature stopping: The model decides it has enough information when it doesn't.
Thought: I found the company's revenue. That's enough.
Answer: "The revenue is $50M."
← But the user asked for PROFIT, not revenue
Fix: Set clear stopping criteria in your prompt. "Only answer when you have ALL the information needed. If in doubt, do one more search."
🛡️
ReAct in the Wild
Every major agent framework implements ReAct under the hood:
LangChain's AgentExecutor — ReAct loop with configurable tools
Claude's tool use — Anthropic's API naturally supports interleaved reasoning + tool calls
OpenAI's Assistants API — ReAct loop managed server-side
AutoGPT — Extended ReAct with memory and self-prompting
CrewAI — Multi-agent ReAct where agents collaborate
When you use any of these, you're using ReAct. The frameworks just handle the loop, error handling, and tool execution for you.
📦
Practical Takeaways
ReAct = Reasoning + Acting — interleave thinking with tool calls for better resultsThe "Thought" step is critical — it improves tool selection and enables error recovery
It's implemented through prompting — the pattern lives in the system prompt, not in special architecture
Every major agent uses ReAct — it's the foundational pattern behind Cursor, Claude Code, Perplexity
Watch for loops and premature stopping — add max steps and clear stopping criteria
Chain of Thought alone can't verify facts — you need tools for grounding in reality
🚀
What's Next?
Episode 32: Multi-Agent Systems — What happens when one agent isn't enough? Orchestration patterns, handoffs, supervisor agents, and swarm intelligence. When you have multiple agents working together, who's in charge?
← Previous
Ep 30: Tool Calling
Next →
Ep 32: Multi-Agent Systems
Next: Episode 32 — Multi-Agent Systems
One agent is powerful. Multiple agents working together? That's where it gets wild — and where most teams get it wrong.
This is part of a 98-episode series covering AI engineering from tokens to production deployment.