AI Agents

The Agent Supervisor Pattern: Orchestrator + Workers in 2026

The supervisor pattern separates planning from execution in AI agents. How to implement an orchestrator that routes tasks to specialist workers.

A single agent given a complex goal - "research this market, write a competitive analysis, and identify three acquisition targets" - will attempt to do all of it in sequence, with the same context window, the same tool set, and the same system prompt. The result is a generalist trying to be a specialist in three different domains simultaneously. Output quality degrades, the context fills with irrelevant intermediate results, and debugging becomes difficult because the entire task lives in one undifferentiated loop. The supervisor pattern solves this by separating planning from execution.

Agent supervisor pattern
The agent supervisor pattern is a multi-agent architecture where a supervisor (orchestrator) agent decomposes a high-level goal into subtasks and delegates each to a specialist worker agent, then synthesises the workers' outputs - enabling parallel execution, domain-specific tool sets per worker, and failure isolation between subtasks.

The supervisor pattern - what it is

The supervisor pattern is an orchestrator agent that receives a high-level goal, decomposes it into subtasks, delegates each subtask to a specialist worker agent, and synthesises the workers' outputs into a final result. The orchestrator does not do domain work itself - it plans, routes, monitors, and assembles. The workers do not plan - they receive a specific, scoped task and execute it with domain-specific tools and context.

```text
User goal
    │
    ▼
┌─────────────────────────────┐
│   Supervisor / Orchestrator  │
│   - decomposes the goal      │
│   - routes to workers        │
│   - handles failures         │
│   - synthesises results      │
└──────────┬──────────────────┘
           │ delegates subtasks
    ┌──────┼──────┐
    ▼      ▼      ▼
 Worker  Worker  Worker
(search) (write) (verify)
```

When to apply the supervisor pattern

Apply the supervisor pattern when two or more of these are true:

  • The goal requires qualitatively different types of work (research vs writing vs validation)
  • Each type of work benefits from different tools, context, or system prompts
  • The subtasks can be executed in parallel to reduce total latency
  • Failure in one subtask should not cancel all other work
  • The total task is too long to fit in one agent's context window

Do not apply the supervisor pattern when the task is sequential and each step depends entirely on the previous one - a simple chain is more predictable and cheaper.

Implementing the orchestrator

The orchestrator's job is decomposition and routing. It should receive the goal, produce a structured task plan, and invoke workers for each task. Structured output via tool use is the most reliable way to get a machine-readable task plan from the orchestrator:

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

const planningTool: Anthropic.Tool = {
  name: 'create_task_plan',
  description: 'Decompose the user's goal into specific subtasks for worker agents.',
  input_schema: {
    type: 'object' as const,
    properties: {
      tasks: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            id: { type: 'string' },
            worker: { type: 'string', enum: ['researcher', 'writer', 'verifier'] },
            instruction: { type: 'string', description: 'Specific, self-contained instruction for this worker' },
            depends_on: { type: 'array', items: { type: 'string' }, description: 'IDs of tasks that must complete first' },
          },
          required: ['id', 'worker', 'instruction', 'depends_on'],
        },
      },
    },
    required: ['tasks'],
  },
};

async function orchestrate(goal: string): Promise {
  // Phase 1: planning
  const planResponse = await client.messages.create({
    model: 'claude-sonnet-5',
    max_tokens: 2048,
    system: 'You are a task planner. Decompose the user goal into specific subtasks for specialist workers. Each task must be self-contained - the worker receives only its instruction, nothing else.',
    tools: [planningTool],
    tool_choice: { type: 'any' },
    messages: [{ role: 'user', content: goal }],
  });

  const planBlock = planResponse.content.find(b => b.type === 'tool_use') as Anthropic.ToolUseBlock;
  const { tasks } = planBlock.input as { tasks: Task[] };

  // Phase 2: execution (respecting dependencies)
  const results = await executeTaskPlan(tasks);

  // Phase 3: synthesis
  const synthesisResponse = await client.messages.create({
    model: 'claude-sonnet-5',
    max_tokens: 4096,
    system: 'Synthesise the worker outputs into a coherent final response to the user's original goal.',
    messages: [{
      role: 'user',
      content: `Original goal: ${goal}

Worker outputs:
${formatResults(results)}`,
    }],
  });

  return synthesisResponse.content[0].type === 'text' ? synthesisResponse.content[0].text : ', ';
}

