LangGraph overview

What is LangGraph?

– LangGraph overview:
LangGraph is an AI orchestration framework based on graph theory and state machines.
It enables building cyclic agentic applications.
These applications can make decisions, test code, and handle errors autonomously.

– How things articulate together:
To set up a LangGraph agent, concepts articulate sequentially:
First, you declare a State Schema, that represents the shared memory of the application.
Then, you define Nodes (by writing Python functions) to read the state and return state deltas.
Next, you link Nodes with Edges to direct the execution flow.
Finally, you compile everything into a runnable Agent executed by the Pregel Runtime engine.

– The main concepts:
– State and Channels (Data contract and memory)
– Nodes (Business execution units)
– Edges (Routing and control flow)
– Agent Materialization (Compilation and execution lifecycle)
– Runtime and Persistence (Pregel and Checkpointer)

State and Channels

– State and State Schema definition:
The State Schema is the structural blueprint (defined via a typed structure like TypedDict or Pydantic).
The State is the runtime instance of this schema; it is the central shared memory accessed by the nodes.
It declares the expected keys, data types, and merge rules (Reducers) for state updates.
It acts as the data contract passed to StateGraph(StateSchema).

– Defining a State Schema (AgentState):

from typing import TypedDict, Annotated, Optional # Python standard library
import operator                                   # Python standard library
 
# The State Schema declares the keys and their Channel behaviors
class AgentState(TypedDict):
    query: str                                 # Simple key (LastValue Channel)
    generated_code: Optional[str]              # Simple key (LastValue Channel)
    errors: Annotated[list[str], operator.add] # Reducer: appends lists via operator.add (standard library)

– How a Node manipulates the State:
A Node receives a State snapshot (a read-only view of all keys at the start of the Superstep) as input.
It performs its internal business logic (including any external call if needed: LLMs, banking services, etc.).
It returns a partial dictionary (a delta) containing only the modified keys.

– Concrete example of State reading and writing:

def generate_code_node(state: AgentState) -> dict:
    # 1. READ: Extract the value from the "query" channel
    user_query = state["query"]
 
    # 2. PROCESS: Business logic or LLM call
    code = llm_client.generate(user_query)
 
    # 3. WRITE: Return only the key to modify (the delta)
    return {"generated_code": code}

– How Channels and Reducers work under the hood:
As a developer, you only declare **Keys** and optional **Reducers** in your State Schema.
Under the hood, LangGraph automatically maps each key to an internal memory container called a Channel.

– Default behavior (LastValue Channel):
Without a Reducer annotation, LangGraph creates a default LastValue channel.
New values returned by a Node overwrite previous values, while omitted keys remain unchanged.

Beware: Inside a LastValue Channel, the execution flow behavior is deterministic: if more than one node attempts to update (which effectively means overwrite) concurrently the same LastValue key, LangGraph throws an error.

– Accumulator behavior (Reducers):
When you annotate a key with a Reducer, LangGraph attaches a combination function to the channel.
Instead of overwriting, the channel executes this function to merge new incoming data with existing data.
In sequential execution, reducers allow appending instead of overwriting while in parallel execution, these are mandatory to satisfy LangGraph’s internal behavior.

Examples of Reducers:
Standard Python Operators: operator.add (appends lists or adds numbers).
Custom Functions: Any Python function with signature (current_value, new_value) -> updated_value (e.g., merging dictionaries or deduplicating entries).
Framework Helpers: Specialized reducers like LangGraph’s add_messages, which manages chat history by appending new LLM messages, updating existing ones by ID, or handling deletions.

Nodes

– Node definition:
A Node is an autonomous execution unit (a Python function).
It reads the snapshot of the state, performs internal business logic, and returns a state delta.

– Writing Nodes (examples):

# 1. Generation Node: extracts query, generates code, updates 'generated_code'
def generate_code_node(state: AgentState) -> dict:
    prompt = state["query"]
    code = llm_client.generate(prompt)
    return {"generated_code": code}
 
# 2. Validation Node: checks code quality and appends potential errors to the State
def validate_code_node(state: AgentState) -> dict:
    code = state.get("generated_code", "")
    if "def " not in code:
        return {"errors": ["Invalid code format: missing function definition."]}
    return {"errors": []}

– Node isolation:
Each node is strictly isolated.
It does not know which node executed before it nor which one will execute after it.

Edges and Routing

– Static Edges (add_edge):
Define a deterministic and unconditional transition from Node A to Node B.
builder.add_edge("retrieve_node", "generate_node")

