Skip to Content

LangGraph (Stateful Agents with Cycles)

When to use

Build persistent, multi-step, branching agents with cycles — not just linear prompt-to-response chains.

Analogy

A flowchart with sticky notes. Nodes are stations (call a tool, call a model, decide) and edges are arrows. The graph can loop back, branch on a decision, or pause and resume — instead of just running end-to-end.

Data-flow diagram

   START -> call_model -> should_continue?
                         |
                    yes  <-+-> no
                     |           |
                  call_tool    END
                     |
                  -> call_model

   state: dict passed along every hop

Deep explanation

LangGraph is a graph-based agent framework from the same team as LangChain. You define a state schema (TypedDict), nodes (functions), and edges (conditional or fixed). State is passed along every transition; cycles let the agent iterate (think-act-observe). Use LangGraph when you need persistent memory, branching logic, or multi-step workflows that simple chains cannot represent.

Examples

Example 1

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
import operator
 
class State(TypedDict):
    messages: Annotated[list, operator.add]
 
def call_model(state):
    return {'messages': ['hello from model']}
 
g = StateGraph(State)
g.add_node('model', call_model)
g.add_edge(START, 'model')
g.add_edge('model', END)
app = g.compile()
print(app.invoke({'messages': []}))

StateGraph lets you declare nodes and edges; .compile() returns an invokable runnable.

Example 2

# conditional edge: loop until the LLM says 'final'
def decide(state):
    return 'loop' if 'final' not in state['messages'][-1] else END
 
g.add_conditional_edges('model', decide, {'loop': 'model', END: END})
app = g.compile()
print(app.invoke({'messages': []}))

Conditional edges enable loops and branching — the graph iterates until a stop condition fires.

Example 3

# human-in-the-loop interrupt
app = g.compile(interrupt_before=['user_review'])
for state in app.stream({'messages': []},
                        config={'configurable': {'thread_id': '1'}}):
    print(state)
    # app.update_state(...) after human approval

interrupt_before pauses execution before a node, perfect for human approval flows.

Common mistake

Treating LangGraph like a linear chain. Without cycles you only have a fancier Runnable. Use graph patterns (cycles, branches) when the task is non-linear.

Key takeaway

LangGraph = state + nodes + edges + cycles + persistence. Use for branching, looping, memory and human-in-the-loop. Compile once, invoke many.

Production Failure Playbook

Failure scenario 1: non-deterministic-graph

Failure scenario 2: state-explosion