CrewAI takes a different approach to agents than most frameworks. Instead of building a single agent with tools, you define a team of agents – each with a specific role, goal, and backstory – and let them collaborate on a set of tasks. Think of it like assigning a project to a small team where each person has a clear job.

The framework handles delegation, context passing between agents, and process orchestration. You pick whether agents work sequentially (assembly line) or hierarchically (manager delegates to workers). As of early 2026, CrewAI is at version 0.86+ and runs on Python 3.10 through 3.13.

Install CrewAI

1
pip install crewai crewai-tools

The crewai package is the core framework. crewai-tools gives you pre-built tools for web search, file reading, and more. If you want the CLI for scaffolding projects:

1
pip install crewai[tools]

You need an LLM provider key. CrewAI defaults to OpenAI, but supports Anthropic, Ollama, and any OpenAI-compatible endpoint:

1
export OPENAI_API_KEY="sk-your-key-here"

To use a different provider, set the llm parameter on each agent or configure it globally.

Build a Research and Writing Crew

Here is a complete working example: two agents that research a topic and then write a blog post about it. The researcher gathers information, and the writer turns that research into polished content.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool

# Tool for web search (requires SERPER_API_KEY env var)
search_tool = SerperDevTool()

# Define the researcher agent
researcher = Agent(
    role="Senior Research Analyst",
    goal="Find accurate, up-to-date information about {topic}",
    backstory=(
        "You're a seasoned research analyst with 15 years of experience "
        "in technology journalism. You're known for digging beyond surface-level "
        "results and finding primary sources."
    ),
    tools=[search_tool],
    verbose=True,
    allow_delegation=False,
)

# Define the writer agent
writer = Agent(
    role="Technical Content Writer",
    goal="Write a clear, engaging article about {topic} based on the research provided",
    backstory=(
        "You're a technical writer who specializes in making complex topics "
        "accessible. You write in a direct, conversational style and always "
        "back up claims with specifics from the research."
    ),
    verbose=True,
    allow_delegation=False,
)

# Define tasks
research_task = Task(
    description=(
        "Research {topic} thoroughly. Find the latest developments, "
        "key players, technical details, and real-world applications. "
        "Focus on facts and data, not opinions."
    ),
    expected_output=(
        "A detailed research brief with key findings, statistics, "
        "and source URLs organized by subtopic."
    ),
    agent=researcher,
)

writing_task = Task(
    description=(
        "Using the research brief, write a 500-word article about {topic}. "
        "Include an opening hook, 3 main sections with subheadings, "
        "and a practical takeaway for the reader."
    ),
    expected_output="A polished article in markdown format, ready to publish.",
    agent=writer,
)

# Assemble the crew
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, writing_task],
    process=Process.sequential,
    verbose=True,
)

# Run it
result = crew.kickoff(inputs={"topic": "AI agents in production"})
print(result)

The {topic} placeholder gets replaced at runtime with whatever you pass to kickoff(inputs=...). The sequential process means the researcher finishes first, and its output automatically becomes context for the writer’s task.

Agent Anatomy

Every CrewAI agent has three required fields that shape its behavior:

  • role – What the agent does. This appears in prompts and tells the LLM what persona to adopt. Be specific: “Senior Data Analyst” works better than “Analyst.”
  • goal – What the agent is trying to achieve. This drives the agent’s decision-making. Make it concrete and measurable.
  • backstory – Context that shapes how the agent approaches problems. This is where you inject domain expertise, working style, and constraints.

Optional Agent Parameters

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
agent = Agent(
    role="QA Engineer",
    goal="Review the article for factual accuracy and clarity",
    backstory="You've spent a decade in technical editing...",
    llm="gpt-4o",                    # Override default model
    max_iter=5,                       # Max reasoning iterations
    max_rpm=10,                       # Rate limit for API calls
    allow_delegation=True,            # Can delegate to other agents
    memory=True,                      # Enable long-term memory
    cache=True,                       # Cache tool results
)

Setting allow_delegation=True lets an agent hand off subtasks to other crew members. This is powerful in hierarchical crews but can cause unexpected loops in sequential ones. Start with it disabled and enable it only when you need it.

Process Types

CrewAI supports two process types that control how agents interact.

Sequential

1
2
3
4
5
crew = Crew(
    agents=[researcher, writer, editor],
    tasks=[research_task, writing_task, editing_task],
    process=Process.sequential,
)

