Skip to main content
← Back to Blog

Building AI Agents with LangGraph

The framework for stateful, multi-step LLM workflows

LangGraph models AI agents as directed graphs. Nodes are actions, edges are transitions, state persists across steps. It's a state machine for LLM applications.

Best for: Tool-calling agents, multi-step workflows, human-in-the-loop systems, multi-agent orchestration

Code examples: github.com/mshoaibiqbal/langgraph-agents


Why LangGraph?

LangChain handles straightforward LLM chains well. But production agents need capabilities that simple chains can't provide:

CapabilityLangChainLangGraph
Linear flow
Loops (retry, iterate)
Conditional branching⚠️ Limited
Persistent state
Human-in-the-loop
Parallel execution⚠️ Limited / Manual
Crash recovery

The key insight: agents are state machines. LangGraph makes this explicit.


Core Concepts

The Graph Model

Every LangGraph application has three components:

┌───────────────────────────────────────────────────────┐
│                        STATE                          │
│    (TypedDict that flows through all nodes)           │
└───────────────────────────┬───────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────┐
│                        NODES                          │
│  (Functions that read state, do work, return updates) │
└───────────────────────────┬───────────────────────────┘
                            │
                            ▼
┌───────────────────────────────────────────────────────┐
│                        EDGES                          │
│      (Connections that define flow between nodes)     │
└───────────────────────────────────────────────────────┘

State

State is a TypedDict that all nodes can read from and write to:

class AgentState(TypedDict):
    messages: list[BaseMessage]  # Conversation history
    context: str                  # Retrieved information
    decision: str                 # Agent's current decision

Each node receives the full state and returns a dict of updates. LangGraph merges updates automatically.

Nodes

Nodes are just Python functions. They receive state, perform work, and return updates:

def analyze(state: AgentState) -> dict:
    # Do work with state["messages"]
    return {"decision": "proceed"}  # Updates only the 'decision' field

Edges

Edges connect nodes. Two types:

  1. Static edges - Always follow this path
  2. Conditional edges - Choose path based on a function's return value
graph.add_edge("start", "analyze")                    # Static
graph.add_conditional_edges("analyze", router_fn)     # Conditional

Architectural Patterns

Pattern 1: Tool-Calling Loop

The most common pattern. The LLM decides whether to use tools or respond directly.

                    ┌─────────┐
                    │  START  │
                    └────┬────┘
                         │
                         ▼
                    ┌─────────┐
                    │   LLM   │◀──────────┐
                    └────┬────┘           │
                         │                │
                   [tool calls?]          │
                    /        \            │
                   /          \           │
                  ▼            ▼          │
             ┌────────┐   ┌────────┐      │
             │  TOOLS │   │  END   │      │
             └───┬────┘   └────────┘      │
                 │                        │
                 └────────────────────────┘

Flow: User message → LLM decides → (use tools → execute → back to LLM) OR respond

When to use: Calculator agents, search agents, API-calling agents, any agent with tools

Example: 02_tool_calling_agent.py


Pattern 2: Fan-Out / Fan-In (Parallel Execution)

Multiple agents work in parallel, then results are combined.

                     ┌─────────┐
                     │  START  │
                     └────┬────┘
                          │
                          ▼
                ┌─────────────────┐
                │ detect_language │
                └────────┬────────┘
                         │
            ┌────────────┼────────────┐
            │            │            │
            ▼            ▼            ▼
      ┌──────────┐ ┌────────────┐ ┌─────────┐
      │ security │ │performance │ │  style  │  ← PARALLEL
      └────┬─────┘ └─────┬──────┘ └────┬────┘
           │             │             │
           └─────────────┼─────────────┘
                         │
                         ▼
                ┌─────────────────┐
                │   coordinator   │
                └────────┬────────┘
                         │
                         ▼
                     ┌───────┐
                     │  END  │
                     └───────┘

Key insight: When multiple edges originate from the same node, LangGraph executes them in parallel automatically.

When to use: Code review (multiple reviewers), document analysis (extract different aspects), research (gather from multiple sources)

Example: 03_code_review_agents.py


Pattern 3: Router (Conditional Branching)

