Graph Components & Agent Implementation

Build a full ReAct agent with tools — tool nodes, conditional routing, tool binding, and error handling.

Advanced · 22 min read

Building a ReAct Agent with Tools

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.messages import BaseMessage, HumanMessage
from langchain_core.tools import tool
import operator, math

@tool
def calculator(expression: str) -> str:
    """Evaluate a mathematical expression like '2 * (3 + 4)'."""
    try:
        result = eval(expression, {"__builtins__": {}}, {"math": math, "sqrt": math.sqrt})
        return f"Result: {result}"
    except Exception as e:
        return f"Error: {str(e)}"

@tool
def reverse_string(text: str) -> str:
    """Reverse a string."""
    return f"Reversed: {text[::-1]}"

tools = [calculator, reverse_string]

class AgentState(TypedDict):
    messages: Annotated[list[BaseMessage], operator.add]

llm_with_tools = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools(tools)

def agent_node(state: AgentState) -> AgentState:
    return {"messages": [llm_with_tools.invoke(state["messages"])]}

def should_use_tools(state: AgentState) -> str:
    last = state["messages"][-1]
    return "tools" if hasattr(last, "tool_calls") and last.tool_calls else "end"

builder = StateGraph(AgentState)
builder.add_node("agent", agent_node)
builder.add_node("tools", ToolNode(tools))
builder.set_entry_point("agent")
builder.add_conditional_edges("agent", should_use_tools, {"tools": "tools", "end": END})
builder.add_edge("tools", "agent")
graph = builder.compile()

result = graph.invoke({
    "messages": [HumanMessage(content="What is sqrt(144) + 50? Also reverse the word 'Python'.")]
})
for msg in result["messages"]:
    print(f"[{msg.__class__.__name__}]: {msg.content}")

Part of the LangChain, LangGraph & Vector DBs series on Tekivex. Browse all tutorials or explore our open-source products.