Library · Autonomous and multi-agent systems

Multi-agent orchestration: LangGraph, CrewAI and Mastra vs. Claude Code

Engineer70 minUpdated: October 2026
74 of 105 in the library

Module: 18. Advanced Orchestration | Time: ~30 min theory + 40 min practice


The gist

One agent is one musician. It can play a melody. But not a symphony.

Multi-agent orchestration is when you have a conductor and an orchestra. The conductor (the orchestrator) doesn't play: they direct the violins, the cellos, the percussion (specialized agents). Each one plays their part. Together they make music no one could play alone.

LangGraph, CrewAI, Mastra and native Claude Code are different ideas about how to build that orchestra. Each has its own philosophy, its own strengths, its own learning curve.

In this lesson we'll take an honest look at them and figure out the main thing: when Claude Code's built-in tools are enough, and when you need to bring in a separate framework.

🎨 Picture this: choosing between an orchestra and a jazz quartet. The orchestra is more powerful, but it needs a hall, a schedule, 80 musicians. The quartet is less formal, but in one evening it can improvise what an orchestra would rehearse for a month. Different jobs call for different ensembles.


🎯 Decision tree: subagents vs. a framework

The main question of this lesson isn't "which framework is best," it's "are Claude Code's built-in subagents enough for you, or have you hit a wall and need an outside tool."

Use native Claude Code subagents if:

  • ✓ You don't need Python/TS as a runtime (Markdown + the Agent tool is enough)
  • ✓ The workflow is linear or has simple branches
  • ✓ Your team already uses Claude Code
  • ✓ You don't need a state machine with 5+ states
  • ✓ Fewer than 5 agents on the team

Bring in an external framework (LangGraph / CrewAI / Mastra) if:

  • ✓ You need loops that return state ("try → fix → try again")
  • ✓ 5+ states and conditional edges
  • ✓ Human-in-the-loop is a required part of the flow, not an option
  • ✓ Your team already knows Python (LangGraph / CrewAI) or TS (Mastra)
  • ✓ 10+ agents with complex orchestration

Default to Claude Code subagents. A framework = when you've clearly hit a wall.

🎨 Picture this: a bike vs. a car. For a store a mile away, the bike is faster (no parking, no starting the engine, no filling up). But to drive to another city, you need a car. Don't buy a car if all you do is ride to the store.


Key concepts

  • Orchestrator: the main agent that breaks the task down and hands out subtasks to specialized agents
  • Subagent / Worker: an agent with a narrow specialty: only search, only code generation, only QA, only translation
  • Graph-based orchestration (LangGraph): an agent graph where nodes = agents and edges = transitions, including loops and conditional branching
  • Role-based crews (CrewAI): teams of agents with human-readable roles (researcher, writer, editor), like a real department
  • State machine: a finite-state machine that tracks which state the agent is in and where to go next
  • Human-in-the-loop: a built-in pause point where the system waits for a human to confirm before a critical action
  • Context isolation: each subagent works in its own context without cluttering the shared one. The orchestrator only gets the result
  • Parallel execution: several agents work at the same time on different parts of the task

Theory

Why have several agents at all

In theory, one agent with a long context can do everything. In practice, it degrades.

The problem is called "context rot": when the context grows very large, the LLM's reasoning quality gets worse. The agent starts "forgetting" instructions from the beginning, repeating itself, losing the thread.

A multi-agent architecture solves this by isolating contexts:

  • The orchestrator holds only the high-level plan and the status of the tasks
  • Each subagent works in a clean context with its own specific task
  • The orchestrator gets a condensed result, not the subagent's entire conversation

The second advantage is parallelism. One agent does tasks one after another. Five agents can work in parallel. Research that takes one agent a long time can be done noticeably faster by five.

🎨 Picture this: a construction crew vs. one jack-of-all-trades. The one handyman will build the house, but slowly, and they can't work on the foundation and the roof at the same time. With a crew, someone digs, someone lays bricks, someone does the roof, and the house is done noticeably faster.


LangGraph: a graph machine for complex states

LangGraph from LangChain is a framework for building agents as directed graphs (both DAGs and graphs with cycles).

The key concept: the agent flow is a graph. Nodes are agents or functions. Edges are the transitions between them. Edges can be conditional: "if the result contains an error → go to error_handler, otherwise → go to finalizer."

