Classic projects: crew.py and YAML

CrewAI · Practical · 12 min

Most existing CrewAI code and many flow templates use this layout, so you will meet it constantly.

Before the JSON-first layout, every CrewAI project looked like this, and crewai create crew NAME --classic still produces it:

  • config/agents.yaml and config/tasks.yaml: agent and task text, keyed by name.
  • crew.py: a class that builds the crew in Python.
  • main.py: functions such as run(), train() and test() that crewai run and friends call.

crew.py uses decorators: the @name lines placed above a class or function, which register or wrap it.

  • @CrewBase on the class loads the two YAML files into self.agents_config and self.tasks_config.
  • @agent on a method that returns an Agent; @task on one that returns a Task. They collect the results into self.agents and self.tasks automatically.
  • @crew on the method that returns the final Crew.

The method names must match the YAML keys (researcher in YAML, def researcher in Python). Tasks keep the order in which their methods appear in the class.

Why choose classic? You can attach Python objects directly: custom tool instances, Pydantic output classes, callbacks and guardrail functions. In YAML the agent: field of a task refers to an agent by name, and {placeholders} work as before. Flow projects (crewai create flow) also generate crews in this classic style.

YAML uses > to write long text across several lines; the line breaks become spaces.

Example

# config/agents.yaml ->  researcher:
#                           role: >
#                             {topic} Senior Data Researcher
#                           goal: >   ...   backstory: >   ...

from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task

@CrewBase
class ResearchCrew:
    @agent
    def researcher(self) -> Agent:          # name matches the YAML key
        return Agent(config=self.agents_config["researcher"], verbose=True)

    @task
    def research_task(self) -> Task:
        return Task(config=self.tasks_config["research_task"])

    @crew
    def crew(self) -> Crew:
        return Crew(agents=self.agents, tasks=self.tasks, process=Process.sequential)

# main.py:  ResearchCrew().crew().kickoff(inputs={"topic": "AI LLMs"})

Remember

  • --classic creates crew.py plus config/agents.yaml and config/tasks.yaml
  • @CrewBase, @agent, @task, @crew collect objects into a Crew
  • Method names must match YAML keys

Watch out for

  • Renaming a YAML key but not the matching @agent/@task method, which raises a KeyError on load.

Check yourself

  1. In a classic project, what connects the method `def research_task(self)` to its description text?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.crewai.com/en/learn/using-annotations

Builds on

Unlocks

Nothing builds on this yet.