Agentic AI 5 min

ReAct: Reasoning and Acting

The foundational pattern behind most agents: interleaved reasoning traces and tool calls that build on each other.

ReAct (Reasoning + Acting) is the most influential agent pattern in modern AI. Published by Yao et al. in 2022, it interleaves chain-of-thought reasoning traces with tool-use actions, creating a loop that is transparent and much easier to debug than older approaches.

ReAct Loop - Thought ยท Action ยท Observation
๐Ÿ’ฌ
User Query"What is AAPL's drop from its 52-week high?"
๐Ÿ’ญ
ThoughtI need the current stock price first
โšก
Actionsearch("AAPL stock price today")
๐Ÿ‘๏ธ
ObservationAAPL is trading at $189.43
๐Ÿ’ญ
ThoughtNow I need the 52-week high
โšก
Actionsearch("AAPL 52-week high 2026")
๐Ÿ‘๏ธ
Observation52-week high was $237.23
๐Ÿ’ญ
Final Thought(237.23 โˆ’ 189.43) / 237.23 = 20.1%
โœ…
AnswerAAPL is 20.1% below its 52-week high
Why ReAct Works
The interleaved reasoning gives the model a working scratchpad to plan each next action based on what it just observed. Without the thought steps, models tend to loop or take actions that do not move toward the goal.
Two Ways to Implement the Loop
The original ReAct paper has the model write 'Action: tool_name("input")' as plain text, which your code then parses with a regex. Most production systems today instead use the model provider's native structured function-calling API, where the model returns a typed tool-call object directly, no text parsing needed. The Thought-Action-Observation logic is identical either way. The code below uses the text-parsed version because it makes every part of the loop visible, and it is the exact approach used in the companion repo's `research-agent`.
python
import json
import re
import httpx
from openai import OpenAI

client = OpenAI()

SYSTEM_PROMPT = """You solve tasks step by step. Use this EXACT format:

Thought: [your reasoning about what to do next]
Action: [tool_name]
Action Input: [JSON input for the tool]

When you have the final answer:
Thought: I have enough information.
Action: finish
Action Input: {"answer": "your final answer here"}

Available tools:
- search: Search the web. Input: {"query": "your search query"}
- finish: End the task. Input: {"answer": "..."}
"""

def search(query: str) -> str:
    resp = httpx.post("https://api.tavily.com/search",
                       json={"api_key": TAVILY_API_KEY, "query": query, "max_results": 3})
    results = resp.json().get("results", [])
    return "\n".join(f"{r['title']}: {r['content'][:200]}" for r in results)

TOOLS = {"search": search}

def parse_action(text: str) -> tuple[str, dict] | None:
    """Extract the Action and Action Input from the model's raw text."""
    action_match = re.search(r"Action:\s*(.+)", text)
    input_match = re.search(r"Action Input:\s*(\{.+?\})", text, re.DOTALL)
    if not action_match:
        return None
    action = action_match.group(1).strip()
    try:
        action_input = json.loads(input_match.group(1)) if input_match else {}
    except json.JSONDecodeError:
        action_input = {}
    return action, action_input

def run_react_agent(task: str, max_iterations: int = 10) -> str:
    history = [{"role": "user", "content": task}]

    for _ in range(max_iterations):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=[{"role": "system", "content": SYSTEM_PROMPT}] + history,
        )
        text = response.choices[0].message.content
        history.append({"role": "assistant", "content": text})

        parsed = parse_action(text)
        if not parsed:
            break

        action, action_input = parsed
        if action == "finish":
            return action_input.get("answer", "No answer produced.")

        if action in TOOLS:
            observation = TOOLS[action](**action_input)
        else:
            observation = f"Error: unknown tool '{action}'"

        history.append({"role": "user", "content": f"Observation: {observation}"})

    return "Max iterations reached without a final answer."
Run the Real Thing
This is a trimmed version of `agentic/research-agent/agent.py` in the companion repo, which has real PDF reading, source deduplication, and an eval harness (`python eval.py`) that tests the parser and the full loop end to end. The next module lesson builds on that eval harness directly.
What's Next
ReAct is one way to structure the loop, not the only one. Next, two alternative patterns and when they beat ReAct.