Resonate Python SDK
An orchestrator coordinates three specialist AI agents in sequence: researcher collects findings, writer drafts an article, reviewer approves it. Each agent handoff is a durable checkpoint.
If the writer fails mid-generation -- API timeout, crash, rate limit -- Resonate retries it automatically. The researcher does NOT re-run. The orchestrator resumes exactly at the failed step.
- Sequential agent delegation -- orchestrator calls researcher -> writer -> reviewer in order
- Durable handoffs -- each agent result is cached at its checkpoint; no agent re-runs on partial failure
- Crash recovery -- writer fails on first attempt in crash mode, retries without re-running researcher
- Human-in-the-loop hook -- natural extension point for approval before publishing (see
src/agent.py)
- Python 3.12+
- Resonate Server running locally
- OpenAI API key
git clone https://github.com/resonatehq-examples/example-multi-agent-orchestration-py
cd example-multi-agent-orchestration-py
uv sync
cp .env.example .env
# Edit .env and set OPENAI_API_KEYresonate devexport OPENAI_API_KEY=sk-...
uv run agentIn a separate terminal:
Happy path -- full pipeline runs to completion:
resonate invoke orchestration.1 --func orchestrate \
--arg "The future of durable execution in AI applications" \
--arg false=== Resonate Multi-Agent Orchestration ===
Mode: HAPPY PATH
Topic: "The future of durable execution in AI applications"
Pipeline: researcher -> writer -> reviewer -> [human approval] -> publish
[researcher] Researching: "The future of durable execution..."...
[researcher] Complete (312 chars)
[writer] Writing article (attempt 1)...
[writer] Complete (487 chars)
[reviewer] Reviewing draft...
[reviewer] APPROVED: The article clearly explains the concept...
Crash mode -- writer fails on first attempt, retries while researcher result is preserved:
resonate invoke orchestration.crash --func orchestrate \
--arg "Distributed systems in 2026" \
--arg true[researcher] Researching: "Distributed systems in 2026"...
[researcher] Complete (312 chars)
[writer] Writing article (attempt 1)...
Runtime. Function 'writer' failed with 'RuntimeError: Writer agent connection reset (simulated)' (retrying...)
[writer] Writing article (attempt 2)...
[writer] Complete (487 chars)
[reviewer] Reviewing draft...
[reviewer] APPROVED: ...
Notice: researcher runs once. Writer retries once. Reviewer runs once. The retry message comes from Resonate -- you wrote no retry logic.
- Researcher does not re-run on writer failure -- its result is cached at the
await ctx.run(...)checkpoint - No retry code in the orchestrator --
orchestrateis pure sequential logic - Each await is a checkpoint -- crash the worker after any agent call and restart; it resumes from there
- Human approval hook -- see the comment in
src/agent.pyfor how to addctx.promise()blocking
The entire orchestrator is a handful of lines in src/agent.py:
async def orchestrate(ctx: Context, topic: str, crash_on_writer: bool = False):
# Each await is a durable checkpoint
# If any agent fails, Resonate retries that step only
findings = await ctx.run(researcher, topic)
draft = await ctx.run(writer, topic, findings, crash_on_writer)
review = await ctx.run(reviewer, draft)
# Human-in-the-loop (production pattern):
# approval = await ctx.promise()
# approved = await approval
approved = "APPROVED" in review.upper()
return {
"status": "published" if approved else "rejected",
"topic": topic,
"findings": findings,
"draft": draft,
"review": review,
}The orchestrator has a comment showing how to add real human approval. Replace the simulated approval with:
approval = await ctx.promise()
approved = await approvalNote:
ctx.promise()in SDK v0.7.0 takes noidparameter — the promise id is opaque and auto-assigned by the server. To resolve the promise externally, retrieve its id from the server's promise-list API (e.g.GET /promises) and then resolve it:
# 1. Find the promise id
curl http://localhost:8001/promises
# 2. Resolve it by id
curl -X POST http://localhost:8001/promises/<promise-id>/resolve \
-H 'content-type: application/json' \
-d '{"data": true}'The workflow blocks until the promise is resolved, survives restarts, and works across services.
example-multi-agent-orchestration-py/
├── src/
│ ├── __init__.py
│ └── agent.py # researcher, writer, reviewer + orchestrator
├── pyproject.toml
├── .env.example
└── README.md
Sequential agent orchestration is often a hosted problem: a platform provides step-level retries, dashboard observability, and an event-based execution model with routing infrastructure. This example solves the narrower problem -- coordinate three specialist agents in order, survive any single agent failure, without leaving the process.
The orchestrator is a few lines of async/await. Each await ctx.run(agent, ...) is a durable checkpoint: a crash or API failure retries only that agent. Researcher output is cached at its checkpoint before writer starts; writer output is cached before reviewer starts. There is no retry configuration, no step metadata, no routing schema -- just sequential await ctx.run calls.
Human-in-the-loop extends naturally. await ctx.promise() blocks the workflow until the promise is resolved externally. The orchestrator survives restarts while waiting; the approval is the checkpoint. No hosted approval surface required.
- Resonate documentation
- Multi-Agent Orchestration (TypeScript)
- Hacker News Research Agent (Python)
- Deep Research Agent (Python)
Apache 2.0