What sets LangGraph apart from simple chains is its support for loops. An agent can go back to a previous step. That's critical for patterns like "try → if it didn't work → fix it → try again."

State is the central object that gets passed between nodes and modified. Each agent reads the fields of the state it needs and writes its own results.

python
# A minimal LangGraph agent example (Python)
from typing import TypedDict
from langgraph.graph import StateGraph, END

# Define the state
class ResearchState(TypedDict):
    query: str
    sources: list[str]
    draft: str
    final: str
    iteration: int

# Nodes are functions or agents
def researcher(state: ResearchState) -> ResearchState:
    # here the agent searches for information
    return {"sources": ["source1", "source2"], "iteration": state["iteration"] + 1}

def writer(state: ResearchState) -> ResearchState:
    # here the agent writes a draft
    return {"draft": "Draft based on sources..."}

def should_revise(state: ResearchState) -> str:
    # Conditional edge: go to revision or finish
    if state["iteration"] < 3 and len(state["draft"]) < 500:
        return "revise"
    return "finalize"

# Build the graph
graph = StateGraph(ResearchState)
graph.add_node("researcher", researcher)
graph.add_node("writer", writer)

graph.set_entry_point("researcher")
graph.add_edge("researcher", "writer")
graph.add_conditional_edges("writer", should_revise, {
    "revise": "researcher",  # loop back!
    "finalize": END
})

app = graph.compile()
result = app.invoke({"query": "AI trends", "iteration": 0})

Human-in-the-loop in LangGraph is a built-in way to pause the graph (with interrupt_before on a node, or by calling interrupt() inside it), wait for your input and continue. This needs a checkpointer that saves the state. Details: LangGraph documentation.

When to choose LangGraph:

  • You need loops that go back (try → fix → try again)
  • Complex conditional branching (different branches for different types of input)
  • A state machine with 5+ states
  • Human-in-the-loop as a required part of the flow
  • A Python project, and the team knows LangChain

When you don't need it: for a linear pipeline with no branching, LangGraph is overengineering. A simple chain of agents without loops works with ordinary calls.


CrewAI: teams with roles, like a real department

CrewAI thinks about multi-agent systems through the metaphor of a team (a crew). Not a graph, not a graph with states, but people with roles who carry out tasks.

Three main concepts:

  1. Agent: role + backstory + LLM + tools
  2. Task: a specific task with a description, expected output and assigned agent
  3. Crew: a team of agents + a list of tasks + a process (sequential or hierarchical)

CrewAI is especially good for parallel research teams: when you need several independent experts working on one topic from different angles.

python
# Example: a CrewAI research team
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool, WebsiteSearchTool

search_tool = SerperDevTool()
web_tool = WebsiteSearchTool()

# Agent 1: market researcher
market_researcher = Agent(
    role="Market Research Analyst",
    goal="Find and analyze the latest data on the market for AI developer tools",
    backstory="""You are an experienced market analyst with 10 years in the field.
    You specialize in technology markets.
    You always back up your conclusions with concrete data and numbers.""",
    verbose=True,
    allow_delegation=False,
    tools=[search_tool, web_tool],
    llm="anthropic/claude-sonnet-5-5"  # CrewAI supports Claude; the model is set as "provider/model"; current models: the What's current page
)

# Agent 2: technical expert
tech_expert = Agent(
    role="Technical Expert",
    goal="Evaluate the technical capabilities and limitations of AI tools",
    backstory="""You are a senior engineer with deep AI/ML expertise.
    You can explain complex technical concepts in plain language.
    You're skeptical of marketing claims and look at real benchmarks.""",
    verbose=True,
    allow_delegation=False,
    tools=[web_tool],
    llm="anthropic/claude-sonnet-5-5"
)

# Agent 3: financial analyst
financial_analyst = Agent(
    role="Financial Analyst",
    goal="Evaluate the ROI and business potential of AI tools",
    backstory="""You are a financial analyst specializing in tech startups.
    You can build unit economics and assess market potential.
    You always look at the pricing model and margins.""",
    verbose=True,
    allow_delegation=False,
    tools=[search_tool],
    llm="anthropic/claude-haiku-4-5"  # A simpler task, so we use a cheaper model
)

# Agent 4: writer-synthesizer
report_writer = Agent(
    role="Strategic Report Writer",
    goal="Synthesize the research into a clear, actionable report",
    backstory="""You are an experienced technical writer and strategy consultant.
    You can turn complex analytical data into clear recommendations.
    You write for tech founders and CTOs.""",
    verbose=True,
    allow_delegation=False,
    tools=[],
    llm="anthropic/claude-sonnet-5-5"
)

