What is a ReAct agent?
A ReAct agent (Reason + Act) is an AI agent architecture where the LLM alternates between reasoning steps and action steps — calling tools, observing results, and reasoning again — until it reaches a final answer. ReAct was introduced in the paper "ReAct: Synergizing Reasoning and Acting in Language Models" by Yao et al. (2022, arXiv:2210.03629) and is now the default architecture for most tool-calling AI agents.
The name combines Reasoning and Acting. The key insight: LLMs are better at completing tasks when they can interleave reasoning ("I need to find the current weather") with actions ("call get_weather('Tokyo')") rather than planning everything upfront.
How the ReAct loop works
User input
↓
[Reason] LLM thinks: "I need the weather in Tokyo. I'll call get_weather."
↓
[Act] Agent calls: get_weather(city="Tokyo")
↓
[Observe] Result: "22°C, sunny"
↓
[Reason] LLM thinks: "I have the answer. I can now respond."
↓
Final answer to user
Each cycle is a "thought → action → observation" triplet. The loop continues until the LLM decides it has enough information to respond, or a recursion limit is hit.
ReAct vs plain LLM call
| Plain LLM call | ReAct agent | |
|---|---|---|
| Steps | 1 | Multiple (until done) |
| Tool calls | No | Yes |
| Can look things up | No | Yes |
| Can write and run code | No | Yes |
| Handles multi-step tasks | No | Yes |
| Stops when? | After one response | When goal is met or limit hit |
Build a ReAct agent in Python
AgentFlow ships a prebuilt ReactAgent that implements the full Reason-Act loop. You only supply the tools.
from agentflow.prebuilt.agent import ReactAgent
from agentflow.core.state import AgentState, Message
def get_stock_price(ticker: str) -> str:
"""Get the current stock price for a ticker symbol."""
# Replace with a real API call
prices = {"AAPL": "182.50", "GOOG": "175.30", "MSFT": "420.10"}
return f"${prices.get(ticker.upper(), 'not found')}"
def calculate(expression: str) -> str:
"""Evaluate a mathematical expression. E.g. '182.50 * 100'."""
try:
return str(eval(expression, {"__builtins__": {}}, {}))
except Exception as e:
return f"Error: {e}"
agent = ReactAgent(
model="google/gemini-2.5-flash",
system_prompt=[{"role": "system", "content": "You are a financial assistant. Use tools to look up real data."}],
tools=[get_stock_price, calculate],
)
app = agent.compile()
result = app.invoke(
{"messages": [Message.text_message("What is 100 shares of AAPL worth?")]},
config={"thread_id": "finance-1", "recursion_limit": 10},
)
print(result["messages"][-1].text())
# Agent will: call get_stock_price("AAPL") → get $182.50
# Then call: calculate("182.50 * 100") → get 18250.0
# Then respond: "100 shares of AAPL are worth $18,250."
Building a ReAct agent with a custom graph
For full control over routing logic, build the graph manually:
from agentflow.core.graph import Agent, StateGraph, ToolNode
from agentflow.core.state import AgentState, Message
from agentflow.utils import END
tools = [get_stock_price, calculate]
tool_node = ToolNode(tools)
agent_node = Agent(
model="google/gemini-2.5-flash",
system_prompt=[{"role": "system", "content": "Financial assistant."}],
tool_node="TOOLS",
)
def route(state: AgentState) -> str:
last = state.context[-1] if state.context else None
if last and getattr(last, "tools_calls", None) and last.role == "assistant":
return "TOOLS"
return END
graph = StateGraph(AgentState)
graph.add_node("AGENT", agent_node)
graph.add_node("TOOLS", tool_node)
graph.add_conditional_edges("AGENT", route, {"TOOLS": "TOOLS", END: END})
graph.add_edge("TOOLS", "AGENT")
graph.set_entry_point("AGENT")
app = graph.compile()
The conditional edge route is the heart of the ReAct loop: if the LLM returned tool calls, route to the tool node; otherwise, the agent is done.
Serving a ReAct agent
Point agentflow.json at your compiled app and start the server:
{
"agent": "graph.agent:app",
"env": ".env"
}
agentflow api
You now have a production REST + SSE API for your ReAct agent with no extra code.
When to use a ReAct agent
ReAct is the right pattern when:
- The task requires looking up information from external tools
- The number of tool calls is not known in advance
- The agent needs to adapt its plan based on intermediate results
ReAct is not the right pattern when:
- You need a guaranteed sequence of steps (use a fixed graph instead)
- You need structured output in a specific schema (use
StructuredOutputAgent) - You are orchestrating multiple specialized agents (use
SupervisorTeamAgentorSwarmAgent)