L'articolo precedente ha coperto cosa sono i dynamic workflow di Claude Opus 4.8 — l'architettura, i benchmark, i prezzi e il rischio di token explosion. Questo articolo è il companion implementativo: come eseguire concretamente un workflow, cosa configurare prima di farlo, e come replicare la stessa pipeline in Python quando serve controllo programmatico. L'esempio pratico è una code review di una pipeline dati: ogni modulo riceve il suo agente dedicato, gli agenti girano in parallelo, e l'orchestratore aggrega i risultati in un report strutturato.

Esistono due modi distinti per usare i workflow di Claude Opus 4.8 in produzione. Non sono intercambiabili — ognuno ha un modello di controllo diverso, una superficie di integrazione diversa e failure mode diversi. Scegliere quello sbagliato per il proprio contesto è l'errore più comune che i team fanno quando adottano i workflow agentici per la prima volta.

Track A
Claude Code CLI
  • Claude decide come orchestrare
  • Fino a 16 agenti concorrenti, 1.000 per run
  • Fornisci la direzione, non il codice di coordinamento
  • Ideale per: task open-ended su codebase
  • Tempo di setup: minuti (AGENTS.md + un comando)
  • Visibilità costi: usage.iterations nell'output
Track B
Python SDK
  • Sei tu a scrivere il codice di orchestrazione
  • Concorrenza limitata dal tuo Semaphore + rate limit
  • Controllo totale: retry logic, kill switch, routing personalizzato
  • Ideale per: pipeline strutturate e ripetibili
  • Tempo di setup: ore (orchestrator class + telemetria)
  • Visibilità costi: oggetto usage esplicito per chiamata

Il caso d'uso — code review di una pipeline Python

L'esempio di codebase in questo articolo è un progetto Python ETL standard: quattro moduli, ciascuno con problemi noti che una code review dovrebbe rilevare — error handling mancante, nessuna type annotation, concatenazione SQL non sicura e casi limite non testati. L'obiettivo è assegnare a ogni modulo un agente dedicato, con generazione di test e proposte di fix prodotte in parallelo, poi aggregate in un unico report.

Struttura del progetto — il codebase target
etl_pipeline/
├── AGENTS.md                    # agent instructions (shared by both tracks)
├── src/
│   ├── extractors/
│   │   └── api_extractor.py     # fetches data from external API
│   ├── transformers/
│   │   └── data_transformer.py  # normalises and validates raw records
│   ├── loaders/
│   │   └── db_loader.py         # writes to PostgreSQL via SQLAlchemy
│   └── validators/
│       └── schema_validator.py  # validates Pydantic schemas against data
└── tests/                       # empty — agents will populate this

Track A — Claude Code CLI con ultracode

Prerequisiti

I dynamic workflow richiedono Claude Code v2.1.154 o superiore. Verifica la versione con claude --version e aggiorna con claude update (oppure npm update -g @anthropic-ai/claude-code se preferisci npm). Devi anche passare a Opus 4.8 all'interno della sessione — il motore di workflow si attiva solo sui modelli che supportano l'effort xhigh (Anthropic, 2026b).

Disponibilità per piano: i workflow sono attivi per default sui piani Max e Team. Gli utenti Pro possono attivarli manualmente da /config. I piani Enterprise hanno i workflow disabilitati per default e richiedono l'abilitazione da parte dell'amministratore attraverso le impostazioni gestite.

Step 1 — Scrivi AGENTS.md

AGENTS.md è il documento che Claude legge prima di iniziare qualsiasi lavoro. Scrivilo prima di attivare il workflow — Claude lo usa per pianificare l'orchestrazione, allocare gli agenti e rispettare i vincoli. Istruzioni vaghe producono orchestrazioni vaghe; istruzioni specifiche producono output circoscritti e revisionabili.

AGENTS.md — istruzioni per il workflow di code review
# AGENTS.md — ETL Pipeline Code Review

## Context
Python ETL pipeline. Four modules in src/. Tests in tests/ are empty and must be populated.
The pipeline runs in production on AWS Lambda with a 15-second timeout per invocation.

## Review criteria (apply to every module)
1. Security: SQL injection, secrets in code, unsafe deserialization, unvalidated inputs
2. Error handling: all IO operations must have try/except with specific exception types
3. Type annotations: all public functions must have full signatures (PEP 484)
4. Testability: functions with side effects must be injectable (no hard-coded dependencies)

## Output format per module
For each file, produce three artefacts:
- ISSUES.md  — numbered list of issues with severity (critical / high / medium)
- tests/test_[module].py — pytest file that fails on the identified issues
- PATCH.diff  — unified diff that fixes all issues and makes the tests pass

## Forbidden
- Do NOT modify files outside src/ and tests/
- Do NOT add external dependencies not already in requirements.txt
- Do NOT rewrite a module from scratch — patch the existing code

## Concurrency note
Each module is independent. All four can be reviewed in parallel.
Assign one primary agent and one adversarial reviewer per module.

Step 2 — Avvia il workflow

Claude Code CLI — avvio del workflow di review
# 1. Navigate to the project root (where AGENTS.md lives)
cd etl_pipeline/

# 2. Start a Claude Code session
claude

# 3. Inside the session: switch to Opus 4.8
/model claude-opus-4-8

# 4. Set ultracode effort — activates xhigh reasoning + workflow engine
/effort ultracode

# 5. Trigger the workflow — include "workflow" anywhere in the prompt
Create a workflow to review all four Python modules in src/ following AGENTS.md,
producing ISSUES.md, a test file, and a PATCH.diff for each module.
Modules are independent — run reviews in parallel.

# Claude will now plan the orchestration, confirm the approach,
# then dispatch agents. Each wave runs up to 16 agents concurrently.
# Track spend in real time:
/usage
Cosa succede dentro il workflow

Dopo l'invio del prompt, Claude Code entra in modalità pianificazione: legge AGENTS.md, esplora la directory src/ e scrive uno script JavaScript di coordinamento interno che gestisce loop, branching e decisioni sul numero di agenti. Lancia poi gli agenti a ondate fino a 16 concorrenti per volta — il limite è pensato per proteggere le risorse CPU locali. Per questo task a quattro moduli vedrai: un agente di pianificazione che mappa le dipendenze tra moduli, quattro agenti di review primari (uno per modulo), quattro revisori avversariali che controllano ogni review alla ricerca di problemi mancati, e un agente di sintesi che produce un SUMMARY.md finale. Claude Code mostra il progresso nel terminale — il comando /usage visualizza la spesa in token per iterazione (Anthropic, 2026b).

Quando il workflow termina, torna subito all'effort standard: /effort high. Ultracode applica il ragionamento xhigh a ogni messaggio della sessione — incluse le domande di follow-up banali dopo il completamento del workflow.

Track B — Orchestrazione con il Python SDK

L'approccio CLI funziona bene per task one-off dove Claude può esplorare liberamente il codebase. Per pipeline pianificate, integrazione CI/CD, o workflow che necessitano di retry logic personalizzata e contabilità dei costi, serve un orchestratore Python — una classe che gestisce le chiamate API Anthropic, applica i limiti di concorrenza, tiene traccia della spesa in token e gestisce la compattazione nelle sessioni lunghe.

Perché costruire il proprio orchestratore

L'SDK Python ti dà tre cose che la CLI non può offrire: comportamento deterministico di retry sugli errori API, un token budget kill switch imposto nel tuo codice (non dipendente dai guardrail opzionali di Anthropic), e output strutturato che alimenta direttamente i sistemi downstream — un database, una notifica Slack, un gate CI — senza intervento manuale.

La classe orchestratore

Python — AsyncWorkflowOrchestrator con token kill switch
import asyncio
import anthropic
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional

@dataclass
class ReviewResult:
    file_path: str
    issues: str = ""
    test_code: str = ""
    patch: str = ""
    tokens_used: int = 0
    error: Optional[str] = None


class AsyncWorkflowOrchestrator:
    """Parallel code review orchestrator with token budget enforcement."""

    SYSTEM_PROMPT = """You are a senior Python engineer performing a code review.
For the file provided, return a JSON object with three keys:
  "issues": markdown list of issues with severity labels
  "test_code": complete pytest file that fails on the issues you found
  "patch": unified diff that fixes all issues and makes the tests pass
Return ONLY the JSON — no prose before or after."""

    def __init__(
        self,
        model: str = "claude-opus-4-8",
        token_budget: int = 200_000,  # kill switch threshold
        max_concurrency: int = 5,       # respect API rate limits
    ):
        self.client = anthropic.AsyncAnthropic()
        self.model = model
        self.token_budget = token_budget
        self.tokens_spent = 0
        self._killed = False
        self._semaphore = asyncio.Semaphore(max_concurrency)
        self._lock = asyncio.Lock()   # guards tokens_spent counter

    async def _charge_tokens(self, tokens: int) -> bool:
        """Thread-safe token accounting. Returns False if budget exceeded."""
        async with self._lock:
            self.tokens_spent += tokens
            if self.tokens_spent > self.token_budget:
                self._killed = True
                return False
        return True

    async def review_file(self, path: Path) -> ReviewResult:
        result = ReviewResult(file_path=str(path))

        if self._killed:
            result.error = "Workflow killed: token budget exceeded"
            return result

        async with self._semaphore:
            try:
                content = path.read_text(encoding="utf-8")
                response = await self.client.messages.create(
                    model=self.model,
                    max_tokens=8192,
                    thinking={"type": "enabled", "budget_tokens": 10_000},
                    system=[{
                        "type": "text",
                        "text": self.SYSTEM_PROMPT,
                        "cache_control": {"type": "ephemeral", "ttl": "1h"}
                    }],
                    messages=[{
                        "role": "user",
                        "content": f"Review this file ({path.name}):\n\n```python\n{content}\n```"
                    }]
                )

                # Charge tokens — kill if over budget
                total = response.usage.input_tokens + response.usage.output_tokens
                result.tokens_used = total
                if not await self._charge_tokens(total):
                    result.error = f"Budget exceeded after {self.tokens_spent:,} tokens"
                    return result

                # Extract the text block (skip thinking blocks)
                text = next(
                    b.text for b in response.content if b.type == "text"
                )
                import json
                data = json.loads(text)
                result.issues    = data.get("issues", "")
                result.test_code = data.get("test_code", "")
                result.patch     = data.get("patch", "")

            except Exception as e:
                result.error = str(e)

        return result

    async def run(self, src_dir: Path) -> list[ReviewResult]:
        files = list(src_dir.rglob("*.py"))
        tasks = [self.review_file(f) for f in files]
        return await asyncio.gather(*tasks)

Eseguire l'orchestratore

Python — script end-to-end con generazione del report
import asyncio
from pathlib import Path
from orchestrator import AsyncWorkflowOrchestrator

async def main():
    src = Path("etl_pipeline/src")
    out = Path("review_output")
    out.mkdir(exist_ok=True)

    orchestrator = AsyncWorkflowOrchestrator(
        model="claude-opus-4-8",
        token_budget=300_000,  # ~$1.50 at Standard tier — adjust to your comfort
        max_concurrency=4,      # 4 files in parallel, one API call each
    )

    print(f"Reviewing {src} — kill switch at {orchestrator.token_budget:,} tokens")
    results = await orchestrator.run(src)

    # Write artefacts and print summary
    errors, reviewed = [], []
    for r in results:
        name = Path(r.file_path).stem
        if r.error:
            errors.append(r)
            print(f"  ✗ {name}: {r.error}")
            continue

        (out / f"{name}_ISSUES.md").write_text(r.issues)
        (out / f"test_{name}.py").write_text(r.test_code)
        (out / f"{name}.patch").write_text(r.patch)
        reviewed.append(r)
        print(f"  ✓ {name}: {r.tokens_used:,} tokens")

    total = orchestrator.tokens_spent
    cost  = (total / 1_000_000) * 5.00   # Standard tier input rate
    print(f"\nTotal: {len(reviewed)} reviewed, {len(errors)} failed")
    print(f"Tokens: {total:,} | Estimated cost: ${cost:.4f}")
    print(f"Output: {out}/")

asyncio.run(main())
Il calcolo dei token per questo esempio

Un tipico modulo Python da 150–300 righe costa circa 2.000–4.000 token di input passato con il system prompt (che viene messo in cache dopo la prima chiamata). Quattro moduli in parallelo: circa 8.000–16.000 token di input totali, più l'output. Al prezzo Standard ($5,00/M input, $25,00/M output), la review completa di un progetto a quattro moduli costa meno di $0,15. Il token_budget=300_000 nell'esempio è una rete di sicurezza conservativa — al prezzo Standard equivale a $1,50 di esposizione massima prima che il kill switch scatti.

Il system prompt condiviso viene messo in cache per 1 ora dopo che il primo agente lo scrive. Gli agenti 2–4 leggono dalla cache e vengono fatturati $0,50/M invece di $5,00/M su quei token — un risparmio del 90% sul blocco di input più costoso. Per questo impostare la cache del system prompt prima di inviare gli agenti paralleli vale ampiamente l'overhead di una riga di codice.

Gestione delle sessioni lunghe — compattazione nell'orchestratore Python

Per le pipeline che girano continuamente — una review notturna su una codebase grande, o un agente di monitoraggio che accumula contesto per ore — è necessario gestire la compattazione nell'orchestratore. Il pattern della guida tecnica si applica direttamente: abilita pause_after_compaction, intercetta la stop reason, persisti il riassunto e riprendi (Anthropic, 2026d).

Python — wrapper di chiamata compaction-aware per sessioni lunghe
async def call_with_compaction(
    client: anthropic.AsyncAnthropic,
    messages: list,
    vector_store,
    session_id: str,
    **kwargs,
) -> anthropic.types.Message:
    """Wrap a beta messages call; handles compaction transparently."""
    response = await client.beta.messages.create(
        betas=["compact-2026-01-12"],
        context_management={
            "edits": [{
                "strategy": "compact_20260112",
                "trigger": {"type": "token_count", "threshold": 80_000},
                "instructions": "Preserve all file paths, issue IDs, and patch hunks verbatim.",
                "pause_after_compaction": True,
            }]
        },
        messages=messages,
        **kwargs,
    )

    if response.stop_reason == "compaction":
        block = next(b for b in response.content if b.type == "compaction")
        summary = block.content or ""           # guard against null (known edge case)
        if summary:
            await vector_store.upsert(session_id, summary)
        # Resume with empty messages — server has already replaced history
        return await call_with_compaction(client, [], vector_store, session_id, **kwargs)

    return response

Scegliere tra i due approcci

Punto di decisione Usa Claude Code CLI Usa Python SDK
Struttura del task Open-ended — lo scope non è completamente definito a priori Strutturato — sai esattamente quali file ricevono quale trattamento
Trigger Manuale, interattivo, o pianificato via cron + shell script Qualsiasi evento Python: CI webhook, schedule, API call, queue message
Output File scritti su disco; Claude Code li formatta Dati strutturati (JSON, dataclass) che il tuo codice elabora
Error handling Claude Code riprova internamente; visibilità limitata Controllo totale: exponential backoff, retry parziale, skip selettivo
Visibilità costi /usage nella sessione; non facilmente esportabile Oggetto usage per chiamata — logga in qualsiasi sistema di osservabilità
Compattazione Automatica; non puoi fermarti e ispezionare il riassunto pause_after_compaction esplicito — controlli la persistenza

