VOOZH about

URL: https://tech-insider.org/crewai-tutorial-multi-agent-ai-python-2026/

⇱ How to Build Multi-Agent AI with CrewAI Python in 13 Steps [2026]


Skip to content
April 14, 2026
24 min read

CrewAI has become the fastest-growing multi-agent AI framework in 2026, with over 14,800 monthly searches and a rapidly expanding developer community. If you want to build AI systems where multiple agents collaborate on complex tasks – research, analysis, content creation, coding – CrewAI offers the most intuitive Python framework for the job. This step-by-step tutorial walks you through building a complete multi-agent system from scratch, covering installation, agent creation, tool integration, and production deployment across 13 detailed steps.

Unlike single-prompt AI interactions, CrewAI lets you define specialized agents with distinct roles, goals, and backstories that work together as a crew. Each agent can use different tools, delegate tasks to other agents, and produce structured outputs. Whether you are building an automated research pipeline, a content generation system, or a data analysis workflow, this tutorial gives you every line of code you need. By the end, you will have a fully working multi-agent project that you can extend for your own use cases.

Prerequisites and Environment Setup

Before diving into CrewAI, make sure your development environment meets these requirements. CrewAI requires Python 3.10 or higher (up to Python 3.13) and works on macOS, Linux, and Windows. You will also need an API key from at least one LLM provider – OpenAI, Anthropic, Google Gemini, or Azure OpenAI are all supported through CrewAI’s built-in LiteLLM integration.

RequirementMinimum VersionRecommended
Python3.103.12 or 3.13
pip23.024.0+
Operating SystemmacOS 12+, Ubuntu 20.04+, Windows 10+macOS 14+, Ubuntu 22.04+
RAM4 GB8 GB+
LLM API KeyOpenAI, Anthropic, or GeminiOpenAI GPT-4o or Claude Sonnet
Disk Space500 MB1 GB+ (for dependencies)

CrewAI supports multiple LLM providers out of the box through its LiteLLM integration. You can use OpenAI models (GPT-4o, GPT-4o-mini), Anthropic models (Claude Sonnet, Claude Haiku), Google Gemini models, Azure-hosted models, and local models through Ollama. This flexibility means you can start with a free-tier API key and scale to more powerful models as your agents grow in complexity.

Step 1: Install CrewAI and Create Your First Project

CrewAI provides a CLI tool that scaffolds a complete project structure for you. Start by installing CrewAI with pip, then use the CLI to create a new project. The framework uses uv as its package manager internally, which handles dependency resolution and virtual environment creation automatically.

👁 Step 1: Install CrewAI and Create Your First Project
# Install CrewAI
pip install crewai

# Verify installation
crewai version

# Create a new CrewAI project
crewai create crew research_crew

# Navigate into your project
cd research_crew

# Install project dependencies
crewai install

The crewai create crew command generates a well-organized project structure with separate YAML configuration files for agents and tasks, a main crew definition file, and a properly configured pyproject.toml. This structure follows CrewAI’s recommended pattern of separating agent configuration from business logic, making it easier to modify agents without touching Python code.

After running crewai install, your project directory looks like this:

research_crew/
├── .gitignore
├── pyproject.toml
├── README.md
└── src/
 └── research_crew/
 ├── __init__.py
 ├── main.py
 ├── crew.py
 ├── config/
 │ ├── agents.yaml
 │ └── tasks.yaml
 └── tools/
 ├── __init__.py
 └── custom_tool.py

Common Pitfall #1: If you see a ModuleNotFoundError during installation, make sure you are using Python 3.10 or higher. Run python --version to check. CrewAI does not support Python 3.9 or earlier. If you have multiple Python versions installed, use python3.12 -m pip install crewai to target the correct version.

Step 2: Configure Your LLM Provider

Before your agents can function, you need to configure an LLM provider. CrewAI uses environment variables for API key management and supports switching between providers without changing your agent code. Set your preferred provider’s API key as an environment variable, and CrewAI will automatically route requests through LiteLLM.

# Option 1: OpenAI (most common)
export OPENAI_API_KEY="sk-your-openai-key-here"

# Option 2: Anthropic Claude
export ANTHROPIC_API_KEY="sk-ant-your-anthropic-key-here"

# Option 3: Google Gemini
export GOOGLE_API_KEY="your-google-api-key-here"

# Option 4: Local models with Ollama (free, no API key needed)
# First install Ollama from https://ollama.com
# Then pull a model:
ollama pull llama3.1

For production use, store API keys in a .env file in your project root and load them with python-dotenv. Never hardcode API keys in your Python files. CrewAI automatically reads from .env files when using the CLI runner.

