CrewAI Multi-Agent Teams: Building Workflows (2026)
CrewAI simplifies multi-agent coordination by letting you define agents with specialized roles and assign them tasks with explicit dependencies. Unlike LangGraph's explicit state graphs, CrewAI uses a task-based model: you describe what you want done (the task), assign it to an agent, and the framework orchestrates the agent's internal loop (reason, decide on tools, execute, loop until done). This article shows how to build a content creation team (researcher, writer, editor) that collaborates to produce a polished article.
Agent Design: Role, Goal, and Expertise
An agent in CrewAI is defined by its role, goal, and tools. The role (e.g., "Research Analyst") shapes the system prompt; the goal articulates what success looks like; tools define what the agent can do.
from crewai import Agent
from langchain_anthropic import ChatAnthropic
researcher = Agent(
role="Senior Research Analyst",
goal="Gather comprehensive, accurate information from reliable sources.",
backstory=(
"You are an expert researcher with 15 years of experience in data "
"analysis and information synthesis. You prioritize accuracy and cite sources."
),
tools=[web_search_tool, document_reader_tool],
llm=ChatAnthropic(model="claude-3-5-sonnet-20241022"),
verbose=True, # Log internal reasoning
max_iterations=5, # Prevent infinite loops within the agent
)
writer = Agent(
role="Content Writer",
goal="Produce clear, engaging, SEO-friendly content.",
backstory=(
"You are a professional writer with expertise in technical documentation "
"and blog content. You write for clarity and engagement."
),
tools=[], # Writer doesn't need tools; it consumes research output
llm=ChatAnthropic(model="claude-3-5-sonnet-20241022"),
verbose=True,
)
editor = Agent(
role="Editorial Director",
goal="Ensure content is polished, factually accurate, and on-brand.",
backstory=(
"You are an experienced editor with a keen eye for clarity, tone, "
"and technical accuracy. You catch errors others miss."
),
tools=[web_search_tool], # Search to fact-check claims
llm=ChatAnthropic(model="claude-3-5-sonnet-20241022"),
verbose=True,
)
The backstory field is critical—it shapes the agent's expertise and personality, improving the quality of reasoning and decision-making. A well-written backstory is worth 10+ additional examples in prompt tuning.
Defining Tasks
Tasks describe what you want an agent to do. A task has a description (what to do), expected output (format/structure), and an agent (who does it). Tasks can depend on previous tasks—CrewAI will pass the output of one task as context to the next.
from crewai import Task
research_task = Task(
description=(
"Research the topic: 'AI Safety Frameworks in 2026'. Find at least 5 "
"credible sources (academic papers, industry reports, documentation). "
"Summarize the key frameworks, their objectives, and trade-offs."
),
expected_output="A detailed research report with citations and source links.",
agent=researcher,
output_file="research_report.md" # Save output for later reference
)
writing_task = Task(
description=(
"Based on the research provided, write a comprehensive 2000-word article "
"on AI Safety Frameworks. Structure it with an introduction, 5 major sections, "
"and a conclusion. Use the research findings to support claims."
),
expected_output="A well-structured article in Markdown format.",
agent=writer,
context=[research_task], # Use research_task's output as context
output_file="draft_article.md"
)
editing_task = Task(
description=(
"Review the article for clarity, accuracy, and engagement. Check that "
"all claims are supported by the research. Suggest improvements for tone, "
"structure, and readability. Fact-check any statistics mentioned."
),
expected_output="An edited article with inline comments and a summary of changes.",
agent=editor,
context=[research_task, writing_task], # Provide all prior outputs
output_file="final_article.md"
)
The context field passes previous task outputs to the agent as background. This lets agents build on each other's work without explicit message passing.
Assembling a Crew
A crew is a collection of agents and tasks. When you call crew.kickoff(), CrewAI executes tasks in dependency order, managing agent handoffs.
from crewai import Crew
crew = Crew(
agents=[researcher, writer, editor],
tasks=[research_task, writing_task, editing_task],
verbose=2, # 0 = silent, 1 = minimal, 2 = detailed logs
max_iterations=15 # Max total iterations across all agents
)
# Execute the crew
result = crew.kickoff()
print("Final Output:")
print(result)
CrewAI automatically:
- Executes research_task with the researcher agent
- Passes the output to writing_task, which runs with the writer agent
- Passes both previous outputs to editing_task, which runs with the editor agent
- Returns the final result
No explicit message passing or state management—the framework handles it.
Tool Binding and Integration
Tools are how agents interact with the outside world. CrewAI uses LangChain's tool format (Python functions with docstrings that define schema).
from langchain.tools import tool
import requests
@tool("web_search")
def web_search_tool(query: str) -> str:
"""Search the web for information about a topic.
Args:
query: The search query string.
Returns:
A string containing search results and snippets.
"""
# In production, use Tavily, SerpAPI, or similar
results = requests.get(
"https://api.tavily.com/search",
params={"api_key": "YOUR_API_KEY", "query": query}
).json()
formatted = ""
for result in results.get("results", [])[:5]:
formatted += f"Title: {result['title']}\nURL: {result['url']}\nSnippet: {result['content']}\n\n"
return formatted
@tool("document_reader")
def document_reader_tool(file_path: str) -> str:
"""Read and summarize a document (PDF, TXT, MD).
Args:
file_path: Path to the document to read.
Returns:
The document content or a summary.
"""
with open(file_path, 'r') as f:
content = f.read()
# For large documents, you'd summarize here
return content[:2000] + "..." if len(content) > 2000 else content
Assign tools to agents via the tools parameter. An agent can use any tool assigned to it. The framework auto-generates JSON schemas from tool signatures so Claude can invoke them.
Memory and Persistence
CrewAI includes a memory system (short-term and long-term). By default, memory is in-memory (lost on restart). For production, configure a persistent backend.
from crewai.memory import EntityMemory, ShortTermMemory, LongTermMemory
# In-memory memory (default for demos)
short_memory = ShortTermMemory()
long_memory = LongTermMemory()
entity_memory = EntityMemory()
crew = Crew(
agents=[researcher, writer, editor],
tasks=[research_task, writing_task, editing_task],
memory=short_memory, # Store conversation history
verbose=2
)
# For persistent memory, use a custom backend
# (CrewAI docs show examples with Chroma, Milvus, etc.)
Memory lets agents reference past information within a single run. For cross-session persistence, you'd need to save the crew state manually or use external storage.
Async and Parallel Execution
By default, CrewAI executes tasks sequentially. For independent tasks, you can parallelize.
# Sequential (default): task1 → task2 → task3
result = crew.kickoff()
# For independent tasks, use task processes
# This is advanced and less commonly needed; check CrewAI docs for details
Most content workflows are sequential (research → write → edit), so sequential execution is appropriate.
Handling Failures and Retries
Agents sometimes fail (tool errors, timeouts, poor reasoning). CrewAI handles retries internally (via max_iterations on agents), but you should also handle task-level failures.
try:
result = crew.kickoff()
except Exception as e:
print(f"Crew execution failed: {e}")
# Fallback logic here
If a task times out, you can catch the exception and either retry or skip the task. More sophisticated error handling (retry with modified prompt, escalate to human) requires custom code.
Key Takeaways
- Agents have roles, goals, and tools; they act autonomously within a task.
- Tasks describe goals and expected outputs; dependencies ensure handoffs between agents.
- Crews orchestrate agents and tasks; the framework manages execution order and context passing.
- Memory is available but requires custom backends for production persistence.
- Tools are LangChain-compatible functions; agents auto-bind and invoke them.
Frequently Asked Questions
Can an agent use a tool multiple times in one task?
Yes. An agent will loop within its max_iterations, deciding whether to call a tool again or move on. If a tool call fails, the agent can retry or try a different approach.
How do I monitor agent progress?
Set verbose=2 on the crew. This logs all agent reasoning, tool calls, and results. For production, use a logging framework and capture agent logs to a file or service.
What's the difference between agent goal and task description?
The agent's goal is its overarching objective ("produce accurate research"). The task's description is a specific job within that goal ("research AI safety frameworks"). One agent can execute multiple tasks over a conversation.
Can I run agents in parallel?
Not directly by default in CrewAI. Sequential is the norm. If you need parallelism, use async execution or run separate crews in different processes (advanced).
How expensive is a multi-agent crew?
A 3-agent crew researching and writing an article costs roughly 10-15 cents (Claude 3.5 Sonnet, 2026 prices). Costs rise with task complexity (more tool calls, larger context). Budget for 5-20 model calls per task.