Skip to main content
Async agents run non-blocking AI tasks with await, letting you process multiple requests in parallel or embed agents inside async web servers.
The user sends concurrent requests; each 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.
Knowledge search and memory writes also offload to threads. On the async path, knowledge.search(...) and task-callback memory writes (store_in_memory) each run via asyncio.to_thread, so an asyncio.gather(...) fan-out doesn’t stall on one slow embedding or DB write. The built-in in-memory adapter is thread-safe under these parallel writes. See Async Safety.

Configuration Options

Set async_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.
If task A fails permanently, B and C are marked as skipped (their upstream failed) instead of running against empty context.
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)

The synchronous 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.

Failed turns leave chat_history clean

A failed await agent.achat(...) or await agent.astart(...) rolls back chat_history to its pre-turn state, so the user message from the failed turn never lingers without a reply.
Behaviour change in PraisonAI PR #4739 — parity with the sync chat() path. Both async generic exception handlers in _achat_impl now call self._rollback_chat_history_to(chat_history_length). If you had a manual .chat_history.pop() workaround around await agent.achat(...), you can remove it.
“Clean” means “unchanged from just before the call” — not “empty”, and not “user message present with no reply”. Retry on a transient without polluting the next turn:
See Reliability Guarantees for the full set of default correctness behaviours.

Best Practices

Call asyncio.run(main()) once at the program entry point. Avoid calling it inside already-running event loops — use await there instead.
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.
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.
Callbacks can be async (async def callback(output): ...). The runtime dispatches them safely even when called from within a running event loop.

Workflows

Build parallel and sequential multi-agent workflows

Async Crew Kickoff

Run YAML-defined crews asynchronously

Async Safety

Offloaded knowledge search, memory writes, and the thread-safe in-memory adapter

Cost Tracking

Async cost telemetry is now at parity with sync (#4887)

Spawn & Announce

Reliable async sub-agent spawn — no hangs, safe sync/async mixing