Async, batch and streaming runs

CrewAI · Advanced · 10 min

Web apps and batch jobs need runs that don't block, scale over many items and show progress.

Async kickoff. await crew.kickoff_async(inputs=...) (or the newer akickoff) runs a crew without blocking. async/await is Python's way of letting one program wait on many slow operations at once: while one crew waits for the model, others make progress. Use it in async web servers such as FastAPI, and with asyncio.gather to run several independent crews concurrently. Flows have kickoff_async too.

Many inputs. crew.kickoff_for_each(inputs=[{...}, {...}]) runs the crew once per dictionary and returns a list of outputs. kickoff_for_each_async does the same concurrently. Watch provider rate limits: set max_rpm on the crew, or process inputs in smaller batches.

Streaming. Set stream=True on the crew. kickoff then returns a streaming object instead of a finished result. Iterate over it to receive chunks as agents produce them, then read .result for the final CrewOutput. This lets a user interface show progress instead of a long blank wait.

Replaying. After a run, crewai log-tasks-outputs lists the task outputs of the latest kickoff, and crewai replay -t <task_id> re-runs from that task onwards, reusing earlier outputs. That is cheaper than starting over when you tweak a late task.

Each crew run is independent, so concurrency is safe as long as your own tools are safe to call in parallel. A tool that writes to one shared file is not.

Example

import asyncio
from crewai import Agent, Crew, Task

def make_crew(stream: bool = False) -> Crew:
    a = Agent(role="Namer", goal="Name products", backstory="Short, catchy names.")
    t = Task(description="Suggest a name for {product}.", expected_output="One name.", agent=a)
    return Crew(agents=[a], tasks=[t], stream=stream, max_rpm=30)

async def main():
    crew = make_crew()
    names = await asyncio.gather(
        crew.kickoff_async(inputs={"product": "a smart mug"}),
        crew.kickoff_async(inputs={"product": "a bike lock"}),
    )
    print([n.raw for n in names])                      # two names

asyncio.run(main())

batch = make_crew().kickoff_for_each(inputs=[{"product": "tea"}, {"product": "socks"}])
print(len(batch))                                      # 2

streaming = make_crew(stream=True).kickoff(inputs={"product": "a desk lamp"})
for chunk in streaming:
    print(chunk.content, end="", flush=True)            # text appears as it is generated
print("\nFinal:", streaming.result.raw)

Remember

  • kickoff_async/akickoff + asyncio.gather run crews concurrently
  • kickoff_for_each runs one crew over many inputs
  • stream=True returns a stream of chunks, then .result

Watch out for

  • Launching hundreds of concurrent kickoffs without max_rpm and hitting provider rate limits.

Check yourself

  1. A late task's prompt needs a tweak, and earlier tasks were expensive. What avoids re-running them?

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

Official docs: docs.crewai.com/en/learn/kickoff-async