LangChain’s multi-agent setup allows a collection of specialized AI agents to collaborate on a complex task, each agent leveraging its unique strengths to contribute to the overall goal.

Imagine you have a task that requires both deep knowledge retrieval and creative content generation. Instead of a single, monolithic LLM trying to do both (and likely failing at one or both), you can set up multiple agents. One agent might be a "Research Agent" with access to a vector database and web search capabilities, while another is a "Writing Agent" optimized for prose generation and summarization. The "Orchestrator Agent" then acts as the conductor, directing queries to the appropriate specialized agent and synthesizing their outputs.

Here’s a simplified look at how this might play out in practice. Let’s say we want to generate a blog post comparing two new smartphones.

from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
from langchain.tools import Tool
import json

# --- Define Specialized Agents and their Tools ---

# Tool for researching smartphone specs
def research_specs(query: str) -> str:
    # In a real scenario, this would query a database or search engine
    print(f"Researching specs for: {query}")
    if "Pixel 8 Pro" in query:
        return json.dumps({
            "model": "Pixel 8 Pro",
            "display": "6.7-inch OLED",
            "processor": "Tensor G3",
            "camera": "50MP main, 12MP ultrawide, 48MP telephoto",
            "battery": "5050 mAh"
        })
    elif "iPhone 15 Pro" in query:
        return json.dumps({
            "model": "iPhone 15 Pro",
            "display": "6.1-inch OLED",
            "processor": "A17 Bionic",
            "camera": "48MP main, 12MP ultrawide, 12MP telephoto",
            "battery": "3274 mAh"
        })
    return "Could not find specs for this model."

# Tool for creative writing/summarization
def generate_content(query: str) -> str:
    print(f"Generating content for: {query}")
    # This would typically involve another LLM call, but we'll simulate it
    if "comparison" in query:
        return f"Here's a comparison based on the provided information: The {query.split(' vs ')[0]} and the {query.split(' vs ')[1]} offer distinct features..."
    return "Generated content based on the prompt."

# --- Define the Orchestrator Agent ---

# The orchestrator needs to know which tools are available to the specialized agents
# and how to route requests. This is a simplified routing mechanism.
tools = [
    Tool(
        name="SpecResearcher",
        func=research_specs,
        description="Use this tool to get detailed technical specifications for smartphone models. Input should be the model name, e.g., 'Pixel 8 Pro specs'."
    ),
    Tool(
        name="ContentGenerator",
        func=generate_content,
        description="Use this tool to generate creative text, summaries, or comparisons. Input should be a descriptive prompt."
    )
]

# The orchestrator's prompt guides its decision-making
orchestrator_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are an AI Orchestrator. You decide which specialized agent should handle the user's request. Available tools are: {tools}. Respond with the tool name and its arguments."),
    ("human", "{input}")
])

# Create an agent for the orchestrator
orchestrator_llm = ChatOpenAI(model="gpt-4o", temperature=0)
orchestrator_agent = create_tool_calling_agent(orchestrator_llm, tools, orchestrator_prompt)
orchestrator_executor = AgentExecutor(agent=orchestrator_agent, tools=tools, verbose=True)

# --- Simulate a user request ---
user_input = "Compare the Pixel 8 Pro and the iPhone 15 Pro, focusing on camera and battery life."

# The orchestrator receives the input and decides the next step
# In a real multi-agent system, the orchestrator might call itself recursively
# or delegate to other orchestrators. Here, we'll manually simulate the flow
# by showing what the orchestrator *would* do.

# Orchestrator's thought process (simulated):
# 1. User wants a comparison. This requires generating content.
# 2. To compare, I need specs. This requires the SpecResearcher.
# 3. I should first get specs for both phones.

print("--- Orchestrator Initiating Task ---")
# First, get specs for Pixel 8 Pro
specs_pixel = research_specs("Pixel 8 Pro specs")
print(f"Specs for Pixel 8 Pro: {specs_pixel}")

# Then, get specs for iPhone 15 Pro
specs_iphone = research_specs("iPhone 15 Pro specs")
print(f"Specs for iPhone 15 Pro: {specs_iphone}")

# Now, combine the information and generate the comparison
combined_info = f"Pixel 8 Pro: {specs_pixel}\niPhone 15 Pro: {specs_iphone}"
final_output = generate_content(f"comparison of Pixel 8 Pro and iPhone 15 Pro based on this data: {combined_info}")

print("\n--- Final Blog Post Snippet ---")
print(final_output)

The core idea is that the orchestrator agent acts as a central controller. It receives the initial user query and, based on its prompt and the available tools (which are themselves functions of specialized agents or services), decides which tool to call. In a more complex setup, the output of one tool might become the input for another, or the orchestrator might pass the user’s request to a different orchestrator agent with a different set of tools. This creates a hierarchical or networked structure where agents can delegate tasks and collaborate.

The power comes from breaking down a complex problem into smaller, manageable sub-problems, each handled by an agent trained or configured for that specific sub-task. For instance, an agent focused on code generation might have access to code interpreters and documentation APIs, while a data analysis agent would be connected to SQL databases or data visualization libraries. The orchestrator’s prompt is critical here; it needs to be sophisticated enough to understand the user’s intent and map it to the correct agent and tool.

The most surprising thing about orchestrating multiple agents is how easily a simple LLM can be made to "reason" about complex workflows by framing the problem as a series of tool-use decisions. The LLM isn’t actually executing code or running complex logic itself; it’s merely selecting the most appropriate tool from a list provided in its context and formatting the input for that tool. The "intelligence" is distributed across the specialized tools and the orchestrator’s ability to choose among them.

The fundamental mechanism at play is the agent’s ability to parse its own output. When an agent is configured with create_tool_calling_agent and AgentExecutor, it’s designed to generate JSON outputs that describe which tool to call and with what arguments. The AgentExecutor then parses this JSON, executes the specified tool, and feeds the tool’s return value back into the agent for the next step of reasoning. This loop continues until the agent determines it has completed the task.

A common pitfall is over-reliance on a single LLM for all decision-making. While the orchestrator LLM makes the routing decisions, the actual "work" is done by the specialized agents. If your specialized agents are poorly designed or lack the necessary tools, the entire system will falter, regardless of how brilliant the orchestrator’s routing is. For example, if the research_specs function above returned incomplete or incorrect data, the generate_content agent would produce a flawed comparison.

The next challenge you’ll likely encounter is managing the state and context across multiple agent calls, especially when tasks become more iterative or require shared memory.

Want structured learning?

Take the full Langchain course →