Skip to content
NLEN
Illustration: ReAct patterns dissected: reasoning and action

ReAct patterns dissected: how reasoning and actions merge

By Ivo Donker — compiled with AI assistance (Claude & Gemini) · August 23, 2026

What you need to know first

This article falls under module 6 (Responsible & Outlook). To fully grasp the mechanics of ReAct, it helps to have a clear understanding of the fundamentals of autonomous systems. First read the difference between an agent and a chatbot to understand the shift from one-way traffic to iterative processing. In addition, this methodology builds directly on structured prompting; check out the analysis on reasoning in intermediate steps via chain-of-thought to see how lines of thought are built up internally.

Language models excel at language understanding and pattern recognition, but they are inherently closed systems. Without external connections, they have no knowledge of current facts, cannot guarantee deterministic calculations, and cannot perform physical or digital actions. Early attempts to solve this split into two directions: generating internal reasoning traces on one hand, and making direct API calls on the other. Both approaches on their own proved vulnerable to hallucinations or blind failure.

The ReAct pattern (derived from Reasoning and Acting) solves this by weaving reasoning and acting together into a continuous, dynamic feedback loop. Instead of drawing up a static plan in advance or immediately calling a tool, the model first formulates a thought about the current status, chooses a targeted action, observes the actual system output, and adjusts its next thought accordingly. In this article, we dissect the internal anatomy of this cycle, analyze concrete implementations, and map out the structural vulnerabilities.

The anatomy of the ReAct cycle: Thought, Action, Observation

The core of the ReAct paradigm consists of three fundamental building blocks that continuously follow one another until a final answer is reached. The model runs in a controlled execution loop in which each step enriches the context for the next step. This prevents a language model from having to 'guess' at intermediate results.

The three steps manifest as follows in the interaction history:

This alternation creates a self-correcting system. When an observation produces an error message or an unexpected result, the model notices this in the next Thoughtstep, and can formulate an alternative action instead of getting stuck in a hallucinated assumption.

Why pure action or pure reasoning fails

To understand the value of ReAct, we look at the two extremes that preceded this pattern: pure Chain-of-Thought (CoT) and uncoordinated tool execution (Action-only).

With a pure CoT approach, the model generates a long series of intermediate steps without interacting with external systems. This works excellently for closed mathematical problems or logic puzzles where all variables are contained in the prompt. However, as soon as the context requires external facts (such as exchange rates, current database records, or specific domain knowledge), CoT leads to compounding errors: a small inaccuracy in step two mutates into a complete hallucination by step five.

With an Action-only approach, the model skips the reasoning step and generates API calls directly based on the initial question. As a result, it lacks the ability to break complex goals down into subgoals. The model lacks the token 'workspace' needed to determine which parameter choices make sense. The result is often a series of inefficient, repetitive, or faulty calls, because the system does not evaluate along the way whether an action has brought it closer to the goal.

Property Pure Chain-of-Thought Action-only (Direct APIs) ReAct (Fluid integration)
External fact verification None (fully dependent on weights) Direct via tools, but without synthesis Continuous via intermediate observations
Self-correction Very low with faulty premises Limited to syntax error handling High: model reinterprets errors
Context overhead Low to medium Low High due to accumulating history
Suitable for multi-hop tasks Moderate (errors stack up) Poor (no planning capacity) Excellent

Constructing the execution loop: from prompt to tool call

In practice, implementing a ReAct system requires a tight control layer outside the model itself. The runtime injects a system prompt that describes the available tools and enforces the desired output format. Modern implementations rely heavily on native schemas for this; consult the analysis on tool calling and structured output to discover how models today parse JSON schemas instead of plain text.

Below is a conceptual Python loop demonstrating how a ReAct orchestrator drives the interaction between generation, parsing, and execution:

def run_react_agent(user_query: str, max_steps: int = 5) -> str:
  context = f"Gebruikersvraag: {user_query}\n"
  
  for step in range(max_steps):
    # 1. Genereer Thought + Action
    response = llm.generate(
      prompt=context,
      stop_sequences=["Observation:"]
    )
    context += response.text
    
    # Controleer of het model klaar is
    if "Final Answer:" in response.text:
      return response.text.split("Final Answer:")[-1].strip()
    
    # 2. Parse de gewenste tool-aanroep
    action_name, action_input = parse_action(response.text)
    
    # 3. Voer de tool extern uit (niet door LLM gegenereerd)
    tool_result = execute_tool(action_name, action_input)
    
    # 4. Injecteer de observatie terug in de context
    observation_str = f"\nObservation: {tool_result}\n"
    context += observation_str
    
  return "Taak niet voltooid binnen het maximaal aantal stappen."

