The ReAct Framework: Reason + Act Agent Loops
While Chain-of-Thought enables internal deduction, LLMs remain fundamentally limited by static training weights. They cannot know today's stock price, query private databases, or verify math formulas. The ReAct (Reason + Act) framework bridges this chasm by interleaving internal reasoning with external tool execution.
1. The Tri-Phase Agent Loop: Thought → Action → Observation
Developed by Yao et al. (Princeton & Google Research, 2022), ReAct creates an autonomous feedback cycle where the model acts as the reasoning brain and external APIs serve as its sensory organs:
Internal Deliberation
The model generates reasoning about its current state: What information is needed? What tool should be called next?
Tool Invocation
The model outputs a formatted call with parameters: Search["Apple Q3 earnings 2026"] or Calculate[142 * 18.5].
Environment Feedback
The execution runtime intercepts the action, executes the real API, and injects the live results back into the model's prompt.
2. Why ReAct Outperforms Other Paradigms
| Paradigm | Failure Mode | ReAct Advantage |
|---|---|---|
| Reason Only (CoT) | Hallucinates dynamic facts or makes calculation errors when doing arithmetic internally. | ReAct executes real calculators and live search APIs, ensuring mathematical and factual precision. |
| Act Only (Direct API) | Invokes tools blindly without tracking context. When a tool errors (e.g. 404 or empty search), it gets stuck in an infinite loop. | The Thought step allows the agent to diagnose failures: "Search returned no results, let me rewrite the query." |
3. Production Python ReAct Implementation from Scratch
Here is a dependency-free, complete implementation of a ReAct agent loop in Python using OpenAI's client:
import re
from openai import OpenAI
client = OpenAI()
# 1. Define Available Tools
def calculator(expression: str) -> str:
try:
# Safe mathematical evaluation
return str(eval(expression, {"__builtins__": {}}, {}))
except Exception as e:
return f"Error: {e}"
def search_database(query: str) -> str:
mock_db = {
"apple revenue 2025": "$391.0 Billion",
"nvidia revenue 2025": "$96.3 Billion"
}
return mock_db.get(query.lower(), "Data not found.")
TOOLS = {
"Calculator": calculator,
"Search": search_database
}
# 2. The ReAct System Scaffold
SYSTEM_PROMPT = """
Answer questions by running an iterative Thought -> Action -> Observation loop.
You have access to the following tools:
- Calculator[expression]: Evaluates a mathematical Python expression.
- Search[query]: Queries business financial databases.
Use the following exact format:
Question: the input question
Thought: your reasoning about what step to take
Action: ToolName[argument]
Observation: the result of the action
... (this Thought/Action/Observation can repeat up to 5 times)
Thought: I now know the final answer
Final Answer: the ultimate concise answer to the question
"""
def run_react_agent(question: str, max_turns: int = 5):
prompt_history = f"Question: {question}\n"
print(f"\n=== User Question: {question} ===")
for turn in range(max_turns):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt_history}
],
stop=["Observation:"],
temperature=0.0
)
output = response.choices[0].message.content.strip()
print(f"\n[Agent Output]:\n{output}")
prompt_history += output + "\n"
if "Final Answer:" in output:
break
# Parse Action: ToolName[argument]
match = re.search(r"Action:\s*([A-Za-z]+)\[(.*)\]", output)
if match:
tool_name, tool_arg = match.groups()
if tool_name in TOOLS:
observation = TOOLS[tool_name](tool_arg.strip())
else:
observation = f"Error: Tool '{tool_name}' does not exist."
print(f"\n[Runtime Observation]:\nObservation: {observation}")
prompt_history += f"Observation: {observation}\n"
else:
print("No action found. Ending loop.")
break
# Test the ReAct Loop
run_react_agent("How much larger was Apple's 2025 revenue compared to Nvidia?")4. ReAct vs. Native LLM Function Calling
While classical ReAct uses text matching (e.g. Action: Search[query]), modern frontier LLMs offerNative Tool Calling (OpenAI Function Calling, Anthropic Tools, Gemini Tools). Under the hood, native tool calling is the exact same ReAct feedback loop, but with JSON validation enforced directly at the token decoding layer:
Best Practice in 2026
Always use native tools=[...] APIs rather than string regex parsing whenever available. Native function calling guarantees zero parsing errors, handles nested arrays/objects cleanly, and supportsparallel tool calling (triggering multiple tools in a single turn).
Step-Back & Generated Knowledge
Continue to the next hands-on prompt engineering guide in this series.
Explore Related Tools
Hand-picked utilities and calculators related to this tool.
