Tutorial: orchestrare un agente di review con i Dynamic Workflows di Claude Code

Tutorial hands-on: costruire un workflow di review multi-agente in Claude Code con le primitive agent(), pipeline() e parallel(), riproducendo il pattern trova-verifica avversariale-sintesi del comando bundled deep-research. La feature è in research preview.

AIGovernanceOpen SourceCompliance Open SourceAILLMAgenticTutorialClaude CodeGovernance

Premessa: cosa stiamo costruendo e in che stato è la feature

A fine maggio, in concomitanza con claude-opus-4-8, Anthropic ha introdotto in Claude Code i Dynamic Workflows: script JavaScript che Claude scrive e un runtime esegue per orchestrare molti subagent in background. Ne abbiamo descritto l’architettura in Claude Opus 4.8 e i Dynamic Workflows; qui passiamo alla pratica e costruiamo un workflow di review che riusa il pattern del comando bundled /deep-research.

Un avvertimento prima di iniziare, da prendere alla lettera: i Dynamic Workflows sono in research preview, non in disponibilità generale. Primitive, parole chiave di attivazione e limiti del runtime possono cambiare. Il codice qui sotto è un esempio didattico, non un’API stabile: serve a illustrare il pattern, non a essere copiato in produzione senza verifica sulla versione installata.

Prerequisiti

La feature richiede una versione recente di Claude Code ed è disponibile sui piani a pagamento (sul piano Pro va attivata dalla riga Dynamic workflows in /config), oltre che via API e sui cloud Bedrock, Vertex e Foundry. Non si scrive lo script a mano: lo si fa generare descrivendo il compito a Claude, eventualmente con la parola chiave di attivazione. Quando una run fa quello che serve, lo script si può salvare come comando riutilizzabile (/workflows, tasto s).

Il pattern di /deep-research applicato alla review

/deep-research fa fan-out di ricerche su più angoli, incrocia le fonti e sottopone ogni affermazione a una verifica avversariale a votazione — agenti indipendenti che tentano di refutare il claim — restituendo un report citato con i claim non sopravvissuti già scartati. Sono tre fasi: trova → verifica avversariale → sintesi.

Lo stesso schema si trasferisce di peso a una code review. Invece di affermazioni da refutare abbiamo rilievi (finding) da confermare: per ogni dimensione di review (sicurezza, correttezza, performance, manutenibilità) un agente trova i problemi, e un secondo agente — il revisore avversariale, ciò che qui chiamiamo informalmente lo skeptic (non è un termine ufficiale) — tenta di smontarli. Sopravvive solo ciò che resiste.

Lo script

Tre primitive fanno il lavoro. agent() esegue un singolo subagent; con l’opzione schema forza un output strutturato validato a livello di chiamata di tool, con retry automatico in caso di mismatch. pipeline() fa scorrere gli item attraverso gli stadi senza barriera, così una dimensione può essere alla verifica mentre un’altra è ancora alla scoperta. parallel() è invece una barriera: attende il completamento di tutti i task.

// Review multi-dimensione: trova -> verifica avversariale a voto -> sintesi.
// `args` arriva dall'invocazione del comando (es. il diff o i path).
const dimensions = ["security", "correctness", "performance", "maintainability"];

const findingSchema = {
  type: "object",
  properties: {
    file: { type: "string" },
    line: { type: "number" },
    severity: { type: "string", enum: ["low", "medium", "high"] },
    claim: { type: "string" },
  },
  required: ["file", "claim", "severity"],
};

// Stadio 1 + 2 in pipeline: ogni dimensione scorre senza barriera.
const reviewed = await pipeline(dimensions, [
  // Stadio 1 — find: un agente cerca rilievi su una sola dimensione.
  async (dim) => {
    const findings = await agent({
      prompt: `Rivedi il diff per problemi di ${dim}. Solo rilievi concreti.`,
      context: args,
      schema: { type: "array", items: findingSchema },
    });
    return { dim, findings };
  },
  // Stadio 2 — adversarial verify: 3 "skeptic" indipendenti votano ogni rilievo.
  async ({ dim, findings }) => {
    const survived = [];
    for (const f of findings) {
      const votes = await parallel(
        [0, 1, 2].map(() =>
          agent({
            prompt: `Tenta di refutare questo rilievo. Reale e azionabile?`,
            context: { finding: f, diff: args },
            schema: { type: "object", properties: { upheld: { type: "boolean" } } },
          })
        )
      );
      const upheld = votes.filter((v) => v.upheld).length;
      if (upheld >= 2) survived.push({ ...f, votes: upheld });
    }
    return { dim, survived };
  },
]);

// Stadio 3 — sintesi: un solo agente compone il report finale citato.
const report = await agent({
  prompt: "Componi un report di review dai soli rilievi sopravvissuti, raggruppati per severità.",
  context: reviewed,
});

I passaggi, in breve. Lo stadio 1 lancia un agente per dimensione e ne raccoglie i rilievi nello schema validato; lo stadio 2 sottopone ogni rilievo a tre revisori avversariali in parallel() e tiene solo quelli con almeno due voti a favore (la soglia è arbitraria, regolatela); lo stadio 3 sintetizza. I risultati intermedi vivono in variabili dello script (reviewed), non nella finestra di contesto: è ciò che permette di processare molto lavoro senza saturare il contesto.

Limiti del runtime da tenere a mente

Il runtime impone vincoli precisi: fino a 16 agenti concorrenti e un massimo di 1.000 agenti per run. Non c’è input utente a metà run — per un sign-off tra le fasi, ogni fase va eseguita come workflow a sé. E soprattutto: lo script non accede direttamente a filesystem o shell; lo fanno gli agenti. Lo script coordina, gli agenti agiscono. Tre dimensioni per quattro rilievi con tre voti ciascuno sono già una quarantina di agenti: con un diff grande si arriva in fretta al tetto, e conviene provare prima su una slice ristretta per stimare il costo in token.

Cosa significa in pratica, e l’aggancio governance

Per chi valuta di adottarlo, il punto non è la novità sintattica ma dove finiscono i controlli. I subagent di un workflow girano sempre in modalità acceptEdits ed ereditano la tool allowlist della sessione, qualunque sia la modalità di permessi della conversazione: le edit ai file sono auto-approvate, mentre comandi shell, fetch web e tool MCP fuori allowlist possono ancora fermare la run. La feature è disattivabile a più livelli — da /config, con disableWorkflows in settings.json, via variabile d’ambiente, oppure centralmente con i managed settings a livello di organizzazione.

Questi sono esattamente i punti di controllo che contano quando un singolo comando spawna decine di agenti: cosa possono toccare, cosa viene auto-approvato, chi può spegnere tutto. È la tesi che in noze chiamiamo Secure Governance e che sta dietro ad Admina, il framework Open Source di governance dell’AI che sponsorizziamo, e al paradigma OISG: un workflow di review codificato è ripetibile e leggibile, ma è la governance dell’orchestrazione — allowlist, modalità di approvazione, interruttori — a renderlo adottabile in un contesto regolato.

Link: Orchestrate subagents at scale with dynamic workflows — Claude Code Docs · Introducing dynamic workflows in Claude Code

Vuoi supporto? Sei sotto attacco? Stato dei servizi
Vuoi supporto? Sei sotto attacco? Stato dei servizi