await, letting you process multiple requests in parallel or embed agents inside async web servers.
astart() yields on I/O so the server stays responsive under load.
Quick Start
1
Simple Usage
Use
astart() inside an async function:2
With Configuration
Run multiple agents in parallel with
asyncio.gather:How It Works
Sync vs Async Methods
Async performance:
await agent.achat() no longer blocks the event loop on message persistence. The file-locked disk write is offloaded via asyncio.to_thread, so concurrent async turns stay responsive. The sync path is unchanged.Configuration Options
Setasync_execution=True on individual tasks to mark them for async execution:
Common Patterns
Parallel Requests
Async Callback
Failure semantics.
PraisonAIAgents.arun_all_tasks waits for every sibling async task to complete before re-raising the first exception it saw. Predictable behaviour: no orphaned background tasks continue to mutate self.tasks after the workflow has surfaced a failure.Failure cascade in asequential()
asequential() now skips downstream tasks whose upstream dependency permanently failed, matching what sequential() already did. Before this change the async entry point astart() could run a task against context data its upstream had never produced — the task ran with empty context and emitted garbage. Now the failure short-circuits and cascades a skip down the dependency chain.
Behaviour verified against merged source in PraisonAI PR #3960. The async path (
asequential() / astart()) now mirrors the sync sequential() dependency-cascade check.Inside a Web Framework (FastAPI)
praisonai.run(...) API is also safe to call from an async
context — as of the 2026-07-30 release it dispatches through the shared async
bridge and works from FastAPI handlers, notebooks, and async def tests
without a RuntimeError. Prefer await agent.astart(...) when you can, but
sync mode is a valid fallback when a library only exposes a sync surface.
Lifecycle Hooks in Async Workflows
on_task_start and on_task_complete fire from astart() / arun_task() exactly as they do from start():
async def callbacks are awaited; sync callbacks are offloaded to the default executor. See Multi-Agent Hooks for the full contract.
Best Practices
Always use asyncio.run() as the entry point
Always use asyncio.run() as the entry point
Call
asyncio.run(main()) once at the program entry point. Avoid calling it inside already-running event loops — use await there instead.Limit concurrency to avoid rate limits
Limit concurrency to avoid rate limits
Use
asyncio.Semaphore(n) to cap simultaneous LLM calls: async with asyncio.Semaphore(5): result = await agent.astart(...). Start with n=5 and tune based on your API limits.Handle errors per-task with gather(return_exceptions=True)
Handle errors per-task with gather(return_exceptions=True)
Pass
return_exceptions=True to asyncio.gather() so one failed task doesn’t cancel the others. Check each result for isinstance(result, Exception) before using it.Use async callbacks for async downstream work
Use async callbacks for async downstream work
Callbacks can be async (
async def callback(output): ...). The runtime dispatches them safely even when called from within a running event loop.Related
Workflows
Build parallel and sequential multi-agent workflows
Async Crew Kickoff
Run YAML-defined crews asynchronously

