In this tutorial, you’ll implement the ReAct (Reasoning and Acting) pattern, where agents alternate between reasoning about what to do and taking actions to accomplish goals.
Create state that tracks both messages and reasoning steps.
from typing import Annotated, Sequencefrom langchain_core.messages import BaseMessage, SystemMessagefrom langgraph.graph import StateGraph, START, END, add_messagesfrom typing_extensions import TypedDictclass ReactState(TypedDict): """State for ReAct agent.""" messages: Annotated[Sequence[BaseMessage], add_messages] iterations: int
2
Create reasoning tools
Define tools for the agent to use during reasoning.
from langchain_core.tools import tool@tooldef search_wikipedia(query: str) -> str: """Search Wikipedia for information. Args: query: The search query Returns: Summary from Wikipedia """ # Mock implementation - replace with real Wikipedia API wiki_data = { "python": "Python is a high-level programming language known for its simplicity...", "langgraph": "LangGraph is a framework for building stateful, multi-actor applications...", "ai": "Artificial Intelligence (AI) is the simulation of human intelligence..." } query_lower = query.lower() for key, value in wiki_data.items(): if key in query_lower: return value return f"No Wikipedia article found for '{query}'"@tooldef calculate_math(expression: str) -> str: """Evaluate a mathematical expression. Args: expression: Math expression to evaluate Returns: The calculated result """ try: result = eval(expression) return f"Result: {result}" except Exception as e: return f"Error calculating: {str(e)}"@tooldef analyze_text(text: str) -> str: """Analyze text and provide statistics. Args: text: Text to analyze Returns: Text statistics """ word_count = len(text.split()) char_count = len(text) sentence_count = text.count('.') + text.count('!') + text.count('?') return f"""Text Analysis: - Words: {word_count} - Characters: {char_count} - Sentences: {sentence_count} """tools = [search_wikipedia, calculate_math, analyze_text]
3
Create the ReAct agent node
Build the reasoning agent with explicit prompting.
from langchain_openai import ChatOpenAI# Initialize model with toolsmodel = ChatOpenAI(model="gpt-4", temperature=0)model_with_tools = model.bind_tools(tools)# ReAct system promptREACT_PROMPT = """You are a ReAct (Reasoning and Acting) agent. For each user query, follow this pattern:1. THOUGHT: Reason about what you need to do2. ACTION: Use a tool if needed3. OBSERVATION: Analyze the result4. Repeat until you can provide a final answerBe explicit about your reasoning process. Think step-by-step."""def agent_node(state: ReactState) -> dict: """ReAct agent that reasons before acting.""" messages = state["messages"] iterations = state.get("iterations", 0) # Add system prompt for ReAct pattern full_messages = [ SystemMessage(content=REACT_PROMPT), *messages ] # Call model response = model_with_tools.invoke(full_messages) return { "messages": [response], "iterations": iterations + 1 }
4
Create tool execution node
Build the node that executes tool calls.
from langgraph.prebuilt import ToolNodetool_node = ToolNode(tools)
5
Add routing with iteration limit
Create a router that limits iterations to prevent infinite loops.
MAX_ITERATIONS = 10def should_continue(state: ReactState) -> str: """Determine next step in ReAct loop.""" messages = state["messages"] last_message = messages[-1] iterations = state.get("iterations", 0) # Check iteration limit if iterations >= MAX_ITERATIONS: return "end" # If model wants to use tools, execute them if last_message.tool_calls: return "continue" # Otherwise, we're done return "end"
6
Build the ReAct graph
Assemble the complete ReAct agent.
# Initialize graphgraph = StateGraph(ReactState)# Add nodesgraph.add_node("agent", agent_node)graph.add_node("tools", tool_node)# Set entry pointgraph.add_edge(START, "agent")# Add conditional routing from agentgraph.add_conditional_edges( "agent", should_continue, { "continue": "tools", "end": END })# After tools, return to agent for reasoninggraph.add_edge("tools", "agent")# Compileapp = graph.compile()
7
Run the ReAct agent
Test the agent with reasoning-intensive queries.
from langchain_core.messages import HumanMessage# Simple queryresult = app.invoke({ "messages": [HumanMessage(content="What is LangGraph and calculate 10 * 15")], "iterations": 0})print(result["messages"][-1].content)# Agent will:# 1. Think: Need to search for LangGraph AND do a calculation# 2. Act: Use search_wikipedia tool# 3. Observe: Read Wikipedia result# 4. Act: Use calculate_math tool# 5. Observe: Get calculation result# 6. Respond: Provide comprehensive answer# Complex multi-step queryresult = app.invoke({ "messages": [HumanMessage( content="""Find information about Python, then analyze the text you found, and finally calculate how many words per sentence on average.""" )], "iterations": 0})print(f"Iterations used: {result['iterations']}")print(result["messages"][-1].content)# Agent will reason through multiple steps:# 1. Search Wikipedia for Python# 2. Analyze the returned text# 3. Calculate words per sentence# 4. Provide final answer
8
Complete example with logging
Here’s the full code with execution logging:
from typing import Annotated, Sequencefrom langchain_core.messages import BaseMessage, HumanMessage, SystemMessagefrom langchain_core.tools import toolfrom langchain_openai import ChatOpenAIfrom langgraph.graph import StateGraph, START, END, add_messagesfrom langgraph.prebuilt import ToolNodefrom typing_extensions import TypedDict# Stateclass ReactState(TypedDict): messages: Annotated[Sequence[BaseMessage], add_messages] iterations: int# Tools@tooldef search_wikipedia(query: str) -> str: """Search Wikipedia for information.""" return f"Python is a high-level programming language known for simplicity."@tooldef calculate_math(expression: str) -> str: """Evaluate a mathematical expression.""" try: return f"Result: {eval(expression)}" except Exception as e: return f"Error: {str(e)}"tools = [search_wikipedia, calculate_math]# Modelmodel = ChatOpenAI(model="gpt-4", temperature=0)model_with_tools = model.bind_tools(tools)REACT_PROMPT = """You are a ReAct agent. Think step-by-step:1. THOUGHT: Reason about the task2. ACTION: Use tools if needed3. OBSERVATION: Analyze resultsRepeat until you can answer."""# Nodesdef agent_node(state: ReactState) -> dict: messages = [SystemMessage(content=REACT_PROMPT), *state["messages"]] response = model_with_tools.invoke(messages) return {"messages": [response], "iterations": state.get("iterations", 0) + 1}tool_node = ToolNode(tools)# RouterMAX_ITERATIONS = 10def should_continue(state: ReactState) -> str: if state.get("iterations", 0) >= MAX_ITERATIONS: return "end" if state["messages"][-1].tool_calls: return "continue" return "end"# Graphgraph = StateGraph(ReactState)graph.add_node("agent", agent_node)graph.add_node("tools", tool_node)graph.add_edge(START, "agent")graph.add_conditional_edges("agent", should_continue, {"continue": "tools", "end": END})graph.add_edge("tools", "agent")app = graph.compile()# Run with loggingprint("ReAct Agent Starting...\n")result = app.invoke({ "messages": [HumanMessage(content="What is Python and what is 25 * 4?")], "iterations": 0})print(f"\nCompleted in {result['iterations']} iterations")print(f"\nFinal Answer: {result['messages'][-1].content}")
ReAct Agent Starting...[Agent reasoning through steps]THOUGHT: I need to search for Python information and calculate 25 * 4ACTION: Using search_wikipedia for PythonOBSERVATION: Found Python descriptionACTION: Using calculate_math for 25 * 4OBSERVATION: Result is 100Completed in 3 iterationsFinal Answer: Python is a high-level programming language known for simplicity. The calculation 25 * 4 equals 100.
@tooldef reflect_on_progress(current_state: str) -> str: """Reflect on progress toward the goal. Args: current_state: Description of current progress """ return f"Reflection: Analyzing '{current_state}' - consider alternative approaches"# Agent can now reflect on its own reasoning
def select_tools_dynamically(query: str) -> list: """Select relevant tools based on query.""" all_tools = [search_wikipedia, calculate_math, analyze_text] # Simple keyword matching selected = [] if any(word in query.lower() for word in ["search", "find", "what is"]): selected.append(search_wikipedia) if any(word in query.lower() for word in ["calculate", "math", "compute"]): selected.append(calculate_math) if any(word in query.lower() for word in ["analyze", "statistics"]): selected.append(analyze_text) return selected or all_tools
The ReAct pattern is powerful for complex tasks requiring multi-step reasoning. The explicit reasoning traces make agent behavior more interpretable and debuggable.