Implementing worker agents

Workers are purpose-built agents with narrow scope. Each worker gets: a system prompt describing its specialisation, the tools relevant to its domain only, and the specific instruction from the orchestrator - nothing else. No shared state, no awareness of sibling workers:

const workerConfigs: Record = {
  researcher: {
    model: 'claude-sonnet-5',
    system: 'You are a research specialist. Use the search and fetch tools to gather accurate, cited information. Return structured findings with source URLs.',
    tools: [webSearchTool, fetchPageTool],
    maxIterations: 10,
  },
  writer: {
    model: 'claude-sonnet-5',
    system: 'You are a writing specialist. Transform research findings into clear, well-structured prose. Do not add information not present in the provided research.',
    tools: [], // writer gets no tools - works from provided context only
    maxIterations: 1,
  },
  verifier: {
    model: 'claude-haiku-4-5-20251001', // Fast model for verification tasks
    system: 'You are a fact-checker. Verify specific claims against provided sources. Return a structured report of confirmed, unconfirmed, and contradicted claims.',
    tools: [fetchPageTool],
    maxIterations: 5,
  },
};

async function runWorker(workerType: string, instruction: string): Promise {
  const config = workerConfigs[workerType];
  if (!config) throw new Error(`Unknown worker type: ${workerType}`);

  return runAgentLoop(instruction, config.tools, config.maxIterations, config.system, config.model);
}

Dependency resolution and parallel execution

The most powerful feature of the supervisor pattern is parallelism. Tasks without dependencies can execute simultaneously, collapsing wall-clock time significantly. A plan with three independent tasks that each take 5 seconds executes in 5 seconds with parallelism, not 15:

async function executeTaskPlan(tasks: Task[]): Promise> {
  const results = new Map();
  const remaining = [...tasks];

  while (remaining.length > 0) {
    // Find tasks whose dependencies are all complete
    const ready = remaining.filter(
      task => task.depends_on.every(dep => results.has(dep))
    );

    if (ready.length === 0) {
      throw new Error('Circular dependency detected in task plan');
    }

    // Execute ready tasks in parallel
    const settled = await Promise.allSettled(
      ready.map(async task => {
        // Inject dependency outputs into the instruction if needed
        const enrichedInstruction = injectDependencyContext(task, results);
        const result = await runWorker(task.worker, enrichedInstruction);
        return { id: task.id, result };
      })
    );

    for (const outcome of settled) {
      if (outcome.status === 'fulfilled') {
        results.set(outcome.value.id, outcome.value.result);
        remaining.splice(remaining.indexOf(ready.find(t => t.id === outcome.value.id)!), 1);
      } else {
        // Handle worker failure - log and continue with partial results
        console.error('Worker failed:', outcome.reason);
      }
    }
  }

  return results;
}

Production considerations

In production, the supervisor pattern introduces failure surface area at each worker boundary:

  • Worker timeouts - Individual workers can hang in long tool-call loops. Set a timeout per worker (30 to 120 seconds depending on task type) and treat timeout as a partial failure, not a full abort.
  • Cost monitoring - Each worker is a separate agentic loop with its own token usage. Track cost per worker type to identify which workers are consuming disproportionate budget.
  • Orchestrator model choice - The orchestrator's planning quality determines everything downstream. Use a capable model (Sonnet or Opus) for the orchestrator, even if workers use cheaper models for their execution.
  • Result size limits - Worker outputs that are very long will push the synthesis prompt over context limits. Cap worker output at 2,000 to 4,000 tokens and instruct workers to summarise if they exceed that.

Supervisor pattern vs alternatives

The supervisor pattern is not always the right choice. A sequential prompt chain (output of step N is input to step N+1) is simpler and more debuggable when tasks are strictly ordered. A single generalist agent with many tools is sufficient when the task domain is coherent and the context window comfortably fits the work. The supervisor pattern earns its overhead when the task is broad enough that multiple specialisations genuinely improve output quality - typically when the task involves research, transformation, and validation as distinct phases, each of which a generalist does worse than a specialist.

The supervisor pattern handles task decomposition and routing, but individual workers still run the agentic observe-think-act loop internally. For a comparison of the supervisor approach against swarm architectures where agents communicate peer-to-peer, see agent swarm vs pipeline.