LangGraph Architecture

LangGraph is a library for building stateful, multi-actor applications with LLMs. Unlike standard LangChain which favors linear "Chains," LangGraph is built on a State Machine (Graph) model. This is the foundation of modern Flow Engineering.

Why Graphs?

In a production agentic system, you need three things that chains cannot provide:

  1. Cycles: The ability for the agent to go back to a previous step (e.g., "Tool failed, try again").
  2. State Persistence: Saving the agent's progress to a database so it can resume after a crash.
  3. Human-in-the-Loop: Pausing the agent and waiting for a human to approve a sensitive tool call (e.g., delete_database).

The Core Concept: The State Object

The "State" is the single source of truth passed between every node in the graph. In LangGraph, you define a schema (usually a TypedDict) that tracks the conversation history and any extracted data.

Concrete Example: A Basic ReAct Graph

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_core.messages import BaseMessage, HumanMessage
import operator

# 1. Define the State
class AgentState(TypedDict):
    # 'operator.add' tells LangGraph to append new messages instead of overwriting
    messages: Annotated[list[BaseMessage], operator.add]

# 2. Define Nodes (The Logic)
def call_model(state: AgentState):
    response = model.invoke(state["messages"])
    return {"messages": [response]}

def call_tool(state: AgentState):
    # Logic to execute the tool chosen by the model
    return {"messages": [tool_result]}

# 3. Build the Graph
workflow = StateGraph(AgentState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", call_tool)

workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("tools", "agent")

app = workflow.compile()

Advanced Patterns: The "Checkpoint"

LangGraph’s most powerful feature is its Checkpointer. By passing a thread ID, you can save the entire state of the graph to SQLite or Postgres after every node execution.

# Enable persistence
from langgraph.checkpoint.sqlite import SqliteSaver
memory = SqliteSaver.from_conn_string(":memory:")
app = workflow.compile(checkpointer=memory)

# Resume a specific thread
config = {"configurable": {"thread_id": "user_session_42"}}
app.invoke({"messages": [HumanMessage(content="Check my balance")]}, config)

Comparison: LangGraph vs. Legacy Agents

FeatureLangChain AgentExecutorLangGraph
Control"Black Box" (Automatic)Explicit (You define the nodes)
CyclesLimitedNative
StateChat History OnlyArbitrary Structured Data
PersistenceNone (Built-in)Native (First-class)

Architectural Insight: Use LangGraph when your agent needs to be deterministic at the macro-level (the paths it can take) but agentic at the micro-level (what it does at each node).

Further Reading