To specify which model your agents should use, you configure it in the agent definition. CrewAI defaults to gpt-4o if you have an OpenAI key set, but you can override this per agent. For example, you might use a powerful model like gpt-4o for your lead research agent and a faster, cheaper model like gpt-4o-mini for summary agents. The model string follows LiteLLM’s naming convention – use anthropic/claude-sonnet-4-20250514 for Claude, gemini/gemini-2.0-flash for Gemini, or ollama/llama3.1 for local Ollama models.

Common Pitfall #2: A frequent error is AuthenticationError: Incorrect API key provided. Double-check that your environment variable is set in the current shell session. Running echo $OPENAI_API_KEY should display your key. If you are using a .env file, make sure it is in the project root directory, not a subdirectory.

Step 3: Define Your Agents in YAML

Agents are the core building blocks of any CrewAI system. Each agent has a role, a goal, and a backstory that shapes how it approaches tasks. CrewAI’s YAML-based configuration makes it easy to define and modify agents without changing Python code. Open src/research_crew/config/agents.yaml and define your agents.

# src/research_crew/config/agents.yaml

researcher:
 role: >
 Senior Research Analyst
 goal: >
 Conduct thorough research on {topic} and gather
 thorough, accurate, and up-to-date information
 from multiple sources.
 backstory: >
 You are a seasoned research analyst with 15 years of
 experience in technology analysis. You are known for
 your ability to find obscure but critical data points
 and synthesize complex information into clear insights.
 You always verify facts from multiple sources before
 reporting them.
 llm: openai/gpt-4o
 max_iter: 15
 verbose: true

writer:
 role: >
 Technical Content Writer
 goal: >
 Transform research findings on {topic} into a
 well-structured, engaging, and technically accurate
 article that readers will find valuable.
 backstory: >
 You are an award-winning technical writer who has
 published over 500 articles for major tech publications.
 You excel at making complex topics accessible without
 sacrificing accuracy. Your writing style is clear,
 concise, and engaging.
 llm: openai/gpt-4o
 max_iter: 10
 verbose: true

editor:
 role: >
 Senior Content Editor
 goal: >
 Review and polish the article about {topic} to ensure
 factual accuracy, proper structure, grammar, and
 readability. Ensure all claims are supported by data.
 backstory: >
 You are a meticulous editor with a background in both
 journalism and computer science. You have a sharp eye
 for factual errors, logical inconsistencies, and unclear
 writing. You never let a piece go to publication without
 thorough verification.
 llm: openai/gpt-4o-mini
 max_iter: 8
 verbose: true

Notice how each agent has a distinct personality and expertise. The {topic} placeholder gets replaced at runtime with your actual topic, making these agent definitions reusable across different research projects. The max_iter parameter controls how many reasoning loops each agent can perform – higher values allow more thorough work but cost more in API calls. Setting verbose: true lets you see each agent’s thought process in real time during execution.

You can also specify different LLM models per agent. In this configuration, the researcher and writer use gpt-4o for higher-quality reasoning, while the editor uses gpt-4o-mini since editing requires less creative reasoning and more pattern matching. This strategy can reduce your total API costs by 40-60% without sacrificing output quality on the most critical steps.

Step 4: Define Tasks in YAML

Tasks define the specific work each agent performs. Each task has a description, expected output, and is assigned to a specific agent. Tasks can also depend on other tasks, creating a workflow pipeline. Open src/research_crew/config/tasks.yaml and define your task pipeline.

# src/research_crew/config/tasks.yaml

research_task:
 description: >
 Research the topic: {topic}

 Your research must include:
 1. Current state and latest developments (2025-2026)
 2. Key statistics and market data with sources
 3. Technical specifications and capabilities
 4. Comparison with competing solutions
 5. Expert opinions and industry perspectives
 6. At least 10 specific data points with numbers

 Compile your findings into a structured research brief.
 expected_output: >
 A detailed research brief with at least 10 verified
 facts, statistics, and expert quotes about {topic}.
 Each fact must include its source.
 agent: researcher

writing_task:
 description: >
 Using the research brief provided, write a thorough
 technical article about {topic}.

 Requirements:
 - 2000+ words
 - Clear introduction with hook
 - 5+ sections with descriptive headings
 - Data tables where appropriate
 - Code examples if relevant
 - Conclusion with actionable takeaways

 Use only the facts from the research brief. Do not
 invent or assume any data points.
 expected_output: >
 A complete, publication-ready technical article about
 {topic} with proper formatting, data tables, and
 a clear narrative structure.
 agent: writer
 context:
 - research_task

editing_task:
 description: >
 Review and edit the article about {topic}.

 Check for:
 1. Factual accuracy against the research brief
 2. Logical flow and structure
 3. Grammar and style consistency
 4. Data accuracy in all tables and statistics
 5. Clear and compelling headline

 Make corrections directly in the text and provide
 a brief editor's note with key changes made.
 expected_output: >
 The final edited article with all corrections applied,
 plus a brief editor's note listing the changes made.
 agent: editor
 context:
 - research_task
 - writing_task