Un approccio ibrido funziona bene per i team maturi: usa Claude Code CLI per le run esplorative (nuove codebase, task ambigui, proof-of-concept), poi codifica il pattern riuscito in un orchestratore Python per l'uso pianificato in produzione. Il file AGENTS.md viaggia invariato tra i due approcci — è la specifica stabile che sia Claude Code che i tuoi agenti Python leggono.

Checklist di produzione prima del go-live

01
Imposta token_budget prima della prima run, non dopo la prima fattura
Parti a 2× il costo di una singola review manuale. Se un modulo costa 4.000 token da revisionare, quattro moduli in parallelo dovrebbero costare meno di 16.000 — imposta il budget a 40.000 per la prima run. La policy no-refund di Anthropic sul consumo agentico fuori controllo è esplicita; il tuo kill switch è l'unica protezione che hai.
02
Metti in cache il system prompt prima di inviare gli agenti paralleli
Tutti gli agenti condividono lo stesso system prompt. La prima chiamata scrive la cache; ogni chiamata parallela successiva la legge a $0,50/M invece di $5,00/M. Con max_concurrency=4, solo una delle quattro chiamate parallele scrive la cache — le altre sono letture istantanee. Imposta 'ttl': '1h' se tutti gli agenti finiranno entro un'ora.
03
Valida l'output JSON prima di scrivere gli artefatti
Gli agenti restituiscono occasionalmente prosa ben formata invece di JSON (specialmente a livelli di effort più bassi o su input brevi). Avvolgi la chiamata json.loads() in un try/except e loga la risposta raw in caso di fallimento — non scartarla silenziosamente. Un ReviewResult con error impostato è molto più utile di un file silenziosamente vuoto.
04
Mantieni max_concurrency a 4–6 e gestisci i 429 con backoff esponenziale
Con più di 6–8 chiamate API concorrenti a Opus 4.8 inizierai a ricevere errori 429 — la soglia esatta dipende dal tier dell'account e dal carico corrente della piattaforma. Implementa exponential backoff sui 429 invece di un sleep fisso: intercetta anthropic.RateLimitError, attendi 2tentativo secondi (con cap a 60s) e riprova. Il Semaphore nell'orchestratore limita la concorrenza al momento del dispatch; il backoff gestisce i limiti transitori durante l'esecuzione.
05
Torna a /effort high non appena il task pesante è finito
In Claude Code, ultracode si applica a ogni messaggio della sessione — incluse le domande di follow-up banali dopo il completamento del workflow. Dimenticarsi di eseguire /effort high dopo la fine di un workflow significa che il tuo prossimo "quali file hai modificato?" brucia token di ragionamento xhigh. Questa è una fonte comune di picchi di costo inspiegabili nei deployment di team.
Riferimenti
  1. Anthropic. (2026a). Introducing Claude Opus 4.8. anthropic.com/news/claude-opus-4-8
  2. Anthropic. (2026b). Introducing dynamic workflows in Claude Code. claude.com/blog/introducing-dynamic-workflows-in-claude-code
  3. Anthropic. (2026c). Python SDK — Claude API Docs. platform.claude.com/docs/en/api/sdks/python
  4. Anthropic. (2026d). Compaction — Claude API Docs. platform.claude.com/docs/en/build-with-claude/compaction
  5. Anthropic. (2026e). Prompt caching — Claude API Docs. platform.claude.com/docs/en/build-with-claude/prompt-caching
  6. Anthropic. (2026f). CLI reference — Claude Code Docs. code.claude.com/docs/en/cli-reference
M
Michele Mader
Technical Leader · AI Systems & Data Engineering

Guido la direzione tecnica di prodotti dati basati su AI per clienti enterprise — definendo architetture, scegliendo lo stack tecnologico e gestendo la delivery dal roadmap alla produzione.

Connettiti su LinkedIn