ReAct is the reasoning pattern that powers most real-world AI agents. It gives a model the ability to plan a course of action, execute steps one at a time, observe results, and adjust — all within a structured loop.
The Core Loop
ReAct defines three alternating elements:
Thought → Action → Observation → Thought → Action → Observation → ... → Answer
| Element | What it is |
|---|---|
| Thought | The model's internal reasoning: what it knows, what it needs, what to do next |
| Action | A tool call or step: search("query"), calculate(expr), lookup(url), etc. |
| Observation | The result of the action: search results, computed value, retrieved data |
The model never sees the final answer until the loop naturally terminates — either by reaching sufficient information or hitting a maximum step count.
Why This Matters
Without a loop, a model has to answer purely from memory. With ReAct:
- It can gather information dynamically (search the web, query a database)
- It adjusts plans based on what it actually finds
- It can recognize when early assumptions were wrong and change course
- Complex multi-step tasks become tractable
A model asked "What's the weather in the city that won the most recent FIFA World Cup final?" can't answer reliably from training data alone. With ReAct, it searches for the most recent World Cup final winner, identifies the city, then searches for that city's current weather — using real, live data.
The ReAct Prompt Pattern
ReAct is implemented via few-shot examples that demonstrate the Thought/Action/Observation format:
Answer the following question as best you can. Use the available tools:
- search(query): returns web search results
- calculator(expression): evaluates a mathematical expression
Use this format:
Thought: [your reasoning about what to do next]
Action: [tool_name(input)]
Observation: [result of the action — you will receive this]
... (repeat as needed)
Final Answer: [your answer]
Question: What is 15% of the annual revenue of the company that makes the iPhone?
Thought: I need to find Apple's annual revenue, then calculate 15% of it.
Action: search("Apple Inc annual revenue 2024")
Observation: Apple reported $391 billion in revenue for fiscal year 2024.
Thought: Now I can calculate 15% of $391 billion.
Action: calculator(0.15 * 391000000000)
Observation: 58650000000
Thought: 15% of Apple's revenue is $58.65 billion.
Final Answer: $58.65 billion (15% of Apple's $391 billion FY2024 revenue)
Few-Shot ReAct Examples
The key to making ReAct work reliably is high-quality few-shot examples. Each example should demonstrate:
- A realistic task that requires multiple steps
- At least one reasoning correction (adjusting plan based on observation)
- The final answer format
Example showing plan adjustment:
Question: Find the population of the capital city of the country that
hosted the 2018 FIFA World Cup.
Thought: I need to find which country hosted the 2018 World Cup.
Action: search("2018 FIFA World Cup host country")
Observation: Russia hosted the 2018 FIFA World Cup.
Thought: Now I need Russia's capital city.
Action: search("capital city of Russia")
Observation: Moscow is the capital of Russia.
Thought: Now I need Moscow's population.
Action: search("Moscow population 2024")
Observation: Moscow has approximately 12.5 million residents in the city proper.
Final Answer: Moscow, Russia's capital, has approximately 12.5 million people.
Implementing ReAct in Code
Modern LLM APIs implement the Act and Observe steps for you through native tool calling. The model returns structured tool calls; your code runs them and sends the results back:
import anthropic
client = anthropic.Anthropic()
tools = [
{"name": "search", "description": "Search the web", "input_schema": {...}},
{"name": "calculator", "description": "Evaluate math expressions", "input_schema": {...}},
]
def run_agent(question: str, max_steps: int = 10):
messages = [{"role": "user", "content": question}]
for step in range(max_steps):
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
system=system_prompt,
tools=tools,
messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
return response # final answer (check for "refusal" or "max_tokens" too)
# The model may call several tools at once: run them all,
# then return every result in a single user message
tool_results = [
{
"type": "tool_result",
"tool_use_id": block.id,
"content": execute_tool(block.name, block.input),
}
for block in response.content
if block.type == "tool_use"
]
messages.append({"role": "user", "content": tool_results})
Notice there's no Thought: parsing. On a reasoning model the Thought step happens in the model's hidden reasoning before each tool call.
ReAct in 2026: native tools and hidden reasoning
The Thought → Action → Observation loop is still how agents work. What changed is who writes each part down.
Actions are structured tool calls, not text. Early ReAct agents printed Action: search("...") as plain text, and your code parsed it with regex. Every current API returns tool calls as structured blocks with a tool name and JSON arguments, so there's nothing to parse. See function calling.
Thoughts happen in the model's hidden reasoning. Reasoning models (GPT-6 Astra, Claude Fable 5.1 and Opus 5.5, Gemini 3.x) think before each response, and Claude's adaptive thinking also runs between tool calls. The planning and self-correction ReAct asked for in visible text now happens there. Requiring a written Thought: before every action duplicates that work, and you pay for it twice.
What that means for your prompts:
- Describe the goal, the tools and the stopping condition. Skip the Thought/Action/Observation format instructions when you use native tool calling on a reasoning model.
- Keep the good rules. "Check each result before moving on" and "search again with a refined query if a result is ambiguous" still improve agent behavior.
- For debugging, ask for short progress notes. One line before each tool call ("Checking 2026 revenue next") gives you a readable trace without full visible reasoning. Most APIs can also return a summary of the hidden reasoning.
- Use the explicit text format when there's no built-in reasoning: small or local open-weight models, or frameworks that only pass text back and forth. There, written Thoughts still do exactly what the original paper showed.
ReAct Failure Modes
| Failure | Cause | Fix |
|---|---|---|
| Infinite loop | No stopping condition | Set max_steps and final answer format |
| Wrong tool selected | Ambiguous tool descriptions | Write very clear, specific tool descriptions |
| Ignores observations | Model doesn't integrate feedback | Reinforce "use observation to update your plan" in system prompt |
| Hallucinated observations | Model fabricates tool results | Use actual tool execution, not pure text generation |
| Gets stuck | Task too complex for available tools | Break task into subtasks or add more capable tools |
ReAct vs. Plan-and-Execute
| Pattern | Description | Best for |
|---|---|---|
| ReAct | Interleave thought + action + observation | Dynamic tasks where plan depends on results |
| Plan-and-Execute | Make a full plan first, then execute | Tasks where the full structure is known upfront |
ReAct is more adaptive; Plan-and-Execute is more efficient when the plan is clear. Many production agents use Plan-and-Execute for structured phases and ReAct for dynamic sub-tasks.
Key Takeaways
- ReAct = Thought + Action + Observation, repeated in a loop
- The model adapts its plan based on what actions actually return
- Implement with function calling + a loop with max step limit
- Few-shot examples of the Thought/Action/Observation format are critical
- Works best when you have clear, well-described tools with predictable outputs