Orchestrator-Workers
Break complex tasks into a plan, delegate steps to specialized workers, and synthesize results into a final output.
This workflow pattern is experimental. Interfaces are subject to change as the OS Protocol specification evolves.
Overview
The Orchestrator-Workers pattern uses a central orchestrator to decompose a complex task into a structured plan, delegate each step to a specialized worker, and synthesize the collected results into a final output. It is the most powerful multi-agent pattern for tasks that require multiple specialized capabilities working together. Workers operate independently on their assigned steps, and the orchestrator aggregates their results with full awareness of the original goal.
Pattern Diagram
TypeScript API
Import from osprotocol/workflows/orchestrator-workers.
PlanStep
A single unit of work in the execution plan.
interface PlanStep {
id: string
description: string
worker: string
input: string
dependsOn?: string[]
}id— unique identifier for the stepdescription— human-readable summary of what the step doesworker— name of the worker responsible for this stepinput— the prompt or data passed to the workerdependsOn— optional list of step IDs that must complete before this step runs
Plan
The full execution plan produced by the orchestrator.
interface Plan {
steps: PlanStep[]
goal: string
}steps— ordered list of steps to executegoal— the original objective driving the plan
WorkerResult
The result returned by a worker after executing a step.
interface WorkerResult<T = unknown> {
stepId: string
worker: string
success: boolean
data?: T
error?: string
}stepId— the ID of the step this result corresponds toworker— name of the worker that produced the resultsuccess— whether the step completed without errordata— the output produced by the worker on successerror— error message if the step failed
OrchestratorWorkersWorkflow
The main interface extending the base Workflow.
interface OrchestratorWorkersWorkflow<Output> extends Workflow<Output> {
plan(prompt: string): Promise<Plan>
delegate(step: PlanStep): Promise<WorkerResult>
synthesize(results: WorkerResult[], plan: Plan): Promise<Output>
}plan— generates a structuredPlanfrom the input promptdelegate— sends a singlePlanStepto the assigned worker and returns aWorkerResultsynthesize— combines all results with the original plan to produce the finalOutput
WorkerConfig
Configuration for registering a worker with the orchestrator.
interface WorkerConfig {
name: string
description: string
workflow: Workflow<unknown>
capabilities: string[]
}name— unique identifier used inPlanStep.workerdescription— what this worker is specialized to doworkflow— the underlying workflow the worker executescapabilities— list of capability tags used for worker selection
Usage Examples
Create a Plan and Execute
const result = await orchestrator.run("Research and summarize AI agent frameworks")
// Or step by step:
const plan = await orchestrator.plan("Research and summarize AI agent frameworks")
const results: WorkerResult[] = []
for (const step of plan.steps) {
const result = await orchestrator.delegate(step)
results.push(result)
}
const output = await orchestrator.synthesize(results, plan)Register Workers
const workers: WorkerConfig[] = [
{
name: "researcher",
description: "Searches the web and retrieves relevant information",
workflow: searchWorkflow,
capabilities: ["search", "retrieve", "browse"],
},
{
name: "analyst",
description: "Analyzes data and identifies patterns",
workflow: analysisWorkflow,
capabilities: ["analyze", "compare", "rank"],
},
{
name: "writer",
description: "Drafts and edits written content",
workflow: writingWorkflow,
capabilities: ["write", "summarize", "edit"],
},
]Handle Step Dependencies
Use dependsOn to ensure steps run in the correct order when outputs from one step feed into another.
const plan: Plan = {
goal: "Produce a competitive analysis report",
steps: [
{
id: "step-1",
description: "Gather data on competitor A",
worker: "researcher",
input: "Find key features and pricing for Competitor A",
},
{
id: "step-2",
description: "Gather data on competitor B",
worker: "researcher",
input: "Find key features and pricing for Competitor B",
},
{
id: "step-3",
description: "Write comparative analysis",
worker: "writer",
input: "Compare the two competitors based on research results",
dependsOn: ["step-1", "step-2"],
},
],
}
// Execute steps respecting dependencies
for (const step of plan.steps) {
const deps = step.dependsOn ?? []
const depsComplete = deps.every(id =>
results.find(r => r.stepId === id && r.success)
)
if (depsComplete) {
results.push(await orchestrator.delegate(step))
}
}Integration
- Routing — use routing to select which orchestrator handles an incoming task
- Parallelization — combine with parallelization to execute independent steps concurrently
- Evaluator-Optimizer — wrap synthesized output in an evaluator loop for iterative refinement
- Runs — configure timeouts, retries, and approval gates on orchestrator runs