Vai al contenuto principaleVai al footer
PatternscriptadvancedEseguibileguided-flow

Supervisor and workers: route subtasks to specialists

A supervisor decomposes a request into typed subtasks, assigns each to a named specialist with its own instruction and its own slice of context, and a synthesiser merges the results while preserving disagreement.

Fatti chiave

Livello
advanced
Runtime
Python • API OpenAI
Pattern
Flusso ispezionabile con confini di sistema visibili
Interazione
Sandbox live • Script
Aggiornato
27 luglio 2026

Naviga questo esempio

Vista rapida del flusso

Come questo esempio si muove tra input, esecuzione e risultato rivedibile

Supervisor and workers: route subtasks… -> Route with explicit logic -> Run the agent task -> Load environment keys -> Initialize OpenAI client -> Send chat request

Ingresso

Supervisor and workers: route subtasks…

Processo

Route with explicit logic

Esito

Load environment keys

Perché esiste questa pagina

Questo esempio è mostrato sia come codice sorgente reale che come pattern di interazione orientato al prodotto, così i discenti possono collegare implementazione, UX e doctrine senza lasciare la libreria.

Flusso visivoCodice realeSandbox o walkthroughAccesso MCP

Relazioni con la doctrine

Queste sono forme di ragionamento: come un agente decide, quando agisce, cosa ricorda. Per comporre l'interfaccia intorno a esso, vedi l'AI Pattern Blueprint. Per trigger, pianificazione, caricamento del contesto e ripristino, vedi Agent runtime architecture.

Strutturale

Questo pattern esprime questi principi direttamente nel codice.

  • P9 · Rappresentare il lavoro delegato come un sistema, non solo come una conversazione

    This is the pattern that makes P9 concrete. The work exists as a typed Plan of Subtasks with named owners, not as a message stream: which specialist was engaged, on what question, and what each returned are all separate addressable objects. A supervisor that passes a growing chat transcript between agents has the shape without the property.

    supervisor_workers.py:Plan

Gap predefinito

La forma classica di questo pattern li viola per impostazione predefinita. Ognuno riporta la correzione.

  • P2 · Assicurarsi che il lavoro in background rimanga percepibile

    Specialists run one after another with nothing emitted between the plan and the synthesis unless the caller prints it. A multi-specialist run is the longest-latency shape in this corpus and the easiest to mistake for a hang. Fix, applied here: emit which specialist is running, and its question, before each call rather than after.

    supervisor_workers.py:run

  • P6 · Esporre uno stato operativo significativo, non la complessità interna

    Every specialist's raw output is surfaced by default, so the operator sees internal chatter rather than the state of the work. At three specialists it is readable; at ten it is noise that hides which lens actually mattered. Fix: report progress as the count of specialists complete plus any disagreement detected, and keep the per-specialist text behind an inspection step.

    supervisor_workers.py:run

  • P8 · Rendere espliciti i passaggi, le approvazioni e i blocchi

    The supervisor's plan executes as soon as it is produced. Nothing pauses between deciding to engage four specialists and spending four model calls doing it. Fix: treat the plan as a proposal with its cost stated, and gate execution on approval when the fan-out or its cost crosses a threshold you set.

    supervisor_workers.py:run

Le relazioni con la doctrine sono analisi di prima parte, non il risultato di una validazione. Esegui il validatore sul tuo codice per ottenere un verdetto con punteggio.

Come dovrebbe essere usato questo esempio nella piattaforma?

Usa prima la sandbox per comprendere il pattern di esperienza, poi ispeziona il sorgente per vedere come il confine del prodotto, il confine del modello e il confine della doctrine sono effettivamente implementati.

UX pattern: Flusso ispezionabile con confini di sistema visibili
Assicurarsi che il lavoro in background rimanga percepibile
Esporre uno stato operativo significativo, non la complessità interna
Rendere espliciti i passaggi, le approvazioni e i blocchi

Riferimenti sorgente

