What is LangGraph?
LangGraph is a library for building stateful, multi-actor applications with LLMs. While LCEL chains flow in a straight line, LangGraph lets you build cycles, branches, and loops — exactly what you need for agents that can retry, reflect, or wait for human input.
Nodes
Python functions that read and update the shared state.
Edges
Connections between nodes — direct or conditional based on state.
State
A typed dict that persists across every node in the graph.
Key insight: LCEL is for pipelines (input → transform → output), like the RAG pipeline. LangGraph is for workflows with cycles, conditions, and persistence — the foundation for production agents.
Defining State
State is a TypedDict with Annotated fields. The annotation tells LangGraph how to merge updates — either replace or append.
from typing import Annotated, TypedDict
from langgraph.graph import add_messages
from langchain_core.messages import BaseMessage
class State(TypedDict):
# add_messages reducer: new messages are appended, not replaced
messages: Annotated[list[BaseMessage], add_messages]
# Plain field: each node's return value replaces the previous
next_step: str
iteration_count: int
LangGraph ships the add_messages reducer for chat history. You can also write custom reducers — any function with signature (existing, update) → new.
Nodes
A node is any Python callable that receives the current state and returns a dict of updates. Only the keys you return are updated — other state fields are left unchanged.
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
llm = ChatOpenAI(model="gpt-4o-mini")
def call_llm(state: State) -> dict:
"""Call the LLM and return its response."""
response = llm.invoke(state["messages"])
# Returning {"messages": [response]} appends to the list
return {"messages": [response]}
def check_length(state: State) -> dict:
"""Count iterations and update step."""
count = state.get("iteration_count", 0) + 1
return {"iteration_count": count}
Building the Graph
from langgraph.graph import StateGraph, START, END
# 1. Create the graph with your state schema
builder = StateGraph(State)
# 2. Add nodes
builder.add_node("llm", call_llm)
builder.add_node("counter", check_length)
# 3. Add edges (fixed)
builder.add_edge(START, "llm") # graph starts at llm
builder.add_edge("llm", "counter") # llm always goes to counter
builder.add_edge("counter", END) # counter always ends
# 4. Compile into a runnable
graph = builder.compile()
# 5. Invoke
result = graph.invoke({
"messages": [HumanMessage(content="What is LangGraph?")]
})
print(result["messages"][-1].content) # AI response
Conditional Edges
Use add_conditional_edges to route to different nodes based on state. The router function returns the name of the next node.
def should_continue(state: State) -> str:
"""Route based on iteration count."""
if state["iteration_count"] >= 3:
return "end"
last_message = state["messages"][-1]
if "DONE" in last_message.content:
return "end"
return "continue"
builder = StateGraph(State)
builder.add_node("llm", call_llm)
builder.add_node("counter", check_length)
builder.add_edge(START, "llm")
builder.add_edge("llm", "counter")
builder.add_conditional_edges(
"counter", # from this node
should_continue, # call this function
{"continue": "llm", "end": END}, # map return values to nodes
)
graph = builder.compile()
This creates a loop: the graph calls the LLM, checks state, and either loops back or ends — the core pattern for ReAct agents covered in LangGraph Agents.
MessagesState Shortcut
For chat-based graphs, LangGraph provides MessagesState — a built-in state with the messages field and add_messages reducer already configured.
from langgraph.graph import StateGraph, MessagesState, START, END
def call_llm(state: MessagesState):
response = llm.invoke(state["messages"])
return {"messages": [response]}
graph = (
StateGraph(MessagesState)
.add_node("llm", call_llm)
.add_edge(START, "llm")
.add_edge("llm", END)
.compile()
)
result = graph.invoke({"messages": [("user", "Hello!")]})
print(result["messages"][-1].content)
Streaming Graph Output
LangGraph supports streaming so you can display partial results as the graph runs — node by node or token by token.
# Stream state updates after each node
for chunk in graph.stream({"messages": [("user", "Explain recursion")]}):
for node_name, state_update in chunk.items():
print(f"[{node_name}]", state_update)
# Stream LLM tokens (requires streaming=True on the model)
llm_streaming = ChatOpenAI(model="gpt-4o-mini", streaming=True)
for chunk in graph.stream(
{"messages": [("user", "Write a poem")]},
stream_mode="messages", # token-level streaming
):
print(chunk[0].content, end="", flush=True)
LangGraph Basics FAQ
What is LangGraph?
LangGraph is a LangChain library for building stateful, multi-actor applications with LLMs. You define your application as a graph of nodes (Python functions) connected by edges, with a shared state object flowing between them. Unlike a straight-line chain, a graph can include cycles, branches, and loops, which is exactly what agents need to retry, reflect, or wait for human input.
What is the difference between LangGraph and LCEL chains?
LCEL chains are for linear pipelines: input flows through a fixed sequence of transforms to an output. LangGraph is for workflows that need cycles, conditional routing, and persistent state across steps. Use LCEL when the flow is a straight line, and reach for LangGraph when you need loops, branching decisions, or an agent that calls tools repeatedly until it is done.
What is state and StateGraph in LangGraph?
State is a typed dictionary (a TypedDict) that persists across every node in the graph; each field can use a reducer that controls whether updates replace or append values. StateGraph is the builder you create with your state schema, then add nodes and edges to before calling compile() to produce a runnable graph. The add_messages reducer is built in for chat history.
What are nodes and edges in LangGraph?
A node is any Python callable that receives the current state and returns a dict of updates; only the keys you return are changed. An edge connects one node to the next. Fixed edges always go to the same node, while conditional edges call a router function that inspects the state and returns the name of the next node, letting the graph branch or loop.
When should I use LangGraph instead of a simple chain?
Use LangGraph when your application needs to loop, make decisions based on intermediate results, retain memory across steps, or pause for human input. Typical cases include ReAct agents that call tools until a task is complete, self-correcting workflows that reflect and retry, and multi-actor systems. For a single prompt-to-answer pass, a plain LCEL chain is simpler and enough.
How do I compile and run a LangGraph graph?
Create a StateGraph with your state schema, add your nodes with add_node, wire them with add_edge or add_conditional_edges, and connect START and END. Call builder.compile() to get a runnable graph, then use graph.invoke(initial_state) for a single result or graph.stream(initial_state) to display partial output node by node or token by token.