skip to content

In a CrewAI @CrewBase project, how is the Crew assembled from the decorated methods?

level: middleimportance: should knowfreq 45%

answer

  1. one class is the whole crew
  2. you never build the lists yourself
  3. file order is run order
  4. prompts live in YAML beside it
  5. hooks wrap every kickoff

basics

~20 s

@CrewBase turns a class into a crew definition: methods decorated as agents and tasks are collected automatically into self.agents and self.tasks, and the @crew method returns a Crew built from those lists plus the process you choose.

solid answer

~50 s

The `crewai` CLI scaffolds a project as a class decorated with `@CrewBase`. Inside it, `agents_config` and `tasks_config` point at YAML files, individual methods decorated as agents return `Agent` objects and methods decorated as tasks return `Task` objects, and one method decorated with `@crew` returns the assembled `Crew`. The part interviewers probe is the collection step. You do not build the lists by hand: the decorators register each method's product, so inside the `@crew` method `self.agents` and `self.tasks` are already populated, in declaration order, and you pass them straight to `Crew(agents=self.agents, tasks=self.tasks, process=Process.sequential)`. Declaration order therefore *is* execution order for a sequential crew — reordering methods in the file changes what runs when, which surprises people. The class also supports before- and after-kickoff hooks, so you can normalise inputs before the run and post-process the `CrewOutput` after it, keeping that logic beside the crew definition rather than in the caller.

code

python · 24 lines
python
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task

@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"], 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,   # collected automatically
            tasks=self.tasks,     # collected automatically, in declaration order
            process=Process.sequential,
            verbose=True,
        )

go deeper

for a junior

Know that a CrewAI project is usually one @CrewBase class holding the agents, tasks and a crew method, with the prompt text kept in YAML files beside it.

for a middle

Explain the collection step: the decorators register what each method returns, so self.agents and self.tasks are already populated inside the @crew method, in declaration order, and that method returns the assembled Crew.

for a senior

Point out that declaration order is execution order for a sequential crew, so task method ordering is behaviour that deserves a test, and use the before/after kickoff hooks to keep input validation and output telemetry in one place.

for a principal

Treat the class as the crew's public contract — YAML prompts as reviewable configuration, hooks as the enforcement point for input validation and cost telemetry — so crews across teams share one shape rather than each caller inventing its own wrapper.

## What @CrewBase is for Writing a crew inline — construct agents, construct tasks, construct `Crew`, call `kickoff` — is fine in a notebook and awkward in a repository. `@CrewBase` is CrewAI's project convention: one class holds the whole crew definition, prompts live in YAML beside it, and the runnable entry point is a single method. The `crewai` CLI scaffolds exactly this layout, and most production CrewAI code you will read is shaped this way. ## The anatomy A `@CrewBase` class typically declares: - **`agents_config`** and **`tasks_config`** — paths to the YAML files holding agent and task definitions, conventionally under a `config/` directory. Keeping prompt text in YAML means changing wording is a config edit, not a code change. - **Agent-producing methods**, each decorated so the framework registers the `Agent` it returns. - **Task-producing methods**, each decorated so the framework registers the `Task` it returns. - **One `@crew` method** that returns the assembled `Crew`. - **Optional before/after kickoff hooks** that run around every execution. ## The collection mechanism This is the mechanism worth being precise about. The agent and task decorators do not merely mark methods for documentation — they register the objects those methods produce with the class. By the time the `@crew` method runs, `self.agents` and `self.tasks` are populated lists, in the order the methods were declared. So the body of the `@crew` method is usually a single construction: `Crew(agents=self.agents, tasks=self.tasks, process=Process.sequential, verbose=True)` Three consequences follow: 1. **Declaration order is execution order** under `Process.sequential`. Moving a task method up or down the file changes the pipeline. That is convenient and easy to do accidentally in a large refactor, so treat the ordering of task methods as significant code. 2. **Every decorated task is in the crew.** There is no separate registration list to forget, and equally no way to define a task in the class and quietly leave it out — if you want it excluded, remove the decorator or the method. 3. **You can still override.** Nothing stops the `@crew` method from passing an explicit subset, or from adding crew-level options such as `process`, `manager_llm`, `planning`, `memory`, `max_rpm` and `verbose`. The auto-collected lists are a convenience, not a cage. ## The kickoff hooks The before-kickoff hook receives the inputs and can modify them before interpolation — normalising a user-supplied topic, injecting a tenant identifier, validating required keys and failing fast rather than letting a placeholder go unfilled. The after-kickoff hook receives the run's output and can post-process it — persisting `tasks_output`, emitting `token_usage` as a metric, or reshaping the result for a caller. Putting this logic in the class keeps the crew's contract in one file; putting it in every caller is how two callers drift apart. ## Running it The scaffolded project exposes a small entry module that instantiates the class, calls the `@crew` method to get the `Crew`, and calls `kickoff` with inputs; the CLI's run command drives that entry point. In a service you do the same thing yourself: build the crew object once at startup if it is stateless in your usage, or per request if you want the isolation. ## What interviewers are checking That you know the decorated methods self-assemble rather than being wired manually, and that you understand the ordering implication. A candidate who says 'you append each agent to a list in the crew method' has read the inline examples but not a real project. A candidate who adds 'and because declaration order is execution order in a sequential crew, task method ordering is load-bearing' has shipped one. ## Version note Describes the CrewAI 0.x project layout produced by the `crewai` CLI.

  • Why is the ordering of task methods in a @CrewBase class load-bearing?
    Because the task decorator collects them in declaration order into `self.tasks`, and a sequential crew executes that list in order. Reordering methods during a tidy-up silently reorders the pipeline, so a step can end up running before the step that produces its input. Treat task method order as behaviour, and cover the pipeline with a test that asserts the sequence.
  • What belongs in a before-kickoff hook rather than in the caller?
    Anything that must hold for every execution: validating that required input keys are present, normalising or sanitising user-supplied values before they are interpolated into prompts, and stamping run identifiers. Keeping it in the class means two different callers cannot drift into two different contracts, and the crew's input requirements are documented where the crew is defined.
  • Can the @crew method override the auto-collected lists?
    Yes — `self.agents` and `self.tasks` are ordinary lists you may filter, reorder or ignore, and the method is where you set crew-level options such as process, manager configuration, planning, memory and max_rpm. The auto-collection is a convenience for the common case; if you need a conditional crew shape, building the lists explicitly in that method is perfectly idiomatic.

saying these in an interview costs you the question

  • Thinks you must append each agent and task to a list manually
  • Believes the @crew method's return value is the run result
  • Assumes task method order in the file has no effect on execution
  • Expects a decorated task to be excluded from the crew unless referenced
  • Puts input validation in every caller instead of a kickoff hook

context