# Tasks
market_task = Task(
    description="""Research the market for AI developer tools in 2026.
    Find: the top 5 players, market size, growth rate, key trends.
    Use current data, no older than 6 months.""",
    expected_output="A structured market analysis: size, players, trends, numbers",
    agent=market_researcher,
    async_execution=True  # In parallel with the other tasks!
)

tech_task = Task(
    description="""Do a technical analysis of the top 3 AI coding assistants (Claude Code, Cursor, GitHub Copilot).
    Compare: supported languages, context window, integrations, latency, accuracy.""",
    expected_output="A comparison table with technical specs and benchmarks",
    agent=tech_expert,
    async_execution=True  # In parallel!
)

financial_task = Task(
    description="""Analyze pricing and ROI for AI coding assistants.
    Calculate: cost per developer/month, time saved, payback period.
    Compare pricing models: subscription vs. usage-based.""",
    expected_output="A financial model with an ROI calculation and a pricing comparison",
    agent=financial_analyst,
    async_execution=True  # In parallel!
)

synthesis_task = Task(
    description="""Synthesize all three studies into a final strategy report.
    Structure: Executive Summary (1 page), Market Overview, Tech Comparison, Financial Analysis, Recommendations.
    Audience: a tech founder deciding whether to adopt AI tools.""",
    expected_output="A complete 5-7 page strategy report, ready to present",
    agent=report_writer,
    context=[market_task, tech_task, financial_task]  # Depends on the previous ones
)

# Assemble the team
crew = Crew(
    agents=[market_researcher, tech_expert, financial_analyst, report_writer],
    tasks=[market_task, tech_task, financial_task, synthesis_task],
    process=Process.sequential,  # or Process.hierarchical with a manager agent
    verbose=True,
    memory=True,  # Agents remember previous interactions
    max_rpm=10    # Rate limiting for API calls
)

# Run it
result = crew.kickoff()
print(result.raw)

Installation:

bash
pip install crewai crewai-tools

When to choose CrewAI:

  • You need several independent experts working in parallel
  • The task breaks down well by role ("researcher + tech person + finance person → synthesizer")
  • You want human-readable configuration (roles, backstories, goals)
  • You already have a Python team and are used to describing agents declaratively

When you don't need it: for simple tasks, CrewAI is overkill. If the task is linear with no parallelism, a single Claude API call is cheaper and faster.


Mastra: a new-generation TypeScript framework

Mastra is a relatively young open-source framework (it appeared in 2024), built by a team that includes people from Gatsby. TypeScript-first, works smoothly with Vercel and Next.js, with a developer experience like Next.js.

Mastra's main principles:

  • TypeScript as a first-class citizen: everything is typed, and autocomplete works out of the box
  • Workflows + Agents: workflows for deterministic flows, agents for dynamic ones
  • Memory: built-in agent memory with pluggable storage
  • Models by string: the model is set as "provider/model", with no separate provider imports
  • Modern DX: a local playground for testing agents and hot reload in dev

Mastra's API has changed: older blog examples with new Workflow, new Step and triggerSchema no longer match the current version (it's now createWorkflow and createStep). Check the documentation.

typescript
// Example: a Mastra agent with a workflow (TypeScript)
import { Mastra } from "@mastra/core";
import { Agent } from "@mastra/core/agent";
import { createTool } from "@mastra/core/tools";
import { createStep, createWorkflow } from "@mastra/core/workflows";
import { z } from "zod";
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic();

// Define the tools
const searchTool = createTool({
  id: "web-search",
  description: "Searches the internet for current information",
  inputSchema: z.object({
    query: z.string().describe("Search query"),
  }),
  outputSchema: z.object({ results: z.array(z.string()) }),
  execute: async ({ query }) => {
    // A real search via Serper, Tavily, etc. goes here
    return { results: [`Results for: ${query}`] };
  },
});

// Create a research agent. The model is set as a "provider/model" string
const researchAgent = new Agent({
  id: "research-agent",
  name: "Research Agent",
  instructions: `You are a professional researcher.
  You find current information on the request, structure it,
  and highlight the key facts with sources.`,
  model: "anthropic/claude-sonnet-5-5",   // current models: the What's current page
  tools: { searchTool },
});

// Workflow steps: each step describes its input and output data
const researchStep = createStep({
  id: "research",
  inputSchema: z.object({ topic: z.string() }),
  outputSchema: z.object({ research: z.string() }),
  execute: async ({ inputData, mastra }) => {
    const agent = mastra.getAgent("researchAgent");
    const result = await agent.generate(
      `Research the topic: ${inputData.topic}. Find current data and trends.`
    );
    return { research: result.text };
  },
});

const summarizeStep = createStep({
  id: "summarize",
  inputSchema: z.object({ research: z.string() }),
  outputSchema: z.object({ summary: z.string() }),
  execute: async ({ inputData }) => {
    const response = await anthropic.messages.create({
      model: "claude-haiku-4-5",
      max_tokens: 500,
      messages: [
        {
          role: "user",
          content: `Condense this research into 3 key takeaways:\n\n${inputData.research}`,
        },
      ],
    });
    const summary = response.content.map((b) => (b.type === "text" ? b.text : "")).join("");
    return { summary };
  },
});

const researchWorkflow = createWorkflow({
  id: "research-workflow",
  inputSchema: z.object({ topic: z.string() }),
  outputSchema: z.object({ summary: z.string() }),
})
  .then(researchStep)
  .then(summarizeStep)
  .commit();

// Initialize Mastra
const mastra = new Mastra({
  agents: { researchAgent },
  workflows: { researchWorkflow },
});

// Run it (check the documentation for your Mastra version for how to run it)
const run = await mastra.getWorkflow("researchWorkflow").createRun();
const result = await run.start({ inputData: { topic: "AI agents" } });
console.log(result);

When to choose Mastra:

  • Your whole stack is TypeScript/Node.js
  • You need native integration with Vercel/Next.js
  • You want a modern, typed developer experience
  • You're building a SaaS where the agents are a backend service

Mastra's weak spots: the ecosystem is younger than LangGraph's or CrewAI's, there are fewer ready-made integrations, and the documentation is still actively evolving.


Native Claude Code: when you don't need a separate framework

Claude Code has built-in multi-agent support through the Agent tool (it used to be called Task) and subagent files in .claude/agents/.

How it works:

  • You describe the orchestration logic in CLAUDE.md
  • Claude Code calls subagents on its own through the Agent tool
  • Each subagent works in an isolated context
  • The orchestrator gets the result and carries on
Type this into the chat
---
name: research-crew
description: Orchestrator for a research team. Use it for tasks that require researching a topic from several angles.
---

# .claude/agents/research-crew.md

## Description
An orchestrator agent that coordinates a research team.

## Behavior
When I get a research task:
1. I launch in parallel:
   - market-researcher: market research
   - tech-analyst: technical analysis
   - financial-model: financial calculations
2. I collect the results from all three
3. I pass them to report-writer for synthesis
4. I return the final report

## Subagents
- market-researcher (market-researcher.md)
- tech-analyst (tech-analyst.md)
- financial-model (financial-model.md)
- report-writer (report-writer.md)

What changed by October 2026. Subagents run in the background by default. The /agents wizard was removed: you create a subagent by asking Claude, or with a file in .claude/agents/. New additions: agent view (claude agents shows all background sessions, research preview), agent teams (several sessions with a shared task list; experimental, off by default) and dynamic workflows (/workflows): a script that launches many subagents and double-checks their results. This covers some of the jobs people used to bring in an external framework for. Details: Subagents, Agent teams and What's current.

When native Claude Code is enough:

  • No complex conditional branches or loops
  • The orchestration logic is simple and linear
  • A small team (1-3 developers)
  • You want minimal dependencies
  • You already use Claude Code as your main tool

When native Claude Code isn't enough:

  • You need real loops that break on a condition (LangGraph)
  • Complex state management between agents
  • You need integration with external systems (vector DBs, webhooks, queues)
  • You need observability and tracing of agent calls

Comparison table

Parameter LangGraph CrewAI Mastra Native Claude Code
Language Python Python TypeScript Markdown/any
Paradigm State graph Teams with roles Workflow + Agents CLAUDE.md + Agent tool
Learning curve Steep Medium Medium Gentle
Loops/state machine ✅ Native ⚠️ Limited ⚠️ Workflow ⚠️ Through workflows and scripts
Parallelism ✅ Yes ✅ async_execution ✅ Yes ✅ Parallel subagents
Human-in-the-loop ✅ Built in ⚠️ Manual ⚠️ Manual ✅ Plan mode and permission prompts
Ecosystem Large (LangChain) Medium, growing Young Anthropic ecosystem
Observability LangSmith Built-in UI Built-in tracing Session log
Best for Complex state machines Parallel teams TypeScript SaaS Simple pipelines
Price Free + LangSmith Free + cloud Free + cloud Included in a paid Claude plan, or pay per token

Current prices and versions: What's current.


Pattern: Claude Code as the orchestrator, CrewAI as an external service

The most flexible approach is a mixed architecture: Claude Code orchestrates the high-level flow, and calls CrewAI as an external service for heavy parallel work.

python
# crew_service.py — wrapping CrewAI as a FastAPI service
from fastapi import FastAPI
from pydantic import BaseModel
from crewai import Agent, Task, Crew, Process

app = FastAPI()

class ResearchRequest(BaseModel):
    topic: str
    output_format: str = "report"

@app.post("/research")
async def run_research_crew(request: ResearchRequest):
    """
    This endpoint is called by the Claude Code orchestrator.
    Inside, a parallel CrewAI team runs.
    """
    researcher = Agent(
        role="Senior Researcher",
        goal=f"Find everything important about: {request.topic}",
        backstory="An experienced analyst with internet access",
        llm="anthropic/claude-sonnet-5-5",
        verbose=False  # Turn verbose off in production
    )

    writer = Agent(
        role="Technical Writer",
        goal="Turn the research into a clear, structured document",
        backstory="A technical documentation specialist",
        llm="anthropic/claude-haiku-4-5",
        verbose=False
    )

    research_task = Task(
        description=f"Research: {request.topic}. Find facts, numbers, trends.",
        expected_output="Structured data with sources",
        agent=researcher
    )

    write_task = Task(
        description=f"Write a {request.output_format} based on the research",
        expected_output=f"A finished {request.output_format} for the client",
        agent=writer,
        context=[research_task]
    )

    crew = Crew(
        agents=[researcher, writer],
        tasks=[research_task, write_task],
        process=Process.sequential,
        verbose=False
    )

    result = crew.kickoff()
    return {"result": result.raw, "topic": request.topic}

Now a Claude Code subagent just calls this service:

bash
# In .claude/agents/research-orchestrator.md
# Claude Code calls CrewAI as an external API
curl -X POST http://localhost:8000/research \
  -H "Content-Type: application/json" \
  -d '{"topic": "AI orchestration frameworks", "output_format": "report"}'

OpenAI Swarm and AutoGen: a quick overview

OpenAI Swarm is an experimental minimalist framework from OpenAI; it has been replaced by the OpenAI Agents SDK (openai-agents-python). Two concepts: Agents and Handoffs. An agent can "hand off" control to another agent. Simple and clear, aimed at OpenAI's models.

Microsoft AutoGen is a framework for multi-agent conversations. Since October 2025, AutoGen has been in maintenance mode: according to the repository's README, it doesn't get new features, and Microsoft points new users to the Microsoft Agent Framework (its successor for enterprise scenarios). If you run into AutoGen in older tutorials, check the current documentation.

For most tasks an independent developer will tackle, LangGraph or CrewAI + Claude is a more practical choice than heavy enterprise frameworks.

🎨 Picture this: An enterprise framework is a Boeing 747. Powerful, reliable, flies far. But getting it in the air takes a crew of 50 and certification. CrewAI is a Cessna. One pilot, start it up and take off.


How to decide: a selection flowchart

Code
Is the task simple and linear, with no branches?
  → Native Claude Code. Don't overcomplicate it.

Need "try → fix → repeat" loops?
  → LangGraph

Need several parallel experts on one topic?
  → CrewAI

Whole stack in TypeScript, deploying to Vercel?
  → Mastra

Need enterprise reliability, lots of compliance?
  → Microsoft Agent Framework (AutoGen's successor)

Want a mixed approach?
  → Claude Code orchestrator + CrewAI/LangGraph as an external service

Practice

Step 1: Install CrewAI and the dependencies

bash
# Create the project
mkdir multiagent-crew && cd multiagent-crew
python -m venv venv && source venv/bin/activate  # Windows: venv\Scripts\activate

# Install the dependencies
pip install crewai crewai-tools anthropic python-dotenv fastapi uvicorn

# The .env file
echo "ANTHROPIC_API_KEY=sk-ant-your-key-here" > .env
echo "SERPER_API_KEY=your-serper-key" >> .env  # for internet search

Step 2: The simplest CrewAI setup with two agents

python
# simple_crew.py — start with the minimum
import os
from dotenv import load_dotenv
from crewai import Agent, Task, Crew

load_dotenv()

# Set up the model for CrewAI
os.environ["ANTHROPIC_API_KEY"] = os.getenv("ANTHROPIC_API_KEY")

# Two agents
researcher = Agent(
    role="Researcher",
    goal="Find the key information on the topic",
    backstory="An analyst with experience in the tech industry",
    llm="anthropic/claude-haiku-4-5",  # A cheap model for research
    verbose=True
)

writer = Agent(
    role="Writer",
    goal="Write clear, useful content based on the research",
    backstory="A technical writer specializing in AI topics",
    llm="anthropic/claude-sonnet-5-5",  # A smarter model for the final text
    verbose=True
)

# Tasks
research_task = Task(
    description="Find 5 key trends in multi-agent AI systems in 2026",
    expected_output="A list of 5 trends with a short description of each (50-100 words per trend)",
    agent=researcher
)

write_task = Task(
    description="""Based on the research, write a short-form article (500 words)
    for software developers. Tone: professional, no filler, specific.""",
    expected_output="A finished 500-word article in markdown",
    agent=writer,
    context=[research_task]
)

# Crew
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    verbose=True
)

# Run
print("Starting the team...")
result = crew.kickoff()
print("\n" + "="*50)
print("RESULT:")
print("="*50)
print(result.raw)

Step 3: A parallel team of 4 agents

python
# parallel_crew.py — a real parallel team
import os
from dotenv import load_dotenv
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool

load_dotenv()

search = SerperDevTool()

# Four specialized experts
market_analyst = Agent(
    role="Market Analyst",
    goal="Analyze the market situation and the competitors",
    backstory="A market analyst focused on SaaS and AI tools",
    tools=[search],
    llm="anthropic/claude-haiku-4-5",
    verbose=False
)

tech_reviewer = Agent(
    role="Technical Reviewer",
    goal="Evaluate the technical side and the integrations",
    backstory="A senior developer who knows all the popular AI frameworks",
    tools=[search],
    llm="anthropic/claude-haiku-4-5",
    verbose=False
)

pricing_expert = Agent(
    role="Pricing Expert",
    goal="Figure out the pricing and ROI",
    backstory="A financial analyst specializing in SaaS metrics",
    tools=[search],
    llm="anthropic/claude-haiku-4-5",
    verbose=False
)

strategy_writer = Agent(
    role="Strategy Consultant",
    goal="Synthesize all the data into actionable recommendations",
    backstory="A McKinsey-level consultant who writes clear executive briefs",
    llm="anthropic/claude-sonnet-5-5",  # A strong model for the final synthesis
    verbose=True
)

# Parallel tasks (async_execution=True)
market_task = Task(
    description="Research the market for multi-agent AI frameworks: size, players, 2026 trends",
    expected_output="Market analysis: 3-5 key players, trends, market size",
    agent=market_analyst,
    async_execution=True  # IN PARALLEL
)

tech_task = Task(
    description="Compare technically: LangGraph, CrewAI, Mastra — capabilities, limitations, ecosystem",
    expected_output="A technical comparison with specific use case examples",
    agent=tech_reviewer,
    async_execution=True  # IN PARALLEL
)

pricing_task = Task(
    description="Break down the pricing models and ROI: what it costs to use, what the savings are",
    expected_output="A financial analysis with specific numbers and an ROI calculation",
    agent=pricing_expert,
    async_execution=True  # IN PARALLEL
)

# Synthesis waits for all three parallel tasks
synthesis_task = Task(
    description="""Synthesize the three analyses into an executive brief for a CTO.
    Structure: Situation → Conclusions → Recommendations (top 3, actionable).
    1 page max. No filler.""",
    expected_output="An executive brief in markdown, 1 page, ready to present",
    agent=strategy_writer,
    context=[market_task, tech_task, pricing_task]  # Depends on all three
)