Different paths based on classification or decision.

                     ┌─────────┐
                     │  START  │
                     └────┬────┘
                          │
                          ▼
                    ┌────────────┐
                    │ classifier │
                    └─────┬──────┘
                          │
            ┌─────────────┼─────────────┐
            │             │             │
            ▼             ▼             ▼
      ┌─────────┐   ┌───────────┐   ┌─────────┐
      │ billing │   │ technical │   │ general │
      └────┬────┘   └─────┬─────┘   └────┬────┘
           │              │              │
           └──────────────┼──────────────┘
                          │
                          ▼
                      ┌───────┐
                      │  END  │
                      └───────┘

When to use: Support ticket routing, intent classification, workflow selection


Pattern 4: Human-in-the-Loop

Pause execution for human review, then resume.

                      ┌─────────┐
                      │  START  │
                      └────┬────┘
                           │
                           ▼
                      ┌─────────┐
                      │ extract │
                      └────┬────┘
                           │
                           ▼
                      ┌──────────┐
                      │ validate │
                      └────┬─────┘
                           │
                     [confidence?]
                      /         \
                     /           \
                    ▼             ▼
        ┌────────────────┐   ┌─────────────┐
        │ HUMAN REVIEW   │   │ auto_approve│
        └───────┬────────┘   └──────┬──────┘
                │                   │
                └─────────┬─────────┘
                          │
                          ▼
                     ┌──────────┐
                     │ finalize │
                     └────┬─────┘
                          │
                          ▼
                      ┌───────┐
                      │  END  │
                      └───────┘

Key components:

  • MemorySaver - Checkpoints state so execution can resume
  • interrupt_before - Specifies which nodes pause for human input
memory = MemorySaver()
app = graph.compile(
    checkpointer=memory,
    interrupt_before=["human_review"]
)

When to use: Document approval, content moderation, high-stakes decisions


Pattern 5: Iterative Refinement

Loop until quality threshold met or max iterations reached.

                    ┌─────────┐
                    │  START  │
                    └────┬────┘
                         │
                         ▼
                    ┌──────────┐
                    │ generate │◀─────────┐
                    └────┬─────┘          │
                         │                │
                         ▼                │
                    ┌──────────┐          │
                    │ evaluate │          │
                    └────┬─────┘          │
                         │                │
                   [good enough?]         │
                    /        \            │
                   /          \           │
                  ▼            ▼          │
             ┌────────┐   ┌────────┐      │
             │  END   │   │ retry  │──────┘
             └────────┘   └────────┘

When to use: Content generation with quality checks, self-correcting code generation, iterative summarization


State Management

Accumulating State

For fields that accumulate across parallel nodes, use Annotated with operator.add:

from typing import Annotated
import operator

class State(TypedDict):
    # Each parallel node can append to this list without conflicts
    reviews_complete: Annotated[list[str], operator.add]

State Design Principles

  1. Keep state minimal - Store IDs and references, not large objects
  2. Separate concerns - Input fields, processing fields, output fields
  3. Consider serialization - State must be JSON-serializable for checkpointing

Error Handling

Design compensating transactions for rollback scenarios:

┌───────────┐     ┌───────────┐     ┌───────────┐
│  reserve  │────▶│  payment  │────▶│   ship    │────▶ END
│ inventory │     │           │     │           │
└─────┬─────┘     └─────┬─────┘     └───────────┘
      │                 │
      │ [failure]       │ [failure]
      │                 │
      ▼                 ▼
┌─────────────────────────────────────────────────┐
│              handle_failure                     │
│      (release inventory, refund, notify)        │
└─────────────────────────────────────────────────┘

Use dedicated error-handling nodes rather than try-catch in every node.


When to Use What

Use CaseRecommended Approach
Simple Q&ALangChain (no graph needed)
Single tool callLangChain with tools
Multi-step tool useLangGraph tool loop
Parallel processingLangGraph fan-out/fan-in
Human approvalLangGraph with checkpointer
Complex workflowsLangGraph with conditional edges
Multi-agent systemsLangGraph with specialized nodes

Resources


LangGraph reached 1.0 in late 2025. It's production-ready and deployed by Klarna, Replit, and Elastic.