Async Crew Kickoff
Run PraisonAI crews with native async execution — no thread offload required
from praisonaiagents import Agent
import asyncio
agent = Agent(name="async-agent", instructions="Process tasks asynchronously.")
async def main():
result = await agent.astart("Run this task asynchronously.")
print(result)
asyncio.run(main())Run PraisonAI crews with native async execution from FastAPI, Jupyter, Discord bots, and other event loop contexts. The user awaits agent.astart() from an async app; the crew kickoff runs natively on the event loop without thread offload.
Each agenerate_crew_and_kickoff() run opens an async_scoped_bridge() around the adapter call, so concurrent tenants each get their own per-run bridge and a stuck coroutine in one cannot park the shared loop for the others.
Quick Start
- FastAPI Route
Native async execution — no worker threads, true cooperative multitasking:
python from fastapi import FastAPI import praisonai app = FastAPI() @app.post("/run") async def run_crew(): result = await praisonai.arun(agent_file="agents.yaml") return {"result": result} - Jupyter Notebook
Works directly in async cells without blocking:
python import praisonai result = await praisonai.arun(agent_file="agents.yaml") print(result)
How It Works
Non-blocking startup
Config loading, adapter setup, and workflow preparation run in a worker thread via asyncio.to_thread, so a slow disk read or heavy adapter import never stalls the event loop.
The _aload_config and _aprepare_for_run helpers wrap the blocking open() / yaml.safe_load / adapter setup calls, keeping the loop free to serve other requests while startup work runs off-thread.
What’s actually async
| Adapter | Async path | Notes |
|---|---|---|
praisonai (praisonaiagents) | Native — AgentTeam.astart() | True cooperative async |
crewai | Native — crew.kickoff_async() | Requires crewai ≥ 0.28; older installs transparently fall back to thread offload (PR #4909) |
autogen / ag2 | Thread offload (default fallback) | AutoGen v0.2 is sync-only; v0.4 / ag2 are stubs |
Workflow mode
YAML files with process: workflow also run natively async via YAMLWorkflowParser + workflow.astart() — no extra configuration needed.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
agent_file | str | Required | Path to the agent YAML file |
framework | str | None | Framework to use (auto-detected if None) |
tools | list | None | Classes, plain functions or framework tool instances (CrewAI/LangChain BaseTool). Unrecognised entries are logged and skipped — see Passing tools |
agent_yaml | str | None | Direct YAML content as string |
cli_config | dict | None | CLI configuration overrides (explicit keys win over **kwargs) |
model / llm | str | None | Selects the LLM — same as the CLI’s --llm flag |
session | str | None | Resume a session — same as the CLI’s --resume flag |
**kwargs | any | — | Any other keyword argument is forwarded through cli_config to the generator, mirroring the CLI’s pass-through |
strict_validation | bool | None | Recognised loose key — forwarded via cli_config to scope YAML validation strictness per run. Falls back to PRAISONAI_VALIDATE_STRICT. |
tool_timeout_workers | int | None | Recognised loose key — forwarded via cli_config to size the sync-tool timeout pool per run. Falls back to PRAISONAI_TOOL_TIMEOUT_WORKERS (default 32). |
strict_validation and tool_timeout_workers are recognised loose keys — **kwargs forwards them through cli_config for per-run multi-tenant isolation.
See Advanced CLI options from Python for the full alias table and precedence rules.
Common Patterns
FastAPI Background Task
import asyncio
import praisonai
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
async def run_crew_background():
result = await praisonai.arun(agent_file="agents.yaml")
# Store result in database, send notification, etc.
print(f"Background crew completed: {result}")
@app.post("/start-crew")
async def start_crew(background_tasks: BackgroundTasks):
background_tasks.add_task(run_crew_background)
return {"message": "Crew started"}Concurrent Crew Execution
import asyncio
import praisonai
async def run_multiple_crews():
# Run all crews concurrently with true async
results = await asyncio.gather(
praisonai.arun(agent_file="research.yaml"),
praisonai.arun(agent_file="analysis.yaml"),
praisonai.arun(agent_file="summary.yaml")
)
return resultsCLI options from an async handler
arun() (and run()) accept any extra keyword argument the CLI accepts. model=/llm= and session= get friendly Python aliases; everything else is forwarded through the same cli_config bridge the CLI already uses.
import praisonai
async def handler():
# model= is aliased to --llm; session= is aliased to --resume
result = await praisonai.arun(
"agents.yaml",
model="gpt-4o",
session="chat-42",
)
return resultAn explicit cli_config= still wins over loose kwargs — pass either, not both, for the same key.
Best Practices
Cancellation and timeouts propagate
Under native async, asyncio.CancelledError and asyncio.wait_for now actually cancel SDK work instead of being trapped behind a worker thread:
import asyncio
import praisonai
async def cancelable_crew():
try:
# This will properly cancel if timeout is reached
result = await asyncio.wait_for(
praisonai.arun(agent_file="agents.yaml"),
timeout=30.0
)
return result
except asyncio.TimeoutError:
print("Crew execution timed out and was cancelled")
return NoneUse asyncio.gather for concurrent crews
When running multiple crews, use asyncio.gather for parallel execution:
import asyncio
import praisonai
# ✅ Concurrent execution with native async
crew1_task = praisonai.arun(agent_file="crew1.yaml")
crew2_task = praisonai.arun(agent_file="crew2.yaml")
results = await asyncio.gather(crew1_task, crew2_task)
# ❌ Sequential execution (slower)
result1 = await praisonai.arun(agent_file="crew1.yaml")
result2 = await praisonai.arun(agent_file="crew2.yaml")Framework detection works transparently
Both praisonai and crewai use native async — AgentTeam.astart() and crew.kickoff_async() respectively. AutoGen v0.2 still falls back to the bounded thread-offload pool because its initiate_chats is sync-only.
import praisonai
# Native async for both praisonai and crewai (crewai ≥ 0.28)
result = await praisonai.arun(agent_file="agents.yaml", framework="crewai")Handle errors gracefully in async contexts
Wrap async crew execution in try-catch blocks:
import asyncio
import logging
import praisonai
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
async def safe_crew_run():
try:
result = await praisonai.arun(agent_file="agents.yaml")
return {"success": True, "result": result}
except Exception as e:
logger.error(f"Crew execution failed: {e}")
return {"success": False, "error": str(e)}