Learnixo

CrewAI Multi-Agents · Lesson 11 of 16

Asynchronous Task Execution

What Is Async Task Execution?

By default, CrewAI runs tasks sequentially. When you set async_execution=True on a task, CrewAI runs it concurrently with other async tasks. This can significantly reduce total execution time when tasks are independent.


When Tasks Can Be Parallelized

A task can run asynchronously if:

  • It does NOT depend on the output of another task running at the same time
  • It can run to completion with only its initial inputs
  • Its results are needed by a later (synchronous) task that collects everything

Parallelizable:

  • Research task for drug A (doesn't depend on research for drug B)
  • Research task for drug B (doesn't depend on research for drug A)
  • Regulatory check (independent of research)

Not parallelizable:

  • Writing a comparison document (depends on both research tasks completing first)

Basic Async Example

Python
from crewai import Agent, Task, Crew, Process

# Agents
pharma_researcher = Agent(
    role="Pharmaceutical Researcher",
    goal="Research drug pharmacology and clinical data",
    backstory="Expert pharmacologist with access to medical databases",
    verbose=True,
)

regulatory_expert = Agent(
    role="Regulatory Affairs Expert",
    goal="Analyze drug regulatory status and approval information",
    backstory="15 years experience in FDA and EMA regulatory affairs",
    verbose=True,
)

market_analyst = Agent(
    role="Market Analyst",
    goal="Analyze drug market position and competitive landscape",
    backstory="Healthcare market research specialist",
    verbose=True,
)

synthesizer = Agent(
    role="Report Synthesizer",
    goal="Combine all research into a comprehensive drug profile",
    backstory="Medical writer who specializes in integrating complex information",
    verbose=True,
)

# Three independent research tasks  run in parallel
pharmacology_task = Task(
    description="Research the pharmacology and clinical profile of Ibuprofen",
    expected_output="Detailed pharmacology report: mechanism, PK/PD, efficacy data, safety profile",
    agent=pharma_researcher,
    async_execution=True,  # Runs concurrently
)

regulatory_task = Task(
    description="Research the regulatory status of Ibuprofen globally",
    expected_output="Regulatory report: FDA approval date, indications, black box warnings, global status",
    agent=regulatory_expert,
    async_execution=True,  # Runs concurrently
)

market_task = Task(
    description="Analyze the market position of Ibuprofen",
    expected_output="Market report: market size, key competitors, brand vs generic, pricing",
    agent=market_analyst,
    async_execution=True,  # Runs concurrently
)

# This task must wait for all three  it's synchronous
synthesis_task = Task(
    description=(
        "Synthesize the pharmacology, regulatory, and market research into a "
        "comprehensive Ibuprofen drug profile report for pharmaceutical executives."
    ),
    expected_output=(
        "Executive drug profile (1000 words) with sections: "
        "Clinical Overview, Regulatory Status, Market Position, Strategic Recommendations"
    ),
    agent=synthesizer,
    context=[pharmacology_task, regulatory_task, market_task],
    # async_execution=False (default) — waits for all context tasks to complete
)

crew = Crew(
    agents=[pharma_researcher, regulatory_expert, market_analyst, synthesizer],
    tasks=[pharmacology_task, regulatory_task, market_task, synthesis_task],
    process=Process.sequential,
    verbose=True,
)

result = crew.kickoff()
print(result.raw)

Timing effect: Without async, this would take ~3× the time of one research task (all sequential). With async, the three research tasks run in parallel, so the total time is ~1× (plus synthesis time).


Synchronization Point

The key pattern:

[Async Task A] ─┐
[Async Task B] ─┼─→ [Sync Task D (collects A, B, C output)]
[Async Task C] ─┘

The synchronization task:

  • Is NOT async
  • Has context=[task_a, task_b, task_c]
  • CrewAI automatically waits for all context tasks before running it

You can have multiple synchronization points:

[Async A] ─┐
[Async B] ─┴─→ [Sync E (context=[A,B])] ─┐
[Async C] ─┐                              ├─→ [Sync G (context=[E,F])]
[Async D] ─┴─→ [Sync F (context=[C,D])] ─┘

Async Tasks with kickoff_async()

You can also run the entire crew asynchronously using kickoff_async():

Python
import asyncio

async def run_drug_analysis(drug_name: str):
    crew = Crew(
        agents=[pharma_researcher, regulatory_expert, synthesizer],
        tasks=[pharmacology_task, regulatory_task, synthesis_task],
        process=Process.sequential,
    )

    result = await crew.kickoff_async(inputs={"drug_name": drug_name})
    return result

# Run multiple drug analyses concurrently
async def analyze_drug_class(drugs: list[str]):
    tasks = [run_drug_analysis(drug) for drug in drugs]
    results = await asyncio.gather(*tasks)
    return results

# Analyze 5 NSAIDs in parallel
if __name__ == "__main__":
    drugs = ["Ibuprofen", "Naproxen", "Aspirin", "Celecoxib", "Diclofenac"]
    results = asyncio.run(analyze_drug_class(drugs))
    for drug, result in zip(drugs, results):
        print(f"{drug}: {result.raw[:100]}")

kickoff_for_each(): Batch Processing

Run the same crew for a list of inputs:

Python
inputs_list = [
    {"drug_name": "Ibuprofen"},
    {"drug_name": "Naproxen"},
    {"drug_name": "Aspirin"},
]

# Sequential (one at a time)
results = crew.kickoff_for_each(inputs=inputs_list)

# Parallel (all at once)
results = crew.kickoff_for_each_async(inputs=inputs_list)

kickoff_for_each_async is the most efficient option when you have many independent crew runs.


Performance Comparison

For 3 research tasks that each take 10 seconds:

| Mode | Total Time | |---|---| | Sequential tasks | ~30 seconds | | Async tasks (parallel) | ~10 seconds + synthesis | | kickoff_for_each (sequential) | ~30 seconds per crew run | | kickoff_for_each_async | ~10 seconds for all runs |


When NOT to Use Async

  • When tasks share state or have data dependencies (async breaks these)
  • When the LLM API has strict rate limits — parallel calls will hit rate limits faster
  • When debugging — sequential execution is much easier to trace and understand
  • When total tasks fewer than 3 — the orchestration overhead doesn't pay off for tiny parallelization

Always profile your crew in sequential mode first to understand the baseline. Add async only when you've confirmed tasks are truly independent.