The context field is crucial – it tells CrewAI that the writing task depends on the research task’s output, and the editing task depends on both. This creates a sequential pipeline where each agent builds on the previous agent’s work. Without proper context linking, agents would work in isolation and produce disconnected outputs.

Common Pitfall #3: Forgetting to link tasks with context is the most common mistake new CrewAI users make. If your writer produces content that ignores the researcher’s findings, check that context: [research_task] is properly set in your tasks YAML. Also ensure the task key names match exactly – YAML is case-sensitive.

Step 5: Build the Crew Definition

Now wire everything together in the crew definition file. The crew.py file is where you define how agents and tasks combine into a functioning crew. CrewAI provides a decorator-based API that reads your YAML configuration and assembles the crew automatically.

👁 Step 5: Build the Crew Definition
# src/research_crew/crew.py

from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task

@CrewBase
class ResearchCrew:
 """Research crew for producing high-quality articles."""

 agents_config = "config/agents.yaml"
 tasks_config = "config/tasks.yaml"

 @agent
 def researcher(self) -> Agent:
 return Agent(
 config=self.agents_config["researcher"],
 tools=[], # We'll add tools in Step 7
 allow_delegation=False,
 )

 @agent
 def writer(self) -> Agent:
 return Agent(
 config=self.agents_config["writer"],
 tools=[],
 allow_delegation=False,
 )

 @agent
 def editor(self) -> Agent:
 return Agent(
 config=self.agents_config["editor"],
 tools=[],
 allow_delegation=False,
 )

 @task
 def research_task(self) -> Task:
 return Task(config=self.tasks_config["research_task"])

 @task
 def writing_task(self) -> Task:
 return Task(config=self.tasks_config["writing_task"])

 @task
 def editing_task(self) -> Task:
 return Task(
 config=self.tasks_config["editing_task"],
 output_file="output/final_article.md",
 )

 @crew
 def crew(self) -> Crew:
 return Crew(
 agents=self.agents,
 tasks=self.tasks,
 process=Process.sequential,
 verbose=True,
 )

The @CrewBase decorator integrates your class with CrewAI’s project system, automatically loading YAML configurations and wiring up agents and tasks. The Process.sequential setting tells CrewAI to run tasks one after another in the order they are defined. For tasks that can run in parallel, you can switch to Process.hierarchical, where a manager agent coordinates work across other agents.

Setting allow_delegation=False on agents prevents them from delegating their work to other agents. This is recommended when you want predictable, deterministic workflows. For more creative or exploratory tasks, setting allow_delegation=True lets agents decide at runtime whether to pass subtasks to other agents in the crew.

Step 6: Create the Main Entry Point and Run

The main entry point handles user input and kicks off the crew execution. Edit src/research_crew/main.py to accept a topic parameter and pass it to your crew. CrewAI’s input system lets you pass dynamic values that replace placeholders in your YAML configurations.

# src/research_crew/main.py

#!/usr/bin/env python
import sys
from research_crew.crew import ResearchCrew


def run():
 """Run the research crew."""
 inputs = {
 "topic": "AI coding assistants in 2026"
 }
 result = ResearchCrew().crew().kickoff(inputs=inputs)
 print("nn========== FINAL OUTPUT ==========n")
 print(result.raw)
 print(f"nToken usage: {result.token_usage}")


def train():
 """Train the crew for a given number of iterations."""
 inputs = {
 "topic": "AI coding assistants in 2026"
 }
 try:
 ResearchCrew().crew().train(
 n_iterations=int(sys.argv[1]),
 filename=sys.argv[2],
 inputs=inputs,
 )
 except Exception as e:
 raise Exception(f"Training error: {e}")


if __name__ == "__main__":
 run()

Run your crew with the CrewAI CLI:

# Run with the CLI
crewai run

# Or run directly with Python
python -m research_crew.main

# Expected output (abbreviated):
# [2026-04-14 10:23:15] Working Agent: Senior Research Analyst
# [2026-04-14 10:23:15] Starting Task: Research the topic: AI coding assistants...
# ...
# [2026-04-14 10:24:02] Working Agent: Technical Content Writer
# [2026-04-14 10:24:02] Starting Task: Using the research brief provided...
# ...
# [2026-04-14 10:25:18] Working Agent: Senior Content Editor
# ...
# ========== FINAL OUTPUT ==========
# [The complete edited article appears here]
# Token usage: UsageMetrics(total_tokens=18432, prompt_tokens=14201, ...

The kickoff() method returns a CrewOutput object containing the final result, token usage metrics, and individual task outputs. The token_usage field is particularly useful for monitoring costs – a typical three-agent run with GPT-4o consumes 15,000-25,000 tokens depending on the topic complexity.

Common Pitfall #4: If you get CrewAI Error: Task configuration not found, check that your task method names in crew.py match exactly with the keys in tasks.yaml. For example, if your YAML has research_task:, your method must be named research_task, not researchTask or research.

Step 7: Add Tools to Your Agents

Tools are what make CrewAI agents truly powerful. Without tools, agents can only reason about their training data. With tools, they can search the web, read files, scrape websites, execute code, and interact with APIs. CrewAI includes several built-in tools and supports custom tool creation for any use case.

First, install the CrewAI tools package:

# Install CrewAI tools
pip install 'crewai[tools]'

# This installs tools for:
# - Web searching (SerperDevTool)
# - Web scraping (ScrapeWebsiteTool)
# - File reading (FileReadTool)
# - Directory reading (DirectoryReadTool)
# - PDF reading (PDFSearchTool)
# - Code execution (CodeInterpreterTool)
# - And many more

Now update your crew.py to equip agents with tools:

# Updated crew.py with tools

from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from crewai_tools import (
 SerperDevTool,
 ScrapeWebsiteTool,
 FileReadTool,
)

@CrewBase
class ResearchCrew:
 """Research crew with tool-equipped agents."""

 agents_config = "config/agents.yaml"
 tasks_config = "config/tasks.yaml"

 @agent
 def researcher(self) -> Agent:
 return Agent(
 config=self.agents_config["researcher"],
 tools=[
 SerperDevTool(),
 ScrapeWebsiteTool(),
 ],
 allow_delegation=False,
 )

 @agent
 def writer(self) -> Agent:
 return Agent(
 config=self.agents_config["writer"],
 tools=[FileReadTool()],
 allow_delegation=False,
 )

 @agent
 def editor(self) -> Agent:
 return Agent(
 config=self.agents_config["editor"],
 tools=[],
 allow_delegation=False,
 )

 # ... tasks and crew methods remain the same

The SerperDevTool requires a SERPER_API_KEY environment variable (free tier available at serper.dev). When the researcher agent decides it needs current information, it automatically formulates search queries, executes them through the Serper API, and incorporates the results into its reasoning. The ScrapeWebsiteTool lets the agent read full web pages when it needs deeper information than search snippets provide.

Common Pitfall #5: Giving agents too many tools slows them down and increases token usage. Each tool’s description is included in every LLM prompt, consuming tokens even when the tool is not used. Start with 2-3 essential tools per agent and add more only when you see agents struggling to complete their tasks. A focused agent with 2 tools outperforms a generalist agent with 10 tools.

Step 8: Create Custom Tools

Built-in tools cover common use cases, but real-world projects often need custom tools. CrewAI makes it straightforward to create tools that connect to your own APIs, databases, or services. Custom tools are defined as Python classes that extend BaseTool or as simple functions decorated with @tool.

# src/research_crew/tools/custom_tool.py

from crewai.tools import BaseTool
from typing import Type
from pydantic import BaseModel, Field
import requests


class StockPriceInput(BaseModel):
 """Input schema for StockPriceTool."""
 ticker: str = Field(
 ..., description="The stock ticker symbol (e.g., AAPL, MSFT)"
 )


class StockPriceTool(BaseTool):
 name: str = "Stock Price Lookup"
 description: str = (
 "Fetches the current stock price and basic financial "
 "data for a given ticker symbol. Use this when you need "
 "real-time stock market data."
 )
 args_schema: Type[BaseModel] = StockPriceInput

 def _run(self, ticker: str) -> str:
 """Execute the stock price lookup."""
 url = f"https://query1.finance.yahoo.com/v8/finance/chart/{ticker}"
 headers = {"User-Agent": "Mozilla/5.0"}
 try:
 response = requests.get(url, headers=headers, timeout=10)
 data = response.json()
 meta = data["chart"]["result"][0]["meta"]
 price = meta["regularMarketPrice"]
 prev_close = meta["chartPreviousClose"]
 change = ((price - prev_close) / prev_close) * 100
 return (
 f"Ticker: {ticker}n"
 f"Current Price: ${price:.2f}n"
 f"Previous Close: ${prev_close:.2f}n"
 f"Change: {change:+.2f}%"
 )
 except Exception as e:
 return f"Error fetching stock data for {ticker}: {str(e)}"

The Pydantic input schema provides clear documentation to the LLM about what parameters the tool expects, helping agents use tools correctly. The description field is critical – it tells the agent when and why to use this tool. Write descriptions that focus on use cases, not implementation details. To use this custom tool, add it to your agent’s tool list: tools=[StockPriceTool()].

For simpler tools, CrewAI also supports a decorator-based approach:

from crewai.tools import tool

@tool("Word Counter")
def word_counter(text: str) -> str:
 """Counts the number of words in a given text.
 Use this to verify article length requirements."""
 count = len(text.split())
 return f"Word count: {count}"

Step 9: Implement Structured Outputs

One of CrewAI’s most powerful features – added in the 1.9.x series – is structured output support. Instead of receiving free-form text from your agents, you can define Pydantic models that enforce a specific output schema. This is essential for building reliable pipelines where downstream agents or systems need predictable data formats.

👁 Step 9: Implement Structured Outputs
# Define structured output models

from pydantic import BaseModel, Field
from typing import List


class ResearchFinding(BaseModel):
 """A single research finding with source."""
 fact: str = Field(description="The specific finding or data point")
 source: str = Field(description="URL or publication name")
 confidence: str = Field(
 description="HIGH, MEDIUM, or LOW confidence level"
 )


class ResearchReport(BaseModel):
 """Complete research report output."""
 topic: str = Field(description="The research topic")
 summary: str = Field(description="Executive summary in 2-3 sentences")
 findings: List[ResearchFinding] = Field(
 description="List of research findings"
 )
 key_statistics: List[str] = Field(
 description="Key numerical data points"
 )
 recommended_sources: List[str] = Field(
 description="Top sources for further reading"
 )

Apply the structured output to your task definition in crew.py:

 @task
 def research_task(self) -> Task:
 return Task(
 config=self.tasks_config["research_task"],
 output_pydantic=ResearchReport, # Enforce structured output
 )

With structured outputs, the research agent’s result is now a validated ResearchReport object. You can access individual findings programmatically: result.pydantic.findings[0].fact. This makes it trivial to feed agent outputs into databases, APIs, or other automated systems. If the LLM output does not match the schema, CrewAI automatically retries with a corrective prompt.

Step 10: Use CrewAI Flows for Complex Orchestration

While Crews handle agent collaboration within a single workflow, CrewAI Flows let you build event-driven pipelines that chain multiple crews together with conditional logic, error handling, and state management. Flows are CrewAI’s answer to complex, multi-stage automation where you need more control than a simple sequential process provides.

# src/research_crew/flow.py

from crewai.flow.flow import Flow, listen, start
from pydantic import BaseModel
from research_crew.crew import ResearchCrew


class ArticleState(BaseModel):
 topic: str = ""
 research_complete: bool = False
 article_draft: str = ""
 final_article: str = ""
 word_count: int = 0


class ArticleFlow(Flow[ArticleState]):

 @start()
 def get_topic(self):
 self.state.topic = "Multi-agent AI frameworks in 2026"
 print(f"Starting flow for topic: {self.state.topic}")

 @listen(get_topic)
 def run_research_crew(self):
 result = (
 ResearchCrew()
 .crew()
 .kickoff(inputs={"topic": self.state.topic})
 )
 self.state.final_article = result.raw
 self.state.word_count = len(result.raw.split())
 print(f"Article complete: {self.state.word_count} words")

 @listen(run_research_crew)
 def validate_output(self):
 if self.state.word_count < 1000:
 print("WARNING: Article is too short, needs revision")
 # Could trigger a revision crew here
 else:
 print(f"Article validated: {self.state.word_count} words")
 # Save to file
 with open("output/article.md", "w") as f:
 f.write(self.state.final_article)
 print("Article saved to output/article.md")


if __name__ == "__main__":
 flow = ArticleFlow()
 flow.kickoff()

Flows use a decorator-based event system. The @start() decorator marks the entry point, and @listen() decorators define which function triggers each subsequent step. State is managed through a Pydantic model (ArticleState), giving you type safety and automatic validation throughout the pipeline. This pattern is particularly powerful for production systems where you need to handle errors, implement retries, or branch logic based on intermediate results.

Common Pitfall #6: Flows and Crews serve different purposes. Use a Crew when you have a group of agents that need to collaborate on a single task. Use a Flow when you need to orchestrate multiple crews or add conditional logic between processing stages. Nesting a Flow inside a Crew (or vice versa incorrectly) leads to confusing execution patterns.

Step 11: Add Memory and Context to Your Agents

CrewAI supports multiple memory types that give agents persistence across tasks and even across crew runs. Memory allows agents to learn from past interactions, avoid repeating mistakes, and build on previous knowledge. This is especially valuable for long-running or recurring workflows.

 @crew
 def crew(self) -> Crew:
 return Crew(
 agents=self.agents,
 tasks=self.tasks,
 process=Process.sequential,
 verbose=True,
 memory=True, # Enable all memory types
 # Memory configuration options:
 # short_term_memory uses RAG for current execution
 # long_term_memory persists across runs
 # entity_memory tracks key entities mentioned
 )

When memory is enabled, CrewAI automatically manages three memory layers. Short-term memory keeps context within the current crew execution, so agents can reference earlier findings without re-processing them. Long-term memory persists across multiple crew runs using a local SQLite database, letting agents recall information from previous executions. Entity memory specifically tracks people, organizations, and concepts mentioned during execution, building a knowledge graph that agents can query.

