Most "AI agent" demos are a single while loop wrapped around a chat completion. They work in a notebook and fall apart in production the moment a server restarts, a user walks away mid-task, or a step needs a human to click "approve." LangGraph is the framework built for the part that comes after the demo: durable, stateful, long-running agents that survive a crash and pick up exactly where they left off.
LangGraph and its higher-level sibling LangChain both hit their first stable 1.0 release on October 22, 2025, with a public commitment to no breaking changes until 2.0. As of July 2026 the library is on the 1.2 line, it is downloaded around 90 million times a month, and it runs in production at Uber, LinkedIn, Klarna, JP Morgan, and Blackrock. This is a practical guide to what LangGraph actually gives you and when to reach for it.
Install and requirements
LangGraph needs Python 3.10 or newer — 3.9 support was dropped when it hit EOL in October 2025. Install it with pip:
pip install -U langgraph
Or, if you use uv (the approach the LangChain team recommends in their own docs):
uv pip install --upgrade langgraph
That is the entire dependency for the low-level graph API. If you also want the fast high-level agent builder covered at the end of this article, add langchain as well.
The mental model: state, nodes, edges
LangGraph asks you to describe your agent as a graph rather than a script. There are exactly three concepts to learn.
State is the single object that flows through your agent — what it remembers. You define its shape with a TypedDict. Nodes are plain Python functions that receive the current state and return an update to it. Edges connect nodes and decide what runs next. That is the whole model, and everything else is built on top of it.
Here is a minimal graph with one node:
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
topic: str
draft: str
def write_draft(state: State) -> dict:
# Your model call would go here.
return {"draft": f"An article about {state['topic']}"}
builder = StateGraph(State)
builder.add_node("write_draft", write_draft)
builder.add_edge(START, "write_draft")
builder.add_edge("write_draft", END)
graph = builder.compile()
result = graph.invoke({"topic": "state machines"})
print(result["draft"])
START and END are the two built-in sentinel nodes marking where execution enters and leaves the graph. Note what the node returns: a partial update, not the whole state. LangGraph merges each node's return value into the running state for you, which is what keeps larger graphs readable.
Branching with conditional edges
A straight line of nodes is just a pipeline. Agents become interesting when they decide what to do next. That is a conditional edge — you attach a router function that inspects the state and returns the name of the next node.
def route(state: State) -> str:
if len(state["draft"]) < 20:
return "expand"
return "publish"
builder.add_conditional_edges("write_draft", route, {
"expand": "expand_node",
"publish": "publish_node",
})
Because the routing logic is an ordinary function, you can drive it with a model's output, a tool result, a confidence score, or a hard-coded rule. Loops fall out of the same mechanism: point an edge back at an earlier node and the graph will cycle until the router sends it to END. This mix of deterministic and model-driven control is exactly what LangGraph is built for, and it is why the framework sits a level below a one-line agent builder.
The feature that earns its keep: durable state
Everything above you could hand-roll. Persistence is the reason to adopt LangGraph. When you compile a graph with a checkpointer, LangGraph saves the full state after every node executes. If the process dies, the graph resumes from the last checkpoint instead of starting over — and the same mechanism gives you multi-turn memory and human-in-the-loop pauses essentially for free.
from langgraph.checkpoint.memory import InMemorySaver
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "user-42"}}
graph.invoke({"topic": "checkpoints"}, config)
The thread_id is what ties a series of calls together into one conversation. Call the graph again with the same thread_id and it continues where it stopped; use a different one and you get a clean session. InMemorySaver is fine for development; for production, swap it for a database-backed checkpointer (LangGraph ships Postgres and SQLite savers) and your agent state survives restarts and deploys. Multi-day approval workflows, background jobs that span sessions, and pause-for-human-review flows are all the same feature under the hood.
When you don't need this much control
Here is the honest part most tutorials skip: if your agent is just "call a model, let it use tools, repeat," you should not be writing graphs by hand. LangChain 1.0 introduced create_agent for exactly that default loop, and it runs on the LangGraph runtime underneath, so you keep the durable state without the boilerplate.
from langchain.agents import create_agent
agent = create_agent(
model="openai:gpt-5",
tools=[get_weather],
system_prompt="Help the user by checking the weather in their city.",
)
result = agent.invoke({"messages": [{"role": "user", "content": "Weather in SF?"}]})
One important migration note: the old create_react_agent from langgraph.prebuilt is now deprecated, and its functionality moved into langchain.agents as create_agent. If you are following a tutorial written before late 2025, that is the single change most likely to bite you. create_agent also brings middleware — built-in hooks for human-in-the-loop approval, automatic summarization when context fills up, and PII redaction — that let you customize the loop without dropping down to raw graphs.
The rule of thumb from the LangChain team is clean: reach for create_agent when your workflow fits the model→tools→response loop, and drop down to LangGraph's StateGraph when you need branching, cycles, a mix of deterministic and agentic steps, or tight control over cost and latency. Because agents built with create_agent are themselves graphs, you can nest one inside a larger LangGraph workflow and migrate gradually.
The Bottom Line
LangGraph is not the fastest way to get an agent talking — that is create_agent, and you should start there. LangGraph is the fastest way to get an agent you can trust in production: one whose state persists, whose execution resumes after a crash, and whose control flow you can see and shape instead of hoping a prompt holds it together. Learn the three primitives — state, nodes, edges — install the 1.2 line on Python 3.10+, and add a checkpointer the moment your agent does anything a user would be annoyed to lose. That single decision is the line between a demo and a system.