Voce di libreria
pattern-supervisor-workers
Percorso sorgente
content/patterns/multi-agent/supervisor-workers/supervisor_workers.py
Librerie
openai, pydantic, python-dotenv
Requisiti di runtime
OPENAI_API_KEY
Principi correlati
Assicurarsi che il lavoro in background rimanga percepibile, Esporre uno stato operativo significativo, non la complessità interna, Rendere espliciti i passaggi, le approvazioni e i blocchi, Rappresentare il lavoro delegato come un sistema, non solo come una conversazione

supervisor_workers.py

python
"""
Supervisor and workers: route subtasks to specialists

A supervisor decomposes a request into subtasks, assigns each to a named
specialist, and a synthesiser merges the results. Each specialist runs with
its own instruction and its own slice of context.

    Request -> Plan (typed subtasks) -> Specialists -> Synthesis

The real reason to reach for this is context isolation, not intelligence:
each worker sees only what its subtask needs, so one long transcript does not
drag every step. It is not a general upgrade. Most requests are better served
by one loop, and a team of agents that does not need to be a team is just a
larger bill and more places to fail.

Reach for it when the subtasks are genuinely independent, or when they need
materially different instructions. Not when they merely sound different.

Run: python supervisor_workers.py
"""

from typing import Literal

from dotenv import load_dotenv
from openai import OpenAI
from pydantic import BaseModel, Field

load_dotenv()

client = OpenAI()

MODEL = "gpt-5.4-mini"
MAX_SUBTASKS = 4

#: Each specialist is an instruction, not a model. Adding one is a line here.
SPECIALISTS: dict[str, str] = {
    "technical": (
        "You assess technical feasibility and implementation risk. Name the "
        "specific mechanism that would break, not general caution."
    ),
    "financial": (
        "You assess cost and commercial impact. Give ranges and state what "
        "drives them. Never invent a figure that was not provided."
    ),
    "operational": (
        "You assess who has to run this once it exists, and what they need. "
        "Focus on the ongoing burden rather than the build."
    ),
}


class Subtask(BaseModel):
    specialist: Literal["technical", "financial", "operational"]
    question: str = Field(
        description="The single question this specialist should answer."
    )


class Plan(BaseModel):
    subtasks: list[Subtask] = Field(
        description=(
            "One subtask per specialist that is genuinely needed. Do not "
            "include a specialist whose lens adds nothing to this request."
        )
    )
    reasoning: str = Field(description="Why these specialists and not the others.")


def plan(request: str) -> Plan:
    completion = client.beta.chat.completions.parse(
        model=MODEL,
        messages=[
            {
                "role": "system",
                "content": (
                    "Decompose the request into subtasks for the specialists "
                    "listed. Use only the specialists that genuinely add "
                    "something. Fewer is better: a specialist with nothing "
                    "useful to say costs a call and dilutes the synthesis.\n\n"
                    "Specialists:\n"
                    + "\n".join(f"- {name}: {role}" for name, role in SPECIALISTS.items())
                ),
            },
            {"role": "user", "content": request},
        ],
        response_format=Plan,
    )
    return completion.choices[0].message.parsed


def run_specialist(name: str, question: str) -> str:
    """One worker. Sees its own question, not the whole transcript."""
    completion = client.chat.completions.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": SPECIALISTS[name]},
            {"role": "user", "content": question},
        ],
    )
    return completion.choices[0].message.content or ""


def synthesise(request: str, findings: list[tuple[str, str]]) -> str:
    body = "\n\n".join(f"[{name}]\n{text}" for name, text in findings)
    completion = client.chat.completions.create(
        model=MODEL,
        messages=[
            {
                "role": "system",
                "content": (
                    "Merge the specialist findings into one answer. Attribute "
                    "each point to the specialist it came from. Where two "
                    "specialists disagree, say so rather than averaging them."
                ),
            },
            {"role": "user", "content": f"Request: {request}\n\n{body}"},
        ],
    )
    return completion.choices[0].message.content or ""


