Lesson 05 LangGraph Intro
Theme:

LangGraph Intro

Lesson 05 · Building Your Own Agents · ~12 min read

What LangGraph is — and what it isn’t

Let’s clear up the most common confusion first: LangGraph is not LangChain. LangChain is the older, broader library — chains, retrievers, integrations, prompt templates, the (now-deprecated) agent classes. It bundles a lot of opinions and a lot of dependencies. LangGraph is its newer, narrower sibling focused on one thing: explicit graph-shaped orchestration. You can use LangGraph without ever importing langchain, and this course does exactly that — we call anthropic directly from inside node functions and the dependency footprint stays small.

Contrast LangGraph with the other framework SDKs you’ll meet on this course. The Claude Agent SDK (L03–L04) lives inside the anthropic package — one install, Claude-only. LangGraph is its own package (pip install langgraph) and is LLM-provider-agnostic: nothing in langgraph cares whether your node function calls Claude, GPT, Llama, or a local rules engine. The OpenAI Agents SDK (Module 4) is openai-agents alongside the base openai client. CrewAI (Module 5) is its own package with a role/task vocabulary. LangGraph picks a different abstraction than any of them: it asks you to draw your agent as a graph and hands you the runtime that walks it.

The three primitives

LangGraph’s API surface is tiny. The whole of L05 fits into three concepts:

That’s the whole surface area of L05. Module 6 (Plan F) will revisit advanced primitives — sub-graphs, send/receive for parallel fan-out, persistence-store integrations — when the cross-framework comparison demands them. For now, three concepts is enough to build any of the agents in this module.

Why graphs over chains

A chain assumes the path is fixed: A → B → C. The moment your control flow has any of:

…you’re now in graph territory. A chain can fake some of these with conditional logic inside a single step, but the topology stops being visible. LangGraph makes the topology a first-class declaration: you can render the graph with app.get_graph().draw_mermaid() and look at it.

This visibility pays back in operability. When something goes wrong in production, “show me the graph of what this agent does” is a question with a one-line answer. With a tool-heavy agent (L04 shape), the same question is closer to “read the system prompt and squint.” The graph is both documentation and runtime — they cannot drift apart.

The cost: more boilerplate up front (declare the state, declare the nodes, wire the edges) compared to the Claude Agent SDK’s “just hand me a list of tools.” That cost is worth it when the control flow has a shape worth drawing. It’s not worth it when it doesn’t — which is exactly when you should reach for L04’s setup instead.

State is a TypedDict

Here’s the canonical state-declaration pattern you’ll see in every LangGraph codebase:

from typing import TypedDict, Annotated
import operator

class S(TypedDict):
    question: str
    notes: Annotated[list[str], operator.add]   # accumulating list
    answer: str

Two details matter here:

Don’t mutate state in place

Always return a fresh dict from your node function. Mutating state["notes"].append(...) and returning {} will sometimes work and sometimes silently lose data depending on the checkpointer — the contract is “return a dict of updates,” not “mutate.” Stay on the right side of the contract and reducers will Do The Right Thing.

Edges, conditional edges, and loops

The two-flavour API in code:

g.add_edge("plan", "fetch")               # unconditional
g.add_conditional_edges(                  # conditional
    "critique",
    lambda s: "rewrite" if s["needs_revision"] else "finish",
    {"rewrite": "write", "finish": END},
)

The decision function receives the current state and returns a string. The mapping translates that string into the next node. Two patterns this enables:

Caps are still your responsibility

Loops are why max_turns-style caps remain your job in LangGraph. The runtime won’t stop a graph because it has been running for 30 iterations on its own. If your loop’s exit condition has a bug, you’ll find out via the cost bill. Bake a counter into state and check it in the decision function — the same discipline L03 taught for the Claude Agent SDK applies here too.

Checkpoints with SqliteSaver

This is the headline LangGraph feature and the thing that most justifies the extra boilerplate. The pattern:

from langgraph.checkpoint.sqlite import SqliteSaver

memory = SqliteSaver.from_conn_string("checkpoints.db")
app = g.compile(checkpointer=memory)

config = {"configurable": {"thread_id": "user-42-task-7"}}
app.invoke(initial_state, config=config)

Pass a checkpointer to .compile(), give every invocation a thread_id, and you get three things for free:

  1. Crash resume. After a crash, restart the process. Call app.invoke({}, config={"configurable": {"thread_id": "user-42-task-7"}}) (empty input, same thread). The graph picks up at the last completed checkpoint — the LLM calls and tool calls that already succeeded are not re-run. For a $0.30 pipeline this is the difference between a cheap retry and a full re-bill.
  2. State inspection. app.get_state(config) returns the current snapshot. app.get_state_history(config) walks backwards through every checkpoint. You can stare at the exact state the graph was in before any node fired — invaluable for debugging.
  3. Time-travel / re-run. Find a checkpoint in the history, call .update_state(...) to tweak any field, then .invoke(...) to re-run from there with the new values. This is how you answer questions like “what would have happened if the planner had returned X instead of Y?” without re-running the whole pipeline.

SqliteSaver uses sqlite (stdlib, no extra dependency — pulls from sqlite3) and can run in-memory (:memory:) for tests or on disk for production. Postgres and Redis savers exist (langgraph-checkpoint-postgres, langgraph-checkpoint-redis) for scale; the lab and L06’s pipeline agent stick with sqlite.

When to pick LangGraph (and when not to)

A short decision matrix the reader will revisit in Module 6:

Pick LangGraph when:

Don’t pick LangGraph when:

This is the framing L06 builds on: the planner → fetcher → critique → writer pipeline is the canonical sweet-spot demo — named stages, a conditional branch, a possible loop, and a checkpointer that makes mid-pipeline crashes a non-event. Read on.

References