Quick Start
1
Run the team over a list of inputs
Each input dict fills the
{{placeholder}} slots in your task templates.2
Run it asynchronously
astart_for_each is the async twin — same inputs, same result shape.AgentTeam(tasks=...) must be None or a sequence of Task instances. See AgentTeam Task Validation for the exact rules.How It Works
The team runs once per input dict, restoring its original templates afterwards so it stays reusable.Result Shape
start_for_each returns a plain dict summarising the whole batch.
Each entry in
items has this shape:
Parameters
Error Handling
Choose whether a failing item stops the batch or is recorded and skipped.- continue (default)
- fail_fast
Errors are captured per item; the batch runs to completion.
Common Patterns
Generate bios for many names in one call.Concurrent-run safety
A singleAgentTeam / PraisonAIAgents instance is single-run: it shares its Task objects and instance state across runs, so two runs on the same object at once would corrupt each other. Running the same instance concurrently — from two threads, or two branches of asyncio.gather — now raises RuntimeError instead of silently duplicating tasks or swapping one caller’s result for another’s.
start_for_each and astart_for_each are unaffected: they hold the guard for the whole batch and re-enter it per item on the same thread and task, so batched runs keep working exactly as before.
Fix reference: PraisonAI PR #5121. The re-entrancy guard now tracks ownership via a
contextvars.ContextVar (_run_owner) with a compound identity (id(self), threading.get_ident(), id(asyncio.current_task())). Before #5121, the guard read threading.get_ident() alone — every asyncio Task on one event loop shares a single OS thread id, so two coroutines calling astart() on the same team under asyncio.gather both took the re-entrant branch and bypassed the lock. Nested astart() on the same task still re-enters (batches keep working); a child task copying the parent context can no longer falsely re-enter (the task-id pin catches it).The exact error, so you can grep for it:
This AgentTeam instance is already running; an AgentTeam is not safe to run concurrently on the same object. Create a separate AgentTeam/PraisonAIAgents instance per concurrent run.
See Concurrency for the full list of concurrency guarantees.Best Practices
Use double braces in templates
Use double braces in templates
Placeholders use
{{name}} (double braces). This runs through the team’s built-in variables interpolator, not Python’s str.format. Single braces are left untouched.Pick on_error by job type
Pick on_error by job type
Use
on_error="continue" for bulk jobs where one bad row shouldn’t stop the rest. Use on_error="fail_fast" for evaluation harnesses that must halt on the first failure.token_usage_total is team-cumulative, not batch-item-scoped
token_usage_total is team-cumulative, not batch-item-scoped
token_usage_total comes from PraisonAIAgents.get_token_usage_summary() and reflects this team’s cumulative usage across the whole run (not just the batch item). Since PraisonAI PR #4462, finalized in #4470 (fixes #4446), it is scoped to this instance’s own named agents, so concurrent teams do not leak into each other. For per-item accounting, still reset the team between batches or inspect per-item outputs.The team stays reusable
The team stays reusable
self.variables and the task templates are restored in a finally block after every batch, so you can call start_for_each again — or start() — on the same team without side effects.Related
AgentTeam
The team class behind batch runs.
Multi-Agent Execution
How teams execute tasks.
Dynamic Variables
How
{{placeholder}} interpolation works.Async Crew Kickoff
Running teams asynchronously.

