Most agent frameworks fall into two camps. Either they treat the LLM as the runtime and hide everything else, or they ask you to wire every primitive yourself. CrewAI sits in a useful middle: agents have roles, tasks have expected outputs, and a crew coordinates the work. This guide walks through building a real research-and-writing crew from scratch, with the diagrams and code you need to ship it.
01 What you will build
The example throughout this guide is a simple research-and-writing crew with three specialized agents working in sequence.
- A Researcher Agent gathers information on a topic.
- A Writer Agent turns the research into a blog draft.
- A Reviewer Agent checks clarity, structure, and accuracy.
- A Crew coordinates the work in sequence and produces the final output.
It is small on purpose. Once this skeleton works, every other CrewAI concept (tools, memory, flows, hierarchical execution) snaps onto the same structure.
02 The mental model
Before writing code, hold this picture in your head. A crew accepts a user input, dispatches work to agents, each agent runs its task, and outputs flow forward through the pipeline. Tools are optional capabilities attached to specific agents.
flowchart TD
User[User Input] --> Crew[Crew]
Crew --> Agent1[Researcher Agent]
Crew --> Agent2[Writer Agent]
Crew --> Agent3[Reviewer Agent]
Agent1 --> Task1[Research Task]
Agent2 --> Task2[Writing Task]
Agent3 --> Task3[Review Task]
Agent1 --> Tools[Tools]
Tools --> WebSearch[Web Search]
Tools --> Files[File Reader]
Tools --> APIs[External APIs]
Task1 --> Output1[Research Notes]
Output1 --> Task2
Task2 --> Output2[Blog Draft]
Output2 --> Task3
Task3 --> Final[Final Answer]
The five primitives to remember:
- Agent. A specialized worker with a role, a goal, and a backstory.
- Task. A specific assignment with a description and an expected output.
- Tool. A capability the agent can call (search, file read, API).
- Crew. The orchestrator that assembles agents and tasks and runs them.
- Flow. A higher-level controller for branching, state, and multi-crew workflows.
03 Install CrewAI
CrewAI ships with a CLI that scaffolds a working project. A generated project includes agents.yaml, tasks.yaml, a .env file, main.py, crew.py, and directories for tools and knowledge.
uv tool install crewai crewai create crew research_writer cd research_writer crewai install
To run the generated crew:
crewai run
Most projects want the extra tools package as well. It bundles web search, file readers, and other common adapters.
pip install 'crewai[tools]'
04 Define your agents
An agent in CrewAI is defined by three attributes the model uses to stay consistent: a role, a goal, and a backstory. Optional attributes cover the LLM choice, tools, max iterations, and rate limits.
Open src/research_writer/config/agents.yaml:
researcher:
role: >
Senior AI Researcher
goal: >
Find reliable, concise, and relevant information about the assigned topic.
backstory: >
You are an experienced technology researcher who specializes in identifying
practical trends, useful examples, and credible sources.
writer:
role: >
Technical Blog Writer
goal: >
Turn research notes into a clear, engaging, beginner-friendly blog post.
backstory: >
You write practical technical content for developers and product teams.
You explain complex ideas with examples, structure, and plain language.
reviewer:
role: >
Editorial Reviewer
goal: >
Improve the blog for accuracy, readability, structure, and usefulness.
backstory: >
You are a careful editor who checks whether the final article is clear,
complete, and actionable.
The role tells the agent what hat it wears. The goal tells it what success looks like. The backstory shapes voice and judgment. All three matter. Vague roles produce vague work.
05 Define tasks
A CrewAI task is a specific assignment owned by an agent. It carries a description, an expected output, a responsible agent, and optionally a set of tools. Tasks can run sequentially or hierarchically depending on the crew process.
Open src/research_writer/config/tasks.yaml:
research_task:
description: >
Research the topic: {topic}.
Find the main concepts, practical examples, common mistakes, and a simple
explanation suitable for beginners.
expected_output: >
A structured research brief with bullet points, definitions, examples,
and warnings about common mistakes.
agent: researcher
writing_task:
description: >
Use the research brief to write a complete blog post about {topic}.
The blog should include an introduction, step-by-step guidance, examples,
and a conclusion.
expected_output: >
A complete markdown blog post.
agent: writer
review_task:
description: >
Review the blog post for clarity, accuracy, structure, and usefulness.
Improve weak sections and return the final polished version.
expected_output: >
A polished final markdown blog post ready to publish.
agent: reviewer
The single most important field is expected_output. It tells the agent what "done" looks like.
06 Sequential task flow
When you use the sequential process, each task runs in order and its output becomes context for the next. The crew is the orchestrator that owns the inputs, the dispatch, and the final return.
sequenceDiagram
participant U as User
participant C as Crew
participant R as Researcher
participant W as Writer
participant E as Reviewer
U->>C: Provide topic
C->>R: Run research_task
R-->>C: Research brief
C->>W: Run writing_task with research context
W-->>C: Blog draft
C->>E: Run review_task
E-->>C: Final polished blog
C-->>U: Return final output
Sequential is the right default when each task depends on the previous one. Switch to hierarchical when you want a manager agent to route work based on role and expertise.
07 Wire agents and tasks in crew.py
The crew.py module loads the YAML configuration and assembles the agents, tasks, and process. CrewAI provides decorators that keep the wiring declarative.
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
@CrewBase
class ResearchWriterCrew:
"""Research and writing crew."""
agents_config = "config/agents.yaml"
tasks_config = "config/tasks.yaml"
@agent
def researcher(self) -> Agent:
return Agent(
config=self.agents_config["researcher"],
verbose=True,
)
@agent
def writer(self) -> Agent:
return Agent(
config=self.agents_config["writer"],
verbose=True,
)
@agent
def reviewer(self) -> Agent:
return Agent(
config=self.agents_config["reviewer"],
verbose=True,
)
@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 review_task(self) -> Task:
return Task(
config=self.tasks_config["review_task"],
)
@crew
def crew(self) -> Crew:
return Crew(
agents=[
self.researcher(),
self.writer(),
self.reviewer(),
],
tasks=[
self.research_task(),
self.writing_task(),
self.review_task(),
],
process=Process.sequential,
verbose=True,
)
08 Run the crew
The entry point is small. It builds inputs, kicks off the crew, and prints the result. Anything more interesting belongs in a Flow (see Section 11).
from research_writer.crew import ResearchWriterCrew
def run():
inputs = {
"topic": "How to build AI agents with CrewAI"
}
result = ResearchWriterCrew().crew().kickoff(inputs=inputs)
print(result)
if __name__ == "__main__":
run()
Then run:
crewai run
09 Add tools
Tools let agents take actions beyond plain text generation. CrewAI ships with adapters for web search, file reading, data analysis, and API calls, and you can write your own for custom business logic.
The researcher agent is a natural fit for search and file tools. Give the writer and reviewer fewer tools so they stay focused on their job.
from crewai import Agent
from crewai_tools import SerperDevTool, FileReadTool
search_tool = SerperDevTool()
file_tool = FileReadTool()
researcher = Agent(
role="Senior AI Researcher",
goal="Find reliable information about the assigned topic",
backstory="You are a careful researcher who checks multiple sources.",
tools=[search_tool, file_tool],
verbose=True,
)
10 Agent and tool interaction
When an agent encounters a step it cannot answer from context, it decides whether to call a tool. The tool returns data, the agent reasons over the result, and the loop continues until the task is satisfied.
flowchart LR
Agent[Researcher Agent] --> Decision{Need external info?}
Decision -->|Yes| Tool[Search Tool]
Tool --> Results[Search Results]
Results --> Agent
Decision -->|No| Reason[Reason with existing context]
Reason --> Output[Research Brief]
Agent --> Output
Two rules of thumb. First, give each agent the smallest set of tools that lets it succeed. Every extra tool adds reasoning surface and cost. Second, set a sensible max iterations cap. Agents that loop forever are the most common source of runaway bills.
11 Crews vs Flows
CrewAI has two orchestration ideas. They are not competitors. They are different layers.
Crews are best when you want autonomous collaboration between role-based agents. The crew picks the order, dispatches work, and aggregates output.
Flows are better when you need structured, event-driven control: state management, branching, retries, checkpoints, human review, or multiple crews working together. Flows are the production wrapper around crews.
flowchart TD
Start[Application Starts] --> Flow[Flow Controls Execution]
Flow --> SetTopic[Set Topic]
SetTopic --> RunCrew[Kick Off Crew]
RunCrew --> Researcher[Researcher]
RunCrew --> Writer[Writer]
RunCrew --> Reviewer[Reviewer]
Reviewer --> Blog[Final Blog]
Blog --> Save[Save to File]
Save --> End[Done]
12 Example flow pattern
A Flow is a class with decorated methods. @start marks the entry point. @listen wires up the next step. State is carried on self.state and persists across steps.
from crewai.flow.flow import Flow, start, listen
from research_writer.crew import ResearchWriterCrew
class BlogFlow(Flow):
@start()
def set_topic(self):
self.state["topic"] = "How to build AI agents with CrewAI"
return self.state["topic"]
@listen(set_topic)
def run_blog_crew(self, topic):
result = ResearchWriterCrew().crew().kickoff(
inputs={"topic": topic}
)
self.state["blog"] = str(result)
return self.state["blog"]
@listen(run_blog_crew)
def save_blog(self, blog):
with open("blog.md", "w", encoding="utf-8") as file:
file.write(blog)
return "Blog saved to blog.md"
def kickoff():
BlogFlow().kickoff()
Use Flows the moment your application needs branching, retries, scheduled checkpoints, human review, or coordination across more than one crew. A single sequential crew can run inside a Flow. A Flow cannot run inside a crew.
13 Production checklist
The path from "the demo worked" to "this runs in production" is the same for every CrewAI project. Walk through these phases before you ship.
flowchart TD
A[Define Business Goal] --> B[Break Goal into Tasks]
B --> C[Design Specialized Agents]
C --> D[Assign Tools Carefully]
D --> E[Choose Process: Sequential or Hierarchical]
E --> F[Add Guardrails]
F --> G[Test with Real Inputs]
G --> H[Log Outputs and Failures]
H --> I[Deploy and Monitor]
Before deploying, verify:
- Each agent has one clear responsibility.
- Each task has a measurable expected output.
- Tools are only given to agents that need them.
- Secrets are stored in
.env, not in code. - Outputs are reviewed before being used in high-risk workflows.
- Long-running or branching workflows use Flows instead of a bare crew.
- Iteration caps, timeouts, and cost limits are set on every agent.
14 Common mistakes
Most CrewAI projects fail for the same handful of reasons. They are easy to fix once you can name them.
Creating too many agents
Start with two or three. Add a new agent only when an existing one cannot reasonably cover the responsibility. Five agents that overlap are worse than three that are sharply defined.
Writing vague goals
Bad:
goal: Help with research.
Better:
goal: Find reliable information, summarize key points, and identify practical examples.
Giving every agent every tool
Tools increase power and complexity. They also increase cost. Every tool an agent can see is a tool it might try to use. Be deliberate.
Skipping expected outputs
The expected_output field is how the agent knows when to stop. Use it to define format, length, structure, and quality. Without it, agents either over-deliver or under-deliver, never consistently the right shape.
15 Final architecture
Pulling it all together, here is what a production-ready CrewAI project looks like on disk and at runtime.
flowchart TB
User[User] --> Input[Topic Input]
Input --> Main[main.py]
Main --> CrewFile[crew.py]
CrewFile --> AgentsYAML[agents.yaml]
CrewFile --> TasksYAML[tasks.yaml]
AgentsYAML --> Researcher[Researcher Agent]
AgentsYAML --> Writer[Writer Agent]
AgentsYAML --> Reviewer[Reviewer Agent]
TasksYAML --> ResearchTask[Research Task]
TasksYAML --> WritingTask[Writing Task]
TasksYAML --> ReviewTask[Review Task]
Researcher --> ResearchTask
ResearchTask --> WritingTask
Writer --> WritingTask
WritingTask --> ReviewTask
Reviewer --> ReviewTask
ReviewTask --> Output[Final Blog Post]
16 Conclusion
CrewAI gives you a structured way to build multi-agent systems without turning your codebase into a pile of prompts. The basic pattern is simple. Define agents, assign tasks, assemble a crew, give agents tools when needed, and use Flows when the workflow needs more control.
For a first project, build the small sequential crew above: researcher, writer, reviewer. Once it works on real inputs, add tools, then memory, then guardrails, then Flows. That path keeps your agent system understandable while still leaving room to grow into production-grade automation.
Agents are easy to demo. Production agents are software systems. The framework only helps if you treat it that way.