CrewAI also supports checkpointing through CheckpointConfig, which saves the crew’s state at each task boundary. If a long-running crew fails midway – say, due to an API rate limit – you can resume from the last checkpoint instead of re-running the entire pipeline. This feature was significantly improved in the 1.9.x releases with the addition of SqliteProvider storage for reliable state persistence.

Step 12: Implement Error Handling and Guardrails

Production CrewAI systems need strong error handling. Agents can fail due to API rate limits, network errors, malformed tool outputs, or LLM hallucinations. CrewAI provides several mechanisms for handling these scenarios gracefully.

# Production-ready crew with error handling

from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


@CrewBase
class ProductionCrew:
 agents_config = "config/agents.yaml"
 tasks_config = "config/tasks.yaml"

 @agent
 def researcher(self) -> Agent:
 return Agent(
 config=self.agents_config["researcher"],
 tools=[SerperDevTool()],
 allow_delegation=False,
 max_retry_limit=3, # Retry failed operations
 max_iter=15, # Cap reasoning loops
 respect_context_window=True, # Auto-summarize if too long
 )

 @crew
 def crew(self) -> Crew:
 return Crew(
 agents=self.agents,
 tasks=self.tasks,
 process=Process.sequential,
 verbose=True,
 memory=True,
 max_rpm=30, # Rate limit: max 30 requests per minute
 planning=True, # Enable crew-level planning step
 )


# Running with error handling
def main():
 try:
 result = ProductionCrew().crew().kickoff(
 inputs={"topic": "AI frameworks comparison"}
 )
 logger.info(f"Crew completed. Tokens used: {result.token_usage}")
 logger.info(f"Output length: {len(result.raw)} characters")
 except Exception as e:
 logger.error(f"Crew execution failed: {e}")
 raise

The max_rpm=30 setting prevents agents from overwhelming LLM APIs with too many concurrent requests. The respect_context_window=True setting automatically summarizes intermediate outputs when they exceed the model’s context window, preventing truncation errors. The planning=True setting adds a planning phase before execution where the crew analyzes all tasks and creates an execution strategy, improving output quality for complex workflows.

Step 13: Deploy to Production with CrewAI Flows

👁 Step 13: Deploy to Production with CrewAI Flows

For production deployment, wrap your CrewAI system in a FastAPI web server. This lets you trigger crew executions via HTTP requests, monitor progress, and integrate with existing systems. CrewAI’s async support means you can handle multiple crew executions concurrently.

# api_server.py - Production deployment

from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel
from research_crew.crew import ResearchCrew
import uuid
import asyncio

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

# In-memory job store (use Redis in production)
jobs: dict = {}


class CrewRequest(BaseModel):
 topic: str


class CrewResponse(BaseModel):
 job_id: str
 status: str


def run_crew(job_id: str, topic: str):
 """Run crew in background."""
 try:
 jobs[job_id]["status"] = "running"
 result = ResearchCrew().crew().kickoff(
 inputs={"topic": topic}
 )
 jobs[job_id]["status"] = "completed"
 jobs[job_id]["result"] = result.raw
 jobs[job_id]["tokens"] = str(result.token_usage)
 except Exception as e:
 jobs[job_id]["status"] = "failed"
 jobs[job_id]["error"] = str(e)


@app.post("/research", response_model=CrewResponse)
async def start_research(
 request: CrewRequest, background_tasks: BackgroundTasks
):
 job_id = str(uuid.uuid4())
 jobs[job_id] = {"status": "queued", "topic": request.topic}
 background_tasks.add_task(run_crew, job_id, request.topic)
 return CrewResponse(job_id=job_id, status="queued")


@app.get("/status/{job_id}")
async def get_status(job_id: str):
 if job_id not in jobs:
 return {"error": "Job not found"}
 return jobs[job_id]


# Run with: uvicorn api_server:app --host 0.0.0.0 --port 8000

This deployment pattern gives you a REST API that accepts research topics, runs crew executions in the background, and provides status endpoints for monitoring progress. For production systems, replace the in-memory job store with Redis or PostgreSQL, add authentication, and implement proper logging. CrewAI’s execution is CPU-light (it is mostly waiting for LLM API responses), so a single server can handle dozens of concurrent crew runs.

CrewAI vs LangGraph vs AutoGen: Which Multi-Agent Framework to Choose

CrewAI is not the only multi-agent framework available. LangGraph and Microsoft’s AutoGen are the two main alternatives. Each framework has distinct strengths, and choosing the right one depends on your specific requirements. Here is how they compare on the dimensions that matter most for production use.

