Files
magnus919_agent-skills/langgraph/references/architecture.md
T
Magnus Hedemark 4a73657522 feat: add langgraph expert skill — multi-agent patterns, scaffolds, evals, and production guidance
Comprehensive LangGraph skill covering:
- Core architecture: Graph API, Functional API, state management, agent loops
- Three multi-agent patterns: supervisor (~94% accuracy), swarm (~40% fewer LLM calls),
  hierarchical teams (subgraphs with nested state)
- Persistence: checkpointers vs stores, per-invocation/per-thread/stateless modes
- Production: Agent Server deployment, LangSmith observability, 8 failure modes
- Evals: routing accuracy, resolution coverage, LLM-as-judge methodology
- Troubleshooting: symptom→cause→fix tables per pattern
- 3 Python scripts: supervisor scaffold, swarm scaffold, eval generator
- 3 runnable templates: supervisor, swarm, subgraph composition

Ships 8 reference files, 3 scripts, and 3 templates.
2026-07-08 14:54:53 -04:00

6.7 KiB

Architecture — LangGraph Core Concepts

This reference covers LangGraph's foundational architecture. Read this first if you're new to LangGraph or building a system from scratch.

Overview

LangGraph is a low-level orchestration framework that models agent workflows as directed graphs. It is inspired by Google's Pregel and Apache Beam, with a public interface drawing from NetworkX. Unlike linear chain frameworks (LangChain Expression Language, traditional pipelines), LangGraph supports cycles — essential for agent tool-calling loops, iterative refinement, and multi-agent coordination.

User → [Agent Node] → {tool call?} → [Tool Node] → [Agent Node] → ... → Response
                    ↘ (no tool) → Response

Core Abstractions

State

The graph's shared, persistent data. All nodes read from and write to the same state object. Defined as a TypedDict or Pydantic schema.

Key pattern — reducer annotations: Fields that multiple nodes write to in parallel need a reducer. The operator.add reducer is the most common — it concatenates lists:

from typing import Annotated, TypedDict
import operator

class AgentState(TypedDict):
    messages: Annotated[list, operator.add]  # parallel-safe append
    next_agent: str                          # single writer only
    resolution_notes: Annotated[list[str], operator.add]

Rule of thumb: If multiple nodes can write to the same key, it needs a reducer. If only one node ever writes (e.g., a shared topic field), no reducer needed.

Nodes

Python functions (or runnable objects) that receive state and return state updates:

def my_node(state: AgentState) -> dict:
    # Read from state, do work, return updates
    return {"messages": [AIMessage(content="hello")]}

Nodes can be:

  • LLM calls — invoke a model, return output
  • Tool nodes — execute tool calls from an LLM
  • Python functions — deterministic logic, transformations, validation
  • Subgraphs — nested LangGraph graphs (see multi-agent-hierarchical reference)
  • Agents — LangChain agents wrapped as a single node

Edges

Edges define how execution flows between nodes:

  • add_edge(start, end) — unconditionally traverse from start to end
  • add_conditional_edges(source, router, path_map) — dynamic routing based on state
# Standard edge
builder.add_edge("node_a", "node_b")

# Conditional edge
builder.add_conditional_edges(
    "analyzer",
    route_based_on_state,       # function that returns a node name
    {"billing": "billing_node", "tech": "tech_node", END: END}
)

Two APIs

Graph API (StateGraph)

Full control. You explicitly add nodes and wire edges with method calls.

from langgraph.graph import StateGraph, START, END, MessagesState

builder = StateGraph(MessagesState)
builder.add_node("agent", agent_node)
builder.add_node("tools", ToolNode([search, calculator]))
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
builder.add_edge("tools", "agent")
graph = builder.compile()

Functional API (@task + @entrypoint)

Simpler decorator-based approach for linear or tree-shaped workflows:

from langgraph.func import entrypoint, task

@task
def research(topic: str) -> str:
    return llm.invoke(f"Research {topic}").content

@task
def write_report(research: str) -> str:
    return llm.invoke(f"Write report: {research}").content

@entrypoint()
def workflow(topic: str):
    r = research(topic).result()
    return write_report(r).result()

When to use each:

  • Graph API — complex routing, cycles, multi-agent, subgraphs, conditional flows
  • Functional API — sequential chains, parallel fan-out patterns, simpler orchestration

The Agent Loop (Tool-Calling)

The most common LangGraph pattern is the agent tool-calling loop:

1. LLM node receives messages
2. LLM returns response (may contain tool_calls)
3. Conditional edge: if tool_calls → ToolNode; if no tool_calls → END
4. ToolNode executes tools, returns ToolMessages
5. Edge back to LLM node
6. Repeat from 1
from langgraph.prebuilt import ToolNode, tools_condition

builder = StateGraph(MessagesState)
builder.add_node("llm", llm_call)
builder.add_node("tools", ToolNode([search, calculator]))
builder.add_conditional_edges("llm", tools_condition, {"tools": "tools", END: END})
builder.add_edge("tools", "llm")

tools_condition is a prebuilt router: returns "tools" if the last message has tool_calls, otherwise returns END.

Streaming

LangGraph supports multiple streaming modes:

# Stream all events (most detailed)
for event in graph.stream_events(inputs, version="v3"):
    if event["method"] == "updates":
        print(event["params"]["data"])

# Stream values only (final state per superstep)
for chunk in graph.stream(inputs):
    print(chunk)

# Stream individual tokens from LLM nodes
for chunk in graph.stream(inputs, stream_mode="messages"):
    print(chunk)

Key Architecture Patterns from the Official Guide

Pattern Structure Best For
Prompt Chaining Sequential LLM calls Translation, verification, step-by-step generation
Parallelization Fan-out → aggregate Multi-criteria scoring, parallel research tasks
Routing Classify → dispatch Customer support triage, content-type routing
Orchestrator-Worker Plan → fan-out → synthesize Report writing, code generation across files
Evaluator-Optimizer Generate → evaluate → loop Iterative refinement, quality gates
Agent LLM ↔ tool calls General autonomous agents

Design Principles

  1. State is the source of truth — all inter-node communication happens through state, not through side channels or global variables.
  2. Nodes are pure-ish — a node receives state, does work, returns updates. It should not depend on state that isn't passed to it.
  3. Reducers prevent conflicts — any state key written by multiple nodes in parallel MUST have a reducer.
  4. Start simple, add complexity only when needed — a single agent with good prompts beats a multi-agent system with bad routing. Add agents only when a single prompt or toolset becomes unwieldy.
  5. Use Send() for dynamic fan-out — when you don't know how many workers you'll need at compile time, use Send() to spawn workers dynamically from the orchestrator node.
  6. Subgraph state isolation — subgraphs with different state schemas need a wrapper function to transform state at the boundary. Shared-schema subgraphs can be added directly as nodes.