Chiunque stia costruendo sistemi multi-agente nel 2026 scrive ancora linee rette. Passo uno, poi passo due, poi passo tre – ognuno in attesa del precedente. Ecco perché è lento e come risolverlo.

Il problema che nessuno controlla
Hai costruito un agente multi-step. Funziona. Ed è anche lento.
Pensi che il collo di bottiglia sia il modello. Non lo è.
Il collo di bottiglia è la forma che hai disegnato. Una catena – il passo 1 aspetta il passo 2, il passo 2 aspetta il passo 3 – impone un'esecuzione sequenziale anche quando metà di quei passi non hanno nulla a che fare l'uno con l'altro.
"Riassumi questo documento, poi controlla il meteo" sono due compiti indipendenti travestiti da un unico flusso di lavoro. L'attività meteo non ha bisogno del riassunto. Non ne ha mai avuto bisogno. Ma se lo hai scritto come una catena, aspetta comunque.
Quell'attesa sprecata, moltiplicata per dozzine di passaggi, è dove scompare la maggior parte del tuo tempo di esecuzione.
Capitolo 1 – Loop vs grafi
Un loop è un'unità di auto-miglioramento:
1prova qualcosa → controlla il risultato → aggiusta → riprova
Questo è l'atomo. Un agente, una metrica, che cicla fino a convergenza.
I loop hanno un modo di fallimento noto: ottimizzano esattamente ciò che misuri e nient'altro. Un bot di supporto ottimizzato per chiudere i ticket velocemente chiuderà i ticket velocemente – mentre la soddisfazione crolla silenziosamente. Il loop non vede al di fuori della propria metrica. Questa è la legge di Goodhart che si manifesta nell'architettura del tuo agente.
Un grafo risolve questo problema per progettazione. Invece di un singolo loop che insegue un numero, costruisci una rete di loop che si guardano e si correggono a vicenda. L'output del Nodo A alimenta il Nodo B. Il Nodo C viene eseguito in modo indipendente e controlla entrambi. Nessuna singola metrica guida l'intero sistema – lo fa la struttura.

Per i sistemi di agenti, questo significa un cambiamento concreto: smetti di scrivere un agente che fa tutto dall'inizio alla fine. Progetta prima la forma del lavoro – cosa deve succedere prima di cosa, cosa può essere eseguito contemporaneamente, cosa ha effettivamente bisogno di aspettare.
Capitolo 2 – Nodi, archi e il test che li separa
Un grafo ha esattamente due componenti:
Nodo – un'unità di lavoro. Un agente, un compito, un input, un output.
Arco – una dipendenza reale. L'input del Nodo B richiede l'output del Nodo A.
L'errore che quasi tutti commettono: trattare "e poi" come un arco per impostazione predefinita.
1"Leggi questo codice e poi scrivi il changelog"2"Recupera la pagina dei prezzi e poi riassumi le funzionalità dei concorrenti"
Per ogni "e poi" nel tuo flusso di lavoro, fai una domanda:
Il passo successivo legge effettivamente l'output del passo precedente?
Se sì → arco reale. Mantieni l'ordine sequenziale. Se no → nessun arco. L'attesa è sprecata. Esegui in parallelo.
Se nessun dato attraversa il confine tra due compiti, sono indipendenti – e ogni coppia indipendente che esegui in sequenza è tempo di esecuzione che butti via gratuitamente.
Ecco il test applicato nel codice:
1from dataclasses import dataclass23@dataclass4class TaskNode:5 id: str6 prompt: str7 depends_on: list[str] # ID dei nodi di cui questo ha effettivamente bisogno89def has_real_edge(node_a: TaskNode, node_b: TaskNode) -> bool:10 """11 Il test fondamentale dell'ingegneria dei grafi:12 il prompt di node_b richiede effettivamente l'output di node_a?13 """14 return node_a.id in node_b.depends_on1516# Esempio: la maggior parte delle "catene" si riduce a 2-3 gruppi di dipendenza reali17nodes = [18 TaskNode("audit_routes", "Elenca tutti i file delle route API", []),19 TaskNode("check_auth", "Controlla la copertura del middleware di autenticazione", []),20 TaskNode("fetch_weather", "Ottieni il meteo di oggi", []),21 TaskNode("summarize", "Riassumi i risultati delle route e dell'autenticazione",22 depends_on=["audit_routes", "check_auth"]),23]2425# audit_routes, check_auth, fetch_weather NON hanno archi tra di loro26# Vengono eseguiti in parallelo. Solo "summarize" ha archi reali -- aspetta.
Il tuo attuale agente "fai A, poi B, poi C" è tecnicamente già un grafo. È solo il peggiore possibile – una singola catena in cui se C si blocca, nulla più a valle viene mai eseguito.
Capitolo 3 – Costruire il tuo primo grafo

Requisiti:
- Claude Code (versione recente con supporto per Dynamic Workflows).
- Piano Max, Team o Enterprise – i flussi di lavoro sono attivi per impostazione predefinita. Su Pro, abilitali manualmente.
Apri un repository reale. Non un esempio giocattolo – il vantaggio si vede solo su scala reale.
Il prompt che dà il via al tuo primo grafo:
1Crea un flusso di lavoro per controllare ogni file di route in questo codebase.23Per ogni file di route, controlla in modo indipendente:4- middleware di autenticazione presente5- convalida dell'input su tutti i parametri6- rate limiting configurato7- gestione degli errori che non perde stack trace89Esegui questi controlli in parallelo su tutti i file di route —10non dipendono l'uno dall'altro.1112Dopo che tutti i file sono stati controllati, produci un unico report13consolidato raggruppato per gravità: critico, avviso, informativo.1415Il passaggio di consolidamento deve attendere il completamento di tutti i controlli.16Tutto ciò che lo precede non deve attendere.
Nota la struttura incorporata nel prompt stesso: lavoro parallelo esplicitamente indicato, l'unica dipendenza reale (consolidamento che attende tutti i controlli) esplicitamente nominata. Non stai sperando che l'agente deduca il grafo – lo stai descrivendo.
Cosa succede sotto il cofano – una versione semplificata dell'orchestrazione:
1import asyncio2from anthropic import Anthropic34client = Anthropic()56async def audit_route_file(filepath: str) -> dict:7 """Un nodo. Viene eseguito indipendentemente da ogni altro file di route."""8 response = await client.messages.create(9 model="claude-sonnet-5",10 max_tokens=1000,11 messages=[{12 "role": "user",13 "content": f"""Controlla questo file di route per:14 - middleware di autenticazione, convalida dell'input,15 rate limiting, gestione degli errori1617 File: {filepath}1819 Restituisci JSON: {{"file": "", "issues": [], "severity": ""}}"""20 }]21 )22 return {"file": filepath, "result": response.content[0].text}2324async def consolidate(results: list[dict]) -> str:25 """L'unico arco reale -- attende che ogni nodo di controllo sia terminato."""26 response = await client.messages.create(27 model="claude-opus-4-8",28 max_tokens=2000,29 messages=[{30 "role": "user",31 "content": f"""Consolida questi {len(results)} audit delle route32 in un unico report raggruppato per gravità:3334 {results}"""35 }]36 )37 return response.content[0].text3839async def run_graph(route_files: list[str]):40 # Diramazione -- tutti i nodi indipendenti vengono eseguiti contemporaneamente41 audit_tasks = [audit_route_file(f) for f in route_files]42 results = await asyncio.gather(*audit_tasks)4344 # Ricombinazione -- l'unico nodo con una dipendenza reale45 report = await consolidate(results)46 return report4748# 40 file di route, un prompt, un passaggio in parallelo49results = asyncio.run(run_graph([50 f"routes/{f}.py" for f in ["auth", "users", "billing", "orders"]51 # ...altri 36
40 chiamate API sequenziali a ~8 secondi ciascuna sono oltre 5 minuti. Le stesse 40 chiamate distribuite in parallelo: meno di 15 secondi, limitate dal tuo singolo file più lento, non dalla somma di tutti loro.
Capitolo 4 – Dove i grafi si rompono effettivamente
L'ingegneria dei grafi fallisce in tre punti prevedibili. Conoscili prima di incontrarli.
Collasso del contesto. Distribuisci 1.000 nodi e prova a inserire tutti gli 1.000 output in un unico passaggio di consolidamento, e supererai qualsiasi finestra di contesto ancora prima che inizi la sintesi. Rimedio: stratifica la tua ricombinazione. Raggruppa i nodi in lotti da 20-50, riassumi ogni lotto, poi consolida i riassunti – non gli output grezzi.
1async def layered_consolidate(results: list[dict], batch_size: int = 30):2 """Ricombinazione a strati -- non sintetizzare mai output grezzi su larga scala."""3 batches = [results[i:i+batch_size]4 for i in range(0, len(results), batch_size)]56 batch_summaries = await asyncio.gather(*[7 summarize_batch(batch) for batch in batches8 ])910 # Il consolidamento finale lavora sui riassunti, non su 1.000 risultati grezzi11 return await consolidate(batch_summaries)
Falsa indipendenza. Presumerai che due nodi siano indipendenti perché i loro prompt non si riferiscono l'uno all'altro – ma entrambi scrivono sullo stesso file o colpiscono la stessa API con rate limiting. Questo è un arco nascosto. Rimedio: controlla le risorse condivise, non solo i dati condivisi. Due nodi con un conflitto di scrittura hanno bisogno di un arco anche senza dipendenza di dati.
Fallimento silenzioso del nodo. In una catena, un fallimento ferma tutto – fastidioso ma ovvio. In un grafo, un nodo fallito tra 200 può sparire in un report che sembra completo. Rimedio: ogni passaggio di ricombinazione controlla il conteggio dei nodi rispetto al conteggio previsto prima di sintetizzare, e segnala esplicitamente i gap invece di lavorare silenziosamente con dati parziali.
1async def safe_consolidate(results: list[dict], expected_count: int):2 if len(results) < expected_count:3 missing = expected_count - len(results)4 print(f"ATTENZIONE: {missing} nodi sono falliti silenziosamente. "5 f"Il report sarà incompleto.")6 return await consolidate(results)
Capitolo 5 – Scalare a una flotta reale

Una volta che lo schema funziona con 40 nodi, scalare a centinaia è un cambiamento di configurazione, non una riprogettazione – a patto che tu abbia costruito il grafo correttamente dal Capitolo 2 in poi.
La forma completa per la produzione:
1 Orchestratore2 |3 +--------+-------+-------+--------+4 v v v v v5 Nodo 1 Nodo 2 Nodo 3 ... Nodo N6 (paralleli, senza archi tra di loro)7 | | | |8 +--------+-------+-------+-------+9 v10 Riepilogo Lotto <- ricombinazione a strati11 (gruppi da 30)12 v13 Report Finale <- l'unico vero arco
L'unico lavoro dell'orchestratore: scomporre il compito in nodi, identificare gli archi reali e distribuire. Non fa alcun lavoro da solo – disegna il grafo.
1async def orchestrate(task: str, resources: list[str]):2 """3 Il nodo orchestratore -- scompone, non esegue.4 """5 plan = await client.messages.create(6 model="claude-opus-4-8",7 max_tokens=2000,8 messages=[{9 "role": "user",10 "content": f"""Compito: {task}11 Risorse disponibili: {resources}1213 Scomponi in un grafo:14 - Elenca ogni nodo indipendente (nessun arco condiviso)15 - Elenca le eventuali dipendenze reali tra i nodi16 - Raggruppa i nodi in lotti per la ricombinazione se il conteggio > 501718 Restituisci JSON con: nodes, edges, batch_groups"""19 }]20 )2122 graph = parse_plan(plan.content[0].text)2324 # Esegui i nodi indipendenti in parallelo25 node_results = await asyncio.gather(*[26 execute_node(n) for n in graph["nodes"] if not n["depends_on"]27 ])2829 # Poi esegui i nodi dipendenti, rispettando solo gli archi reali30 final = await execute_dependent_chain(graph["edges"], node_results)3132 return final
Questo è il vero cambiamento che l'ingegneria dei grafi rappresenta: smetti di essere la persona che scrive ogni passaggio e diventi la persona che progetta la struttura delle dipendenze. Gli agenti riempiono i nodi. Tu possiedi gli archi.
Cosa cambia quando pensi per grafi invece che per linee
Un agente lineare con 40 passaggi ha 40 punti di fallimento sequenziale e una latenza 40 volte superiore al suo singolo passaggio più lento.
Un grafo con le stesse 40 unità di lavoro ha tanti punti di fallimento parallelo quante sono le tue dipendenze reali – di solito da 3 a 5 nella maggior parte dei flussi di lavoro – e una latenza limitata dal tuo strato più lento, non dal conteggio totale dei passaggi.
Non è un miglioramento marginale. È la differenza tra un flusso di lavoro che impiega 5 minuti e uno che ne impiega 15 secondi, eseguendo lo stesso identico lavoro sottostante.
Il modello non è mai stato il collo di bottiglia. La linea che hai disegnato lo era.
Questa è un'analisi tecnica dei modelli di orchestrazione multi-agente aggiornata a luglio 2026. Gli esempi di codice sono illustrativi – adatta la gestione degli errori, il rate limiting e la logica di ripetizione al tuo ambiente di produzione prima di distribuire su larga scala.
Grazie per aver letto.



![[Memo] I capi stanno tagliando i ponti con i subordinati meno performanti](/cdn-cgi/image/width=1920,quality=90,format=auto,metadata=none/https%3A%2F%2Fcms-assets.youmind.com%2Fmedia%2F1784827522698_408j7z_HN3Kb76awAAvvjF.jpg)