FeatureCrewAILangGraphAutoGen
Learning CurveLow – YAML + PythonMedium – graph conceptsMedium – conversation patterns
Agent DefinitionYAML config + decoratorsPython functions as nodesPython classes
OrchestrationSequential, hierarchical, or customDirected graph with cyclesConversation-based
Built-in Tools15+ (web, file, code, RAG)Via LangChain ecosystemCode execution, web
Structured OutputsNative Pydantic supportVia LangChainLimited
MemoryShort-term, long-term, entityState persistence via checkpointsConversation history
LangChain DependencyNone (fully independent)RequiredNone
Best ForRole-based agent teamsComplex stateful workflowsResearch and prototyping
Python Version3.10-3.133.9+3.8+

CrewAI’s biggest advantage is its role-based agent paradigm. By defining agents with roles, goals, and backstories, you get behavior that closely mirrors how human teams collaborate. LangGraph offers more control over execution flow through its graph-based approach but requires understanding directed graphs and state management concepts. AutoGen, developed by Microsoft, excels at conversational multi-agent patterns where agents communicate through natural language dialogue.

CrewAI is built entirely independent of LangChain or any other framework, which means fewer dependency conflicts and a smaller installation footprint. LangGraph, by contrast, requires the full LangChain ecosystem. For teams already invested in LangChain, LangGraph is a natural extension. For teams starting fresh or wanting minimal dependencies, CrewAI is typically the better choice.

Troubleshooting CrewAI: 10 Common Issues and Fixes

Even with careful setup, you will encounter issues when building multi-agent systems. Here are the most common problems developers face with CrewAI and their solutions.

IssueCauseSolution
ModuleNotFoundError: No module named ‘crewai’Wrong Python environment activeRun which python to verify, activate correct venv
AuthenticationError from LLM providerAPI key not set or expiredCheck echo $OPENAI_API_KEY, regenerate key if expired
Agent stuck in infinite loopmax_iter too high or task too vagueSet max_iter=10, make task description more specific
Token limit exceededContext window overflow from long outputsSet respect_context_window=True on agents
Tool execution timeoutExternal API slow or unreachableAdd timeout parameter to tool, implement retry logic
YAML parsing errorIndentation or special character issuesUse > for multiline strings, validate with yamllint
Task output not passed to next agentMissing context in task configAdd context: [previous_task_name] in tasks.yaml
Rate limit errors (429)Too many API requests per minuteSet max_rpm=20 on crew, add delays between agents
Memory database lockedConcurrent crew runs sharing SQLiteUse separate memory paths per crew instance
Structured output validation failsLLM output doesn’t match Pydantic schemaSimplify schema, add field descriptions, increase max_iter

Debugging tip: Set verbose=True on both your agents and crew to see full execution logs. CrewAI logs each agent’s thought process, tool calls, and intermediate outputs. For deeper debugging, enable Python logging at the DEBUG level: logging.basicConfig(level=logging.DEBUG). This reveals the exact prompts sent to the LLM and the raw responses received.

Performance tip: If your crew runs are slow, the bottleneck is almost always the LLM API response time. Profile your runs by checking result.token_usage – if prompt tokens far exceed completion tokens, your agent prompts may be overloaded with tool descriptions or context. Reduce tools per agent, simplify task descriptions, or switch to a faster model for non-critical agents.

Advanced Tips for Production CrewAI Systems

Once you have mastered the basics, these advanced patterns will help you build more reliable and efficient multi-agent systems. These techniques are drawn from production deployments and the CrewAI community’s best practices as of 2026.

Hierarchical Process with a Manager Agent

For complex projects where task order is not predetermined, use Process.hierarchical. CrewAI automatically creates a manager agent that delegates tasks to other agents based on their roles and capabilities. The manager decides which agent should handle each subtask and can reassign work if an agent’s output is unsatisfactory.

 @crew
 def crew(self) -> Crew:
 return Crew(
 agents=self.agents,
 tasks=self.tasks,
 process=Process.hierarchical,
 manager_llm="openai/gpt-4o", # Use a powerful model for management
 verbose=True,
 )

Conditional Task Execution

Use task callbacks to implement conditional logic. For example, skip the editing step if the writing output already meets quality thresholds:

def check_quality(output):
 """Callback that runs after a task completes."""
 word_count = len(output.raw.split())
 if word_count Task:
 return Task(
 config=self.tasks_config["writing_task"],
 callback=check_quality,
 )

Cost Optimization Strategies

Multi-agent systems can become expensive if not optimized. Use cheaper models for simple tasks (editing, formatting) and reserve expensive models for complex reasoning (research, analysis). Enable caching to avoid duplicate LLM calls – CrewAI caches tool results and can cache LLM responses when the same prompt is repeated. Monitor token usage per agent to identify which agents are consuming the most resources and optimize their prompts accordingly.

CrewAI Project Template: Complete Working Example

