Skip to main content
When you build an agent with the Graph API, you break the process into discrete steps called nodes. Then you describe the decisions and transitions from each node. Finally, you connect nodes through a shared state that each node can read from and write to. This walkthrough builds a customer support email agent with the Graph API. For the same scenario with the Functional API, see Thinking in the Functional API. To choose between the APIs, see Choosing between Graph and Functional APIs.

Start with the process you want to automate

Imagine that you need to build an AI agent that handles customer support emails. Your product team has given you these requirements:
To implement an agent with the Graph API, this guide shows you five steps. Steps 1 and 2 are planning. Steps 3 through 5 turn that plan into state, nodes, and a compiled graph you can run.

Step 1: Map out your workflow as discrete steps

Start by identifying the distinct steps in your process. Each step becomes a node (a function that does one specific thing). Then sketch how these steps connect. Do this before writing code. Graph API control flow: START through classify and draft, then human review or send, to END Solid arrows are fixed edges: you declare them when you build the graph because the next node is known before a run. Dashed arrows are routes chosen at runtime inside a node with Command. Now that the components are identified, clarify what each node needs to do:
  • Read Email: Extract and parse the email content
  • Classify Intent: Use an LLM to categorize urgency and topic, then route to search, bug tracking, or draft
  • Doc Search: Query your knowledge base for relevant information
  • Bug Track: Create or update an issue in a tracking system
  • Draft Reply: Generate an appropriate response, then route to human review or send
  • Human Review: Escalate to a human agent for approval or handling
  • Send Reply: Dispatch the email response
Some nodes always continue on a fixed edge (Read Email to Classify Intent). Others choose the next node with Command (Classify Intent, Draft Reply, Human Review).

Step 2: Identify what each step needs to do

For each node from Step 1, decide what kind of work it is and what context it needs. Use the categories below, then expand the email-agent examples for concrete fields and retry choices.

LLM steps

Use when you need to understand, analyze, generate text, or make reasoning decisions

Data steps

Use when you need to retrieve information from external sources

Action steps

Use when you need to perform external actions

User input steps

Use when you need human intervention

LLM steps

When a step needs to understand, analyze, generate text, or make reasoning decisions:
  • Static context (prompt): Classification categories, urgency definitions, response format
  • Dynamic context (from state): Email content, sender information
  • Desired outcome: Structured classification that determines routing
  • Static context (prompt): Tone guidelines, company policies, response templates
  • Dynamic context (from state): Classification results, search results
  • Desired outcome: Professional email response ready for review

Data steps

When a step needs to retrieve information from external sources:
  • Parameters: Query built from intent and topic
  • Retry strategy: Yes, with exponential backoff for transient failures
  • Caching: Could cache common queries to reduce API calls

Action steps

When a step needs to perform an external action:
  • When to execute: After approval (human or automated)
  • Retry strategy: Yes, with exponential backoff for network issues
  • Should not cache: Each send is a unique action
  • When to execute: Always when intent is “bug”
  • Retry strategy: Yes, critical to not lose bug reports
  • Returns: Ticket ID to include in the response

User input steps

When a step needs human intervention:
  • Context for decision: Original email, draft response, urgency, classification
  • Expected input format: Approval boolean plus optional edited response
  • When triggered: High urgency, billing, or complex issues
  • On rejection: End the run without sending (human handles the email outside the graph)

Step 3: Design your state

State is the shared memory accessible to all nodes in your agent. Think of it as the notebook your agent uses to keep track of everything it learns and decides as it works through the process.

What belongs in state?

Ask yourself these questions about each piece of data:

Include in state

Does it need to persist across steps? If yes, it goes in state.

Do not store

Can you derive it from other data? If yes, compute it when needed instead of storing it in state.
For the email agent, track:
  • The original email and sender info (cannot reconstruct these later)
  • Classification results (needed by later nodes)
  • Search results or ticket context (expensive to re-fetch)
  • The draft response (needs to persist through review)
  • Execution metadata (for debugging and recovery)

Keep state raw, format prompts on demand

Store raw data in state, not formatted prompt text. Format prompts inside nodes when you need them.
That separation means:
  • Different nodes can format the same data differently
  • You can change prompt templates without changing the state schema
  • Debugging shows the data each node received, not a baked-in prompt string
Define the state now. The nodes in the next steps use these fields.

Step 4: Build your nodes

A node is a Python function that takes the current state and returns updates to it. When the node also chooses the next step, return a Command.

