LangGraph Setup Guide

Complete setup and configuration guide for LangGraph — LangChain's low-level orchestration framework for building stateful agents. Graph-based, durable execution, checkpointing, and human-in-the-loop.

June 12, 2026
langgraphlangchainstate-graphagentspythondurable-executioncheckpointing

LangGraph

LangGraph is LangChain's low-level framework for building stateful, long-running agents. Instead of role-based or conversation-based coordination, LangGraph models agents as directed graphs — nodes are steps, edges are transitions, and state flows through. Built-in checkpointing means agents survive crashes and resume from exactly where they left off.

Companies like Klarna, Replit, and Elastic run LangGraph in production. For a higher-level agent framework built on top of LangGraph, see Deep Agents — same graph engine, more opinionated defaults.

Note:

LangGraph is lower-level than CrewAI or AutoGen. You define nodes, edges, and state schemas explicitly. This gives you full control but more code. Use LangGraph when you need durable execution (agents that run for hours), complex conditional routing, or fine-grained state management. Use CrewAI/AutoGen for rapid prototyping.

Installation

1

Install LangGraph

Python 3.10+ required. Install the core package plus a model provider.

pip install langgraph langchain-openai
2

Configure API Keys

LangGraph is model-agnostic. Set keys for your preferred provider.

export OPENAI_API_KEY=sk-...
# or for Anthropic
export ANTHROPIC_API_KEY=sk-ant-...
3

Verify Installation

Build a minimal graph that routes based on LLM output.

from langgraph.graph import StateGraph, END
from typing import TypedDict

class State(TypedDict):
    messages: list

def chat_node(state):
    # Your LLM logic here
    return {"messages": state["messages"] + [{"role": "ai", "content": "Hello"}]}

graph = StateGraph(State)
graph.add_node("chat", chat_node)
graph.set_entry_point("chat")
graph.add_edge("chat", END)
app = graph.compile()

Core Concepts

Architecture Overview

StateGraph
The core abstraction. Defines the state schema (TypedDict or Pydantic model) and the nodes/edges that operate on it.

Values: StateGraph(StateSchema)

Node
A function that takes state and returns updates. Nodes are the steps in your agent — call an LLM, execute a tool, transform data.

Values: graph.add_node(name, function)

Edge
A transition between nodes. Normal edges are unconditional. Conditional edges route based on the current state.

Values: graph.add_edge(from, to) | graph.add_conditional_edges(from, router, mapping)

Checkpointer
Persists graph state after each step (super-step). Enables durable execution — if the process crashes, resume from the last checkpoint.

Values: MemorySaver (dev), SqliteSaver, PostgresSaver (prod)

Building a Graph

A simple agent that decides whether to call a tool or respond directly.

from typing import TypedDict, Literal, Annotated
from langgraph.graph import StateGraph, END, add_messages
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI

class AgentState(TypedDict):
    messages: Annotated[list, add_messages]

llm = ChatOpenAI(model="gpt-4o")

def call_model(state: AgentState):
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

def route_after_model(state: AgentState) -> Literal["call_tool", END]:
    last_message = state["messages"][-1]
    if hasattr(last_message, "tool_calls") and last_message.tool_calls:
        return "call_tool"
    return END

def execute_tool(state: AgentState):
    last_message = state["messages"][-1]
    # Execute tool calls and return results
    results = []
    for tool_call in last_message.tool_calls:
        # ... tool execution logic
        results.append(f"Tool {tool_call['name']} result: ...")
    return {"messages": [{"role": "tool", "content": r} for r in results]}

# Build the graph
graph = StateGraph(AgentState)
graph.add_node("agent", call_model)
graph.add_node("tools", execute_tool)

graph.set_entry_point("agent")
graph.add_conditional_edges("agent", route_after_model, {"call_tool": "tools", END: END})
graph.add_edge("tools", "agent")  # Loop back to agent after tools

Checkpointing: Durable Execution

A checkpointer persists graph state after every super-step. If your process crashes mid-execution, resume with the same thread ID and the graph picks up where it left off.

from langgraph.checkpoint.sqlite import SqliteSaver

# Dev: in-memory (lost on restart)
checkpointer = MemorySaver()

# Production: SQLite or Postgres (survives restarts)
checkpointer = SqliteSaver.from_conn_string("checkpoints.db")

app = graph.compile(checkpointer=checkpointer)

# First run
config = {"configurable": {"thread_id": "user-123"}}
result = app.invoke({"messages": [{"role": "user", "content": "Research AI trends"}]}, config)

# If the process crashes here, restart and resume:
result = app.invoke(None, config)  # Picks up from last checkpoint

Human-in-the-Loop

Interrupt graph execution at a specific node and wait for human approval before continuing.

from langgraph.types import interrupt, Command

def sensitive_action(state: AgentState):
    # This node requires human approval
    approval = interrupt(f"Approve this action? State: {state}")
    if approval != "yes":
        return {"messages": [{"role": "ai", "content": "Action rejected by human."}]}
    # ... proceed with sensitive action

# The graph pauses at interrupt(). Resume with:
app.invoke(Command(resume="yes"), config)

Memory

LangGraph supports two memory layers:

Memory Types

Short-term (within a run)
State stored in the graph's state object. Available to all nodes within a single invocation. Lost when the run ends if no checkpointer is used.

Values: StateGraph(StateSchema)

Long-term (across runs)
Persistent memory stored in a database. Survives process restarts. Use for user profiles, preferences, and conversation history.

Values: Store (InMemoryStore for dev, PostgresStore for prod)

Model Wiring

LangGraph is model-agnostic — use any LangChain chat model.

# OpenAI
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# Anthropic
from langchain_anthropic import ChatAnthropic
llm = ChatAnthropic(model="claude-sonnet-4-20250514")

# Google
from langchain_google_genai import ChatGoogleGenerativeAI
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash")

# Ollama (local)
from langchain_ollama import ChatOllama
llm = ChatOllama(model="llama3.2", base_url="http://localhost:11434")

# All are dropped into the same graph node
def call_model(state):
    response = llm.invoke(state["messages"])
    return {"messages": [response]}

Note:

Graph complexity. LangGraph's power comes at a cost. A graph with 5 nodes, conditional edges, a checkpointer, and memory is ~100 lines of setup code. For simple chatbots, this is overkill. LangGraph earns its keep when you need durable execution, complex routing, or human-in-the-loop at specific execution points.

Key Takeaway

LangGraph is the right choice when your agent needs to survive restarts or run for extended periods. The checkpointing system means crash recovery is automatic — no custom retry logic. Pair it with LangSmith for debugging: visualize every node transition, inspect state at each step, and trace token usage. The tradeoff is verbosity: you define every node and edge explicitly. CrewAI gives you a working agent in 30 lines; LangGraph gives you a production-grade durable agent in 150.