Run Agents in Parallel
client.compilations.run_instruction(...) dispatches a fresh execution and returns a new run immediately - it does not block until the run finishes. That makes fan-out simple: take one frozen compilation, start one run per row or item, and wait on each run separately.
Runs execute in the FlyMy.AI cloud, so your script only dispatches and polls.
Parallel batches should run from a frozen compilation, not from live agent chats. The frozen instruction replays a fixed plan, so every item goes through the same deterministic pipeline - cheaper and faster than re-planning per item. See Freeze & Re-run.
Fan out with a worker pool
One thread per in-flight run; run_instruction_and_wait dispatches the run and polls (~2s) until it reaches a terminal status.
from concurrent.futures import ThreadPoolExecutor
from flymyai import AgentClient
client = AgentClient(api_key="fly-***")
COMPILATION_ID = 42 # from client.agents.compile_from_run(...)
rows = [
{"item_id": "lead-001", "website_url": "https://example.com"},
{"item_id": "lead-002", "website_url": "https://example.org"},
# ... one dict per item, matching the agent's input_schema
]
def process(row: dict) -> dict:
result = client.compilations.run_instruction_and_wait(
COMPILATION_ID,
variables=row,
timeout=600,
)
return {"row": row, "status": result.status, "output": result.output,
"error": result.error, "run_id": result.id}
with ThreadPoolExecutor(max_workers=10) as pool:
results = list(pool.map(process, rows))
done = [r for r in results if r["status"] == "completed"]
failed = [r for r in results if r["status"] != "completed"]
print(f"{len(done)} completed, {len(failed)} failed")
Prefer asyncio? AsyncAgentClient exposes the same methods - wrap the call in a task per row and bound concurrency with asyncio.Semaphore(10).
Retry failed runs
Each item is an independent run, so retry only the failures - re-dispatch with the same variables:
for r in failed:
retry = client.compilations.run_instruction_and_wait(
COMPILATION_ID, variables=r["row"], timeout=600,
)
if retry.status == "completed":
done.append({"row": r["row"], "output": retry.output, "run_id": retry.id})
Check what the batch cost
Billing is per-use - each run bills its real cost. From the FlyMy MCPs gateway, get_execution_price returns the billed cost of one run and list_execution_prices returns costs across recent runs, so you can total a batch or compute cost per item.
Practical notes
- Idempotent variables - include a stable identifier per item (like
item_idabove) invariables, and make any side-effecting step keyed on it. A retried run then updates the same record instead of creating a duplicate. - Modest concurrency - start with ~10 in-flight runs. Confirm outputs and cost per item look right on a small batch before raising it.
- Retries - treat
failedas retryable and inspecterroron runs that fail twice; the cause is often a bad input row, not the pipeline. - Timeouts -
run_instruction_and_waitraisesTimeoutErrorif a run outlivestimeout; catch it inprocess()if a single slow item should not sink the batch.
Next steps
- Runs - run lifecycle, polling, and freeze semantics
- Python SDK -
run_instruction,run_instruction_and_wait, and the lower-level building blocks