Tasks execute one after another. Each task’s output feeds into the next as context. This is the default and works for most pipelines where order matters.

Hierarchical

1
2
3
4
5
6
crew = Crew(
    agents=[researcher, writer, editor],
    tasks=[research_task, writing_task, editing_task],
    process=Process.hierarchical,
    manager_llm="gpt-4o",
)

A manager agent (automatically created) coordinates the team. It decides which agent handles each task, reviews outputs, and can ask agents to redo work. You must specify manager_llm for this process type. Hierarchical works well when tasks have dependencies that aren’t strictly linear – say, when the editor might send the writer back to revise a section.

Adding Custom Tools

CrewAI tools are simple to build. Any class that extends BaseTool works:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from crewai_tools import BaseTool

class PriceLookup(BaseTool):
    name: str = "Price Lookup"
    description: str = "Looks up the current price of a product by name."

    def _run(self, product_name: str) -> str:
        # Replace with actual API call
        prices = {"widget-a": "$29.99", "widget-b": "$49.99"}
        return prices.get(product_name.lower(), "Price not found")

# Attach to an agent
pricing_agent = Agent(
    role="Pricing Specialist",
    goal="Find accurate pricing for products",
    backstory="You verify product pricing across multiple sources.",
    tools=[PriceLookup()],
)

You can also wrap plain functions with the @tool decorator:

1
2
3
4
5
6
7
from crewai_tools import tool

@tool("Stock Price Checker")
def check_stock(ticker: str) -> str:
    """Check the current stock price for a given ticker symbol."""
    # Your API call here
    return f"{ticker}: $142.50"

Common Errors and Fixes

pydantic ValidationError on Agent Creation

1
2
3
pydantic.error_wrappers.ValidationError: 1 validation error for Agent
role
  field required (type=value_error.missing)

You left out a required field. Every agent needs role, goal, and backstory. All three are mandatory – skip one and Pydantic rejects the entire object.

Rate Limit Errors from Your LLM Provider

1
openai.RateLimitError: Error code: 429 - Rate limit reached for gpt-4o

CrewAI agents can fire many LLM calls in quick succession, especially with verbose=True and multiple agents. Use max_rpm on your agents to throttle them:

1
2
3
4
5
6
agent = Agent(
    role="Researcher",
    goal="...",
    backstory="...",
    max_rpm=10,  # Max 10 requests per minute
)

For longer crews, also consider setting a cheaper model for agents that do simpler work. Your researcher might need GPT-4o, but your formatter probably does fine with GPT-4o-mini.

Task Output is Empty or Generic

If an agent returns vague output like “Here is the research” without substance, tighten up your expected_output field. CrewAI uses this string to validate and guide the agent’s response. Be explicit:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Too vague
expected_output="A summary of the research"

# Much better
expected_output=(
    "A 300-word research brief containing: "
    "3 key statistics with sources, "
    "names of major companies involved, "
    "and a timeline of recent developments."
)

ModuleNotFoundError: No module named ‘crewai_tools’

1
ModuleNotFoundError: No module named 'crewai_tools'

You installed crewai but not the tools package. They are separate:

1
pip install crewai crewai-tools

CrewAI vs. LangGraph

Both frameworks build multi-step agent systems, but they solve different problems. CrewAI gives you role-based agents with built-in collaboration patterns – you define who does what and the framework handles the rest. LangGraph gives you a state machine where you control every edge and node.

Pick CrewAI when you want to prototype agent teams quickly and your workflow maps naturally to roles and tasks. Pick LangGraph when you need fine-grained control over execution flow, custom state management, or complex branching logic.

They are not mutually exclusive. Some teams use CrewAI for high-level orchestration and LangGraph for individual agents that need sophisticated internal logic.

Production Tips

Pin your CrewAI version. The API has changed significantly between releases. Put crewai==0.86.0 (or whatever you tested with) in your requirements.txt so deploys don’t break.

Use callbacks for monitoring. CrewAI supports step and task callbacks that let you log progress, track token usage, and detect stuck agents:

1
2
3
4
5
6
7
8
9
def task_callback(output):
    print(f"Task finished: {output.description[:50]}...")
    print(f"Raw output length: {len(output.raw)}")

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

Keep crews small. Three to five agents is the sweet spot. More agents means more LLM calls, higher latency, and harder debugging. If you need more, split into multiple crews and chain them.

Test agents individually first. Before assembling a crew, run each agent on its task alone. This isolates prompt quality issues from orchestration problems.