> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-5e9cc07a-preview-srmult-1765395526-473a2ea.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-agent

**Multi-agent systems** break a complex application into multiple specialized agents that work together to solve problems.
Instead of relying on a single agent to handle every step, **multi-agent architectures** allow you to compose smaller, focused agents into a coordinated workflow.

Multi-agent systems are useful when:

* A single agent has too many [tools](/oss/python/langchain/tools) and makes poor decisions about which to use.
* [Context](/oss/python/concepts/context) or [memory](/oss/python/langchain/short-term-memory) grows too large for one agent to track effectively.
* Tasks require **specialization** (e.g., a planner, researcher, math expert).
* You need to **enforce sequential constraints** — unlocking capabilities only after certain conditions are met.
* Different teams need to **develop and maintain agents independently**.

## Architectures

There are four main architectures for building multi-agent systems, each suited to different use cases:

| Architecture                        | How it works                                                     | Best for                                        |
| ----------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------- |
| [**Supervisor**](#supervisor)       | A central agent calls sub-agents as tools                        | Task orchestration, parallel execution          |
| [**Dynamic agent**](#dynamic-agent) | One agent with dynamically changing prompts/tools based on state | Multi-stage conversations, sequential unlocking |
| [**Workflow**](#workflow)           | Agents connected via deterministic transitions                   | Pipelines, structured processing                |
| [**Skills**](#skills)               | Specialized prompts invoked on-demand                            | Progressive disclosure, team distribution       |

<Tip>
  You can mix architectures! For example, a **supervisor** can manage **workflow** sub-graphs, or a **dynamic agent** can invoke **skills** at certain stages.
</Tip>

### Choosing an architecture

The right architecture depends on how your agents need to interact with users and each other. Start with these questions to narrow down your choice:

<AccordionGroup>
  <Accordion title="Do sub-agents need to converse directly with users?">
    Use **Dynamic agent** if you need to enforce sequential constraints (e.g., collect a warranty ID before processing a refund).

    Use **Skills** if sub-agents can be invoked freely without strict ordering.
  </Accordion>

  <Accordion title="Do you need deterministic, predictable flow between agents?">
    Use **Workflow**. This gives you explicit control over transitions and makes execution easy to debug.
  </Accordion>

  <Accordion title="Do sub-agents need to run in parallel?">
    Use **Supervisor**. The supervisor can invoke multiple sub-agents simultaneously and coordinate their results.
  </Accordion>

  <Accordion title="Do you need to persist state across sub-agent interactions?">
    Use **Dynamic agent**. State survives across conversation turns, enabling multi-step flows.

    Alternatively, use **Supervisor** with stateful tools if you need centralized orchestration.
  </Accordion>

  <Accordion title="Will different teams develop and maintain agents independently?">
    Use **Skills**. Each skill can be developed, tested, and deployed separately.
  </Accordion>

  <Accordion title="Do you need centralized orchestration without direct user interaction?">
    Use **Supervisor**. Sub-agents return results to the supervisor, which handles all user communication.
  </Accordion>
</AccordionGroup>

### Supervisor

In the **supervisor** architecture, a central supervisor agent coordinates sub-agents by calling them as tools. The supervisor decides which sub-agent to invoke, what input to provide, and how to combine results.

```mermaid theme={null}
graph LR
    A[User] --> B[Supervisor]
    B --> C[Sub-agent A]
    B --> D[Sub-agent B]
    B --> E[Sub-agent C]
    C --> B
    D --> B
    E --> B
    B --> F[User response]
```

**Key characteristics:**

* **Centralized control**: All routing passes through the supervisor
* **Sub-agents as tools**: Each sub-agent is wrapped as a callable tool
* **No direct user interaction**: Sub-agents return results to the supervisor, not the user
* **Parallel execution**: The supervisor can invoke multiple sub-agents in a single turn

Use the supervisor pattern when you have **multiple distinct domains** (e.g., calendar, email, CRM, database), sub-agents **don't need to converse directly** with users, or you want **centralized workflow control**. For simpler cases with just a few tools, use a single agent.

<Card title="Tutorial: Build a supervisor agent" icon="sitemap" href="/oss/python/langchain/supervisor" arrow cta="Learn more">
  Learn how to build a personal assistant using the supervisor pattern, where a central supervisor agent coordinates specialized worker agents.
</Card>

#### Example

The example below shows how a main agent is given access to a single sub-agent via a tool definition:

```python theme={null}
from langchain.tools import tool
from langchain.agents import create_agent


subagent1 = create_agent(model="...", tools=[...])

@tool(
    "subagent1_name",
    description="subagent1_description"
)
def call_subagent1(query: str):
    result = subagent1.invoke({
        "messages": [{"role": "user", "content": query}]
    })
    return result["messages"][-1].content

agent = create_agent(model="...", tools=[call_subagent1])
```

In this pattern:

1. The main agent invokes `call_subagent1` when it decides the task matches the sub-agent's description.
2. The sub-agent runs independently and returns its result.
3. The main agent receives the result and continues orchestration.

#### Where to customize

There are several points where you can control how context is passed between the main agent and its subagents:

1. **Sub-agent name** (`"subagent1_name"`): This is how the main agent refers to the sub-agent. Since it influences prompting, choose it carefully.
2. **Sub-agent description** (`"subagent1_description"`): This is what the main agent knows about the sub-agent. It directly shapes how the main agent decides when to call it.
3. **[Input to the sub-agent](#control-the-input-to-the-sub-agent)**: You can customize this input to better shape how the sub-agent interprets tasks. In the example above, we pass the agent-generated `query` directly.
4. **[Output from the sub-agent](#control-the-output-from-the-sub-agent)**: This is the response passed back to the main agent. You can adjust what is returned to control how the main agent interprets results. In the example above, we return the final message text, but you could return additional state or metadata.

##### Control the input to the sub-agent

There are two main levers to control the input that the main agent passes to a sub-agent:

* **Modify the prompt**: Adjust the main agent's prompt or the tool metadata (i.e., sub-agent's name and description) to better guide when and how it calls the sub-agent.
* **Context injection**: Add input that isn't practical to capture in a static prompt (e.g., full message history, prior results, task metadata) by adjusting the tool call to pull from the agent's state.

```python theme={null}
from langchain.agents import AgentState
from langchain.tools import tool, ToolRuntime

class CustomState(AgentState):
    example_state_key: str

@tool(
    "subagent1_name",
    description="subagent1_description"
)
def call_subagent1(query: str, runtime: ToolRuntime[None, CustomState]):
    # Apply any logic needed to transform the messages into a suitable input
    subagent_input = some_logic(query, runtime.state["messages"])
    result = subagent1.invoke({
        "messages": subagent_input,
        # You could also pass other state keys here as needed.
        # Make sure to define these in both the main and subagent's
        # state schemas.
        "example_state_key": runtime.state["example_state_key"]
    })
    return result["messages"][-1].content
```

##### Control the output from the sub-agent

Two common strategies for shaping what the main agent receives back from a sub-agent:

* **Modify the prompt**: Refine the sub-agent's prompt to specify exactly what should be returned.
  * Useful when outputs are incomplete, too verbose, or missing key details.
  * A common failure mode is that the sub-agent performs tool calls or reasoning but does **not include the results** in its final message. Remind it that the controller (and user) only see the final output, so all relevant info must be included there.
* **Custom output formatting**: Adjust or enrich the sub-agent's response in code before handing it back to the main agent.
  * Example: pass specific state keys back to the main agent in addition to the final text.
  * This requires wrapping the result in a [`Command`](https://reference.langchain.com/python/langgraph/types/#langgraph.types.Command) (or equivalent structure) so you can merge custom state with the sub-agent's response.

```python theme={null}
from typing import Annotated
from langchain.agents import AgentState
from langchain.tools import InjectedToolCallId
from langgraph.types import Command


@tool(
    "subagent1_name",
    description="subagent1_description"
)
# We need to pass the `tool_call_id` to the sub agent so it can use it to respond with the tool call result
def call_subagent1(
    query: str,
    tool_call_id: Annotated[str, InjectedToolCallId],
# You need to return a `Command` object to include more than just a final tool call
) -> Command:
    result = subagent1.invoke({
        "messages": [{"role": "user", "content": query}]
    })
    return Command(update={
        # This is the example state key we are passing back
        "example_state_key": result["example_state_key"],
        "messages": [
            ToolMessage(
                content=result["messages"][-1].content,
                # We need to include the tool call id so it matches up with the right tool call
                tool_call_id=tool_call_id
            )
        ]
    })
```

### Dynamic agent

In the **dynamic agent** architecture, you have a single agent whose behavior (prompt, tools, and capabilities) changes dynamically based on state. Under the hood, this is a state machine implemented via [middleware](/oss/python/langchain/middleware/overview) that performs handoffs through tool calls.

```mermaid theme={null}
graph LR
    A[User] --> B[Dynamic Agent]
    B --> |state: triage| C[Triage config]
    B --> |state: specialist| D[Specialist config]
    C --> B
    D --> B
    B --> A
```

**Key characteristics:**

* **Single agent, multiple configurations**: The agent identity changes based on state
* **State-driven behavior**: Prompts and tools are selected based on a state variable (e.g., `active_agent`)
* **Direct user interaction**: The active agent configuration handles user messages directly
* **Persistent state**: State survives across conversation turns

Use the dynamic agent pattern when you need to **enforce sequential constraints** (unlock capabilities only after preconditions are met), sub-agents need to **converse directly with the user**, or you're building **multi-stage conversational flows**. This pattern is particularly valuable for customer support scenarios where you need to collect information in a specific sequence — for example, collecting a warranty ID before processing a refund.

<Card title="Tutorial: Build a customer support agent with handoffs" icon="people-arrows" href="/oss/python/langchain/customer-support-handoffs" arrow cta="Learn more">
  Learn how to build a customer support workflow using the dynamic agent pattern, where agents pass control through state transitions.
</Card>

<Tip>
  Design dynamic agent flows carefully to avoid creating rigid, frustrating experiences. Users should be able to correct mistakes, change the conversation flow, and navigate back to previous steps. Overly restrictive flows can feel like poorly designed phone trees.
</Tip>

At the core, the dynamic agent pattern relies on [persistent state](/oss/python/langchain/short-term-memory) that survives across conversation turns:

1. **State variable**: A field in your state schema (e.g., `active_agent: str`) tracks which agent configuration is currently active.
2. **State update tool**: The agent uses a tool to change the value of `active_agent` when transitioning to a new stage.
3. **Dynamic configuration**: On each turn, the graph entry point reads `active_agent` from the persisted state and dynamically configures the appropriate system prompt, tools, and behavior.

This pattern creates a state machine where each configuration represents a distinct state with its own behavior and capabilities.

#### Example

Here's an example showing how agents transition between configurations:

```python theme={null}
from langchain.agents import AgentState, create_agent
from langchain.tools import tool, ToolRuntime
from langgraph.types import Command

class SupportState(AgentState):
    """Track which agent is currently active."""
    current_agent: str = "triage"

@tool
def transfer_to_specialist(
    runtime: ToolRuntime[None, SupportState]
) -> Command:
    """Transfer conversation to a specialist agent."""
    return Command(update={"current_agent": "specialist"})

# Each agent configuration has different prompts/tools
agent = create_agent(
    model,
    tools=[transfer_to_specialist, ...],
    state_schema=SupportState
)
```

The key mechanism is using [`Command`](https://reference.langchain.com/python/langgraph/types/#langgraph.types.Command) to update state fields that control which agent configuration is active. Middleware can then read this state and dynamically configure the agent's behavior.

### Workflow

In the **workflow** architecture, agents are connected via deterministic transitions defined at graph construction time. The flow between agents is explicit and predictable.

```mermaid theme={null}
graph LR
    A[User] --> B[Agent A]
    B --> C[Agent B]
    C --> D[Agent C]
    D --> E[Result]
```

**Key characteristics:**

* **Deterministic flow**: Transitions are defined explicitly via graph edges
* **Predictable execution**: You always know which agent runs next
* **No LLM routing decisions**: The graph structure controls flow, not LLM calls
* **Pipeline-style processing**: Each agent processes and passes to the next

Use the workflow pattern when you have a **fixed sequence** of processing steps, agents are **independent** and don't need to be aware of each other, or you want **predictable, debuggable** execution. Common examples include content pipelines (research → draft → edit → publish) and data processing (extract → validate → transform → load).

#### Example

This example shows a content pipeline where each node is a full agent with its own tools:

```python theme={null}
from langgraph.graph import StateGraph, MessagesState, START
from langchain.agents import create_agent

# Create specialized agents for each stage
research_agent = create_agent(
    model="openai:gpt-4o",
    tools=[web_search, fetch_url],
    prompt="You are a research assistant. Search for and gather relevant information on the topic.",
    name="researcher"
)

writing_agent = create_agent(
    model="openai:gpt-4o",
    tools=[create_outline, write_section],
    prompt="You are a writer. Create content based on the research provided.",
    name="writer"
)

editing_agent = create_agent(
    model="openai:gpt-4o",
    tools=[check_grammar, check_style],
    prompt="You are an editor. Refine and improve the content for clarity and correctness.",
    name="editor"
)

# Define the workflow with explicit edges
workflow = (
    StateGraph(MessagesState)
    .add_node("research", research_agent)
    .add_node("write", writing_agent)
    .add_node("edit", editing_agent)
    .add_edge(START, "research")
    .add_edge("research", "write")
    .add_edge("write", "edit")
    .compile()
)

result = workflow.invoke({
    "messages": [{"role": "user", "content": "Write an article about AI agents"}]
})
```

### Skills

In the **skills** architecture, specialized capabilities are packaged as invokable "skills" that augment an agent's behavior. Skills are primarily prompt-driven specializations that an agent can invoke on-demand.

```mermaid theme={null}
graph LR
    A[User] --> B[Main Agent]
    B --> C[Python skill]
    B --> D[SQL skill]
    B --> E[Docs skill]
    C --> B
    D --> B
    E --> B
    B --> A
```

**Key characteristics:**

* **Prompt-driven specialization**: Skills are primarily defined by specialized prompts
* **Progressive disclosure**: Skills become available based on context or user needs
* **Team distribution**: Different teams can develop and maintain skills independently
* **Lightweight composition**: Skills are simpler than full sub-agents

Use the skills pattern when you want a **single agent with many possible specializations**, you **don't need to enforce specific constraints** between skills, or different **teams need to develop capabilities independently**. Common examples include coding assistants (skills for different languages or tasks), knowledge bases (skills for different domains), and creative assistants (skills for different formats).

<Note>
  Skills documentation is coming soon. This pattern is being developed as part of the [deep-agents](https://github.com/langchain-ai/deep-agents) project.
</Note>

## Customize agent context

At the center of multi-agent design is **[context engineering](/oss/python/langchain/context-engineering)** - deciding what information each agent sees. LangChain gives you fine-grained control over:

* Which parts of the conversation or state are passed to each agent.
* Specialized prompts tailored to sub-agents.
* Inclusion/exclusion of intermediate reasoning.
* Customizing input/output formats per agent.

The quality of your system depends on context engineering. The goal is to ensure that each agent has access to the correct data it needs to perform its task, whether it's acting as a tool or as an active agent.

***

<Callout icon="pen-to-square" iconType="regular">
  [Edit the source of this page on GitHub.](https://github.com/langchain-ai/docs/edit/main/src/oss/langchain/multi-agent.mdx)
</Callout>

<Tip icon="terminal" iconType="regular">
  [Connect these docs programmatically](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
</Tip>