– Conditional Edges (add_conditional_edges):
Represent logical switches (conditional structures). They analyze the current state to select the destination route.

– Writing a conditional routing function:
Here, we define a routing rule from the « validate_node » node based on the output of route_validation(): if it returns « retry », control flows back to « generate_node » to attempt code regeneration, otherwise it routes to END to complete the execution.

def route_validation(state: AgentState) -> str:
    if state.get("errors"):
        return "retry"
    return "success"
 
builder.add_conditional_edges(
    "validate_node",
    route_validation,
    {
        "retry": "generate_node",
        "success": END
    }
)

– Parallel Execution (Fan-out & Fan-in):
To run multiple nodes in parallel during the same Superstep, simply attach multiple edges from a single source node. They will execute concurrently, and their outputs will be merged into the State.

– Writing parallel edges (example):
Here « fetch_docs_node » and « fetch_web_node » are executed concurrently at the first cycle (fan-out).
In the second cycle, summarize_node() is executed (fan-in).

# 1. FAN-OUT: "START" triggers both nodes in parallel during Superstep 1
builder.add_edge(START, "fetch_docs_node")
builder.add_edge(START, "fetch_web_node")
 
# 2. FAN-IN: Both parallel nodes converge into a single summary node for Superstep 2
builder.add_edge("fetch_docs_node", "summarize_node")
builder.add_edge("fetch_web_node", "summarize_node")

Agent Materialization

– What is an Agent in LangGraph?:
In LangGraph, an Agent is not a pre-packaged class or black box. It is materialized as a compiled state machine (a CompiledGraph) built by binding a State schema, Nodes (execution functions), and Edges (control flow logic).

– Materializing and executing an Agent:
This example constructs a cyclic agent that generates and validates code with automatic retry logic, then compiles and executes it.

from langgraph.graph import StateGraph, START, END
from langgraph.graph.state import CompiledStateGraph # 1. Import du type explicite du graphe compilé
 
# 1. StateGraph instantiation with the State schema
builder: StateGraph = StateGraph(AgentState)
 
# 2. Registering Nodes (execution units)
builder.add_node("generate_node", generate_code_node)
builder.add_node("validate_node", validate_code_node)
 
# 3. Defining Edges (control flow and routing)
builder.add_edge(START, "generate_node")
builder.add_edge("generate_node", "validate_node")
builder.add_conditional_edges(
    "validate_node", 
    route_validation, 
    {"retry": "generate_node", "success": END}
)
 
# 4. MATERIALIZATION: Compiling into an executable Agent instance
agent: CompiledStateGraph = builder.compile() # Typage explicite
 
# 5. EXECUTION: Invoking the agent with an initial State payload
initial_payload: AgentState = {"query": "Create a fee calculation function", "errors": []}
final_state: AgentState = agent.invoke(initial_payload) # Typage explicite

– Framework Lifecycle:
– Graph Building: Define the State schema and attach Nodes and Edges to a StateGraph builder.

– Compilation: Calling builder.compile() materializes the schema into an executable CompiledGraph object, validating node connections and state channels.

– Invocation: Calling agent.invoke(payload) initializes the Channels with the input payload and triggers the Pregel runtime engine to run the Superstep cycle until END is reached.

Runtime and Persistence

– Compilation:
The builder.compile() method validates the graph schema and generates an execution-ready CompiledStateGraph object.

– Pregel engine and Supersteps:
The Pregel engine orchestrates execution through discrete iterations called Supersteps.
A Superstep is an execution cycle that runs all currently active nodes (in parallel if multiple nodes are triggered at once).

Each Superstep completes in three distinct phases:
1. Execution: Active nodes run independently using the current State snapshot.
2. Synchronization: Node outputs (deltas) are merged into the State via Reducers.
3. Routing: Edges evaluate the updated State to determine which nodes become active for the next Superstep.

– Execution Lifecycle and Termination:
Start: Calling agent.invoke(payload) populates the state channels with the initial payload values (for each key declared in AgentState) and triggers the START Edge, activating the initial node(s) for Superstep 1.
Continuation: Execution iterates dynamically from Superstep to Superstep as long as Edges resolve to active nodes.
End: Execution stops when an Edge resolves to END, or when reaching the safety limit (recursion_limit).

– Checkpointer persistence:
At the end of each Superstep, the Checkpointer saves a state snapshot to the database.
This enables execution pauses for human validation (Human-in-the-Loop) and state inspection/rewinding (Time-Travel).

Ce contenu a été publié dans Non classé. Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *