AI — AI Agent Development

ReAct Pattern Implementation in Python

Direct answer

ReAct interleaves reasoning (Thought), tool use (Action), and results (Observation) in a loop until the model emits a final answer. In Python you implement it by prompting the model with the running transcript, stopping generation at the Observation marker, parsing the action, executing it, appending the real observation, and repeating under a step cap. Native tool-calling APIs have largely replaced text-parsed ReAct, but the loop is worth building once — every modern agent descends from it.

ReAct — Reason plus Act — is the pattern underneath nearly every agent framework: the model thinks out loud, picks an action, sees the result, and repeats. Implementing it raw in Python is the fastest way I know to understand what your framework does for you, and what it hides.

Key facts, with sources

  • LangChain's State of Agent Engineering survey of 1,340 practitioners found 57.3 percent of organizations have agents running in production, with another 30.4 percent actively developing them. (LangChain)
  • The same LangChain survey found 89 percent of organizations have implemented observability for their agents but only 52 percent do systematic evaluation. (LangChain)
  • Deloitte predicts 25 percent of companies using generative AI launched agentic AI pilots in 2025, growing to 50 percent by 2027. (Deloitte Insights)
  • By December 2025 the Model Context Protocol had over 97 million monthly SDK downloads and more than 10,000 active MCP servers in production use. (Pento)
  • PwC's AI agent survey found 79 percent of companies report AI agents are already being adopted, and 66 percent of adopters say agents deliver measurable value through increased productivity. (PwC)
  • In December 2025 Anthropic donated the Model Context Protocol to the Agentic AI Foundation under the Linux Foundation, co-founded with Block and OpenAI, making the agent connector layer vendor-neutral. (Anthropic)

What ReAct actually is

ReAct's insight is that forcing the model to write an explicit Thought before each Action improves action selection: the model commits to a rationale, which constrains the next step to something coherent instead of a reflex. The Observation then grounds the next Thought in reality rather than the model's guess about what the tool returned.

Contrast this with plan-then-execute, where the model writes a full plan upfront and code executes it blindly — efficient when the environment is predictable, brittle when step three's result should change step four. ReAct re-decides after every observation, which is exactly what you want for research, debugging, and multi-system lookups where the path isn't knowable in advance.

The minimal loop

The whole pattern fits in one function: maintain a transcript, ask the model to continue it, stop generation before it fabricates its own Observation, execute the parsed action, append the real observation, and go again. The stop sequence is the load-bearing trick — without it the model happily hallucinates tool results and keeps going, and you'll debug 'why is my agent inventing data' for an afternoon.

A complete text-based ReAct loop
import re
import anthropic

MODEL = "<latest-claude-model-id>"  # always use the latest model id
client = anthropic.Anthropic()

SYSTEM = """Answer the question by interleaving Thought, Action, and Observation.
Available actions: search[query], calculate[expression], finish[answer].
Format:
Thought: <your reasoning>
Action: <name>[<input>]
Stop after each Action and wait for the Observation."""

ACTION_RE = re.compile(r"Action:\s*(\w+)\[(.*)\]", re.DOTALL)

def react(question: str, actions: dict, max_steps: int = 8) -> str:
    transcript = f"Question: {question}\n"
    for _ in range(max_steps):
        response = client.messages.create(
            model=MODEL, max_tokens=1024, system=SYSTEM,
            stop_sequences=["Observation:"],
            messages=[{"role": "user", "content": transcript}],
        )
        text = response.content[0].text
        transcript += text
        match = ACTION_RE.search(text)
        if not match:
            return text  # model answered directly
        name, arg = match.group(1), match.group(2).strip()
        if name == "finish":
            return arg
        observation = actions.get(name, lambda a: f"Unknown action: {name}")(arg)
        transcript += f"\nObservation: {observation}\n"
    return "Step budget exhausted without an answer"

Parsing is the weak point

Regex over free text is where text-based ReAct earns its bad reputation. Models occasionally emit malformed actions — a missing bracket, two actions in one turn, an action name that doesn't exist. Treat every parse failure as a recoverable observation, not an exception: append 'Invalid action format; available actions are search, calculate, finish' to the transcript and let the model retry. It almost always self-corrects on the next step.

Validate the action name against an allowlist before dispatching, and treat the action input as untrusted — it flows from model output, which flows from whatever the tools previously returned. If an observation can contain third-party text (search results, documents), a hostile string in it can steer the next Action; the allowlist and input validation are your floor.

Stop conditions and step budgets

A ReAct loop has three exits and you need all of them: an explicit finish action carrying the answer, a response with no parseable action (the model answered in prose), and the step cap. The cap is not decoration — a model stuck on an unanswerable question will alternate between two searches indefinitely, billing you per cycle.

I add cheap loop detection on top: if the same action with the same input repeats, inject a nudge observation — 'You already tried this; the result will not change. Try a different approach or finish with your best answer.' That single line resolves a surprising share of stuck runs. Log the transcript for every budget-exhausted run; they're the best debugging corpus you'll get.

Graduate to native tool calling

Modern model APIs return tool invocations as structured blocks — name and JSON arguments — which deletes the regex, the format prompt, and the malformed-action class of bugs entirely. The reasoning half of ReAct hasn't disappeared; it moved into the model's native reasoning, interleaved between tool calls without you managing a Thought format.

Everything else transfers unchanged: the transcript becomes a proper messages array with tool-use and tool-result blocks, and your step budgets, loop detection, and allowlists apply identically. I'd only reach for text-based ReAct today with a model that lacks tool calling, or as a teaching exercise — which, to be fair, is the best reason to build it once.

The same loop with native tool calling
response = client.messages.create(
    model=MODEL, max_tokens=1024,
    tools=[search_tool, calculate_tool],  # JSON-schema tool definitions
    messages=messages,
)
if response.stop_reason == "tool_use":
    calls = [b for b in response.content if b.type == "tool_use"]
    # execute each call, append tool_result blocks, and loop — no regex

When to hire senior help

Bring in senior help when the agent must touch production systems or customer data, because integration, security, and reliability are where inexperienced builds fail rather than model quality. If a pilot is stuck at the demo stage, an experienced engineer adding evals and guardrails is usually faster and cheaper than rebuilding from scratch. If your stack includes React Native + Python + AI, a senior engineer who owns the full product beats coordinating multiple juniors.

Bottom line

Dhairya Senjaliya ships AI — AI Agent Development projects worldwide — book a scoping call to discuss your specific situation.

Common pitfalls to avoid

  • Shipping agents with logging but no evals, so teams can see traces but never measure task success rates and regressions ship silently
  • Giving one agent dozens of tools instead of a focused toolset, which degrades tool-selection accuracy and inflates token costs
  • Hand-rolling custom integration glue for every data source instead of using MCP, which is now the vendor-neutral standard backed by Anthropic, OpenAI, and the Linux Foundation
  • Validating only on happy-path demo prompts and skipping failure-mode testing, a core reason roughly 88 percent of agent pilots never reach production

Frequently asked questions

What is the ReAct pattern in AI agents?

ReAct (Reason + Act) is an agent loop where the model alternates explicit reasoning steps (Thought), tool invocations (Action), and tool results (Observation) until it produces a final answer. Writing the rationale before each action improves tool selection, and grounding each new thought in a real observation prevents the model from acting on guessed results. Most modern agent frameworks are refinements of this loop.

Is ReAct still relevant now that models support native tool calling?

The text format — parsing Thought and Action lines with regex — is largely obsolete; native tool calling returns structured invocations and eliminates parsing bugs. But the pattern itself survives: interleaved reasoning and acting is exactly what tool-calling agents do. Step budgets, loop detection, and action allowlists from classic ReAct transfer directly to modern implementations.

How do I stop a ReAct loop from repeating the same action?

Detect it structurally: track executed action-and-input pairs, and when one repeats, inject an observation telling the model the result won't change and to try another approach or finish with its best answer. Pair that with a hard step cap that ends the run into a reviewable failed state — a genuinely stuck model will otherwise alternate between the same lookups indefinitely.

How long does it take to build a production-ready AI agent?

A convincing prototype takes days, but production-grade agents with evals, guardrails, monitoring, and integration into real systems typically take six to twelve weeks. The gap between demo and production is exactly where most pilots stall, so budget for the hardening phase up front.

Which agent framework should we use?

Framework choice matters less than evaluation and observability discipline; plenty of production teams run thin custom loops directly on the model provider's SDK. Pick based on your team's stack and tolerance for lock-in, and standardize integrations on MCP so tools are portable across frameworks.

What does an AI agent cost to run?

Agent tasks routinely consume several times the tokens of a single chat call because of tool loops and retries, so cost scales with loop length and model tier. Prompt caching, batch processing, and routing subtasks to cheaper models typically cut agent costs by 50 to 90 percent.

Bottom line: Dhairya Senjaliya ships AI — AI Agent Development projects worldwide. Book a scoping call at https://dhairyasenjaliya.com/#book-call.

Sources

Related guides

Keep up with new guides

New deep-dive guides on React Native, Python, and AI ship regularly. Subscribe via RSS or follow on LinkedIn.

Want help implementing this?

30-minute scoping call · Clear milestones · Senior engineer ownership