You are viewing a free preview of this lesson.
Subscribe to unlock all 10 lessons in this course and every other course on LearningBro.
The ReAct (Reasoning + Acting) pattern is one of the most influential agent architectures. It interleaves explicit reasoning steps (thoughts) with concrete actions, producing a transparent chain of thought-action-observation that is easy to debug and understand. This lesson covers the pattern in depth, implements it from scratch, and discusses when to use it.
ReAct stands for Reasoning and Acting. The core idea is simple: before taking an action, the agent explicitly writes down its reasoning. After observing the result, it reasons again.
Thought: I need to find the current population of France.
Action: web_search("population of France 2025")
Observation: France has a population of approximately 68.4 million.
Thought: I now have the answer.
Action: finish("The population of France is approximately 68.4 million.")
This is in contrast to simpler approaches where the model directly outputs an action without explaining its reasoning.
┌──────────────────────────────────┐
│ USER QUERY │
└──────────────┬───────────────────┘
│
▼
┌──────────────────────────────────┐
│ THOUGHT │
│ "I need to search for X..." │◄──────────────┐
└──────────────┬───────────────────┘ │
│ │
▼ │
┌──────────────────────────────────┐ │
│ ACTION │ │
│ tool_name(arguments) │ │
└──────────────┬───────────────────┘ │
│ │
▼ │
┌──────────────────────────────────┐ │
│ OBSERVATION │ │
│ (result from tool execution) │───────────────┘
└──────────────┬───────────────────┘
│
▼ (when "finish" action is called)
┌──────────────────────────────────┐
│ FINAL ANSWER │
└──────────────────────────────────┘
Each iteration adds to the agent's scratchpad — the full history of thoughts, actions, and observations.
import json
import re
import math
# Tool registry
TOOLS = {
"calculate": {
"description": "Evaluate a mathematical expression.",
"function": lambda expr: str(eval(expr, {"__builtins__": {}}, {"math": math})),
},
"web_search": {
"description": "Search the web and return results.",
"function": lambda query: f"[Simulated results for: {query}]",
},
"finish": {
"description": "Return the final answer to the user.",
"function": lambda answer: answer,
},
}
def get_tool_descriptions() -> str:
lines = []
for name, tool in TOOLS.items():
lines.append(f"- {name}: {tool['description']}")
return "\n".join(lines)
REACT_SYSTEM_PROMPT = f"""You are a ReAct agent. You solve tasks by alternating between
Thought, Action, and Observation steps.
Available tools:
{get_tool_descriptions()}
Format your response EXACTLY as:
Thought: <your reasoning>
Action: <tool_name>(<arguments>)
When you have the final answer, use:
Thought: <your reasoning>
Action: finish(<your final answer>)
Rules:
- Always start with a Thought before an Action.
- Only use one Action per response.
- Wait for the Observation before your next Thought.
"""
from openai import OpenAI
client = OpenAI()
def parse_action(text: str) -> tuple[str, str]:
"""Extract tool name and argument from 'Action: tool_name(args)'."""
match = re.search(r"Action:\s*(\w+)$(.*)$", text, re.DOTALL)
if not match:
raise ValueError(f"Could not parse action from: {text}")
tool_name = match.group(1)
args = match.group(2).strip().strip('"').strip("'")
return tool_name, args
def react_agent(question: str, max_steps: int = 8) -> str:
"""Run a ReAct agent loop."""
messages = [
{"role": "system", "content": REACT_SYSTEM_PROMPT},
{"role": "user", "content": question},
]
for step in range(max_steps):
# Get the model's thought + action
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
)
reply = response.choices[0].message.content
print(f"--- Step {step + 1} ---")
print(reply)
# Parse the action
try:
tool_name, tool_arg = parse_action(reply)
except ValueError:
messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user", "content": (
"Observation: Could not parse your action. "
"Please use the format: Action: tool_name(arguments)"
)})
continue
Subscribe to continue reading
Get full access to this lesson and all 10 lessons in this course.