-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Overview
The ReAct (Reasoning + Acting) pattern is an agent architecture that alternates between explicit reasoning steps and targeted tool calls. The agent generates reasoning traces (thoughts) before each action, making the decision-making process more transparent and explainable. This pattern is particularly effective for complex multi-step tasks requiring explicit reasoning.
How It Works
The ReAct agent follows a cyclical pattern:
- Thought: LLM generates reasoning about what to do next
- Action: LLM decides which tool to call with what parameters
- Observation: Tool result is fed back to the LLM
- Repeat until reaching a Final Answer
Question: What is the weather in Paris and London?
Thought: I need to check the weather in Paris first
Action: weather_api
Action Input: {"city": "Paris"}
Observation: Paris - 18°C, Sunny
Thought: Now I need to check London
Action: weather_api
Action Input: {"city": "London"}
Observation: London - 15°C, Cloudy
Thought: I have both weather reports, I can now answer
Final Answer: Paris is 18°C and sunny, London is 15°C and cloudy
Reference Implementations
- LangChain ReAct Agent - Official LangChain implementation
- LangGraph ReAct Template - Modern LangGraph implementation
- LangGraph Tutorial: ReAct from Scratch - Step-by-step guide
- Original ReAct Paper - "ReAct: Synergizing Reasoning and Acting in Language Models"
Proposed PyWorkflow Implementation
from pyworkflow import workflow, step, agent
from pyworkflow.agents import ReActAgent, Tool
# Define tools
@step()
async def search(query: str) -> str:
"""Search the web for information."""
return await search_api.query(query)
@step()
async def calculator(expression: str) -> float:
"""Evaluate mathematical expressions."""
return eval(expression)
# Create ReAct agent
@agent(
pattern="react",
model="claude-sonnet-4-5-20250929",
tools=[search, calculator],
max_iterations=10,
)
async def research_agent(query: str):
"""
Research agent with explicit reasoning traces.
The agent will:
1. Generate thoughts about the query
2. Decide which tools to use
3. Observe results
4. Repeat until confident in answer
"""
pass # Agent loop handled by framework
# Use the agent
result = await research_agent.run(
"What is the population of Paris multiplied by 2?"
)
print(result.answer)
print(result.reasoning_trace) # Full thought-action-observation logEvent Types
ReAct agents record these events in PyWorkflow's event log:
-
AGENT_STARTED - Agent execution begins
{"run_id": "abc123", "query": "...", "tools": ["search", "calculator"]} -
AGENT_THOUGHT - LLM generates reasoning
{"thought": "I need to search for Paris population first", "iteration": 1} -
AGENT_ACTION - Tool call initiated
{"action": "search", "action_input": {"query": "Paris population 2026"}, "iteration": 1} -
AGENT_OBSERVATION - Tool result received
{"observation": "Paris population is 2.1M", "iteration": 1} -
AGENT_FINAL_ANSWER - Agent reaches conclusion
{"answer": "4.2 million", "total_iterations": 3} -
AGENT_COMPLETED / AGENT_FAILED - Execution ends
Implementation Details
Prompt Structure
The ReAct prompt template should follow this format:
Answer the following question as best you can. You have access to the following tools:
{tool_descriptions}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {query}
Thought:
Event Replay
During replay, the agent should:
- Skip LLM calls for already-recorded thoughts/actions
- Return cached observations from event log
- Fast-forward to the last iteration before suspension
- Continue reasoning from that point
PyWorkflow Integration
# Internal implementation (pyworkflow/agents/react.py)
class ReActAgent:
async def run(self, query: str):
ctx = get_context()
iteration = 0
while iteration < self.max_iterations:
# Check if thought exists in event log (replay mode)
cached_thought = ctx.get_event(EventType.AGENT_THOUGHT, iteration=iteration)
if cached_thought:
thought = cached_thought.data["thought"]
else:
# Generate new thought
thought = await self._generate_thought(query, history)
await ctx.record_event(EventType.AGENT_THOUGHT, {"thought": thought, "iteration": iteration})
# Similar pattern for action and observation
if "Final Answer:" in thought:
return self._extract_answer(thought)
action, action_input = self._parse_action(thought)
# Execute tool as workflow step (durable, retryable)
observation = await self._execute_tool(action, action_input)
await ctx.record_event(EventType.AGENT_OBSERVATION, {"observation": observation, "iteration": iteration})
iteration += 1
raise MaxIterationsExceeded()Trade-offs
Pros
- Explainability: Explicit reasoning traces make decisions transparent
- Debuggability: Easy to see where agent went wrong
- Human-in-the-loop: Can intervene at thought/action boundaries
- Educational: Great for understanding agent behavior
- Better for complex reasoning: LLM can "think out loud" before acting
Cons
- ~40% slower: Extra LLM tokens for thoughts vs direct tool calling
- Higher cost: More tokens generated per query
- Verbosity: Reasoning traces can be lengthy
- Prompt engineering: Requires careful prompt design to avoid loops
- Token limits: Long conversations may exceed context window
Comparison to Tool-Calling Agents
| Aspect | ReAct | Tool-Calling |
|---|---|---|
| Speed | Slower (reasoning overhead) | ~40% faster |
| Cost | Higher (more tokens) | Lower |
| Explainability | High (visible reasoning) | Low (black box) |
| Use Case | Complex reasoning tasks | Straightforward tool use |
Related Issues
- #[ISSUE_2] - Tool-Calling Agent (simpler, faster alternative)
- #[ISSUE_3] - Plan-and-Execute Agent (different planning strategy)
- #[ISSUE_4] - Router/Dispatcher Agent (meta-routing to ReAct agents)
References
- ReAct: Synergizing Reasoning and Acting in Language Models (2022)
- LangChain ReAct Agent Documentation
- LangGraph ReAct Tutorial
- Using LangChain ReAct Agents to Answer Complex Questions
- LangChain ReAct GitHub Template
Implementation Checklist
- Create
pyworkflow/agents/react.pywith ReActAgent class - Add ReAct prompt templates
- Implement thought-action-observation loop
- Add event types: AGENT_THOUGHT, AGENT_ACTION, AGENT_OBSERVATION
- Implement event replay for agent state
- Add max_iterations safeguard
- Create @agent(pattern="react") decorator
- Add tests for ReAct agent execution
- Add tests for event replay
- Document ReAct agent in examples/
- Add integration tests with mock LLM