def run(request: str) -> str:
    print("  planning...")
    proposal = plan(request)
    print(f"      rationale: {proposal.reasoning}")

    subtasks = proposal.subtasks[:MAX_SUBTASKS]
    if not subtasks:
        # A plan with no subtasks is a real answer: this request did not need
        # a team. Say so rather than inventing work for the specialists.
        message = "No specialist lens applied to this request. Answer it directly."
        print(f"      {message}")
        return message

    findings: list[tuple[str, str]] = []
    for index, subtask in enumerate(subtasks, start=1):
        # Printed before the call, so a slow specialist reads as working.
        print(f"  [{index}/{len(subtasks)}] {subtask.specialist}: {subtask.question}")
        answer = run_specialist(subtask.specialist, subtask.question)
        print(f"      -> {answer[:80]}...")
        findings.append((subtask.specialist, answer))

    print("  synthesising...")
    result = synthesise(request, findings)
    print(f"      {result}")
    return result


if __name__ == "__main__":
    request = (
        "We are considering moving our nightly batch reconciliation to a "
        "streaming pipeline. Should we?"
    )
    print(f"\n> {request}")
    run(request)

Cosa dovrebbe ispezionare il discente nel codice?

Cerca il punto esatto in cui lo scope del sistema è delimitato: definizioni di schema, impostazione del prompt, configurazione di runtime e il punto di chiamata che trasforma l'intenzione dell'utente in un'azione concreta del modello o del workflow.

Cerca i contratti di output e la validazione
Cerca la chiamata di esecuzione esatta
Cerca cosa il prodotto potrebbe esporre all'utente

Come si relaziona la sandbox al sorgente?

La sandbox dovrebbe rendere leggibile l'UX: cosa vede l'utente, cosa sta decidendo il sistema e come il risultato diventa revisionabile. Il sorgente mostra poi come quel comportamento è effettivamente implementato.

Leggi il riepilogo dell'implementazione.
Esplora gli stati utente e di sistema.
Ispeziona il codice sorgente tenendo a mente le decisioni di doctrine evidenziate.
SandboxFlusso ispezionabile con confini di sistema visibili

Guida all'interazione

Usa la sandbox per esplorare l'esperienza visibile all'utente, il lavoro del sistema e la scelta di doctrine che l'esempio sta facendo.

Spiegazione UX

La sandbox spiega cosa dovrebbe vedere l'utente, cosa sta facendo il sistema e dove il controllo o l'ispezionabilità devono rimanere espliciti.

Spiegazione AI Design

La pagina trasforma il codice sorgente in un pattern orientato al prodotto: cosa può decidere il modello, cosa dovrebbe esporre il prodotto e dove il codice deterministico o la revisione devono subentrare.

Guida all'interazione

  1. 1Leggi il riepilogo dell'implementazione.
  2. 2Esplora gli stati utente e di sistema.
  3. 3Ispeziona il codice sorgente tenendo a mente le decisioni di doctrine evidenziate.

Visibile all'utente

A supervisor decomposes a request into typed subtasks, assigns each to a named specialist with its own instruction and its own slice of context, and a synthesiser merges the results while preserving disagreement.

Lavoro del sistema

Il prodotto prepara un task delimitato per il modello o il workflow.

Perché è importante

L'interfaccia dovrebbe rendere il task delegato leggibile prima che avvenga l'automazione.

Usato in corsi e percorsi

Questo esempio attualmente è indipendente nella libreria, ma si connette comunque al sistema dei principi e alla famiglia di esempi più ampia.

Principi correlati

Runtime architecture

Usa questo esempio nei tuoi agenti

Questo esempio è disponibile anche tramite il layer agent-ready del blueprint. Usa la pagina Per agenti per recuperare MCP pubblico, export deterministici e setup per Claude o Cursor.

Definisci trigger, contesto e confini prima di aumentare l'autonomia
Rendi espliciti controllo, osservabilita e recovery nel runtime
Scegli i pattern operativi giusti prima di delegare ai workflow