Generative AI & LLM

Module 10 of 16

Module 10: LangGraph — Stateful Agent Workflows

3 min read549 words
What you'll learn
Explain why LangGraph adds state machines on top of LangChain runnablesBuild graphs with nodes, edges, and conditional routingImplement checkpointing for durable agent runsDesign human-in-the-loop approval stepsRun parallel branches and subgraphs when tasks fork

Duration: 4 hours | Difficulty: Advanced | Prerequisites: Modules 08–09

Learning Objectives

By the end of this module, you will be able to:

  • Explain why LangGraph adds state machines on top of LangChain runnables
  • Build graphs with nodes, edges, and conditional routing
  • Implement checkpointing for durable agent runs
  • Design human-in-the-loop approval steps
  • Run parallel branches and subgraphs when tasks fork

1. Why LangGraph?

Chains are DAGs of runnables. Agents need cycles (retry, revisit tools) and mutable state across turns. LangGraph models your workflow as a graph with explicit state—easier to pause, resume, and audit than a raw while loop.

LangChain LCELLangGraph
Great for linear pipesGreat for loops + branches
You manage memory manuallyFirst-class State + checkpointers
Hard to “pause here”Built for human approvals

Fun Fact: “Compile” on a graph is like building a tiny virtual machine your LLM steps through.

2. Core Concepts: State, Nodes, and Edges

  • State: Typed dict (often TypedDict) merged after each node.
  • Node: Python callable state -> partial state update.
  • Edge: Deterministic transition.
  • Conditional edge: Function returns next node name.

Key Example: Three-node creative pipeline with a review loop cap—shows add_messages, conditional routing, and END.

python
[object Object], typing ,[object Object], Annotated, TypedDict
,[object Object], langgraph.graph ,[object Object], StateGraph, START, END
,[object Object], langgraph.graph.message ,[object Object], add_messages
,[object Object], langchain_openai ,[object Object], ChatOpenAI

llm = ChatOpenAI(model=,[object Object],, temperature=,[object Object],, api_key=,[object Object],)


,[object Object], ,[object Object],(,[object Object],):
    messages: Annotated[,[object Object],, add_messages]
    next_step: ,[object Object],
    draft: ,[object Object],
    iteration: ,[object Object],


,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
    resp = llm.invoke(
        state[,[object Object],]
        + [{,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],}]
    )
    ,[object Object], {,[object Object],: [resp], ,[object Object],: resp.content}


,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
    resp = llm.invoke(
        [
            {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],},
            {,[object Object],: ,[object Object],, ,[object Object],: state.get(,[object Object],, ,[object Object],)},
        ]
    )
    ,[object Object], {,[object Object],: resp.content, ,[object Object],: [resp]}


,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
    draft = state.get(,[object Object],, ,[object Object],)
    resp = llm.invoke(
        [
            {,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],},
            {,[object Object],: ,[object Object],, ,[object Object],: draft},
        ]
    )
    it = state.get(,[object Object],, ,[object Object],) + ,[object Object],
    revise = ,[object Object], ,[object Object], resp.content.upper() ,[object Object], it < ,[object Object],
    ,[object Object], {,[object Object],: [resp], ,[object Object],: ,[object Object], ,[object Object], revise ,[object Object], ,[object Object],, ,[object Object],: it}


,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
    ,[object Object], ,[object Object], ,[object Object], s.get(,[object Object],) == ,[object Object], ,[object Object], END


g = StateGraph(AgentState)
g.add_node(,[object Object],, researcher)
g.add_node(,[object Object],, writer)
g.add_node(,[object Object],, reviewer)
g.add_edge(START, ,[object Object],)
g.add_edge(,[object Object],, ,[object Object],)
g.add_edge(,[object Object],, ,[object Object],)
g.add_conditional_edges(,[object Object],, route_review, {,[object Object],: ,[object Object],, END: END})

app = g.,[object Object],()
out = app.invoke(
    {
        ,[object Object],: [{,[object Object],: ,[object Object],, ,[object Object],: ,[object Object],}],
        ,[object Object],: ,[object Object],,
        ,[object Object],: ,[object Object],,
        ,[object Object],: ,[object Object],,
    }
)
,[object Object],(out[,[object Object],][:,[object Object],])

3. Conditional Routing

Same pattern as support bots: classify → specialist nodes. Keep class labels in an enum-like set so your graph doesn’t sprawl.

PatternUse when
LLM routerFuzzy intents
Rules routerCheap, deterministic

4. Checkpointing and Persistence

Compile with a checkpointer (memory, SQLite, Postgres) to save state after each super-step. Enables:

FeatureUser-visible win
Resume after crashNo duplicate charges / emails
Time travelDebug “what did state look like before tool X?”

Concept: Checkpointing turns agents from scripts into durable workflows.

5. Human-in-the-Loop

Insert interrupt_before / interrupt_after on nodes so execution pauses until an operator approves (API webhook, CLI, internal dashboard).

Good HITL momentBad HITL moment
Send email / transfer fundsEvery trivial retrieval

6. Parallel Execution

Fan-out with Send API (per docs) or run independent nodes whose outputs merge in state reducers—great for “research three sources in parallel.”

ParallelWatch
Faster wall clockRate limits + duplicate work

7. Subgraphs

Embed a graph as a node in a parent graph—useful for “billing sub-flow” vs “technical sub-flow” each with internal complexity.

Try This! Sketch your subgraph boundaries on paper before you nest—deep graphs are hard to trace.

Practice Exercises

Exercise 1: Simple StateGraph (Beginner)

Two-node linear graph logging state after each step.

Exercise 2: Intent Router (Intermediate)

Three handlers + fallback node.

Exercise 3: Human Approval Node (Intermediate)

Pause before send_notification.

Exercise 4: Parallel Research (Advanced)

Three retrievers → merge → single writer.

Exercise 5: Nested Subgraphs (Advanced)

Outer “project” graph, inner “ticket” graph.

Mini-Project: Content Generation Pipeline

Research → draft → legal-lite review → optional human gate → publish stub. Persist with SQLite checkpointer.

Key Takeaways

Key Takeaway

  • LangGraph = explicit state machine for LLM workflows.
  • Conditional edges replace fragile string parsing in agent loops.
  • Checkpointing enables resume, audit, and HITL.
  • Parallel + subgraph patterns match how teams actually ship.
  • If you can’t draw it as a graph, code will fight you—draw first.

Resources for Further Learning

← Previous: LangChain | Next: Multi-Agent →