The ReAct Pattern

Design Pattern · Language Models

ReAct

Reasoning & Acting in Sequential Loops

ReAct (Reasoning + Acting) is a prompting paradigm that interleaves chain-of-thought reasoning with actionable tool calls inside a tight loop. Instead of answering in one shot, the model repeatedly asks itself “what do I think?” then “what should I do?”, observes the result, and iterates — mirroring how a careful human expert reasons through an unfamiliar problem.

Originally introduced for task-solving agents, ReAct has become the backbone of most modern agentic AI systems: web-browsing assistants, code interpreters, database query agents, and multi-step orchestrators. Yao et al., 2022 · “ReAct: Synergizing Reasoning and Acting in Language Models”
T
Thought
The model narrates its internal reasoning in plain language — what it knows, what it doesn’t, and what strategy it will use next. This scratchpad is never sent to a tool; it exists only to improve the quality of the next action.
A
Action
Based on its reasoning, the model emits a structured tool call — a search query, a code execution, an API request, or any other discrete external operation. One action per step keeps the loop auditable and debuggable.
O
Observation
The environment (search engine, code runtime, database) returns a result. This raw output is appended to the context verbatim — no summarization — so the model can reason over exactly what happened.
Repeat
The model re-enters the Thought stage with an enriched context window. It may loop many times: refining its plan, correcting mistakes, or decomposing a complex task into sub-problems before finally deciding enough information has been gathered.
Answer
When the model determines it has sufficient grounding, it emits a final response — grounded in concrete observations rather than hallucinated from parametric memory alone.
react_agent.py
# System prompt fragment ──────────────────────────────
SYSTEM = """
You solve tasks in a Thought → Action → Observation loop.

Thought:  Reason about the current state and what to do next.
Action:   Call exactly ONE tool using the format below.
Observation: The tool result will be appended automatically.
Repeat until you know the answer, then emit a Final Answer.

Available tools: search(query), calc(expr), lookup(key)
"""

# ─────────────────────────────────────────────────────

def react_loop(question: str, max_steps: int = 10):
    history = [("user", question)]
    
    for step in range(max_steps):
        # ① Generate next Thought + Action
        response = llm(SYSTEM, history)
        
        thought = parse_thought(response)    # free-form text
        action  = parse_action(response)     # tool name + args
        
        if action.name == "final_answer":
            return action.args["answer"]       # ⑤ Done
        
        # ② Execute tool
        obs = execute_tool(action)
        
        # ③ Append to context and loop
        history += [
            ("assistant", f"Thought: {thought}\nAction: {action}"),
            ("tool",      f"Observation: {obs}"),
        ]
    
    raise TimeoutError("max_steps reached without final answer")
🔍
Grounded
Answers are anchored to real observations from tools, dramatically reducing hallucination compared to single-pass generation.
🪵
Auditable
Every reasoning step is visible in the transcript. Debugging is straightforward — you can see exactly where the agent went wrong.
🔄
Self-Correcting
If a tool returns unexpected data, the next Thought can revise the strategy. Mistakes in early steps don’t necessarily propagate to the final answer.
🔧
Tool-Agnostic
Any deterministic function — search, code, APIs, databases — can be a tool. The loop structure is independent of what the tools actually do.
📏
Controllable
Step count, allowed tools, and exit conditions are all configurable. You decide the budget and surface area the agent has access to.
Thought 1 I need the current population of France. I’ll search for it before doing any arithmetic.

Action 1 search(“population of France 2024”)

Obs 1 France population 2024: approximately 68,400,000

Thought 2 Good — 68.4 million. Now I multiply by π ≈ 3.14159. I’ll use the calc tool for precision rather than estimating.

Action 2 calc(“68400000 * 3.14159265358979”)

Obs 2 214,884,937.8456…

Thought 3 I have both inputs from verified sources. The computation is complete. I can now give a final answer.

Answer The population of France (~68.4 million) multiplied by π ≈ 214,884,938.
ReAct Pattern  ·  Yao et al. 2022  ·  Reasoning & Acting in Language Models

Leave a Reply

Your email address will not be published. Required fields are marked *