# Assemble the team
crew = Crew(
    agents=[market_analyst, tech_reviewer, pricing_expert, strategy_writer],
    tasks=[market_task, tech_task, pricing_task, synthesis_task],
    process=Process.sequential,
    verbose=True,
    memory=False  # Turned off in this example for speed
)

# Run
print("Starting the parallel team...")
print("market_analyst, tech_reviewer, pricing_expert work at the same time")
print("strategy_writer starts when all three are done")
print("-" * 50)

result = crew.kickoff()

# Save the result
with open("report.md", "w", encoding="utf-8") as f:
    f.write(result.raw)

print("\nReport saved to report.md")

Step 4: Running and debugging

bash
# Run the simple team
python simple_crew.py

# Run the parallel team
python parallel_crew.py

# If you need to debug, set verbose=True on the agents and the Crew in the code

# Check the token usage
# At the end, CrewAI shows the total tokens usage

Common problems and fixes:

bash
# Error: model not found
# Make sure ANTHROPIC_API_KEY is set in .env
# CrewAI looks for the key through os.environ

# Error: rate limit
# Add the max_rpm parameter to Crew:
crew = Crew(..., max_rpm=5)  # at most 5 requests per minute

# The result didn't make it into the context
# Make sure context=[task1, task2] is set on the dependent task

Step 5: Wrapping CrewAI in an API for Claude Code

python
# api_service.py — CrewAI as a microservice
from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn
from crewai import Agent, Task, Crew

app = FastAPI(title="Research Crew API")

class ResearchRequest(BaseModel):
    topic: str
    style: str = "technical"  # technical, executive, casual

@app.post("/research")
async def research(req: ResearchRequest):
    """
    Claude Code calls this endpoint through bash/curl.
    Inside, a parallel CrewAI team runs.
    """
    researcher = Agent(
        role="Researcher",
        goal=f"Research the topic thoroughly: {req.topic}",
        backstory="An experienced analyst",
        llm="anthropic/claude-haiku-4-5",
        verbose=False
    )

    writer = Agent(
        role="Writer",
        goal=f"Write in this style: {req.style}",
        backstory="A technical writer",
        llm="anthropic/claude-sonnet-5-5",
        verbose=False
    )

    research_task = Task(
        description=f"Research: {req.topic}",
        expected_output="A detailed analysis with facts",
        agent=researcher
    )

    write_task = Task(
        description=f"Write in the '{req.style}' style based on the research",
        expected_output="A finished document",
        agent=writer,
        context=[research_task]
    )

    crew = Crew(
        agents=[researcher, writer],
        tasks=[research_task, write_task]
    )

    result = crew.kickoff()
    return {
        "topic": req.topic,
        "result": result.raw,
        "tokens_used": result.token_usage.total_tokens if result.token_usage else 0
    }

@app.get("/health")
def health():
    return {"status": "ok"}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8001)
bash
# Start the service
python api_service.py

# Now Claude Code can call it:
curl -X POST http://localhost:8001/research \
  -H "Content-Type: application/json" \
  -d '{"topic": "Multiagent orchestration trends", "style": "executive"}'

Tools and resources

  • LangGraph: documentation and examples of graph-based orchestration
  • CrewAI: the official docs, example crews, the list of tools
  • Mastra: a TypeScript framework; documentation for workflows and agents
  • Microsoft Agent Framework: AutoGen's successor for multi-agent and enterprise scenarios
  • OpenAI Agents SDK: a minimalist framework from OpenAI (replaced Swarm)
  • LangSmith: observability and tracing for LangGraph agents
  • SerperDev: an API for Google search (integrates with CrewAI tools)
  • Claude Code subagents: Claude Code's built-in multi-agent support

Key takeaways

You don't need a separate framework to get started with multi-agent systems. Native Claude Code with .claude/agents/ and the Agent tool handles 80% of tasks. LangGraph and CrewAI are needed once you have loops, complex state machines or parallel teams of 5+ agents.

CrewAI is the best starting point for parallel research teams. The role-based approach is intuitive: you describe a role like a job posting, and CrewAI handles the coordination. Three independent experts work at the same time and pass their results to a synthesizer, and that's the whole team.

A mixed architecture is often better than a pure one: Claude Code as the orchestrator for the high-level flow + CrewAI or LangGraph as a specialized service for the heavy lifting. You don't have to pick one framework forever.


Next lesson

→ Advanced Computer Use: patterns for advanced scenarios

The mark stays in this browser only and is never sent anywhere. My progress