LangGraph, from state to durable workflow
Imagine a customer asks:
Why was invoice 1042 charged twice?
The first step classifies the request. The next step may read billing records. A policy check may pause the case for approval. A temporary billing API failure may retry. If the process stops overnight, it may need to resume tomorrow without starting again.
That is the kind of workflow LangGraph helps make visible.
What you will understand
Section titled “What you will understand”| Chapter | Question you will be able to answer |
|---|---|
| 1. Graph model | How do state, nodes, and edges fit together? |
| 2. Concepts | Which LangGraph object owns each behavior? |
| 3. Runtime words | What is Python async, and what is LangGraph durability? |
| 4. Execution | What happens from START to END? |
| 5. Failure | How should tools retry, stop, or ask for help? |
| 6. Production review | Which questions expose unsafe workflow design? |
Chapter 1: Build the workflow from five parts
Section titled “Chapter 1: Build the workflow from five parts”- 01Define state
- 02Add nodes
- 03Connect edges
- 04Compile
- 05Run and observe
| Part | Question to ask | Start here |
|---|---|---|
| 1. State | What facts must survive the next step? | State schema |
| 2. Work | Which small function owns this operation? | Nodes |
| 3. Route | What must run next, and who chooses it? | Edges |
| 4. Runtime | How will this run, stream, pause, or resume? | Compile |
| 5. Failure | Which error retries, stops, or needs a person? | Retries and tool failures |
StateGraph is the builder. State is the shared record. Nodes return updates. Edges schedule the next work. compile() produces the runnable graph.1
Chapter 2: Learn the 19 concepts as a map
Section titled “Chapter 2: Learn the 19 concepts as a map”Read the first ten in order once. Use the remaining nine when the workflow needs tools, persistence, parallel work, or recovery.
| # | Concept | Keep this sentence |
|---|---|---|
| 1 | StateGraph | The builder that holds the workflow definition. |
| 2 | State schema | The contract for data moving through the graph. |
| 3 | Reducers and Annotated |
The rule for combining two updates to one state key. |
| 4 | Nodes | Python functions that do one unit of work. |
| 5 | Edges | Static transitions between nodes. |
| 6 | Conditional edges | A routing function chooses the next node. |
| 7 | START and END |
Virtual markers for entry and termination. |
| 8 | compile() |
Validate the definition and create a runnable graph. |
| 9 | invoke() and ainvoke() |
Run to a final result, synchronously or asynchronously. |
| 10 | stream() and astream() |
Observe updates while the graph runs. |
| 11 | Command |
Return a state update and route together, or resume an interrupt. |
| 12 | Send and fan-out |
Create parallel node calls with different inputs. |
| 13 | ToolNode and tools_condition |
Execute model-requested tools and route the tool loop. |
| 14 | Checkpoints and thread_id |
Save state snapshots under one run or conversation identity. |
| 15 | Durability modes | Choose when checkpoint writes must finish. |
| 16 | Interrupt and resume | Persist a pause and continue later with external input. |
| 17 | Subgraphs | Put a smaller graph behind a clear parent interface. |
| 18 | RetryPolicy and tool failures |
Retry classified transient failures at the node boundary. |
| 19 | Async waiting and timeouts | await is Python; durable waiting needs LangGraph state and policy. |
Chapter 3: Keep runtime words in the correct layer
Section titled “Chapter 3: Keep runtime words in the correct layer”| Word | Owned by | What it means |
|---|---|---|
async def |
Python | Define a coroutine function. |
await |
Python | Yield control until an awaitable completes. |
asyncio.sleep() |
Python | Keep an in-memory coroutine paused for a duration. |
ainvoke() / astream() |
LangGraph runnable API | Run or stream the graph asynchronously. |
RetryPolicy |
LangGraph | Re-run a failed node attempt with bounded backoff. |
TimeoutPolicy |
LangGraph | Cancel an async node attempt that exceeds its limit. |
interrupt() |
LangGraph | Save a pause and wait for an external resume value. |
sleep, await, and asyncio.sleep are not LangGraph keywords. Sleeping inside a node does not create a durable timer or checkpoint. Use RetryPolicy for short transient retries and interrupt() plus a checkpointer for a pause that must survive process restarts.2
Chapter 4: Read one graph from beginning to end
Section titled “Chapter 4: Read one graph from beginning to end”The example below keeps the work deliberately small. It classifies a support question and drafts the next response.
Read it in this order:
SupportStatedefines the fields that move through the workflow.classifyreads the question and returns only thecategoryupdate.draft_responsereads the updated category and returns the response.- The builder registers both nodes.
- Edges connect
START → classify → draft_response → END. compile()creates the runnable graph.invoke()supplies the initial state and waits for the final state.
"""A minimal LangGraph workflow with explicit, inspectable state."""
from typing import Literal, TypedDict
from langgraph.graph import END, START, StateGraph
class SupportState(TypedDict): question: str category: Literal["invoice", "general"] response: str
def classify(state: SupportState) -> dict: category = "invoice" if "invoice" in state["question"].lower() else "general" return {"category": category}
def draft_response(state: SupportState) -> dict: if state["category"] == "invoice": response = "I will look up the invoice and cite the matching record." else: response = "I will search the support knowledge base before answering." return {"response": response}
builder = StateGraph(SupportState)builder.add_node("classify", classify)builder.add_node("draft_response", draft_response)builder.add_edge(START, "classify")builder.add_edge("classify", "draft_response")builder.add_edge("draft_response", END)
graph = builder.compile()
result = graph.invoke( {"question": "Why is invoice 1042 duplicated?", "category": "general", "response": ""})print(result["response"])This example does not need an agent. The path is known. LangGraph can still help because the state and sequence are explicit. Add conditional edges only when the next node truly depends on state.
Chapter 5: Decide what happens when a tool fails
Section titled “Chapter 5: Decide what happens when a tool fails”- 01Model requests a tool
- 02ToolNode validates arguments
- 03Tool executes
- 04Exception is handled or escapes
- 05RetryPolicy retries the node or recovery takes over
| Failure | Normal owner | Result |
|---|---|---|
| Malformed model arguments | ToolNode default handler |
Error ToolMessage; the model may correct its call. |
| Temporary upstream failure | Node RetryPolicy |
Exception must escape ToolNode; LangGraph retries the node. |
| Missing permission or bad user input | Application rule | Stop, return a safe error, or ask the user. Do not retry blindly. |
| Retries exhausted | Node error handler or caller | Route to fallback/human review, or let the run fail visibly. |
| Unknown exception | Developer | Let it bubble, trace it, and fix the defect. |
Retries happen around the whole node attempt, not around one line inside a tool. Any side-effecting tool in that node needs an idempotency key or a check-before-write rule.
Chapter 6: Review the workflow before production
Section titled “Chapter 6: Review the workflow before production”- Which state keys may receive parallel updates, and what reducer merges them?
- Which node owns each network call and timeout?
- Which exceptions are transient, user-fixable, policy failures, or defects?
- Can a retry send the same email, payment, or ticket twice?
- Which checkpoint and tenant boundary does
thread_ididentify? - Where does a person approve, reject, or correct the run?
- What stops every loop and caps every fan-out?
Interview answer in 30 seconds
Section titled “Interview answer in 30 seconds”LangGraph is a runtime for stateful workflows. I define a state schema, write small Python nodes, connect them with fixed or conditional edges, compile the graph, and invoke or stream it. A checkpointer can persist state under a thread so the workflow can pause and resume.
RetryPolicyretries classified node failures, whileinterrupt()creates a durable human or external-input boundary. I keep side effects idempotent because a retry repeats the node attempt, and I put a stop rule on every loop and fan-out.
Next: see how checkpoints and threads preserve state, how interrupt and resume pauses work, or how tool failures and retries should behave.
Footnotes
Section titled “Footnotes”-
LangChain, Graph API overview. ↩
-
LangChain, Fault tolerance and Interrupts. ↩