Choosing an API
| Context | Create agent | Run agent |
|---|---|---|
| CLI / script (no event loop) | Agent(config=...) | agent.run(message=...) |
| Async handler (FastAPI, Transport, AgentTeam) | await Agent.create(config=...) | await agent.run_async(message=...) |
Sync path
Use this in CLI scripts and__main__ entry points:
agent.run() internally calls asyncio.run(self.run_async(...)). If a running event loop is already present on the current thread, it raises RuntimeError — use await agent.run_async() instead.
Async path
Use this in async handlers, transports, and anywhere a running event loop exists:Agent.create() instead of Agent() in async contexts. Agent initialization involves async I/O (database writes, MCP discovery, storage restoration). Agent() cannot await, so it may attempt to create a nested event loop — await Agent.create() runs all initialization natively on the current loop.
Writing tools
Sync tools (recommended for most cases)
Define a regular function. The executor automatically dispatches it to a thread pool viaasyncio.to_thread() so it never blocks the event loop:
Async tools
For native async I/O (WebSocket, streaming HTTP), define anasync def implementation. The executor awaits it directly — no thread pool:
How the executor dispatches tools
| Tool type | Dispatch strategy |
|---|---|
| Sync implementation | await asyncio.to_thread(tool.execute, ...) |
| Async implementation | await tool.execute_async(...) — directly awaited |
has_native_async_execute = True (e.g. MCPTool) | await tool.execute_async(...) |
Common anti-patterns
Calling agent.run() inside an async function
Calling agent.run() inside an async function
Using Agent() in an async context
Using Agent() in an async context
Using nest_asyncio.apply()
Using nest_asyncio.apply()
Manually creating event loops
Manually creating event loops
Middleware and async
Middleware hooks (before_model, after_tool, etc.) are a sync API. On the async execution path, the executor runs them via asyncio.to_thread() so they don’t block the main loop. Define middleware methods as regular functions — not async def: