home
diamond Go Premium
Data Engineering Path  ·  Data & AI

The ReAct Pattern: Synergizing Reasoning and Acting

How do we teach a language model to use tools systematically?

In 2022, researchers at Princeton and Google Brain published the landmark paper "ReAct: Synergizing Reasoning and Acting in Language Models" (Yao et al.).

The ReAct pattern combines Reasoning (Chain-of-Thought) with Action (Tool Calling) in an interleaved, step-by-step execution trace.


1. Why Pure Reasoning or Pure Acting Fails

flowchart TD
    subgraph PureReasoning["1. Pure Reasoning (Chain-of-Thought Only)"]
        R1["Question: 'How many rows are in table X?'"]
        R2["Model hallucinates row count from pre-trained weights ❌"]
    end

    subgraph PureActing["2. Pure Acting (Tool Calling Without Thoughts)"]
        A1["Calls tools blindly without intermediate planning"]
        A2["Gets stuck in infinite loops or misses edge cases ❌"]
    end

    subgraph ReActParadigm["3. ReAct (Reasoning + Acting)"]
        RA1["Thought: 'I need to check if table X exists first.'"]
        RA2["Action: list_tables()"]
        RA3["Observation: ['table_x', 'table_y']"]
        RA4["Thought: 'Table exists. Now I will run COUNT(*).'"]
        RA5["Action: execute_sql('SELECT COUNT(*) FROM table_x')"]
        RA6["Observation: 4,521,900"]
        RA7["Thought: 'I have the answer.'"]
        RA8["Final Answer: 'Table X contains 4,521,900 rows.' ✅"]
    end

    style PureReasoning fill:#7f1d1d,color:#fff
    style PureActing fill:#7f1d1d,color:#fff
    style ReActParadigm fill:#064e3b,color:#fff

The ReAct Formula:

$$\text{Trace} = \left( \text{Thought}_t \to \text{Action}_t \to \text{Observation}t \right){t=1}^T$$

  • Thought: The model plans what to do next, explains its reasoning, and evaluates prior results.
  • Action: The model emits a formal command to call an external tool.
  • Observation: The runtime environment executes the tool and injects the output back into the prompt.

2. Hands-on: Building a Pure Python ReAct Loop

Understanding how a ReAct loop runs without any third-party framework:

import re
from typing import Dict, Any

# Mock Database & Environment Tools
def tool_list_tables() -> str:
    return "Available Tables: ['fct_orders', 'dim_customers', 'dim_products']"

def tool_get_table_schema(table_name: str) -> str:
    schemas = {
        "fct_orders": "Schema: order_id INT, customer_id INT, amount_usd NUMERIC, status VARCHAR",
        "dim_customers": "Schema: customer_id INT, name VARCHAR, tier VARCHAR"
    }
    return schemas.get(table_name.strip(), f"Error: Table '{table_name}' does not exist.")

def tool_run_sql(query: str) -> str:
    if "count" in query.lower() and "fct_orders" in query.lower():
        return "Query Result: 1,250,000"
    return "Query Result: [(101, 'Acme Corp', 'Enterprise')]"

AVAILABLE_TOOLS = {
    "list_tables": lambda arg: tool_list_tables(),
    "get_schema": lambda arg: tool_get_table_schema(arg),
    "run_sql": lambda arg: tool_run_sql(arg)
}

# Simulated LLM Agent Brain
class SimulatedReActLLM:
    def __init__(self):
        self.step = 0

    def generate(self, prompt: str) -> str:
        self.step += 1
        if self.step == 1:
            return (
                "Thought: To find the order count, I must first verify the orders table name.
"
                "Action: list_tables[]"
            )
        elif self.step == 2:
            return (
                "Thought: The table name is 'fct_orders'. Now I need to check its schema to ensure it has valid rows.
"
                "Action: get_schema[fct_orders]"
            )
        elif self.step == 3:
            return (
                "Thought: The schema has an 'order_id' column. Now I will execute a COUNT(*) query.
"
                "Action: run_sql[SELECT COUNT(*) FROM fct_orders]"
            )
        else:
            return (
                "Thought: I have the count result.
"
                "Final Answer: The total number of orders in the fct_orders table is 1,250,000."
            )

def run_react_agent(user_question: str, max_turns: int = 5):
    llm = SimulatedReActLLM()
    prompt_history = f"Question: {user_question}
"

    print(f"=== Starting ReAct Agent: '{user_question}' ===
")

    for turn in range(max_turns):
        completion = llm.generate(prompt_history)
        print(f"--- Turn {turn + 1} ---")
        print(completion)
        prompt_history += completion + "
"

        # Check if the agent has reached a final answer
        if "Final Answer:" in completion:
            final_answer = completion.split("Final Answer:")[1].strip()
            print(f"
🎯 [Agent Completed]: {final_answer}")
            return final_answer

        # Parse Action
        match = re.search(r"Action:\s*(\w+)\[(.*?)\]", completion)
        if match:
            tool_name = match.group(1)
            tool_arg = match.group(2)

            if tool_name in AVAILABLE_TOOLS:
                observation = AVAILABLE_TOOLS[tool_name](tool_arg)
            else:
                observation = f"Error: Tool '{tool_name}' not recognized."

            print(f"Observation: {observation}
")
            prompt_history += f"Observation: {observation}
"

if __name__ == "__main__":
    run_react_agent("How many orders do we have recorded in our warehouse?")

Key Takeaways

  • The ReAct pattern alternates between Reasoning (Thought) and Acting (Action).
  • Interleaving prevents hallucinations: The model observes real facts from tools before taking its next step.
  • Dynamic recovery: If an action fails with a SQL error, the next Thought analyzes the error message and formulates an alternative query.
Find this content helpful? ☕ Buy me a coffee

Entity Details

Create New Item

help

Submit Technical Query

Have a question or run into an issue? Describe it below, upload an optional screenshot, and our engineering team will answer it!

image Attach image (optional)

Submit Feedback

build Free Developer Utility Free Tool
gavel

Privacy & Legal Disclaimer

1. Client-Side Browser Processing

All utility tools on DeepEngineerHub (including Image to PDF, Text Formatters, JSON Converters, and Encryptors) execute 100% locally within your client browser using WebAssembly and JavaScript. No uploaded images, text, or documents are transmitted, collected, or stored on remote servers.

2. Limitation of Liability ("As-Is" Provision)

Tools and services are provided free of charge for convenience and educational purposes "as-is" without warranties of any kind. DeepEngineerHub shall not be held liable for any data loss, formatting inconsistencies, or indirect damages resulting from tool usage.

3. Open Source & Third-Party Software

Certain utilities utilize open-source client libraries (such as jsPDF, Mermaid.js, Pyodide) licensed under MIT, Apache, or BSD open licenses. All intellectual property remains with their respective copyright holders.