Here is the complete project structure with all files needed for a working CrewAI research crew. You can clone this pattern and customize it for any multi-agent use case. This template incorporates all the patterns covered in this tutorial – structured outputs, tools, memory, and error handling.

👁 CrewAI Project Template: Complete Working Example
# Complete pyproject.toml

[project]
name = "research_crew"
version = "0.1.0"
description = "Multi-agent research crew built with CrewAI"
requires-python = ">=3.10,=1.9.0",
 "python-dotenv>=1.0.0",
]

[project.scripts]
research_crew = "research_crew.main:run"
train = "research_crew.main:train"
# .env file (add to .gitignore!)

OPENAI_API_KEY=sk-your-key-here
SERPER_API_KEY=your-serper-key-here

With this template, you can get a complete multi-agent crew running in under 10 minutes. Start with the basic three-agent research crew, test it with a simple topic, then gradually add tools, structured outputs, and memory as your requirements grow. The YAML-based configuration means you can experiment with different agent personalities and task descriptions without modifying any Python code.

Related Coverage

More AI and Developer Tutorials

Frequently Asked Questions About CrewAI

What Python version does CrewAI require?

CrewAI requires Python 3.10 or higher and supports up to Python 3.13. The framework is not compatible with Python 3.9 or earlier. If you are using a system Python that is too old, install Python 3.12 or 3.13 using pyenv or your operating system’s package manager and create a virtual environment with the correct version.

Is CrewAI free to use?

Yes, CrewAI is an open-source Python framework available on PyPI and GitHub under the MIT license. You can use it for personal and commercial projects without any licensing fees. However, you will need to pay for the LLM API calls your agents make (OpenAI, Anthropic, etc.), unless you use free local models through Ollama.

How does CrewAI compare to LangGraph?

CrewAI and LangGraph take fundamentally different approaches. CrewAI uses a role-based paradigm where agents are defined by their roles, goals, and backstories. LangGraph uses a graph-based paradigm where agents are nodes in a directed graph with explicit edges defining execution flow. CrewAI is easier to learn and has no LangChain dependency, while LangGraph offers more granular control over execution flow and state management.

Can CrewAI work with local LLMs?

Yes, CrewAI supports local LLMs through Ollama integration. Set the model parameter to ollama/llama3.1 or any other Ollama-hosted model in your agent configuration. Local models are free to use but may produce lower-quality outputs compared to cloud models like GPT-4o or Claude Sonnet. For production use, cloud models are recommended for complex reasoning tasks while local models work well for simpler tasks like formatting or summarization.

How much does it cost to run a CrewAI crew?

Cost depends entirely on your LLM provider and the complexity of your tasks. A typical three-agent crew with GPT-4o consumes 15,000-25,000 tokens per run, costing approximately $0.10-$0.25. Using GPT-4o-mini for non-critical agents reduces costs by 60-80%. You can monitor exact token usage through the result.token_usage attribute returned by every crew execution.

Can I use CrewAI with Anthropic Claude models?

Yes, CrewAI supports Anthropic Claude models through its built-in LiteLLM integration. Set your ANTHROPIC_API_KEY environment variable and use the model string anthropic/claude-sonnet-4-20250514 or anthropic/claude-haiku-4-5-20251001 in your agent configuration. Claude models work with all CrewAI features including tools, structured outputs, and memory.

What is the difference between CrewAI Crews and Flows?

Crews define a group of agents that collaborate on tasks within a single execution context. Flows are higher-level orchestration layers that chain multiple crews together with conditional logic, state management, and event-driven triggers. Use Crews for single-purpose agent teams and Flows for multi-stage pipelines where you need control over the execution path between stages.

How do I debug CrewAI agents that produce poor results?

Start by setting verbose=True on both agents and the crew to see the full reasoning chain. Check that task descriptions are specific enough – vague instructions produce vague outputs. Verify that tasks are properly linked with context in your YAML configuration. If an agent is ignoring tool results, check that the tool’s description clearly explains when to use it. Increasing max_iter gives agents more reasoning steps but also increases costs.

👁 Sofia Lindström

Sofia Lindström

Editor-in-Chief

Sofia Lindström is the Editor-in-Chief at Tech Insider, where she leads editorial strategy and oversees coverage across AI, cybersecurity, and enterprise technology. With over a decade in Swedish tech journalism, she previously served as technology editor at Dagens Industri and covered the Nordic startup ecosystem for Breakit. Sofia holds an MSc in Media Technology from KTH Royal Institute of Technology and is a frequent speaker at Web Summit and Slush. She is passionate about making complex technology accessible to business leaders.

View all articles
👁 Tech Insider
Tech
Insider

Tech Insider delivers in-depth coverage of the technologies shaping the future: AI, cybersecurity, cloud computing, hardware, and the trends that matter.

Company

Explore

Categories

© 2026 Tech Insider Media AB. All rights reserved.