Skip to main content

ReAct: Synergizing Reasoning and Acting

Paper: Yao et al., 2022 - arXiv:2210.03629 Venue: ICLR 2023 Field: NLP · AI Agents · Tool Use

Production Viability Rating

DimensionRatingNotes
Compute🟢 Consumer GPUWorks with any LLM, including small open-source models
Implementation🟢 DaysThe pattern is simple; integrations take time
Production Ready🟢 Ship it nowThis pattern is in production everywhere

Plain English Summary

Before ReAct, LLMs either thought (chain-of-thought reasoning) or acted (called tools). ReAct interleaves both. The model thinks, acts, observes the result, thinks again, acts again - in a loop - until it reaches an answer.

That's it. That's the whole paper. But the implications are enormous.

The Core Idea

Thought: I need to find the population of Paris.
Act: Search["population of Paris 2024"]
Obs: Paris has a population of approximately 2.1 million.
Thought: Now I have the data. I can answer the question.
Act: Finish["Paris has approximately 2.1 million people."]

The model generates Thought:, Act:, and Obs: as plain text. The system intercepts Act: lines, runs the tool, and injects the Obs: back into context. The model never "knows" it called a tool - it just sees more text.

Why It Matters for Engineers

Before ReAct: Agents were either pure reasoning (hallucinating facts) or pure tool-calling (no reasoning about what to do next).

After ReAct: The reasoning trace is part of the context. This means:

  1. The model can recover from failed tool calls by reasoning about the failure
  2. You can debug agents by reading the thought trace
  3. The pattern works with any LLM that follows instructions

The dirty secret: ReAct is not a framework. It's a prompting pattern. Everything called an "agent framework" today (LangChain, LlamaIndex, AutoGen) is essentially ReAct + tooling.

Implementation Notes

Minimal implementation

REACT_PROMPT = """You are an agent. Respond using this format:
Thought: [your reasoning]
Action: [tool_name]([input])
Observation: [result of action - provided by system]
... repeat until done ...
Final Answer: [answer]

Available tools: {tools}
"""

def run_react_agent(query, tools, llm, max_steps=10):
history = [{"role": "user", "content": REACT_PROMPT.format(tools=tools) + f"\nQuestion: {query}"}]

for _ in range(max_steps):
response = llm.complete(history)
history.append({"role": "assistant", "content": response})

if "Final Answer:" in response:
return response.split("Final Answer:")[-1].strip()

if "Action:" in response:
tool_name, tool_input = parse_action(response)
observation = tools[tool_name](tool_input)
history.append({"role": "user", "content": f"Observation: {observation}"})

return "Max steps reached without answer."

Where it breaks in production

  1. Looping - The model can get stuck in a thought-action loop. Always set max_steps.
  2. Context overflow - Long agent runs fill the context window. Summarize history periodically.
  3. Tool error handling - If a tool fails, the model often hallucinates the observation. Inject explicit error messages.
  4. Prompt sensitivity - The Thought/Act/Obs format must be consistent. Deviations cause parsing failures.

Limitations the Paper Glosses Over

  • Evaluated on simple question-answering benchmarks (HotpotQA, Fever). Real-world tasks are much messier.
  • Assumes the LLM reliably follows the Thought/Act/Obs format. Smaller models often don't.
  • No mention of context management for long task horizons.
  • Tool errors are not systematically addressed.

What Changed After

PaperYearWhat it added
Toolformer (Schick et al.)2023LLMs that learn when to call tools, not just how
OpenAI Function Calling2023Structured tool calling, no prompt hacking needed
AutoGPT2023Long-horizon autonomous agents built on ReAct
LangGraph2024Stateful, cyclical agent graphs replacing linear ReAct

Further Reading

© 2026 EngineersOfAI. All rights reserved.