When we set this up in a production-ready way within distributed architectures, the implementation often shifts to API parameters that trigger specific actions. See the overview on function calling via the API for the exact payload structures and status codes needed to trigger functions programmatically.

A practical example: retrieving Dutch real estate information

Let's walk through a concrete multi-hop question: "What is the average energy label in the neighborhood of Utrecht's city hall, and how many active monuments are registered there?"

A standard model would immediately start hallucinating neighborhood statistics. A ReAct agent solves this step by step:

Thought: Ik moet eerst achterhalen in welke wijk het stadhuis van Utrecht ligt.
Action: bag_lookup(locatie="Stadhuis Utrecht")
Observation: {"adres": "Korte Minrebroederstraat 2", "wijk": "Binnenstad", "gemeente": "Utrecht"}

Thought: Het stadhuis ligt in wijk 'Binnenstad'. Nu moet ik twee datapunten verzamelen: het gemiddelde energielabel en het aantal monumenten in 'Binnenstad, Utrecht'. Ik begin met het energielabel.
Action: ep_online_query(wijk="Binnenstad", gemeente="Utrecht", metric="gemiddeld_label")
Observation: {"wijk": "Binnenstad", "gemiddeld_label": "C", "dekkingsgraad": "68%"}

Thought: Het energielabel is bekend (C). Nu moet ik het aantal actieve monumenten ophalen voor dezelfde wijk.
Action: monumenten_register_query(wijk="Binnenstad", gemeente="Utrecht", status="actief")
Observation: {"wijk": "Binnenstad", "totaal_monumenten": 1421}

Thought: Ik heb beide gegevens verzameld. Ik kan nu het volledige antwoord formuleren voor de gebruiker.
Action: finish()
Final Answer: Het stadhuis van Utrecht bevindt zich in de wijk Binnenstad. In deze wijk is het gemiddelde geregistreerde energielabel C (bij een dekkingsgraad van 68%) en staan er momenteel 1.421 actieve monumenten geregistreerd.

In this scenario, each observation acts as an anchor point. If the BAG lookup produces no result (for example due to a typo), the next Thought could read: "The location was not recognized; let me search for 'Korte Minrebroederstraat' as an alternative search term."

Pitfalls and failure modes in ReAct architectures

Although ReAct is considerably more reliable than static prompts, the dynamics introduce new risks that are inherent to iterative context building.

The main failure mechanisms are:

ReAct versus Plan-and-Solve: architectural trade-offs

ReAct is not the only paradigm for autonomous agents. An important alternative is the Plan-and-Solvepattern (which includes approaches such as BabyAGI and plan-driven decomposition). Where ReAct operates opportunistically and reactively — deciding step by step what is useful right now — a Plan-and-Solve architecture first builds a complete graph of all required subtasks and then executes them sequentially or in parallel.

Dimension ReAct Plan-and-Solve
Planning horizon Short (1 step ahead) Long (full task decomposition upfront)
Flexibility with errors Very high: can switch strategy immediately Moderate: requires dynamic replanning of the entire graph
Token usage Often higher due to repeated context accumulation Often lower per subtask, provided the plan is correct from the start
Parallelization Difficult (each step depends on the previous observation) Easy for independent subgoals
Ideal use case Exploratory research, vague data, debugging Structured workflows with predictable steps

In modern systems, we often see hybrid forms: a high-level planner breaks a complex task down into three sub-projects, after which a ReAct loop runs within each sub-project to handle the actual execution and error resolution.

Testing, evaluating, and observing ReAct agents

Because a ReAct agent navigates a search space non-deterministically, classic software tests fall short. An agent can still arrive at the same correct answer via three different routes with different numbers of steps. To measure effectiveness, engineers analyze both the final result and the trajectory followed. Read the guidelines on evaluations for agents and testing strategies for a methodical approach to unit and integration tests.

At the system level, we use specific metrics to quantify the reliability of the ReAct loop:

For deeper benchmark methodologies and automated evaluation of multi-step interactions, the article on evaluating AI agents through trajectory analysis offers detailed protocols and scoring rubrics.

Continue reading with

Now that the dynamic between reasoning and actions is clear, you can dive deeper into optimizing the reasoning process itself. See how advanced search structures are set up in tree-of-thoughts and graph-reasoning structures. Do you work with models that independently produce long reasoning traces before the output? Then read the analysis on built-in test-time compute in modern reasoning models.