This page shows five runnable examples for LangGraph 1.x in Python: from a minimal graph and an agent with tools to human approval and a small multi-agent system with a supervisor. The concepts behind them (state, nodes, edges, checkpointer) are explained on the LangGraph framework page.
Setup
pip install -U langgraph langchain "langchain[openai]"
export OPENAI_API_KEY=...
export MODEL="openai:<model-name>" # e.g. a current model from your provider
Instead of OpenAI you can use any other provider, for example "langchain[anthropic]" and MODEL="anthropic:<model-name>". All examples read the model from the MODEL environment variable, so you don't have to change them when new models come out.
Example 1: Minimal graph with branching
No language model, just to see the mechanics: a node checks a number, and a conditional edge decides on the next step.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
betrag: float
ergebnis: str
def pruefen(state: State) -> dict:
return {} # change nothing, just pass on
def automatisch(state: State) -> dict:
return {"ergebnis": "approved automatically"}
def manuell(state: State) -> dict:
return {"ergebnis": "sent for manual review"}
def route(state: State) -> str:
return "automatisch" if state["betrag"] < 500 else "manuell"
builder = StateGraph(State)
builder.add_node("pruefen", pruefen)
builder.add_node("automatisch", automatisch)
builder.add_node("manuell", manuell)
builder.add_edge(START, "pruefen")
builder.add_conditional_edges("pruefen", route, ["automatisch", "manuell"])
builder.add_edge("automatisch", END)
builder.add_edge("manuell", END)
graph = builder.compile()
print(graph.invoke({"betrag": 1200}))
# {'betrag': 1200, 'ergebnis': 'sent for manual review'}
Example 2: Agent with tools via create_agent
For a standard agent with tools, use create_agent, available since LangChain 1.0. It replaces the deprecated create_react_agent from langgraph.prebuilt and runs internally as a LangGraph graph.
import os
from langchain.agents import create_agent
from langchain.tools import tool
@tool
def lagerbestand(artikelnummer: str) -> int:
"""Returns the current stock level for an item number."""
return {"A-100": 12, "A-200": 0}.get(artikelnummer, 0)
agent = create_agent(
model=os.environ["MODEL"],
tools=[lagerbestand],
system_prompt="You answer questions about stock levels briefly and factually.",
)
result = agent.invoke({"messages": [{"role": "user", "content": "Is A-200 available?"}]})
print(result["messages"][-1].content)
Example 3: Custom agent loop with StateGraph
If you need more control, for example additional review steps, you build the loop yourself. ToolNode executes the tool calls requested by the model, and tools_condition decides whether to continue or stop.
import os
from langchain.chat_models import init_chat_model
from langchain.tools import tool
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.prebuilt import ToolNode, tools_condition
@tool
def wechselkurs(von: str, nach: str) -> float:
"""Returns a sample exchange rate between two currencies."""
return 1.08 if (von, nach) == ("EUR", "USD") else 1.0
tools = [wechselkurs]
llm = init_chat_model(os.environ["MODEL"]).bind_tools(tools)
def modell(state: MessagesState) -> dict:
return {"messages": [llm.invoke(state["messages"])]}
builder = StateGraph(MessagesState)
builder.add_node("modell", modell)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "modell")
builder.add_conditional_edges("modell", tools_condition) # to "tools" or end
builder.add_edge("tools", "modell") # cycle back to the model
graph = builder.compile()
out = graph.invoke(
{"messages": [{"role": "user", "content": "How much is 250 EUR in USD?"}]},
{"recursion_limit": 10},
)
print(out["messages"][-1].content)
The recursion_limit caps the number of steps and prevents infinite loops.
Example 4: Human approval with interrupt
Before a critical action, the graph pauses. The state is stored in the checkpointer until someone approves or rejects.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command
class State(TypedDict):
entwurf: str
status: str
def entwerfen(state: State) -> dict:
return {"entwurf": "Hello, your order is on its way."}
def freigabe(state: State) -> dict:
entscheidung = interrupt({"frage": "Send this message?", "entwurf": state["entwurf"]})
return {"status": "sent" if entscheidung == "yes" else "discarded"}
builder = StateGraph(State)
builder.add_node("entwerfen", entwerfen)
builder.add_node("freigabe", freigabe)
builder.add_edge(START, "entwerfen")
builder.add_edge("entwerfen", "freigabe")
builder.add_edge("freigabe", END)
graph = builder.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "kunde-42"}}
erster_lauf = graph.invoke({"entwurf": "", "status": ""}, config)
print(erster_lauf["__interrupt__"]) # shows question and draft
ergebnis = graph.invoke(Command(resume="yes"), config)
print(ergebnis["status"]) # sent
In production, replace InMemorySaver with the Postgres or SQLite checkpointer so that approvals can resume even after a server restart.
Example 5: Supervisor with two specialists
A supervisor node uses structured output to decide which specialist works next. Here the specialists are simple model calls with their own role; in real projects they would be separate agents with tools.
import os
from typing import Literal
from pydantic import BaseModel
from langchain.chat_models import init_chat_model
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.types import Command
llm = init_chat_model(os.environ["MODEL"])
class Auswahl(BaseModel):
naechster: Literal["recherche", "text", "fertig"]
def supervisor(state: MessagesState) -> Command[Literal["recherche", "text", "__end__"]]:
prompt = [{"role": "system", "content":
"You coordinate a team. 'recherche' gathers facts, 'text' writes the piece. "
"Answer 'fertig' once a finished text is available."}] + state["messages"]
wahl = llm.with_structured_output(Auswahl).invoke(prompt).naechster
return Command(goto=END if wahl == "fertig" else wahl)
def spezialist(rolle: str):
def node(state: MessagesState) -> Command[Literal["supervisor"]]:
antwort = llm.invoke([{"role": "system", "content": rolle}] + state["messages"])
return Command(goto="supervisor", update={"messages": [antwort]})
return node
builder = StateGraph(MessagesState)
builder.add_node("supervisor", supervisor)
builder.add_node("recherche", spezialist("You gather three verifiable facts on the topic."))
builder.add_node("text", spezialist("You write a short paragraph from the facts."))
builder.add_edge(START, "supervisor")
graph = builder.compile()
out = graph.invoke(
{"messages": [{"role": "user", "content": "Write a paragraph about heat pumps."}]},
{"recursion_limit": 12},
)
print(out["messages"][-1].content)
With Command, a node returns a state update and the next target at the same time. This is the usual form for handoffs between agents in LangGraph 1.x.
Common patterns in practice
| Pattern | Structure | Example |
|---|---|---|
| Agentic RAG | Search, relevance check, new search if needed, then answer | Internal knowledge base with source citations |
| Text-to-SQL | Generate SQL, run it, return errors to the model, retry | Ad hoc analyses for business departments |
| Approval workflow | Draft, interrupt, send |
Emails, quotes, social media posts |
| Supervisor team | Supervisor plus two to four specialists | Research and report, code and review |
Common mistakes when moving to 1.x
- Still using
create_react_agent: It still works but is deprecated. Switch tolangchain.agents.create_agent. MemorySaverin production: It only stores data in memory. Use a persistent checkpointer.- No
recursion_limit: Loops without an upper limit cost money and time. - Old tutorials: Many guides refer to "LangGraph Platform" or pre-1.0 APIs. Check the date.
Frequently asked questions
Which version do the examples run on?
On LangGraph 1.2 and LangChain 1.4 (as of October 2026). The examples only use building blocks of the stable 1.x API, so they should keep working without changes until LangGraph 2.0.
Can I use the examples with another model provider?
Yes. Install the matching package, for example "langchain[anthropic]", and set the MODEL environment variable, for example to anthropic:<model-name>. The code itself stays the same.
Which checkpointer should I use in production?
MemorySaver only stores data in memory and is meant for tests. In production you use a persistent checkpointer, for example for Postgres or SQLite, so that approvals and conversation histories survive a restart.
Should I use create_agent or my own graph?
For an agent with tools and no special workflow logic, create_agent is enough (example 2). Build your own graph as soon as you need branches, approvals or several agents (examples 3 to 5).
