LangGraph for production agents: checkpoints, HITL, and multi-agent graphs
Why chains stall on cycles and resume, and how LangGraph nodes, edges and checkpoint savers make production agents operable, with human in the loop review.
This article is published in English.
Sources: LangGraph Explained: Build Production-Ready AI Agents in 2026
What is LangGraph?
LangGraph is a runtime for building stateful, multi-step agents as explicit graphs: nodes perform work, edges decide what runs next, and shared state persists across steps—including after failures and human pauses.
Two years ago, many “agents” were while-loops around an LLM with no recovery. Mid-task crashes meant restarting from scratch. Graphs make memory, branching, and resume first-class.
Why chains weren’t enough
LCEL chains shine for linear pipelines. They struggle when you need cycles, conditional routing, durable checkpoints, or human approval mid-flight. Those are graph problems.
Core concepts: nodes, edges, and state
- State — typed fields the run carries
- Nodes — functions that update state
- Edges — fixed or conditional transitions
- Compile — validate structure, attach checkpointer
- Invoke/stream — execute with config
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
import operator
# Define your state
class AgentState(TypedDict):
messages: Annotated[list, operator.add]
next_action: str
final_answer: str
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
# 1. Define a tool
@tool
def search_web(query: str) -> str:
"""Search the web for current information."""
# Connect to your actual search API here
return f"Search results for: {query}"
# 2. Define the LLM with tools bound
llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [search_web]
llm_with_tools = llm.bind_tools(tools)
# 3. Define node functions
def call_llm(state):
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state):
last_message = state["messages"][-1]
if last_message.tool_calls:
return "run_tools"
return END
# 4. Build the graph
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_llm)
workflow.add_node("run_tools", ToolNode(tools))
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("run_tools", "agent")
# 5. Compile with a checkpointer
from langgraph.checkpoint.sqlite import SqliteSaver
memory = SqliteSaver.from_conn_string("agents.db")
graph = workflow.compile(checkpointer=memory)
# 6. Run with a thread ID for persistent state
config = {"configurable": {"thread_id": "user-123"}}
result = graph.invoke({"messages": [("user", "What is the latest GPT model?")]}, config)
print(result["messages"][-1].content)
# Add a human approval gate before a risky action
workflow.add_node("human_review", interrupt(before="execute_payment"))
pip install langgraph langchain-openai psycopg2-binary
How state management actually works
Updates merge via reducers. Misconfigured reducers duplicate messages or wipe fields. Treat reducer choice as part of the schema review.
First agent (Python)
Define state → add nodes → wire edges → compile → invoke with a thread id when using a saver.
Human-in-the-loop
Interrupt before side effects; resume with approval payload. This is the operational difference between demos and production agents.
Multi-agent patterns
Supervisor/specialist handoffs, parallel branches, and subgraphs map cleanly onto named nodes with shared or partitioned state.
Persistence and recovery
Database-backed checkpointers survive process restarts. In-memory savers are for tests only.
When to reach for LangGraph
Use it when control flow is non-linear, must pause, or must resume. Prefer simpler chains for single-shot RAG answers with no branching.
Closing
Production agents need explicit state and edges—not prayers around a loop. LangGraph is one durable way to draw that picture in code.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Instrument node start/end events so traces show which branch ran. Graphs without telemetry become black boxes at 2am.
Version graph definitions alongside prompts. A node rename without migration breaks resumed threads that still point at old names.
Skip marketplace promo noise; the engineering value is checkpointed control flow and HITL. Keep tutorials focused on those primitives.
Map each production incident type to a graph feature: timeouts → retries node, bad tool JSON → validation node, operator override → interrupt.
Subgraphs help isolate specialist prompts without losing a single parent thread id for audit.
Avoid stuffing the entire business policy into one god-node; split so failures are attributable.
Map each production incident type to a graph feature: timeouts → retries node, bad tool JSON → validation node, operator override → interrupt.
Subgraphs help isolate specialist prompts without losing a single parent thread id for audit.
Avoid stuffing the entire business policy into one god-node; split so failures are attributable.
Map each production incident type to a graph feature: timeouts → retries node, bad tool JSON → validation node, operator override → interrupt.
Subgraphs help isolate specialist prompts without losing a single parent thread id for audit.
Avoid stuffing the entire business policy into one god-node; split so failures are attributable.
Map each production incident type to a graph feature: timeouts → retries node, bad tool JSON → validation node, operator override → interrupt.
Subgraphs help isolate specialist prompts without losing a single parent thread id for audit.
Avoid stuffing the entire business policy into one god-node; split so failures are attributable.
Map each production incident type to a graph feature: timeouts → retries node, bad tool JSON → validation node, operator override → interrupt.
Subgraphs help isolate specialist prompts without losing a single parent thread id for audit.
Avoid stuffing the entire business policy into one god-node; split so failures are attributable.
Map each production incident type to a graph feature: timeouts → retries node, bad tool JSON → validation node, operator override → interrupt.
Subgraphs help isolate specialist prompts without losing a single parent thread id for audit.