Handle errors appropriately

Before you fill in node bodies, consider how each node can fail. Attach retries on nodes that call external APIs. Use interrupt() for human input. Use this table as a guide: For retry policies, timeouts, compensation handlers, LLM-recoverable loops, when to let errors bubble up, and graph-wide defaults, see Fault tolerance. For this example, attach a retry policy when you add a node that can hit transient failures:

Implementing the email agent nodes

Turn each unit of work into a node. Each node reads the shared state, does one piece of work, and returns updates. When the next step depends on the result, return a Command with both the update and the destination. Human review is still a node: it calls interrupt() inside the function, not a separate graph primitive. The stubs below show the signatures, state updates, and routing for the email agent. Replace the placeholder bodies with your real LLM calls and API integrations. Put these nodes in the same file as the state from Step 3. read_email is a passthrough in this walkthrough. Keeping it as its own node still gives you a checkpoint before classification and a place to add real MIME parsing later.
You now have node functions. Next, wire them into a compiled graph.

Step 5: Wire it together

Connect the nodes into a graph. Because routing lives inside Command nodes, you only need a few fixed edges. Compile with a checkpointer, which is required for interrupt() to pause and resume.
Python nodes that return Command[Literal[...]] declare their destinations in the type hint. LangGraph uses that to know the valid edges.

Try out your agent

After the graph is compiled, invoke it with a sample email. With the placeholder classifier above, this urgent billing message is treated as high urgency, so the run drafts a reply, hits interrupt(), and you can practice resume. Use the same thread_id for the first invoke and the resume invoke.
The graph pauses at interrupt(), saves state to the checkpointer, and waits. When you resume with Command(resume=...), the human_review node re-runs from the start, interrupt() returns your resume value, and the graph continues. The thread_id keeps this conversation’s state together.

Summary and next steps

Key insights

Building this email agent shows the Graph API way of thinking:

Break into discrete steps

Each node does one thing well. Node boundaries are where the graph checkpoints, so failures and resumes redo less work.

State is shared memory

Store raw data, not formatted text. Different nodes can use the same information in different ways.

Nodes are functions

They take state, do work, and return updates. When they route, they return both the update and the next destination with Command.

Errors are part of the flow

Transient failures get node retry policies, user-fixable problems pause with interrupt(), and unexpected errors fail the run.

Human input is first-class

interrupt() pauses execution and saves state. On resume, the node re-runs from the start, so keep work before interrupt() idempotent.

Graph structure stays minimal

You declare essential edges. Nodes that need dynamic routing return Command, which keeps control flow explicit and traceable.

Node granularity trade-offs

Most applications can use the breakdown above. Read this section when you are deciding whether to merge or split nodes. LangGraph checkpoints at node boundaries. When a run resumes after an interrupt or failure, it restarts from the beginning of the node where execution stopped. Smaller nodes mean more frequent checkpoints and less work to repeat. Why this breakdown fits the email agent:
  • Isolation of external services: Doc Search and Bug Track are separate nodes so retries stay scoped to those API calls.
  • Visibility: Classification is its own node, so you can inspect the structured result before the next action.
  • Different failure modes: LLM calls, lookups, and email sends get different retry policies.
  • Testing: Smaller nodes are easier to unit test and reuse.
Merging Read Email and Classify Intent into one node is valid. You lose a checkpoint between them and repeat both on failure. For most agents, the observability benefit of separate nodes is worth it. Caching search results is an application-level choice inside your node functions. LangGraph does not prescribe a cache API for that. More nodes do not necessarily mean slower runs: by default, checkpoints write in the background (async durability mode).

Where to go from here

This walkthrough introduces Graph API design. Extend it with the how-tos and related guides:

Build checklist

Decision guide for state, control flow, Send, Command, and nodes

Define and update state

Schemas, reducers, messages, and private channels

Control flow

Sequences, branches, loops, and recursion limits

Send and Command

Map-reduce fan-out and update-and-route in one step

Configure nodes

Retries, timeouts, runtime context, and caching

Thinking in the Functional API

The same email agent with entrypoints and tasks

Human-in-the-loop patterns

Tool approval, batch approval, and other interrupt patterns

Add memory

Short-term checkpointers and long-term stores

Fault tolerance

Retries, timeouts, error handlers, and graph defaults

Subgraphs

Nested graphs for complex multi-step operations

Streaming

Real-time progress updates for users