CrewAI Multi-Agents

Course Content

CrewAI Multi-Agents

9 sections · 53 lessons

How does CrewAI decide task execution order?


A Flow router picks the branch in plain Python@start: scoring crew@router:score 70 or more?hot: outreach crewnurture: mailing list
Inside a crew the list decides the order; when the order depends on data, a router's returned label does.

What you need to know

Rule 1: list order

Python
crew = Crew(agents=[...], tasks=[research, analyse, write])

research, then analyse, then write. CrewAI checks that context never points to a later task and raises an error if it does.

Rule 2: async tasks and barriers

Python
pricing = Task(..., async_execution=True)reviews = Task(..., async_execution=True)summary = Task(..., context=[pricing, reviews])   # waits for both

pricing and reviews start together. When the crew reaches summary (synchronous), it first waits for all pending async tasks. A crew may end with at most one async task.

Rule 3: conditional tasks

A ConditionalTask runs only if a function of the previous output returns True. It cannot be the first task and cannot be async. Good for simple "only if" steps inside a crew.

When order depends on data: Flows

Python
from pydantic import BaseModelfrom crewai.flow.flow import Flow, start, listen, routerclass LeadState(BaseModel):    company: str = ""    score: int = 0class LeadFlow(Flow[LeadState]):    @start()    def score_lead(self):        result = ScoringCrew().crew().kickoff(inputs={"company": self.state.company})        self.state.score = result.pydantic.score    @router(score_lead)    def route(self):        return "hot" if self.state.score >= 70 else "nurture"    @listen("hot")    def draft_outreach(self):        return OutreachCrew().crew().kickoff(inputs={"company": self.state.company})    @listen("nurture")    def add_to_newsletter(self):        return add_to_mailing_list(self.state.company)LeadFlow().kickoff(inputs={"company": "Acme Logistics"})

Values in kickoff(inputs=...) fill the state's fields. ScoringCrew, OutreachCrew and add_to_mailing_list stand for your own crews and functions. The router's return string chooses the branch; plain Python decides, not an LLM.

A real-life example

A loan-document review system first ran as one crew of seven tasks. Some applicants upload a bank statement and some upload ITR returns, so tasks had lines like "if there is no bank statement, skip this". Agents often tried anyway and invented numbers.

The team moved the order into a Flow:

  • @start classifies uploaded files by type (a small agent call).
  • @router returns "salaried" or "self_employed".
  • Each label has its own small sequential crew with only the tasks that apply.
  • A final @listen(or_(...)) step writes the credit note.

Invented numbers from missing documents disappeared, because the tasks for missing documents no longer existed in that run. Each crew stayed a simple, readable list.

Follow-up questions to expect

  • "Can two crew tasks run in parallel?" — Yes, mark both async_execution=True and join them with a synchronous task that uses context.
  • "How do you loop until something is approved?" — In a Flow: a router returns a label that leads back to an earlier step, with a counter in state to stop after N rounds.
  • "Does hierarchical mode change order?" — No; it changes who does each task, not the list order.