Skip to content

resonatehq-examples/example-multi-agent-orchestration-py

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Multi-Agent Orchestration — Resonate example

Multi-Agent Orchestration

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.

What this demonstrates

  • 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)

Prerequisites

Setup

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_KEY

How to run

1. Start the Resonate server

resonate dev

2. Start the worker

export OPENAI_API_KEY=sk-...
uv run agent

3. Invoke the orchestration

In 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.

What to observe

  1. Researcher does not re-run on writer failure -- its result is cached at the await ctx.run(...) checkpoint
  2. No retry code in the orchestrator -- orchestrate is pure sequential logic
  3. Each await is a checkpoint -- crash the worker after any agent call and restart; it resumes from there
  4. Human approval hook -- see the comment in src/agent.py for how to add ctx.promise() blocking

The code

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,
    }

Extending with human-in-the-loop

The orchestrator has a comment showing how to add real human approval. Replace the simulated approval with:

approval = await ctx.promise()
approved = await approval

Note: ctx.promise() in SDK v0.7.0 takes no id parameter — 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.

File structure

example-multi-agent-orchestration-py/
├── src/
│   ├── __init__.py
│   └── agent.py        # researcher, writer, reviewer + orchestrator
├── pyproject.toml
├── .env.example
└── README.md

Why async/await orchestrates three agents

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.

Learn more

License

Apache 2.0

About

Researcher → writer → reviewer pipeline with durable handoffs. Crash an agent, the previous agent's output is cached.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages