Graph Engineering: come eseguire 1.000 agenti AI in parallelo da un singolo prompt

@0xWast3
INGLESE1 giorno fa · 22 lug 2026
146K
151
19
13
376

TL;DR

Un approfondimento tecnico sulla graph engineering per agenti AI, che illustra come identificare le dipendenze reali e utilizzare l'esecuzione parallela per scalare i flussi di lavoro.

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.

wast3 - inline image

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:

text
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.

wast3 - inline image

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.

text
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:

python
1from dataclasses import dataclass
2
3@dataclass
4class TaskNode:
5 id: str
6 prompt: str
7 depends_on: list[str] # ID dei nodi di cui questo ha effettivamente bisogno
8
9def 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_on
15
16# Esempio: la maggior parte delle "catene" si riduce a 2-3 gruppi di dipendenza reali
17nodes = [
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]
24
25# audit_routes, check_auth, fetch_weather NON hanno archi tra di loro
26# 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

wast3 - inline image

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:

text
1Crea un flusso di lavoro per controllare ogni file di route in questo codebase.
2
3Per ogni file di route, controlla in modo indipendente:
4- middleware di autenticazione presente
5- convalida dell'input su tutti i parametri
6- rate limiting configurato
7- gestione degli errori che non perde stack trace
8
9Esegui questi controlli in parallelo su tutti i file di route —
10non dipendono l'uno dall'altro.
11
12Dopo che tutti i file sono stati controllati, produci un unico report
13consolidato raggruppato per gravità: critico, avviso, informativo.
14
15Il 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:

python
1import asyncio
2from anthropic import Anthropic
3
4client = Anthropic()
5
6async 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 errori
16
17 File: {filepath}
18
19 Restituisci JSON: {{"file": "", "issues": [], "severity": ""}}"""
20 }]
21 )
22 return {"file": filepath, "result": response.content[0].text}
23
24async 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 route
32 in un unico report raggruppato per gravità:
33
34 {results}"""
35 }]
36 )
37 return response.content[0].text
38
39async def run_graph(route_files: list[str]):
40 # Diramazione -- tutti i nodi indipendenti vengono eseguiti contemporaneamente
41 audit_tasks = [audit_route_file(f) for f in route_files]
42 results = await asyncio.gather(*audit_tasks)
43
44 # Ricombinazione -- l'unico nodo con una dipendenza reale
45 report = await consolidate(results)
46 return report
47
48# 40 file di route, un prompt, un passaggio in parallelo
49results = 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.

python
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)]
5
6 batch_summaries = await asyncio.gather(*[
7 summarize_batch(batch) for batch in batches
8 ])
9
10 # Il consolidamento finale lavora sui riassunti, non su 1.000 risultati grezzi
11 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.

python
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

wast3 - inline image

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:

text
1 Orchestratore
2 |
3 +--------+-------+-------+--------+
4 v v v v v
5 Nodo 1 Nodo 2 Nodo 3 ... Nodo N
6 (paralleli, senza archi tra di loro)
7 | | | |
8 +--------+-------+-------+-------+
9 v
10 Riepilogo Lotto <- ricombinazione a strati
11 (gruppi da 30)
12 v
13 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.

python
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}
12
13 Scomponi in un grafo:
14 - Elenca ogni nodo indipendente (nessun arco condiviso)
15 - Elenca le eventuali dipendenze reali tra i nodi
16 - Raggruppa i nodi in lotti per la ricombinazione se il conteggio > 50
17
18 Restituisci JSON con: nodes, edges, batch_groups"""
19 }]
20 )
21
22 graph = parse_plan(plan.content[0].text)
23
24 # Esegui i nodi indipendenti in parallelo
25 node_results = await asyncio.gather(*[
26 execute_node(n) for n in graph["nodes"] if not n["depends_on"]
27 ])
28
29 # Poi esegui i nodi dipendenti, rispettando solo gli archi reali
30 final = await execute_dependent_chain(graph["edges"], node_results)
31
32 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.

Rielabora in YouMind

Turn one viral article into a full content workflow

Collect the source, decode the pattern, create assets, draft the story, and distribute from one AI workspace.

Explore YouMind
Per i creator

Trasforma il tuo Markdown in un articolo 𝕏 pulito

Quando pubblichi i tuoi testi lunghi, formattare immagini, tabelle e blocchi di codice per 𝕏 è una seccatura. YouMind trasforma un'intera bozza Markdown in un articolo 𝕏 pulito e pronto da pubblicare.

Prova Markdown verso 𝕏

Altri pattern da decodificare

Articoli virali recenti

Esplora altri articoli virali