ReAct (Reasoning and Acting) is the foundational design pattern for building AI agents that can think, take actions in the world, and learn from the results. Introduced by Yao et al. in their 2022 paper “ReAct: Synergizing Reasoning and Acting in Language Models,” it combines Chain-of-Thought (CoT) reasoning with tool use into a single interleaved loop: the agent generates a Thought about what to do next, executes an Action (like searching a database or calling an API), observes the result (Observation), and repeats until the task is complete.
Before ReAct, there were two separate approaches to extending LLM capabilities: reasoning-only (CoT prompting, where the model thinks step-by-step but cannot interact with the world) and acting-only (where models directly call tools without explaining their reasoning). ReAct’s key insight was that reasoning and acting are synergistic — reasoning helps the agent plan which action to take next, while action results provide new information that grounds and corrects the reasoning.
Today, ReAct is the default architecture behind virtually every AI agent framework — from LangChain’s create_react_agent to Google’s Genkit agents to Anthropic’s Claude tool-use patterns. Understanding ReAct is essential for building any agentic AI system, as it provides the conceptual foundation that all more complex multi-agent and orchestration patterns build upon.
The ReAct pattern operates as a simple but powerful iterative cycle:
Thought: The LLM generates internal reasoning about the current state and what step to take next. This is the “chain of thought” component. Action: Based on the thought, the agent selects and executes a tool with specific parameters. Execution pauses while waiting for results. Observation: The tool’s output is returned to the agent as new context. The agent incorporates this information and begins the next iteration. Termination: The loop ends when the agent determines it has enough information to produce a final answer, or when a maximum iteration limit is reached.
Here is a concrete example of a ReAct trace for the question: “What is the elevation of the city where the Visiting Media headquarters is located?”
Thought 1: I need to find where Visiting Media's headquarters is located.
I'll search for this information.
Action 1: search("Visiting Media headquarters location")
Observation 1: Visiting Media is headquartered in Nashville, Tennessee.
Thought 2: Now I know the headquarters is in Nashville, TN. I need to find
the elevation of Nashville.
Action 2: search("Nashville Tennessee elevation")
Observation 2: Nashville sits at an elevation of approximately 597 feet
(182 meters) above sea level.
Thought 3: I now have all the information needed to answer the question.
Nashville, TN has an elevation of 597 feet (182 meters).
Action 3: finish("The elevation of the city where Visiting Media's
headquarters is located (Nashville, TN) is approximately
597 feet (182 meters) above sea level.")
A ReAct agent’s behavior is defined by its system prompt, which must communicate three things: the reasoning format, the available tools, and the loop protocol. Here is a minimal but complete implementation:
# ReAct System Prompt Template
You are a helpful assistant that answers questions by reasoning
step-by-step and using tools when needed.
You have access to the following tools:
- search(query): Search the knowledge base for information
- calculate(expression): Evaluate a mathematical expression
- lookup(term): Look up a specific term in the database
For each step, output your reasoning in this format:
Thought: [your reasoning about what to do next]
Action: [tool_name(parameters)]
After receiving an Observation, continue reasoning. When you have
enough information to answer, use:
Thought: [your final reasoning]
Action: finish(your final answer)
Always think before acting. Never skip the Thought step.
The application layer that orchestrates a ReAct agent is surprisingly simple. It is a while loop that alternates between LLM generation and tool execution:
import openai
class ReActAgent:
def __init__(self, system_prompt, tools, max_iterations=10):
self.system_prompt = system_prompt
self.tools = tools # dict of tool_name -> callable
self.max_iterations = max_iterations
def run(self, user_query):
messages = [
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": user_query}
]
for i in range(self.max_iterations):
# 1. LLM generates Thought + Action
response = openai.chat.completions.create(
model="gpt-4",
messages=messages
)
assistant_msg = response.choices[0].message.content
messages.append({"role": "assistant", "content": assistant_msg})
# 2. Parse the action from the response
action_name, action_args = self.parse_action(assistant_msg)
# 3. Check for termination
if action_name == "finish":
return action_args
# 4. Execute the tool and get observation
observation = self.tools[action_name](action_args)
# 5. Feed observation back as context
messages.append({
"role": "user",
"content": f"Observation: {observation}"
})
return "Max iterations reached without a final answer."
Modern frameworks like LangGraph abstract this loop with additional features — streaming, checkpointing, error recovery, and parallel tool execution — but the fundamental pattern remains identical.
The power of ReAct comes specifically from interleaving reasoning and acting, rather than doing all reasoning first or all acting first:
The critical advantage of ReAct over pure CoT is grounding. When the agent encounters uncertainty (“I’m not sure when this hotel was built”), it can take an action to resolve it rather than guessing. This dramatically reduces hallucination rates. The original ReAct paper showed that on the HotpotQA benchmark, ReAct achieved 61% accuracy versus 49% for CoT-only prompting, with significantly fewer hallucinated facts.
Robust ReAct implementations must handle several failure modes:
While the original ReAct paper used text-based action parsing, modern LLM APIs support structured function calling natively. Instead of parsing Action: search("query") from text, the model emits a structured JSON tool call that the API handles automatically:
// Modern function calling (e.g., OpenAI, Anthropic)
{
"role": "assistant",
"content": "I need to find the headquarters location first.",
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "search",
"arguments": "{\"query\": \"Visiting Media headquarters\"}"
}
}]
}
// Tool result fed back
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "Visiting Media is headquartered in Nashville, TN."
}
This structured approach eliminates parsing errors and enables parallel tool execution (multiple actions in a single turn), which is a natural extension of the ReAct pattern supported by frameworks like LangGraph and Genkit.
Customer-facing AI assistants use ReAct to handle queries that require database lookups, calculations, or external API calls. A hotel booking assistant might reason about a guest’s preferences, search availability, check pricing, and compose a recommendation — all within a single ReAct loop. The reasoning traces provide transparency for debugging why the assistant made specific recommendations.
Research agents use ReAct to iteratively search, read, and synthesize information. The agent identifies knowledge gaps through reasoning, fills them through search actions, and builds up a comprehensive answer over multiple iterations. This is the pattern behind tools like Perplexity, OpenAI’s deep research, and Anthropic’s Claude agent capabilities.
Coding agents (like Claude Code, GitHub Copilot, and Cursor) implement ReAct to read files, understand codebases, write code, run tests, observe failures, and iterate. The Thought step reasons about the bug or feature, the Action step reads/writes files or runs commands, and the Observation step feeds back test results or error messages.
Data analysis agents use ReAct to explore datasets interactively: formulate a hypothesis (Thought), run a SQL query or pandas operation (Action), examine the results (Observation), then decide whether to dig deeper or move to the next analysis step. This iterative approach handles the inherently exploratory nature of data analysis far better than single-shot prompting.