By Lordpatil · · 4 min read
LangGraph Core Concepts: A Simple Explainer
A short and concise explanation of the core concepts of LangGraph. It is designed to be easily understandable for anyone new to the framework.
LangGraph Core Concepts: A Simple Explainer
This document provides a short and concise explanation of the core concepts of LangGraph. It is designed to be easily understandable for anyone new to the framework.
Graph API Concepts
At its heart, LangGraph models complex workflows as graphs. Think of it as a flowchart for your application where you can define logic, loops, and conditional branches.
Graphs
A LangGraph is built from three main components:
- State: A shared data structure that holds the current information of your application. It's the "memory" of your graph that gets passed around and updated.
- Nodes: Python functions that perform the actual work. A node takes the current
State, does something (like calling an LLM, running a tool, or performing a calculation), and returns an update for the state. - Edges: These are the connections between nodes. They define the path of execution, telling the graph which node to run next based on the current
State.
In short: Nodes do the work, Edges tell what to do next.
To make a graph runnable, you must compile it. This step checks your graph's structure and prepares it for execution.
# graph_builder is an instance of StateGraph with nodes and edges added
graph = graph_builder.compile()
State
The State defines the data schema for your graph. It's typically a TypedDict. Every node receives the state and can return updates to it.
Schema
The schema is the structure of your state.
from typing_extensions import TypedDict
class MyGraphState(TypedDict):
question: str
answer: str
history: list
Reducers
Reducers are functions that define how state updates are applied. By default, any update from a node overwrites the existing value in the state. You can change this behavior.
For example, to make a list append-only instead of being overwritten, you can use a reducer like operator.add.
from typing import Annotated
from typing_extensions import TypedDict
from operator import add
class MyGraphState(TypedDict):
# 'question' will be overwritten on each update
question: str
# New items for 'history' will be added to the existing list
history: Annotated[list, add]
MessagesState
Since tracking conversation history is very common, LangGraph provides a pre-built MessagesState which automatically manages a list of messages using an add_messages reducer. This is highly convenient for building chatbots.
from langgraph.graph import MessagesState
# Inherit from MessagesState to get a 'messages' key with an appending reducer
class ChatState(MessagesState):
user_name: str
Nodes
Nodes are the workhorses of your graph. They are simple Python functions that receive the current state and return a dictionary of updates.
# A simple node that updates the state
def my_first_node(state: MyGraphState):
# state["question"] would be available here
update = {"answer": "This is a simple answer."}
return update
# Add the node to your graph builder
builder.add_node("node_name", my_first_node)
There are two special nodes:
START: The entry point of the graph.END: A terminal node that stops the execution of that path.
Edges
Edges connect your nodes and define the control flow.
Normal Edges
A normal edge creates a fixed path from one node to another.
# Always go from 'node_1' to 'node_2'
builder.add_edge("node_1", "node_2")
Conditional Edges
A conditional edge uses a function to decide the next node(s) based on the current state. This allows for branching and loops. The function should return the name of the next node.
def should_i_continue(state: MyGraphState) -> str:
if len(state["history"]) > 5:
return "end_process" # Route to the 'end_process' node
else:
return "continue_process" # Route to the 'continue_process' node
# Add a conditional edge from 'some_node'
builder.add_conditional_edges("some_node", should_i_continue)
Advanced Control Flow
LangGraph provides special return types for more complex logic.
Send
Used for dynamic fan-out operations, like in a map-reduce pattern. A conditional edge can return a list of Send objects to trigger multiple node calls in parallel, each with its own specific input.
from langgraph.graph import Send
def fan_out_to_process_subjects(state: MyGraphState):
# For each subject, trigger a 'process_one' node call with a specific subject
return [Send("process_one", {"subject": s}) for s in state['subjects']]
builder.add_conditional_edges("start_node", fan_out_to_process_subjects)
Command
A Command allows a node to both update the state and decide where to go next, combining the roles of a node and a conditional edge. This is very useful for multi-agent systems where an agent needs to pass control to another.
from typing import Literal
from langgraph.types import Command
def agent_node(state: State) -> Command[Literal["other_agent", "end_node"]]:
if some_condition:
# Update state and route to 'other_agent'
return Command(update={"foo": "bar"}, goto="other_agent")
else:
# Just route to the end
return Command(goto="end_node")
Persistence
LangGraph's persistence layer allows you to save and resume the state of a graph at any point. This is the foundation for powerful features like memory, human-in-the-loop, and time-travel debugging.
Checkpointers
A checkpointer automatically saves a snapshot of the graph's state after every step. To use it, you just pass it during graph compilation.
from langgraph.checkpoint.sqlite import SqliteSaver
memory = SqliteSaver.from_conn_string(":memory:")
graph = builder.compile(checkpointer=memory)
Threads and Checkpoints
-
Thread: A single, continuous execution or conversation, identified by a
thread_id. All state changes for a conversation are saved to its thread. To invoke a graph with persistence, you must provide athread_id.config = {"configurable": {"thread_id": "user_123_conversation_1"}} graph.invoke({"question": "Hi!"}, config=config) -
Checkpoint: A snapshot of the graph's state at a specific moment in a thread. LangGraph saves a checkpoint at every step, creating a complete history of the execution that you can inspect or revert to.
Key Applications & Capabilities
Multi-agent Systems
You can build complex systems by composing multiple "agents". In LangGraph, an agent is just a graph (or a node). These agents can be orchestrated by a "supervisor" agent, forming a hierarchy. This pattern is excellent for breaking down complex problems into specialized tasks.
Human-in-the-loop
Thanks to persistence, you can pause a graph's execution at any point (e.g., before a critical tool call) and wait for human input. A user can review the state, approve or reject the action, or even modify the state before the graph resumes.
Visualization
LangGraph can generate diagrams of your graph, which is incredibly helpful for debugging and understanding complex flows.
# Get a PNG image of the graph diagram
Image(graph.get_graph().draw_mermaid_png())
Authored by Chirag Patil