One agent holding fifteen tools and a 2,000-word system prompt tends to get worse, not better. The context fills with instructions it half-follows, and every new capability dilutes the last. A multi-agent system fixes this by splitting the job across role-scoped agents that each carry a small, sharp brief. CrewAI is one of the cleanest ways to build that in Python: you describe agents and tasks in YAML, wire them into a crew, and call crew().kickoff(). This guide takes you from an empty folder to a running crew.
Key Takeaways
- The short answer: run
uv tool install crewai, thencrewai create crew my_project, define your agents inagents.yamland tasks intasks.yaml, and launch withcrewai run. - CrewAI is a standalone framework with no LangChain dependency, so its agent, task, and crew objects are the whole model you need to learn.
- Agents carry a role, goal, and backstory; tasks carry a description and an expected output; a crew binds them with a process that decides execution order.
- Use the sequential process for linear pipelines and the hierarchical process when a manager agent should delegate and review.
- Recent CLI versions scaffold a JSON layout by default; pass
--classicto get the stable YAML and decorator structure this guide uses.
What Do You Need Before You Start?
Keep the prerequisites short so you can get to the interesting part.
- Python 3.10 to 3.13. CrewAI requires
>=3.10and<3.14. Check withpython3 --version. - An LLM API key. This guide uses OpenAI, but CrewAI speaks to most providers through LiteLLM.
- A Serper API key if you want your agent to search the web. It is optional but makes the example useful.
- Comfort running commands in a terminal. No web UI is involved.
How Do You Build and Run a CrewAI Crew?
These seven steps produce a research crew that searches the web and writes a report to disk. You can finish the whole task from this section.
- Install the CrewAI CLI. The maintainers recommend the
uvpackage manager, which keeps each project isolated.
Confirm it landed withcurl -LsSf https://astral.sh/uv/install.sh | sh uv tool install crewaicrewai version. - Scaffold a project. This creates the folder structure, a virtual environment target, and starter config files.
Thecrewai create crew research_crew --classic cd research_crew--classicflag gives you the YAML and Python layout below. Newer CLI builds default to a JSON layout; the YAML structure is the one most tutorials and Stack Overflow answers still assume, so it is the safer place to learn. - Add your API keys. Open the generated
.envat the project root and set your provider keys.
TheOPENAI_API_KEY=sk-your-key MODEL=openai/gpt-4o-mini SERPER_API_KEY=your-serper-keyMODELstring uses the provider/model format LiteLLM expects. Swap inanthropic/claude-3-5-sonnet-latestorollama/llama3.1without touching any other code. - Define your agents. Open
src/research_crew/config/agents.yaml. Each agent is a role, a goal, and a backstory. The backstory is not decoration; it shapes tone and how the agent reasons. Any{topic}placeholder is filled from the inputs you pass at run time.researcher: role: > {topic} Senior Research Analyst goal: > Find the most current, credible facts about {topic} backstory: > You dig past press releases to primary sources and flag anything you cannot verify. writer: role: > Technical Writer goal: > Turn raw research on {topic} into a clear briefing backstory: > You write for busy engineers. Short sentences, no filler, every claim traceable to the research. - Define your tasks. Open
src/research_crew/config/tasks.yaml. A task needs a description, anexpected_outputthat sets the bar, and the agent that owns it. Writeexpected_outputlike an acceptance test, because the model treats it as one.
Theresearch_task: description: > Research {topic}. Use web search for current sources. Note the date and origin of every important fact. expected_output: > A bulleted brief of 8 to 12 findings, each with a source. agent: researcher writing_task: description: > Using the research, write a one-page briefing on {topic}. expected_output: > A markdown briefing with a summary and three sections. agent: writer context: - research_task output_file: output/report.mdcontextkey hands the researcher's output to the writer, which is how information moves between agents. - Wire the crew. Open
src/research_crew/crew.py. The@CrewBaseclass turns your YAML into live objects. Each@agentand@taskmethod reads its config by key, and the@crewmethod sets the process.
Thefrom crewai import Agent, Crew, Process, Task from crewai.project import CrewBase, agent, crew, task from crewai_tools import SerperDevTool @CrewBase class ResearchCrew: agents_config = "config/agents.yaml" tasks_config = "config/tasks.yaml" @agent def researcher(self) -> Agent: return Agent( config=self.agents_config["researcher"], tools=[SerperDevTool()], verbose=True, ) @agent def writer(self) -> Agent: return Agent(config=self.agents_config["writer"], 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"]) @crew def crew(self) -> Crew: return Crew( agents=self.agents, tasks=self.tasks, process=Process.sequential, verbose=True, )self.agentsandself.taskslists are assembled for you from the decorated methods, in the order you declared them. - Install dependencies and run. The entry point in
src/research_crew/main.pypasses your inputs into the{topic}placeholders.
Then from the project root:# main.py already contains a run() like this def run(): ResearchCrew().crew().kickoff(inputs={"topic": "CrewAI in production"})
Watch the trace scroll by, and find your briefing atcrewai install crewai runoutput/report.md.
Which Orchestration Should You Choose?
The process you set on the crew decides how work flows between agents. Three patterns cover almost every case, and picking the wrong one is the most common reason a crew misbehaves.
| Approach | How work is routed | State handling | Best for |
|---|---|---|---|
| Sequential process | Tasks run top to bottom; each can inherit the previous output through context | Implicit, passed task to task | Linear pipelines: research then write then review |
| Hierarchical process | A manager agent plans, delegates to workers, and checks their results | Held by the manager agent | Open-ended goals where the split of work is not known up front |
| Flows | You write Python with @start and @listen decorators that call crews and plain functions | Explicit, in a typed state object you control | Production apps needing branching, loops, and auditable state |
Start with the sequential process. Reach for the hierarchical process only when you genuinely cannot pre-write the task list, since a manager agent adds latency and its own failure modes. When you need branching logic, retries, or human approval steps, graduate to Flows, which wrap crews inside real control flow.
How Do You Give an Agent Tools?
A tool is anything an agent can call: web search, a database query, a Python function. CrewAI ships a library of them in the crewai_tools package, and SerperDevTool in the example is one. You attach tools to the agent that should use them, and the model decides when to call them based on the task.
To write your own, subclass BaseTool, give it a name and a description the model can read, and implement _run. The description matters more than the code; it is the only thing the agent sees when choosing whether to reach for the tool.
What Goes Wrong, and How Do You Fix It?
These are the errors most people hit in the first hour.
- The scaffold looks nothing like this guide. Recent CLI versions default to a JSON layout. Recreate with
crewai create crew name --classicto get the YAML and decorator structure, or adapt the same concepts to the JSON files. - KeyError on an agent or task name. The string you pass to
self.agents_config[...]must match the top-level key in the YAML exactly. A trailing space or a typo breaks the lookup. - The agent ignores the tool. Confirm the tool is in that agent's
tools=[...]list, and that its description states plainly when to use it. Vague descriptions get skipped. - litellm.AuthenticationError. The key is missing or misnamed in
.env. CrewAI reads provider keys by their standard names, so it isOPENAI_API_KEY, not a custom variable. - Placeholders come through literally as {topic}. You forgot to pass that key in
kickoff(inputs={...}). Every placeholder in your YAML needs a matching input. - The crew loops or stalls. Set
max_iteron the agent andmax_rpmon the crew to cap runaway reasoning, and keepverbose=Trueon so you can see where it gets stuck.
What Should You Do Next?
You have a running crew. A few moves make it production-worthy.
- Read the agents and tasks reference to learn options like
allow_delegation, structured outputs, and guardrails. - Switch to the Flows model once you need branching or human-in-the-loop steps.
- Pin your model per agent through the LLM configuration, so a cheap model handles routing and a stronger one handles the writing.
- If you would rather host the model yourself, point CrewAI at a local endpoint. My guide on running LLMs locally with Ollama covers the setup, and pairs with an
ollama/model string. - Compare frameworks before you commit. See how CrewAI stacks against the OpenAI Agents SDK, LangGraph, and Pydantic AI, each of which trades control for convenience differently.