Skip to main content
In the router architecture, a routing step classifies input and directs it to specialized agents. This is useful when you have distinct verticals—separate knowledge domains that each require their own agent.

Key characteristics

  • Router decomposes the query
  • Zero or more specialized agents are invoked in parallel
  • Results are synthesized into a coherent response

When to use

Use the router pattern when you have distinct verticals (separate knowledge domains that each require their own agent), need to query multiple sources in parallel, and want to synthesize results into a combined response.

Basic implementation

The router classifies the query and directs it to the appropriate agent(s). Use Command for single-agent routing or Send for parallel fan-out to multiple agents.
Use Command to route to a single specialized agent:
from langgraph.types import Command

def classify_query(query: str) -> str:
    """Use LLM to classify query and determine the appropriate agent."""
    # Classification logic here
    ...

def route_query(state: State) -> Command:
    """Route to the appropriate agent based on query classification."""
    active_agent = classify_query(state["query"])

    # Route to the selected agent
    return Command(goto=active_agent)
For a complete implementation, see the tutorial below.

Tutorial: Build a multi-source knowledge base with routing

Build a router that queries GitHub, Notion, and Slack in parallel, then synthesizes results into a coherent answer. Covers state definition, specialized agents, parallel execution with Send, and result synthesis.

Stateless vs. stateful

Two approaches:

Stateless

Each request is routed independently—no memory between calls. For multi-turn conversations, see Stateful routers.
Stateless router vs. Subagents: The subagents pattern can also route to multiple agents. Use the stateless router when you need specialized preprocessing or custom routing logic. Use the subagents pattern when you want the LLM to decide which agents to call dynamically.

Stateful

For multi-turn conversations, you need to maintain context across invocations.

Tool wrapper

The simplest approach: wrap the stateless router as a tool that a conversational agent can call. The conversational agent handles memory and context; the router stays stateless. This avoids the complexity of managing conversation history across multiple parallel agents.
@tool
def search_docs(query: str) -> str:
    """Search across multiple documentation sources."""
    result = workflow.invoke({"query": query})  
    return result["final_answer"]

# Conversational agent uses the router as a tool
conversational_agent = create_agent(
    model,
    tools=[search_docs],
    prompt="You are a helpful assistant. Use search_docs to answer questions."
)

Full persistence

If you need the router itself to maintain state, use persistence to store message history. When routing to an agent, fetch previous messages from state and selectively include them in the agent’s context—this is a lever for context engineering.
Stateful routers require custom history management. If the router switches between agents across turns, conversations may not feel fluid to end users when agents have different tones or prompts. With parallel invocation, you’ll need to maintain history at the router level (inputs and synthesized outputs) and leverage this history in routing logic. Consider the handoffs pattern or subagents pattern instead—both provide clearer semantics for multi-turn conversations.

Connect these docs to Claude, VSCode, and more via MCP for real-time answers.