10.05 LangGraph Core Components¶
To translate the concepts of state machines and graph theory into executable Python code, LangGraph provides a minimal but immensely powerful set of foundational building blocks.
Building a LangGraph application means you are no longer writing a top-to-bottom Python script. Instead, you are constructing a graphical map of your program using Nodes, Edges, Conditional Edges, and a unified State.
[!NOTE] Beginner's Cheat Sheet: - State: The Agent's Memory (Usually a dictionary). - Nodes: The Agent's Actions (Python functions that do work). - Edges: The Agent's Paths (Rules that connect Nodes together).
1. The Core Primitives¶
A. State: The Graph's Memory¶
The most critical concept in LangGraph is the State.
In standard Python scripts, you might use global variables or pass dozens of arguments between functions. LangGraph enforces a much cleaner approach: a single, structured data object (the State) that is explicitly passed from Node to Node.
- What is it? Typically implemented as a Python
TypedDictor a PydanticBaseModel. - What does it do? It retains the entire context of the execution—conversation history, intermediate extraction results, routing flags, and error logs.
- How does it change? Nodes do not return final answers; they return updates to the State. LangGraph's engine automatically merges these updates before passing the newly updated State to the next Node.
Let's look at some basic code:
from typing import TypedDict, Annotated
import operator
# 1. We define the blueprint for our State
class AgentState(TypedDict):
# 'Annotated' with 'operator.add' tells LangGraph:
# "Don't overwrite the old messages, append new ones to the list!"
messages: Annotated[list, operator.add]
current_tool: str
retry_count: int
Visualizing the State Flow:
sequenceDiagram
participant S as State Memory
participant R as Reasoner Node
participant T as Tool Node
S->>R: Read full message history
Note over R: LLM evaluates context
R-->>S: Return partial update (Append AIMessage)
Note over S: LangGraph auto-merges
S->>T: Read requested tool calls
Note over T: Execute Python function
T-->>S: Return partial update (Append ToolMessage)
Note over S: LangGraph auto-merges
B. Nodes: Units of Computation¶
A Node is a discrete unit of work within the graph (like a station on an assembly line). In practice, a Node is simply a Python function that accepts the current State as its input and returns a dictionary containing State updates.
Nodes can execute anything: - Calls to OpenAI/Anthropic APIs - SQL queries to your database - Math calculations or web searches
# 2. We define a Node (A worker on our assembly line)
def reasoner_node(state: AgentState):
"""Invokes the LLM to decide the next action."""
# We read from the current state memory
prompt = "Read this history and reply: " + str(state['messages'])
# We ask the LLM for a response
response = llm.invoke(prompt)
# IMPORTANT: We return an UPDATE to the state.
# Because 'messages' has 'operator.add', this response is appended!
return {"messages": [response]}
C. Edges: Deterministic Routing¶
Edges connect Nodes, defining the strict sequence of execution. If Node A has a standard edge to Node B, LangGraph will always transition to Node B once Node A completes.
- Purpose: Enforce rigid pipelines (the conveyor belt on the assembly line) where no AI decision is required.
D. Conditional Edges: Dynamic Routing¶
Conditional Edges breathe life and intelligence into the agent. Instead of a hardcoded path, a conditional edge uses a standard Python function to evaluate the current State and decide which Node to go to next.
- Purpose: Allow the LLM's output (which we saved in the State) to dictate the flow. For example, if the LLM asked to use a specific tool, the conditional edge routes the flow to the Tool Node.
def should_continue(state: AgentState) -> str:
"""This function acts as a traffic cop."""
# Look at the very last message in the state
last_message = state["messages"][-1]
# If the LLM requested to use a tool...
if "tool_calls" in last_message.additional_kwargs:
return "tool_node" # ...send it to the tool node!
# If not, the AI is done thinking. Send it to the end.
return "end"
2. Special Boundary Nodes¶
LangGraph provides two reserved nodes that anchor the workflow. Think of them as the front door and back door of the building.
STARTNode: The entry point. The initial input you type into the graph is routed fromSTARTto the very first active node.ENDNode: The termination point. When a node routes toEND, execution halts completely, and the final State is returned back to you.
3. Putting It Together: Building the Graph¶
To construct the engine, these pieces are snapped together using the StateGraph object. This is where you actually "draw" the map in Python.
from langgraph.graph import StateGraph, START, END
# 1. Initialize the graph, telling it to use our AgentState blueprint
workflow = StateGraph(AgentState)
# 2. Add our workers (Nodes) to the graph
workflow.add_node("reasoner", reasoner_node)
workflow.add_node("tool_node", execute_tools)
# 3. Define the Front Door: Start by going to the reasoner
workflow.add_edge(START, "reasoner")
# 4. Add the Traffic Cop (Conditional Routing)
# After the 'reasoner' runs, call 'should_continue' to decide where to go.
workflow.add_conditional_edges(
"reasoner",
should_continue,
{
"tool_node": "tool_node",
"end": END
}
)
# 5. Add a standard edge back to the reasoner (Creating the Loop!)
# Once the tool finishes, ALWAYS go back to the reasoner.
workflow.add_edge("tool_node", "reasoner")
# 6. Compile the graph into an executable application
app = workflow.compile()
graph TD
classDef sys fill:#eceff1,stroke:#607d8b,stroke-width:2px;
classDef node fill:#e3f2fd,stroke:#1565c0,stroke-width:2px;
classDef router fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;
Start((START)):::sys --> Reasoner[Reasoner Node]:::node
Reasoner --> Condition{should_continue ?}:::router
Condition -- "tool_node" --> Tool[Tool Node]:::node
Condition -- "end" --> End((END)):::sys
Tool -. "State updated with tool result" .-> Reasoner
4. Advanced System Capabilities for Beginners¶
Because we explicitly mapped out the graph (instead of hiding it in messy Python while loops), LangGraph unlocks incredible enterprise features that would otherwise take months to build:
Persistence & Checkpointing (Save States)¶
By giving LangGraph a database (like SQLite or Postgres) when you compile the graph, it automatically "saves the game" after every node execution. - Resiliency: If an OpenAI API timeout crashes the program midway through, the graph can simply resume from the exact Node where it failed. It remembers all the previous State. - Human-in-the-Loop (HITL): You can tell a Node, "Stop and wait for a human." The program sleeps and saves the State constraint to the database. A human can review the AI's requested action, click "Approve", and wake the graph back up!
Time Travel¶
Because every state transition is logged, developers can retrieve historical "save states", rewind the graph to a previous node, manually fix a mistake the AI made (like fixing a typo in a tool argument), and resume execution from the past!
Summary¶
The architectural brilliance of LangGraph is that it moves the complex logic out of the AI prompts and into the software architecture.
- Nodes constrain computation.
- Edges enforce paths.
- The State acts as the unified memory bus.
Together, they allow the construction of cyclic, stateful LLM orchestrations that are infinitely more reliable and observable than naive loop-based scripting.