◈Home ▸Blog ◇Contact
Home/Blog/How to Build a Multi-Agent System With CrewAI
How toLLM EngineeringCrewAIMulti-AgentPython

How to Build a Multi-Agent System With CrewAI

9 min readBy Miloš Mitrović

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, then crewai create crew my_project, define your agents in agents.yaml and tasks in tasks.yaml, and launch with crewai 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 --classic to 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.10 and <3.14. Check with python3 --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.

  1. Install the CrewAI CLI. The maintainers recommend the uv package manager, which keeps each project isolated.
    curl -LsSf https://astral.sh/uv/install.sh | sh
    uv tool install crewai
    Confirm it landed with crewai version.
  2. Scaffold a project. This creates the folder structure, a virtual environment target, and starter config files.
    crewai create crew research_crew --classic
    cd research_crew
    The --classic flag 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.
  3. Add your API keys. Open the generated .env at the project root and set your provider keys.
    OPENAI_API_KEY=sk-your-key
    MODEL=openai/gpt-4o-mini
    SERPER_API_KEY=your-serper-key
    The MODEL string uses the provider/model format LiteLLM expects. Swap in anthropic/claude-3-5-sonnet-latest or ollama/llama3.1 without touching any other code.
  4. 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.
  5. Define your tasks. Open src/research_crew/config/tasks.yaml. A task needs a description, an expected_output that sets the bar, and the agent that owns it. Write expected_output like an acceptance test, because the model treats it as one.
    research_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.md
    The context key hands the researcher's output to the writer, which is how information moves between agents.
  6. Wire the crew. Open src/research_crew/crew.py. The @CrewBase class turns your YAML into live objects. Each @agent and @task method reads its config by key, and the @crew method sets the process.
    from 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,
            )
    The self.agents and self.tasks lists are assembled for you from the decorated methods, in the order you declared them.
  7. Install dependencies and run. The entry point in src/research_crew/main.py passes your inputs into the {topic} placeholders.
    # main.py already contains a run() like this
    def run():
        ResearchCrew().crew().kickoff(inputs={"topic": "CrewAI in production"})
    Then from the project root:
    crewai install
    crewai run
    Watch the trace scroll by, and find your briefing at output/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.

ApproachHow work is routedState handlingBest for
Sequential processTasks run top to bottom; each can inherit the previous output through contextImplicit, passed task to taskLinear pipelines: research then write then review
Hierarchical processA manager agent plans, delegates to workers, and checks their resultsHeld by the manager agentOpen-ended goals where the split of work is not known up front
FlowsYou write Python with @start and @listen decorators that call crews and plain functionsExplicit, in a typed state object you controlProduction 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 --classic to 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 is OPENAI_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_iter on the agent and max_rpm on the crew to cap runaway reasoning, and keep verbose=True on 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.

Sources

M
Miloš Mitrović
Email Marketing for Ecommerce

Have a question or a project?

Whether it is about this post or a system you want built, I'm happy to talk.

Get in touch

404

Post not found. It may have been moved or the link is incorrect.

← Back to the blog
Summarize with AI
ChatGPT, Perplexity, and Grok open with the prompt ready to run. Claude, Gemini, and Copilot open a chat with the prompt copied; press Ctrl+V (Cmd+V on Mac) to paste. The full text is included, so it works even without web access.