./run 59-percento-tre-strati-agenti-agno
Il 59%: perché ho smesso di lasciar fare tutto all'LLM
Diario di uno scaffold multi-agente su Agno: tre strati direttiva/orchestrazione/esecuzione, SOP in linguaggio naturale sopra, Python deterministico sotto. Perché e cosa ho imparato.
- status
- draft
- project
- agno-agents
- updated
- 2026-07-07
- tags
Fai cinque passi con un LLM, ognuno affidabile al 90%. Quanto ci arrivi intero? 0.9^5 = 0.59. Cinquantanove per cento. In pratica: una pipeline agentica di cinque step “quasi sempre giusti” fallisce due volte su cinque. Questo numero — che non è mio, è banale aritmetica — è il motivo per cui ho passato un weekend a costruire uno scaffold che toglie lavoro all’LLM invece di dargliene.
L’idea
agno-agents è un’impalcatura personale per automazioni multi-agente costruita sul framework Agno (v2.5+). Ma il cuore non è Agno: è una regola architetturale che mi sono imposto, scritta nero su bianco nel CLAUDE.md del progetto. Tre strati, responsabilità separate:
- Strato 1 — Direttiva (cosa fare). SOP in Markdown dentro
directives/. Sono istruzioni in linguaggio naturale, scritte come le daresti a un dipendente di medio livello: obiettivi, input, tool da usare, output attesi, casi limite. - Strato 2 — Orchestrazione (decidere). Questo è l’LLM. Il suo unico mestiere è routing intelligente: legge la direttiva, chiama gli script giusti nell’ordine giusto, gestisce gli errori, chiede chiarimenti. Non fa il lavoro sporco.
- Strato 3 — Esecuzione (fare). Script Python deterministici in
execution/. Chiamate API, I/O, database. Testabili, veloci, ripetibili.
La frase che tengo appesa: non provi a scrapare tu il sito — leggi directives/scrape_website.md, prepari input e output, poi lanci execution/scrape_single_site.py. L’LLM non è il muratore, è il caposquadra.
Come funziona davvero
Sopra questi tre strati “filosofici” gira un secondo pattern, più concreto: un meta-team che costruisce altri team. C’è un Architect Team (Tier 1) di 4 agenti — Analyzer, Designer, Soul Writer, Builder — che prende un prompt tipo “mi serve un team che scriva post Instagram per hotel” e sputa fuori un progetto deployabile: file SOUL, un manifest YAML, e la registrazione in DB. Poi il Task Team (Tier 2) esegue davvero.
Il pezzo che mi piace di più è come sono cablati i team annidati nel team_factory.py. Un sotto-team di worker che collaborano, e un sotto-team di reviewer che invece non devono collaborare:
# Worker: condividono contesto, si costruiscono sopra a vicenda
work_team = Team(
name=f"{manifest.project_id}-workers",
mode="coordinate",
members=worker_agents,
share_member_interactions=True, # <-- vedono il lavoro l'uno dell'altro
...
)
# Reviewer: broadcast, ognuno cieco rispetto agli altri
review_team = Team(
name=f"{manifest.project_id}-reviewers",
mode="broadcast",
members=reviewer_agents,
share_member_interactions=False, # <-- niente groupthink
...
)
Quel share_member_interactions=False sui reviewer è una decisione precisa: se i revisori si vedono i punteggi a vicenda, convergono. Volevo pareri indipendenti, quindi ogni reviewer vede solo l’output originale, dà un voto 0-100, e il leader sintetizza. Se anche uno solo sta sotto la soglia, il lavoro torna indietro. La soglia non è un numero magico ma dipende dal dominio: 70 per il marketing, 75 per la ricerca, 80 per il codice, 85 per legale/compliance. Più alta la posta, più cattivo il gate.
Ogni agente è definito da un SOUL — un YAML validato da un modello Pydantic (SoulConfig) che diventa la personalità e i vincoli dell’agente:
name: quality-reviewer
display_name: The Hawk-Eye
role: Quality Reviewer
communication_style: blunt
is_reviewer: true
review_score_threshold: 80
review_criteria:
- "Accuracy: i fatti sono corretti e citati?"
- "Clarity: la scrittura è chiara e strutturata?"
constraints:
- "Approvare lavoro senza feedback specifico e azionabile."
Sotto c’è il resto: 3 provider (openai gpt-4o/mini, anthropic sonnet-4/haiku-4.5, ollama locale), un TOOL_REGISTRY di 12 tool risolti dinamicamente per nome, SQLite per la memoria per-progetto, e un dashboard FastAPI per l’approvazione umana finale. Circa 3.300 righe di Python nel solo execution/.
La decisione dura: due volte umano nel loop
La tentazione, quando costruisci roba agentica, è l’auto-approvazione. “I reviewer hanno detto 88/100, spedisci”. Ho scelto il contrario: nessun output è completo finché un umano non lo firma. Due gate in sequenza — peer review automatica, poi human_approval_tool che serializza l’intero deliverable in JSON e lo mette in coda su dashboard. E se il lavoro fallisce 3+ giri di review, il leader smette di ostinarsi e chiama notify_human_tool con urgenza high. Niente loop infiniti che bruciano token in silenzio.
È lento? Sì. Ma il punto di tutto lo scaffold è che non mi fido dell’LLM per le cose deterministiche, e non mi fido nemmeno dei reviewer LLM per l’ok finale. La coerenza la garantisce Python; il giudizio finale lo garantisco io.
Gotcha onesti
Un progetto è credibile quando ammette dove il README e la realtà divergono. Qui divergono in tre punti:
data/projects/è vuota. Lo scaffold sa come generare e far girare un team, ma non ho ancora committato un progetto end-to-end che sia nato dall’Architect e abbia prodotto un deliverable approvato. Il meta-team è codice + documentazione, non ancora una demo che gira.- Tre file “mirror”, ma uno non esiste. In cima a
CLAUDE.mdc’è scritto “mirrored across CLAUDE.md, AGENTS.md, and GEMINI.md”.CLAUDE.mdeAGENTS.mdsono byte-identici —GEMINI.mdnon c’è. Documentazione che descrive un’intenzione, non lo stato. - La chiave che non porta a nulla. Il
.envesponeGOOGLE_API_KEY, ma il registry provider inconfig.pyconosce soloopenai,anthropic,ollama. Google è cablato nell’ambiente ma non nel codice. - Bonus infrastruttura: ci sono manifest k8s completi (Postgres, ingress, networkpolicy) per un sistema che non ha ancora fatto girare un singolo progetto reale. Ho impalcato il deploy prima del prodotto — l’errore classico.
C’è anche una tensione concettuale che tengo a mente: il “tre strati” della filosofia (direttiva/orchestrazione/esecuzione) e il “due tier” del runtime (Architect → Task Team) sono due mappe diverse dello stesso territorio. Comodo per me, potenzialmente confuso per chiunque altro apra il repo.
Come va
POC, onestamente. Un singolo commit, “full implementation”, che è più un’impalcatura densa che un prodotto. I pezzi ci sono e stanno in piedi: config, sicurezza (validazione path, redazione secret con regex, guardie contro path traversal), factory dei team, loader dei SOUL, dashboard. Quello che manca è il chilometraggio: farlo girare abbastanza da scoprire dove si rompe davvero, e poi self-anneal — che è la parte del CLAUDE.md che mi ripromette di aggiornare le direttive ogni volta che imparo qualcosa. Per ora ho imparato solo scrivendolo.
Cosa ho imparato
- Il 59% è la tesi, non un dettaglio. Ogni decisione dello scaffold — SOP in Markdown, script deterministici, gate multipli — esiste per spostare passi fuori dalla colonna “probabilistico” e dentro quella “deterministico”. Meno passi affida all’LLM, più alto quel numero risale verso il 100%.
- La separazione dei concern è più facile da scrivere che da rispettare. È tentativamente facile far fare “solo un piccolo scraping” all’LLM in orchestrazione. La disciplina è dire di no e scrivere lo script.
- L’indipendenza dei reviewer è un flag booleano.
share_member_interactions=Falseè una riga; il groupthink evitato vale molto di più. - Impalcare il deploy prima di avere un output reale è vanità. I miei manifest k8s sono più maturi della mia cartella
data/projects/vuota. La prossima riga di codice utile non è in Kubernetes: è il primo progetto che nasce, gira e viene approvato per davvero.