
A beginner's guide to LangGraph, explaining StateGraph, nodes, edges, and state management with a practical Python code example.
As LLM applications evolve from simple single-turn prompts to complex, multi-step systems, traditional linear pipelines (like LangChain's standard chains) start to hit their limits. Real-world tasks often require loops, conditional routing, and state persistence—such as an agent trying a task, checking the output, and correcting its own mistakes.
This is where LangGraph comes in. Developed by the LangChain team, LangGraph is a library designed for building stateful, multi-actor applications with LLMs. Unlike standard DAG (Directed Acyclic Graph) runners, LangGraph supports cyclic graphs, making it the perfect tool for building advanced AI agents.
In this beginner's guide, we will break down the core concepts of LangGraph and build a simple, working agent using Python.
LangGraph is built on a few core pillars:
TypedDict or Pydantic model) that is passed from node to node. Each node can read from and write to this state.State as input, performs some computation (like calling an LLM or querying a database), and returns an update to the State.Let's build a simple chatbot agent that can perform a basic math operation using a tool. If the user asks a math question, the agent routes the request to a calculator tool; otherwise, it answers directly.
First, make sure you have the required packages installed:
pip install langgraph langchain-openai
We will define a state that tracks the messages in our conversation. LangGraph provides a built-in MessagesState that simplifies this, but here is how you define one explicitly:
from typing import Annotated, Sequence
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
class AgentState(TypedDict):
# add_messages tells LangGraph to append new messages instead of overwriting the list
messages: Annotated[Sequence[BaseMessage], add_messages]
Next, we define our LLM and the calculator tool.
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two integers together."""
return a * b
tools = [multiply]
# Bind the tools to the model
model = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools(tools)
We need two nodes: one to call the model, and one to execute the tool if needed.
from langchain_core.messages import ToolMessage
from langgraph.prebuilt import ToolNode
# Node to call the model
def call_model(state: AgentState):
messages = state["messages"]
response = model.invoke(messages)
return {"messages": [response]}
# Node to run tools
tool_node = ToolNode(tools)
We need a function that inspects the LLM's response. If the response contains tool calls, we route to the tool node; otherwise, we end the workflow.
def should_continue(state: AgentState):
last_message = state["messages"][-1]
# If the LLM requested a tool call, route to "tools"
if last_message.tool_calls:
return "tools"
# Otherwise, stop
return "__end__"
Now, we stitch everything together using StateGraph.
from langgraph.graph import StateGraph, START, END
# Initialize the graph with our state definition
workflow = StateGraph(AgentState)
# Add our nodes
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)
# Set the entry point
workflow.add_edge(START, "agent")
# Add the conditional edge
workflow.add_conditional_edges(
"agent",
should_continue,
{
"tools": "tools",
"__end__": END
}
)
# Route from the tools node back to the agent to process the tool output
workflow.add_edge("tools", "agent")
# Compile the workflow
app = workflow.compile()
Let's test our compiled application:
from langchain_core.messages import HumanMessage
# Run with a math question (triggers the tool)
events = app.stream(
{"messages": [HumanMessage(content="What is 23 multiplied by 45?")]}
)
for event in events:
for value in event.values():
print("Assistant:", value["messages"][-1].content)
LangGraph is a game-changer for building reliable AI agents. By structuring your LLM workflows as states, nodes, and edges, you gain complete control over the execution flow while keeping the flexibility of LLM decision-making.
For your next project, try adding a database check node or a human approval step to this basic template!