LangChain Multi-Agent

When a task is too complex for a single Agent to handle, you can create multiple Agents with distinct responsibilities and let them collaborate like a team.


Why Do You Need Multi-Agent

Problems with a Single Agent:

  • An overly long system_prompt can cause the model's attention to scatter
  • Too many tools increase the probability of the model choosing the wrong tool
  • Different types of tasks require different expertise and behavioral styles

The multi-agent approach: each Agent focuses on one domain, and complex tasks are completed through collaboration.


Method 1: Sub-Agent as a Tool

Compile the Agent into a CompiledStateGraph, then register it as a tool with the parent Agent:

Example

from dotenv import load_dotenv
load_dotenv()

from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

# Sub-Agent 1: Weather Expert
@tool
def get_weather(city: str) -> str:
    """Query the weather"""
    data = {"Hangzhou": "Sunny, 25°C", "Beijing": "Cloudy, 18°C"}
    return data.get(city, f"{city}: data temporarily unavailable")

weather_agent = create_agent(
    model=model,
    tools=[get_weather],
    name="weather_expert",  # The name is used for identification and logging
    system_prompt="You are a weather expert, specializing in answering weather-related questions. Keep answers concise.",
)

# Sub-Agent 2: Calculation Expert
@tool
def calculate(expression: str) -> str:
    """Calculate mathematical expressions"""
    result = eval(expression, {"__builtins__": {}}, {})
    return f"{expression} = {result}"

math_agent = create_agent(
    model=model,
    tools=[calculate],
    name="math_expert",
    system_prompt="You are a math expert, specializing in mathematical calculations. Keep answers concise.",
)

# Parent Agent: Coordinator
# Register sub-agents as tools
@tool
def ask_weather_expert(question: str) -> str:
    """Consult the weather expert about weather-related questions.

    Args:
question: Questions about the weather
    """

    result = weather_agent.invoke(
        {"messages": [HumanMessage(content=question)]}
    )
    return result["messages"][-1].content


@tool
def ask_math_expert(question: str) -> str:
    """Consult the math expert about mathematical calculation problems.

    Args:
question: Mathematical calculation problems
    """

    result = math_agent.invoke(
        {"messages": [HumanMessage(content=question)]}
    )
    return result["messages"][-1].content


coordinator = create_agent(
    model=model,
    tools=[ask_weather_expert, ask_math_expert],
    system_prompt="""You are the coordination assistant. Choose the appropriate expert based on the user's question:
- Weather-related questions → use ask_weather_expert
- Mathematical calculation problems → use ask_math_expert
- If multiple domains are involved at the same time, consult each expert in sequence"""
,
)

# Test compound question
result = coordinator.invoke({
    "messages": [HumanMessage(
        content="How's the weather in Hangzhou today? If the temperature is 25 degrees, what is it in Fahrenheit?"
        "(Formula: Fahrenheit = Celsius × 9/5 + 32)"
    )]
})
print(result["messages"][-1].content)

Execution result:

根据天气专家的查询,杭州今天晴天,气温25°C。
换算成华氏度:25 × 9/5 + 32 = 77°F。
所以杭州今天25°C,相当于77°F,天气晴好。

Method 2: Distinguish Agents with the name Parameter

When embedding a sub-agent as a tool, setting the name parameter helps track the source of messages:

Example

# The role of the name parameter:
# 1. The name is used in the compiled graph
# 2. The name is used when embedding as a subgraph node into the parent graph
# 3. All AI messages are labeled with this name

agent = create_agent(
    model=model,
    tools=[...],
    name="customer_service",  # Name the Agent
)

Method 3: Agent Routing with Middleware

More complex multi-agent scenarios can achieve dynamic routing through Middleware:

Example

from langchain.agents.middleware import before_model


# Define the tool sets used by different experts
general_tools = [tool_a, tool_b]
admin_tools = [tool_c, tool_d]


@before_model
def route_by_user_role(state, runtime):
    """Dynamically switch available tools based on user role"""
    context = runtime.context
    if context is None:
        return None

    user_role = context.get("user_role", "user")

    # Different roles see different tools
    if user_role == "admin":
        available_tools = general_tools + admin_tools
    else:
        available_tools = general_tools

    # Note: before_model cannot directly modify tools,
    # It needs to be implemented together with wrap_model_call or request.override
    return None

Multi-Agent Architecture Patterns

PatternStructureApplicable Scenario
Coordinator PatternOne parent Agent → multiple sub-agent toolsTasks that are clearly classifiable by type
Relay PatternAgent A's output → Agent B's inputPipeline-style processing (generate → review → polish)
Debate PatternMultiple Agents output in parallel → summarize and make decisionsProblems that require multi-perspective analysis

A multi-agent system increases complexity and Token consumption. Don't use multi-agent just for the sake of "multi-agent" — first try to solve the problem with a single Agent + a well-designed system_prompt and Middleware. Only introduce multi-agent when you truly need domain isolation or independent context.

Other Extensions