What LangGraph actually is
LangGraph is a graph-based orchestration library from the LangChain team for building stateful, multi-step LLM applications. At its core it models computation as a directed graph where each node is a function or runnable and each edge defines how state flows between them.
What separates it from a standard LangChain chain is the ability to loop. A traditional chain is a DAG, you can branch and merge but you cannot return to a previous node. LangGraph explicitly allows cycles, which is what makes it useful for agent-style workflows where the model decides to call a tool, inspect the result, and call another tool, potentially many times, before producing a final answer.
Under the hood LangGraph is a thin orchestration layer. The state is a typed object, typically a TypedDict or Pydantic model, that gets passed between nodes. Each node receives the current state and returns a partial update. The graph executor merges those updates and decides which node runs next based on either static edges or conditional functions that inspect the state.
For someone used to writing Python it will feel closer to writing a state machine than writing a chain. The primitives are small and explicit: StateGraph, nodes, edges, conditional edges, and checkpoints. Everything else is normal Python and the underlying chat model clients you already know.
Setup and authentication
LangGraph is a Python package. The current version installs cleanly into a fresh virtual environment. There is no separate auth step for the library itself, you authenticate against your model provider the same way you would with any LangChain code.
Start by creating a project folder and a virtual environment. Install the core packages with pip. You will need langgraph, langchain-core, and at least one chat model integration such as langchain-openai or langchain-anthropic. If you plan to use checkpointing for memory or human-in-the-loop workflows, install the matching checkpointer package as well, which is bundled in recent releases.
Set your model API key as an environment variable. For OpenAI that is OPENAI_API_KEY, for Anthropic it is ANTHROPIC_API_KEY. LangGraph never stores or proxies these keys, it just passes them to the underlying chat model client at call time, so the usual key hygiene rules apply.
If you want to use LangGraph Studio, the visual debugger, install the langgraph-cli package and run the dev server locally. Studio is optional, the library is fully usable from a notebook or a script, but it does make tracing a misbehaving graph much faster.
First working example
Here is the smallest example that does something interesting. A two-node graph where one node calls a model, the other node evaluates the result, and a conditional edge loops back if the output is too short. No real code fence is needed for the concept but the structure is roughly the following.
Define your state as a TypedDict with a messages list and a revision counter. The messages field follows the standard LangChain message format so you can use ChatPromptTemplate and tool calling directly inside a node.
The first node is a simple callable that takes the state, invokes a chat model with the current messages, appends the response, and increments the counter. The second node inspects the latest message and the counter. If the response is under a length threshold and the counter is below three, it routes back to the first node. Otherwise it routes to END.
Run the graph with graph.invoke passing in an initial HumanMessage in the state. The graph will loop until the conditional edge decides the answer is long enough or the safety counter trips. That loop is the whole point of using LangGraph over a regular chain, and the same shape generalises to tool use. The first node becomes the agent, the second node executes whatever tool the model requested, and a conditional edge decides whether to call another tool or finish. Most production LangGraph code follows this pattern.
Key settings that matter
There are a handful of dials that change behaviour meaningfully and most tutorials skip them entirely.
The first is the checkpointer. By default a graph has no memory, every invocation starts from the initial state. Pass a checkpointer like MemorySaver for in-memory persistence, or a Postgres-backed saver for production. Once a checkpointer is attached, thread_id based conversation continuity comes for free, and you can rewind to any prior checkpoint for human-in-the-loop flows.
The second is the interrupt mechanism. By setting interrupt_before or interrupt_after on a specific node you can pause the graph at that step, inspect or edit the state, and then resume. This is how you build approval flows without writing your own pause-and-resume plumbing, and it works because the checkpointer holds the suspended state.
The third is the recursion limit. LangGraph throws if a graph loops more than the configured number of steps. The default is in the range that works for most prototypes but is often too low for long agent runs. Set it explicitly when you know the worst case, and surface it in your logs so production failures are diagnosable.
The fourth is the state schema design. The state object is the contract between every node in the graph. If you put too much in it the graph becomes hard to reason about. If you put too little, nodes end up passing data through closures or external stores. A useful pattern is to keep working data in the state and reference large blobs by ID, fetching them in nodes that need them.
The fifth is node return shapes. Each node can return a partial state update or a full state replacement. By default the executor merges updates, but you can configure it to replace specific keys. Getting this wrong is a common source of bugs where state silently disappears between nodes, so add a small test that asserts the state shape after each node during development.
The sixth is fan-out and dynamic routing through Send and Command. When you want to run several nodes in parallel or route dynamically to a node decided at runtime, the way you declare which keys map to which nodes matters. Mistakes here produce graphs that compile cleanly but behave incorrectly at runtime, so this is the area where the typed state schema pays for itself.
Where it shines
LangGraph is genuinely good at a few specific patterns. Multi-step tool-using agents are the headline use case. Anywhere the model needs to call external systems, check the result, and decide what to do next, the loop primitive saves you from writing that control flow by hand.
Long-running workflows that survive across processes are another strong fit. Because checkpoints are first class, you can start a graph, persist it, and resume it hours or days later from a different machine. This is hard to do cleanly with ad hoc agent code and trivial with a configured checkpointer.
Human-in-the-loop approval flows are well supported. Pause before a sensitive tool call, present the proposed action to a human, resume on approval. The interrupt mechanism handles the suspend and resume semantics so you do not have to invent your own state machine for it.
Multi-agent systems where several specialised agents hand off to each other map naturally to a graph. Each agent is a node, the routing logic decides who acts next based on the current state, and the trace shows you exactly where the handoff happened.
Production observability is a quiet win. Every graph run produces a structured trace that tools like LangSmith can render, so debugging a failing agent run is mostly a matter of reading the trace top to bottom rather than adding print statements.
Where it fails
LangGraph is not the right tool for simple single-shot prompts. If you just need to call a model and return the answer, a graph is overhead. Use the chat model client directly or a plain LangChain chain.
The learning curve is real. State schemas, reducers, conditional edges, checkpoints, interrupts, and Send semantics together amount to a fair amount of conceptual surface area. For a one-off script this is overkill, and for a team new to the framework the first week is mostly spent fighting the mental model.
Local debugging is not great. Without LangSmith or LangGraph Studio, tracing a bad run means adding print statements to nodes. The library is improving here but it is not yet at the level of a normal Python debugger.
The state object can become a dumping ground. In a large graph the TypedDict grows new fields every time someone adds a feature, and within a few months nobody remembers what half the fields are for. Discipline matters more than the framework suggests, and a state schema review is a useful ritual for any team using LangGraph at scale.
The performance overhead is small but nonzero. Each node call goes through a merge step and a routing step. For latency-critical paths this can matter, though in practice the model call dominates and the orchestration cost is in the noise for most applications.
Finally, the ecosystem is still moving. The current version of the API is reasonable and stable for the core primitives, but helper functions and integrations shift between releases. Pin your versions and read the changelog when you upgrade, especially if you depend on Send, Command, or any of the prebuilt subgraphs.
Practical workflow pattern
For real work the pattern that holds up is to start with a single-node LangGraph even when you do not need the loop. This buys you checkpointing, observability, and a clean migration path when you eventually add conditional edges. The cost is one extra line of code at the entry point.
Treat the state schema as the source of truth for what your workflow knows. When you add a new feature, add a field to the state and a node that writes to it. Resist the urge to hide data in module-level variables or external dicts, because the trace will not capture it and the next person to read the code will be confused.
Use MemorySaver for development and switch to a Postgres or SQLite checkpointer before you deploy. Thread IDs become your conversation or session keys. Build a small wrapper that maps your application’s user and conversation IDs onto LangGraph thread IDs, and store that mapping in your own database rather than relying on the framework to remember it.
Wire LangSmith in early. The cost is an environment variable and a few lines of config, the payoff is that when something breaks in production you have a trace to look at. Add it on day one rather than day thirty, when adding observability means retro-fitting it into nodes that were never designed to expose their internals.
For production deployments, run the graph as a regular Python service. LangGraph does not require any particular runtime, FastAPI plus a worker queue is a common setup. Stream intermediate state to the client if you want to show progress, the graph supports streaming updates per node out of the box.
Finally, keep the first version of any agentic workflow deliberately small. Two or three nodes, one conditional edge, and a checkpointer. Add complexity only when a specific user need forces it. Most failed agent projects fail because the team tried to build the final form on day one, then spent the rest of the time paying interest on that complexity.
For a deeper walkthrough of tools like this and how they fit together, the free Working With Claude field guide covers the ecosystem end to end. Get the guide.