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 make ReAct implementation straightforward via function calling:
tools = [
{"name": "search", "description": "Search the web", ...},
{"name": "calculator", "description": "Evaluate math expressions", ...},
]
messages = [system_prompt_with_examples, {"role": "user", "content": question}]
for step in range(max_steps):
response = client.messages.create(
model="claude-opus-4-6",
tools=tools,
messages=messages
)
if response.stop_reason == "end_turn":
return response.content # Final answer reached
# Process tool calls
tool_use = next(b for b in response.content if b.type == "tool_use")
result = execute_tool(tool_use.name, tool_use.input)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": [tool_result_block(tool_use.id, result)]})
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