Progettare sistemi agentici che non collassano in produzione
Perché molti sistemi agentici falliscono in produzione e come progettare agenti affidabili con macchine a stati, contratti espliciti per gli strumenti, retry e osservabilità profonda.

Dai demo impressionanti ad agenti fragili in produzione
I sistemi agentici sono applicazioni dove un LLM non si limita a rispondere a un prompt, ma decide cosa fare dopo: quali strumenti chiamare, quali dati recuperare, quali passi eseguire e quando ha finito. Riuniscono un modello, un insieme di strumenti (API, database, servizi), un ciclo di pianificazione/esecuzione e l'infrastruttura che collega il tutto.
In un demo questo sembra magia: un agente elabora un piano, chiama alcuni strumenti e restituisce un risultato perfetto. Il percorso felice è breve, la latenza è bassa e niente fallisce contemporaneamente.
Perché i demo funzionano e la produzione si rompe
Sotto carichi reali, lo stesso agente viene stressato in modi che il demo non ha mai visto:
- Le API vanno in timeout, restituiscono dati parziali o cambiano contratti.
- Più richieste gareggiano per risorse condivise e corrompono lo stato.
- Conversazioni di lunga durata gonfiano la memoria e superano i limiti di contesto.
- Errori sottili del modello si sommano attraverso molte chiamate a strumenti.
Il risultato: comportamenti fluttuanti difficili da riprodurre, corruzione silenziosa dei dati e flussi utente che occasionalmente si bloccano o girano all'infinito.
L'impatto reale sul business
Gli agenti instabili non solo riducono la “soddisfazione”. Essi:
- Scatenano incidenti e pagine on-call.
- Producono risposte sbagliate che si propagano nei sistemi downstream.
- Erodono la fiducia degli utenti: le persone smettono di usare la funzione.
- Gonfiano i costi cloud tramite retry e loop incontrollati.
Su cosa si concentra questa guida
Questo articolo parla di pattern di ingegneria, non di “prompt migliori”. Vedremo macchine a stati, contratti espliciti per gli strumenti, strategie di retry e gestione degli errori, controllo di memoria e concorrenza, e pattern di osservabilità che rendono i sistemi agentici prevedibili sotto carico — non solo impressionanti sul palco.
Perché la maggior parte delle architetture agentiche fallisce a scala
La maggior parte dei sistemi agentici sembra funzionare bene in un demo con percorso felice. Falliscono quando traffico, strumenti e casi limite arrivano insieme.
Comportamenti fragili: loop, blocchi, lavoro parziale, errori silenziosi
L'orchestrazione ingenua presume che il modello farà “la cosa giusta” in una o due chiamate. Nell'uso reale emergono pattern ricorrenti:
- Loop: l'agente continua a ripianificare o a richiamare lo stesso strumento perché non riconosce mai completamento o fallimento.
- Blocchi: l'agente attende uno strumento o un sottocompito senza timeout, lasciando le sessioni utente appese.
- Lavoro parziale: l'agente completa metà del workflow (es.: scrive una bozza di email ma non la invia, genera un piano ma non esegue i passi).
- Errori silenziosi: gli strumenti falliscono o gli schemi non corrispondono, ma l'agente restituisce comunque una risposta plausibile con dati mancanti o errati.
Senza stati espliciti e condizioni di fine, questi comportamenti sono inevitabili.
Non-determinismo nascosto e inaffidabilità degli strumenti
Il campionamento degli LLM, la variabilità di latenza e i tempi degli strumenti creano un non-determinismo nascosto. Lo stesso input può seguire rami diversi, invocare strumenti differenti o interpretare in modo diverso i risultati degli strumenti.
A scala, i problemi degli strumenti predominano:
- Timeout e instabilità da API e database upstream
- Schema drift tra i contratti degli strumenti e ciò che i servizi effettivamente restituiscono
- Formati di errore incoerenti che l'agente non ha imparato a gestire
Ognuno di questi si trasforma in loop spurii, retry o risposte finali incorrette.
La concorrenza amplifica i casi limite e l'inadeguatezza del prodotto
Ciò che si rompe raramente a 10 RPS si romperà costantemente a 1.000 RPS. La concorrenza rivela:
- Condizioni di gara sullo stato condiviso o sulle cache
- Limiti di rate esauriti che causano fallimenti a catena degli strumenti
- Herde di retry scatenate da un singolo problema di dipendenza
I team di prodotto spesso si aspettano workflow deterministici, SLA chiari e auditabilità. Gli agenti, lasciati senza vincoli, offrono comportamento probabilistico e best‑effort con garanzie deboli.
Quando le architetture ignorano questa discrepanza — trattando gli agenti come servizi tradizionali invece che come pianificatori stocastici — i sistemi diventano imprevedibili proprio quando l'affidabilità conta di più.
Principi di design per sistemi agentici pronti per la produzione
Agenti pronti per la produzione sono meno questione di “prompt intelligenti” e più di progettazione disciplinata dei sistemi. Un modo utile di pensarli è come macchine piccole e prevedibili che occasionalmente chiamano un LLM, non come blob misteriosi che occasionalmente toccano i tuoi sistemi.
Cosa rende un agente adatto alla produzione?
Quattro proprietà contano più di tutte:
- Sicurezza: l'agente deve rispettare vincoli su accesso ai dati, effetti collaterali e promesse agli utenti. Ciò significa permessi espliciti, guardrail sugli strumenti e gestione attenta di output non affidabili.
- Prevedibilità: dati gli stessi input e stato, l'agente dovrebbe comportarsi entro una banda stretta e prevedibile. Dovresti poter spiegare cosa può e cosa non può fare.
- Debuggabilità: quando qualcosa va storto, devi poter tracciare il percorso: quale stato, quale decisione, quale strumento, quale chiamata al modello. Niente loop nascosti, niente “pensieri” opachi senza struttura.
- Tolleranza al cambiamento: puoi aggiornare modelli, strumenti o strategie senza riscrivere l'intero sistema.
Non ottieni queste proprietà solo dai prompt. Le ottieni dalla struttura.
Preferire workflow espliciti ai loop libero‑formali
Il pattern predefinito con cui molti team iniziano è: “while not done, call the model, let it think, maybe call a tool, repeat”. È facile da prototipare e difficile da gestire.
Un pattern più sicuro è rappresentare l'agente come un workflow esplicito:
- Definisci un insieme finito di stati (es.:
COLLECTING_INPUT,PLANNING,EXECUTING_STEP,WAITING_ON_HUMAN,DONE). - Definisci quali transizioni sono consentite tra stati.
- Usa l'LLM principalmente per decisioni locali: scegliere lo stato successivo, selezionare uno strumento o riempire parametri.
Questo trasforma l'agente in una macchina a stati dove ogni passo è ispezionabile, testabile e riproducibile. I loop libero‑formali sembrano flessibili, ma sono i workflow espliciti a rendere gli incidenti debuggabili e il comportamento verificabile.
Spezzare l’"agente divino" in skill modulari
Agenti monolitici che “fanno tutto” sono attraenti, ma creano accoppiamenti stretti fra responsabilità non correlate: pianificazione, retrieval, logica di business, orchestrazione UI e altro.
Invece, componi piccoli agenti o skill ben delimitati:
- Un planner che scompone i compiti.
- Un executor che esegue passi concreti.
- Uno specialista per ogni dominio (fatturazione, supporto, analytics, ecc.).
Ogni skill può avere la propria macchina a stati, strumenti e regole di sicurezza. La logica di composizione diventa allora un workflow di livello superiore, non un prompt in continua espansione dentro un unico agente.
Questa modularità mantiene ogni agente sufficientemente semplice da ragionare e ti permette di evolvere una capacità senza destabilizzare il resto.
Separare policy, stato e strumenti
Un modello mentale utile è dividere un agente in tre livelli:
-
Decision policy (prompt LLM + modello)
Incapsula come l'agente sceglie le azioni successive, interpretato sotto vincoli stringenti. Dovresti poter sostituire il modello, regolare la temperatura o affinare i prompt senza toccare il wiring del sistema. -
Macchina a stati / motore di workflow
Gestisce dove sei nel processo, quali transizioni sono possibili e come persistere il progresso. La policy suggerisce una mossa; la macchina a stati la valida e la applica. -
Layer degli strumenti
Implementa cosa può effettivamente accadere nel mondo: API, database, code, servizi esterni. Gli strumenti espongono contratti stretti e ben tipizzati e applicano autorizzazioni, rate limit e validazione degli input.
Forzando questa separazione eviti la trappola di nascondere la logica di business nei prompt o nelle descrizioni degli strumenti. L'LLM diventa un componente decisionale dentro un involucro deterministico chiaro, non l'involucro stesso.
Progettare per la semplicità e la chiarezza
Gli agenti agentici più affidabili non sono i demo più impressionanti — sono quelli il cui comportamento puoi spiegare su una lavagna.
Concretamente:
- Mantieni ogni agente focalizzato su un singolo compito e una metrica di successo principale.
- Codifica workflow e transizioni di stato esplicitamente invece che in prose.
- Lascia che gli LLM scelgano fra opzioni ben definite, non inventino procedure intere da zero.
Questa preferenza per agenti piccoli, componibili e ben strutturati permette ai sistemi di crescere senza collassare sotto la loro complessità.
Modellare i workflow agentici come macchine a stati esplicite
La maggior parte delle implementazioni di agenti inizia come un loop di "think, act, observe" attorno a una chiamata LLM. Va bene per i demo, ma diventa rapidamente opaco e fragile. Un approccio migliore è trattare l'agente come una macchina a stati esplicita: un insieme finito di stati, con transizioni ben definite scatenate da eventi.
Rappresentare i flussi agentici come stati e transizioni
Invece di lasciare che il modello decida implicitamente cosa fare dopo, definisci un piccolo diagramma di stati:
- PLAN – interpreta la richiesta dell'utente, scompone in passi, sceglie gli strumenti.
- CALL_TOOL – esegue una singola chiamata a uno strumento (o un batch) con input validati.
- VERIFY – controlla gli output degli strumenti rispetto a invarianti semplici o verifiche aggiuntive del modello.
- RECOVER – gestisce errori: retry, fallback o escalation.
- DONE – restituisce una risposta finale e chiude il workflow.
- FAILED – errore terminale con motivo e contesto chiari.
Le transizioni tra questi stati sono scatenate da eventi tipizzati come UserRequestReceived, ToolCallSucceeded, ToolValidationFailed, TimeoutExceeded o HumanOverride. Ogni evento, insieme allo stato corrente, determina lo stato successivo e le azioni.
Questo rende retry e timeout semplici: alleghi policy a stati individuali (es.: CALL_TOOL può ritentare 3 volte con backoff esponenziale, PLAN potrebbe non ritentare affatto) invece di spargere logica di retry nel codice.
Esteriorizzare lo stato per resilienza e scala
Persisti lo stato corrente e il contesto minimo in uno store esterno (database, coda o motore di workflow). L'agente diventa allora una funzione pura:
next_state, actions = transition(current_state, event, context)
Questo abilita:
- Resilienza – se un worker muore a metà esecuzione, un altro può riprendere dall'ultimo stato persistito.
- Scalabilità orizzontale – worker stateless consumano eventi, aggiornano stato ed emettono eventi successivi.
- Replay e compensazioni – puoi ricostruire una run, rilanciarla da uno stato qualsiasi o eseguire azioni compensative quando un flusso va rollbackato.
Benefici per ragionamento e audit
Con una macchina a stati, ogni passo del comportamento dell'agente è esplicito: quale stato aveva, quale evento si è verificato, quale transizione è scattata e quali effetti collaterali sono stati prodotti. Questa chiarezza accelera il debug, semplifica le indagini sugli incidenti e crea una traccia d'audit naturale per revisioni di compliance. Puoi dimostrare, dai log e dalla cronologia di stato, che azioni rischiose sono prese solo da stati specifici e sotto condizioni definite.
Progettare contratti affidabili per gli strumenti degli agenti
Gli agenti si comportano in modo molto più prevedibile quando gli strumenti sembrano meno "API nascoste in prosa" e più interfacce ben progettate con garanzie esplicite.
Definisci il contratto, non solo il prompt
Ogni strumento dovrebbe avere un contratto che copra:
- Schema di input: campi obbligatori, tipi, enum, vincoli, default.
- Schema di output: payload di successo, campi nullable e cosa significa "nessun risultato".
- Modello di errore: errori tipizzati (es.:
InvalidInput,NotFound,RateLimited,TransientFailure) con semantiche chiare. - SLA: aspettative di latenza, target di disponibilità e limiti di rate.
Esponi questo contratto al modello come documentazione strutturata, non come un muro di testo. Il planner dell'agente dovrebbe sapere quali errori sono ritentabili, quali richiedono intervento umano e quali devono fermare il workflow.
JSON rigoroso, validazione rigorosa
Tratta I/O degli strumenti come qualsiasi altra API di produzione:
- Usa schemi JSON rigorosi (es.: OpenAPI, JSON Schema) per input e output.
- Valida prima della chiamata (per catturare errori del modello) e dopo (per intercettare regressioni dello strumento).
- Ripara automaticamente piccoli problemi (es.: coercizione di tipo) ma loggali per tuning successivo.
Questo ti permette di semplificare i prompt: invece di istruzioni verbose, affidati a indicazioni guidate da schema. Vincoli chiari riducono argomenti allucinati e sequenze di chiamate insensate.
Versioning e compatibilità
Gli strumenti evolvono; gli agenti non dovrebbero rompersi ogni volta che succede.
- Versiona i contratti degli strumenti (
v1,v1.1,v2) e lega gli agenti a una versione. - Deprecare campi gradualmente; mantieni i campi vecchi leggibili per un periodo.
- Aggiungi campi in modo retrocompatibile; evita di cambiare semanticamente senza notifica.
La logica di pianificazione può così mescolare agenti e strumenti a diversi livelli di maturità in sicurezza.
Gestire il fallimento e le modalità degradate
Progetta i contratti pensando al fallimento parziale:
- Consenti risultati parziali con dettagli di errore per elemento.
- Definisci una risposta degradabile (es.: dati cache, approssimativi o obsoleti) invece di un errore netto.
- Marca quali campi sono “best effort” e quali sono “must have”.
L'agente può così adattarsi: continuare il workflow con funzionalità ridotte, chiedere conferma all'utente o passare a uno strumento di fallback.
Confini di sicurezza e autorizzazione
I contratti degli strumenti sono un luogo naturale per codificare limiti di sicurezza:
- Definisci cosa lo strumento può leggere o modificare.
- Richiedi parametri espliciti per azioni sensibili (es.:
confirm: true). - Distingui operazioni a scopo utente da quelle a scopo di sistema.
Combina questo con controlli server‑side; non fare affidamento solo sul modello perché si "comporti".
Perché buoni contratti semplificano gli agenti
Quando gli strumenti hanno contratti chiari, validati e versionati, i prompt possono essere più corti, l'orchestrazione diventa più semplice e il debug molto più agevole. Sposti complessità da istruzioni in linguaggio naturale fragili a schemi e policy deterministici, riducendo chiamate a strumenti allucinatorie e effetti collaterali inaspettati.
Retry, idempotenza e pattern di gestione degli errori
I sistemi agentici affidabili assumono che tutto fallirà prima o poi: modelli, strumenti, reti, persino il tuo livello di coordinamento. L'obiettivo non è evitare il fallimento, ma renderlo economico e sicuro.
Idempotenza: la base per retry sicuri
Idempotenza significa: ripetere la stessa richiesta ha lo stesso effetto visibile esternamente che farla una sola volta. Questo è critico per gli agenti LLM, che spesso rilanciano chiamate a strumenti dopo fallimenti parziali o risposte ambigue.
Rendi gli strumenti idempotenti per progettazione:
- Request ID: ogni chiamata include un
request_idstabile. Lo strumento memorizza questo ID e restituisce lo stesso risultato se lo vede di nuovo. - Upsert invece di insert: usa semantiche "create-or-update" indicizzate su una chiave di business naturale o sintetica, non su ID auto‑increment.
- Checksum e versioning: allega hash di contenuto o numeri di versione così che lo strumento possa rilevare duplicati, scritture obsolete o conflitti.
Strategie di retry che non esplodono i costi
Usa retry strutturati per fallimenti transitori (timeout, rate limit, 5xx): backoff esponenziale, jitter per evitare thundering herd e un massimo tentativi rigoroso. Logga ogni tentativo con correlation ID così puoi tracciare il comportamento dell'agente.
Per fallimenti permanenti (4xx, errori di validazione, violazioni di regole di business), non ritentare. Esporre un errore strutturato all'agent policy così può rivedere il piano, chiedere all'utente o scegliere uno strumento alternativo.
Circuit breaker e fallback
Implementa circuit breaker sia a livello agente sia a livello strumento: dopo ripetuti fallimenti, blocca temporaneamente le chiamate a quello strumento e fallisci velocemente. Abbina questo a fallback definiti: modalità degradate, dati cache o strumenti alternativi.
Evita retry ciechi dal loop agente. Senza strumenti idempotenti e classi di errore chiare, moltiplicheresti solo effetti collaterali, latenza e costi.
Gestire memoria, stato e consistenza dei dati per gli agenti
Agenti affidabili nascono da una chiara separazione tra cosa è statoato e dove vive.
Stato a breve termine vs memoria a lungo termine
Tratta un agente come un servizio che gestisce una richiesta:
- Stato a breve termine: tutto ciò che serve per completare il task corrente o il sottotask. Include obiettivo attivo, passo corrente, output degli strumenti, decisioni parziali e variabili di controllo (retry residui, ramo scelto, ecc.). Deve essere limitato e eliminabile al termine del workflow.
- Memoria a lungo termine: informazioni che devono sopravvivere tra esecuzioni e sessioni: profili utente, preferenze, decisioni passate, cronologia di progetto e scorciatoie apprese.
Mescolare i due porta a confusione e bug. Per esempio, mettere risultati effimeri in “memoria” fa sì che gli agenti riutilizzino contesto obsoleto in conversazioni future.
Dove conservare lo stato
Hai tre opzioni principali:
- In‑context (solo prompt) – semplice, bassa latenza, ma limitato e non durevole. Ottimo per stato a breve termine in una singola run.
- Store esterno – database, cache o vector store. Usalo per memoria a lungo termine e qualsiasi stato che deve sopravvivere a restart o coordinare più worker.
- Ibrido – conserva lo stato autorevole esternamente; carica solo ciò che serve nel contesto per il passo successivo.
Una buona regola: l'LLM è una funzione stateless su un oggetto stato esplicito. Persisti quell'oggetto fuori dal modello e rigenera prompt da esso.
Evitare l'anti‑pattern “log come memoria”
Un fallimento comune è usare log di conversazione, trace o prompt come memoria di fatto.
Problemi:
- Il retrieval diventa ad hoc e fragile.
- Fatti importanti sono sepolti in testo lungo.
- Più run possono contraddirsi senza una chiara politica di "last write wins".
Definisci invece schemi di memoria strutturati: user_profile, project, task_history, ecc. Deriva i log dallo stato, non il contrario.
Coerenza con dati e strumenti condivisi
Quando più strumenti o agenti aggiornano le stesse entità (es.: record CRM o stato di un ticket), servono controlli di consistenza base:
- Usa single source of truth per entità chiave (es.: ordine, ticket, documento).
- Preferisci contratti idempotenti: gli strumenti devono gestire retry in modo sicuro usando ID stabili e semantiche di upsert.
- Applica concorrenza ottimistica (numeri di versione, timestamp) quando agenti possono gareggiare per aggiornare lo stesso record.
Per operazioni ad alto valore, registra un decision log separato dal log conversazionale: cosa è cambiato, perché e basandosi su quali input.
Snapshot ed esecuzioni riprendibili
Per sopravvivere a crash, deploy e limiti di rate, i workflow dovrebbero essere riprendibili:
- Dopo ogni passo significativo, persisti uno snapshot di stato: passo corrente, input, risultati strumenti e azioni pendenti.
- Rendi ogni transizione della macchina a stati riproducibile dallo snapshot.
- Su fallimento o restart, ricarica l'ultimo snapshot e continua invece di ricominciare da capo.
Questo abilita anche il time travel debugging: puoi ispezionare e riprodurre esattamente lo stato che ha portato a una cattiva decisione.
Privacy, retention e memoria minima
La memoria è tanto un rischio quanto un asset. Per agenti in produzione:
- Modella esplicitamente cosa non deve mai essere archiviato (es.: segreti, documenti grezzi, PII sensibile). Usa redaction o hashing quando opportuno.
- Definisci politiche di retention per tipo di memoria (livello sessione, 30 giorni, blocco legale, ecc.).
- Dai agli utenti strumenti per vedere e cancellare la loro memoria a lungo termine.
- Evita di conservare prompt completi o input strumenti quando un sommario strutturato più piccolo è sufficiente.
Tratta la memoria come un prodotto: progettata, versionata e governata — non come un dump testuale che cresce senza controllo.
Concorrenza, limiti di velocità e backpressure nei sistemi agentici
Gli agenti appaiono sequenziali su una lavagna ma si comportano come sistemi distribuiti sotto carico reale. Appena hai molti utenti concorrenti, strumenti e job in background, affronti condizioni di gara, lavoro duplicato e problemi di ordinamento.
Pericoli di concorrenza nei workflow agentici
Modalità di fallimento comuni:
- Race condition: due esecuzioni agente aggiornano lo stesso ticket, carrello o documento in concorrenza, sovrascrivendosi.
- Lavoro duplicato: retry o worker mal configurati processano lo stesso task due volte (es.: addebitare un pagamento doppiamente).
- Effetti fuori ordine: le chiamate agli strumenti terminano in un ordine inatteso e un risultato più vecchio sovrascrive uno più nuovo.
Mitighi tutto questo con contratti idempotenti, stato di workflow esplicito e locking ottimistico/pessimistico a livello dati.
Code vs flussi sincroni
I flussi sincroni request–response sono semplici ma fragili: ogni dipendenza deve essere su, entro i limiti e veloce. Quando gli agenti fan out verso molti strumenti o sottotask paralleli, sposta passi di lunga durata o con effetti secondari dietro una coda.
L'orchestrazione basata su code ti permette di:
- Controllare la concorrenza con pool di worker
- Centralizzare retry e deduplicazione
- Isolare strumenti lenti o instabili dalla latenza percepita dall'utente
Limiti di velocità e backpressure
Gli agenti tipicamente incontrano tre classi di limiti:
- Modelli: token per minuto, richieste per minuto, dimensione del contesto
- Strumenti: servizi interni con QPS o vincoli CPU
- API upstream: quote di terze parti e cap rigidi
Ti serve uno strato esplicito di rate limit con soglie per utente, tenant e globali. Usa token bucket o leaky bucket per applicare le policy e restituisci errori chiari (es.: RATE_LIMIT_SOFT, RATE_LIMIT_HARD) così gli agenti possono fare backoff con grazia.
Il backpressure è come il sistema si protegge sotto stress. Strategie utili:
- Scartare prima traffico non critico
- Degradare funzionalità (contesti più piccoli, meno chiamate a strumenti)
- Mettere in pausa code a bassa priorità mantenendo i flussi critici
Monitora segnali di saturazione: profondità delle code, utilizzo dei worker, tassi di errore e percentili di latenza. Code in crescita insieme a latenza e 429/503 sono l'avviso precoce che gli agenti stanno sovraccaricando l'ambiente.
Osservabilità: tracing, metriche e log per il comportamento degli agenti
Non puoi rendere un agente affidabile se non riesci a rispondere a due domande velocemente: cosa ha fatto? e perché l'ha fatto? L'osservabilità per i sistemi agentici serve a rendere quelle risposte economiche e precise.
Cosa devi vedere
Progetta l'osservabilità in modo che un singolo task abbia una traccia che collega:
- Ogni passo dell'agente e transizione di stato
- Ogni chiamata a strumento e relativa risposta
- Ogni invocazione del modello e variante del prompt
All'interno di quella traccia allega log strutturati per decisioni chiave (scelta di routing, revisione del piano, trigger di guardrail) e metriche per volume e salute.
Una traccia utile in genere include:
- Metadati del task: tenant, utente, canale, priorità
- Stato agente: nome stato corrente, stato successivo, conteggio retry
- I/O degli strumenti: input, output, latenza, errori, stato del circuit breaker
- Chiamate al modello: ID del template del prompt, nome modello, conteggio token, latenza
Logging e redaction
Logga prompt, input e output degli strumenti in forma strutturata, ma falli passare prima attraverso uno strato di redaction:
- Maschera PII e segreti
- Trunca payload troppo grandi con hash per correlazione
- Marca i campi con livelli di sensibilità per controllare retention e accesso
Mantieni il contenuto grezzo dietro feature flag negli ambienti non produttivi; la produzione dovrebbe defaultare a viste redatte.
Metriche che contano davvero
Al minimo monitora:
- Tasso di successo / fallimento dei task per agente e caso d'uso
- Conteggio medio e P95 di passi per task
- Latenza: end-to-end e per tool/model
- Costo per task (token, spesa strumenti) e per outcome riuscito
Quando succedono incidenti, buone tracce e metriche ti permettono di passare da “l'agente è instabile” a una frase precisa come: “P95 dei task fallisce in ToolSelection dopo 2 retry a causa di un nuovo schema in billing_service,” riducendo la diagnosi da ore a minuti e fornendo leve concrete per la correzione.
Strategie di test e valutazione per i sistemi agentici
Testare agenti significa testare sia gli strumenti che chiamano sia i flussi che collegano il tutto. Trattalo come testing di sistemi distribuiti, non solo come tweaking dei prompt.
Unit test: contratti degli strumenti, non prompt
Inizia con unit test al confine dello strumento:
- Valida schemi: campi obbligatori, enum, range e invarianti.
- Verifica idempotenza e semantica degli errori (quali errori, quali codici, retryability).
- Asserisci che gli strumenti gestiscano input malformati con grazia e restituiscano failure strutturati.
Questi test non dipendono mai dall'LLM. Chiami lo strumento direttamente con input sintetici e asserti l'output o il contratto d'errore esatto.
Test di integrazione: flussi e comportamenti multi‑step
I test di integrazione esercitano il workflow agente end‑to‑end: LLM + strumenti + orchestrazione.
Modellali come test basati su scenari:
- Percorsi felici per le journey utente chiave (prenotazione, rimborso, escalation, ecc.).
- Casi limite: dati mancanti, fallimenti parziali degli strumenti, timeout, limiti di rate.
- Interazioni cross‑strumento: quando l'output dello strumento A alimenta lo strumento B.
Questi test verificano transizioni di stato e chiamate a strumenti, non ogni token dell'LLM. Controlla: quali strumenti sono stati chiamati, con quali argomenti, in quale ordine e quale stato/risultato finale ha raggiunto l'agente.
Fixture deterministiche per LLM e strumenti
Per mantenere i test ripetibili, fissa sia le risposte LLM sia gli output degli strumenti.
- Registra le risposte LLM una volta (per prompt + modello + config) e salvale come fixture JSON.
- Mocka i sistemi esterni dietro gli strumenti così i test non colpiscono servizi live.
- Usa seed espliciti e configurazioni a temperatura fissa nei test.
Un pattern tipico:
with mocked_llm(fixtures_dir="fixtures/llm"), mocked_tools():
result = run_agent_scenario(input_case)
assert result.state == "COMPLETED"
Suite di regressione per prompt e schemi
Ogni cambiamento di prompt o schema dovrebbe scatenare una run di regressione non negoziabile:
- Mantieni un corpus curato di input più stati attesi, trace di strumenti o classificazioni.
- Blocca questi come golden files; le diff evidenziano cambiamenti comportamentali.
- Approvare o rollbackare esplicitamente qualsiasi drift nei flussi critici.
L'evoluzione degli schemi (aggiunta di campi, restrizioni di tipo) ha le sue casse di regressione per intercettare agenti o strumenti che presumono il vecchio contratto.
Valutazione offline prima del rollout
Non pubblicare mai un nuovo modello, policy o strategia di routing direttamente nel traffico di produzione.
Invece:
- Riesegui il corpus di regressione offline contro la nuova configurazione.
- Esegui test di replay su interazioni storiche campionate.
- Calcola metriche automatiche (successo dei task, tassi di errore strumenti, latenza, costo) e, dove serve, valutazioni umane su un campione.
Solo dopo aver superato i gate offline una variante nuova dovrebbe arrivare in produzione, idealmente dietro feature flag e rollout graduale.
Gestione dei dati di test e anonimizzazione
I log degli agenti spesso contengono dati utente sensibili. I test devono rispettare questo.
- Costruisci dataset di test da input anonimizzati o sintetici.
- Rimuovi o hash identificatori, PII in testo libero e segreti prima di archiviare log o fixture.
- Segmenta accessi: gli ingegneri possono vedere trace comportamentali, ma non i segreti utente grezzi.
Codifica queste regole nella CI così nessun artefatto di test può essere generato o salvato senza controlli di anonimizzazione.
Operare, monitorare ed evolvere gli agenti in produzione
Operare agenti in produzione assomiglia più a gestire un sistema distribuito che a spedire un modello statico. Ti servono controlli per il rollout, obiettivi di affidabilità chiari e gestione disciplinata delle modifiche.
Strategie di rollout sicure
Introdurre nuovi agenti o comportamenti gradualmente:
- Shadow mode: esegui l'agente in parallelo al sistema esistente, logga le sue decisioni ma non lasciare che impattino gli utenti. Confronta i risultati offline.
- Canary: esponi una piccola porzione di traffico (es.: 1–5%) alla nuova versione agente. Monitora errori, latenza e qualità prima di scalare.
- A/B test: per flussi user‑facing, confronta nuovo vs vecchio agente su KPI di prodotto, non solo metriche del modello.
Supporta tutto questo con feature flag e policy configurabili: regole di routing, strumenti abilitati, temperatura, impostazioni di sicurezza. I cambiamenti dovrebbero essere deployabili via config, non codice, e immediatamente reversibili.
SLO e workflow di incident
Definisci SLO che riflettano sia la salute del sistema sia il valore utente:
- Affidabilità: tasso di successo dei task, chiamate agli strumenti e workflow end-to-end.
- Latenza: p50/p95 per percorsi critici.
- Qualità: punteggi di valutazione automatica, distribuzione valutazioni umane o metriche di successo specifiche.
Collega questi ad alert e gestisci gli incidenti come per qualsiasi servizio: ownership chiara, runbook per il triage e passi di mitigazione standard (rollback flag, drain del traffico, modalità safe).
Miglioramento continuo e controllo dei cambiamenti
Usa log, trace e trascrizioni delle conversazioni per affinare prompt, strumenti e policy. Tratta ogni cambiamento come un artefatto versionato con revisione, approvazione e possibilità di rollback.
Evita modifiche silenziose di prompt o strumenti. Senza controllo delle modifiche non puoi correlare regressioni a edit specifici e la risposta agli incidenti diventa congettura invece che ingegneria.
Un'architettura di riferimento per sistemi agentici affidabili
Un sistema agentico pronto per la produzione beneficia di una chiara separazione delle responsabilità. L'obiettivo è mantenere l'agente intelligente nelle decisioni, ma semplice nell'infrastruttura.
Componenti core
1. Gateway / API edge
Punto d'ingresso unico per client (app, servizi, UI). Gestisce:
- Autenticazione e autorizzazione (utente, servizio, tenant)
- Rate limit e quote
- Shaping delle richieste (schemi, limiti di dimensione, validazione basilare)
2. Orchestrator
L'orchestrator è il “tronco”, non il cervello. Coordina:
- Planner: traduce l'intento utente in un workflow o macchina a stati
- State orchestrator: esegue il workflow, traccia stato, gestisce retry e timeout
- Policy engine: applica sicurezza, compliance, strumenti consentiti, regole PII e budget di costo
Gli LLM vivono dietro l'orchestrator, usati dal planner e da strumenti specifici che richiedono comprensione del linguaggio.
3. Layer di strumenti e storage
La logica di business rimane nei microservizi esistenti, code e sistemi dati. Gli strumenti sono wrapper sottili attorno a:
- Servizi interni HTTP/gRPC
- Database, vector store, cache
- API esterne
L'orchestrator invoca gli strumenti tramite contratti stretti, mentre i sistemi di storage rimangono fonte di verità.
Integrazione, controlli e telemetria
Applica auth e quote al gateway; applica sicurezza, accesso ai dati e policy nell'orchestrator. Tutte le chiamate (LLM e strumenti) emettono telemetria strutturata verso una pipeline che alimenta:
- Trace per comportamento passo‑passo
- Metriche per SLO e limiti di rate
- Audit log per sicurezza e compliance
- Contabilità dei costi per utente, progetto e strumento
Un'architettura più semplice (gateway → singolo orchestrator → strumenti) è più facile da operare; aggiungere planner separati, policy engine e model gateway aumenta flessibilità, a scapito di più coordinazione, latenza e complessità operativa.
Mettere tutto insieme e passi successivi per il tuo team
Hai ora gli ingredienti principali per agenti che si comportano prevedibilmente sotto carico reale: macchine a stati esplicite, contratti chiari per gli strumenti, retry disciplinati e osservabilità profonda. Il passo finale è trasformare queste idee in pratiche ripetibili per il tuo team.
I pattern chiave, in una sola immagine
Pensa a ogni agente come a un workflow stateful:
- Una macchina a stati definisce i passi legali (plan → gather → act → summarize, ecc.) e le transizioni tra essi.
- I contratti degli strumenti definiscono cosa può fare ciascuna azione, con schemi stretti, timeout e superfici di errore.
- Retry e idempotenza proteggono ogni interazione esterna in modo che i replay siano sicuri e gli effetti non si applichino due volte.
- Osservabilità (trace, metriche, log) rende ogni decisione e chiamata a strumenti spiegabile e debuggabile.
Quando questi pezzi sono allineati, ottieni sistemi che degradano con grazia invece di collassare di fronte ai casi limite.
Checklist leggera per portare un agente in produzione
Prima di esporre un agente prototipo agli utenti reali, conferma:
- Workflow: stati e transizioni sono espliciti; niente loop nascosti, niente catene di strumenti non limitate.
- Contratti: ogni strumento ha input/output tipizzati, modalità di fallimento chiare e timeout.
- Sicurezza: guardrail su input, output e azioni (rate limit, allowlist, quote).
- Retry: policy definite per strumento; chiavi di idempotenza per tutte le chiamate con effetti.
- Stato: memoria e stato persistente sono scopi, versionati e recuperabili.
- Osservabilità: puoi rispondere a “cosa è successo?” per qualsiasi sessione utente con una singola traccia.
- Testing: hai test basati su scenari e suite di regressione per prompt, strumenti e policy.
Se manca qualcosa, sei ancora in modalità prototipo.
Come i team possono dividere le responsabilità
Una configurazione sostenibile solitamente separa:
- Team prodotto: gestiscono comportamento agente, prompt, strumenti di dominio e dataset di valutazione.
- Team platform / infra: gestiscono il framework macchina a stati, SDK comuni per gli strumenti, logging e tracing, enforcement delle policy e infrastruttura di valutazione condivisa.
Questo permette ai team prodotto di muoversi velocemente mentre i team platform impongono affidabilità, sicurezza e controllo dei costi.
Estensioni future e iterazione sicura
Una volta stabilite basi solide, puoi esplorare:
- Policy basate su apprendimento: usare i trace loggati per migliorare routing, selezione degli strumenti e strategie di fallback.
- Reinforcement learning: ottimizzare per outcome a lungo termine come completamento task o revenue, non solo risposte singole.
- Workflow auto‑tuning: regolare automaticamente temperature, strumenti o sottoprocessi in base alle performance osservate.
Il progresso qui dovrebbe essere incrementale: introduce componenti di apprendimento dietro feature flag, con valutazione offline e guardrail forti.
Il tema ricorrente è sempre lo stesso: progetta per il fallimento, preferisci la chiarezza all'astuzia e iterare dove puoi osservare e ripristinare in sicurezza. Con questi vincoli, i sistemi agentici smettono di essere prototipi spaventosi e diventano infrastruttura su cui la tua organizzazione può fare affidamento.
Domande frequenti
Cos'è un sistema agentico e in cosa differisce da una normale app LLM?
Un sistema agentico è un'applicazione in cui un LLM non si limita a rispondere a un singolo prompt ma decide cosa fare dopo: quali strumenti chiamare, quali dati recuperare, quale passo di un workflow eseguire e quando fermarsi.
A differenza di una semplice chat completion, un sistema agentico combina:
- Una policy decisionale (LLM + prompt)
- Un workflow o macchina a stati che monitora il progresso
- Un insieme di strumenti (API, database, servizi)
- Infrastruttura per retry, persistenza dello stato, logging e osservabilità
In produzione, l'LLM diventa un componente decisionale all'interno di un contenitore deterministico più ampio — non l'intero sistema.
Perché agenti che sembrano eccellenti nei demo spesso falliscono in produzione?
I demo di solito mostrano un percorso ideale: un singolo utente, strumenti che funzionano perfettamente, nessun timeout, nessuna deriva degli schemi e conversazioni brevi. In produzione gli agenti affrontano:
- Strumenti instabili: timeout, errori 5xx e formati di risposta che cambiano
- Concorrenza: molti utenti che competono per risorse condivise e limiti di velocità
- Sessioni di lunga durata: contesti gonfi, confusione nella memoria e deriva dello stato
- Errori modellistici che si accumulano: piccoli passi falsi che si propagano attraverso molte chiamate a strumenti
Senza workflow espliciti, contratti e gestione degli errori, questi fattori generano loop, blocchi, lavori parziali ed errori silenziosi che non compaiono in ambienti di demo.
Come rendo un agente prevedibile e facile da debuggare?
Fai operare l'LLM dentro una struttura chiara invece che in un loop libero:
- Modella l'agente come una macchina a stati con un insieme finito di stati e transizioni consentite.
- Usa l'LLM solo per scelte locali (es.: quale strumento chiamare dopo, come riempire i parametri), non per inventare flussi arbitrari.
- Persiste lo stato esternamente in modo che ogni transizione sia riproducibile e verificabile.
- Mantieni gli agenti piccoli e focalizzati: un lavoro principale e una metrica di successo primaria.
Così puoi spiegare, testare e fare il debug passo passo invece di inseguire opachi "pensieri" dell'agente.
Cosa significa modellare un agente come una macchina a stati?
Significa modellare l'agente come un workflow con stati nominati ed eventi tipizzati invece di while not done: call LLM.
Stati tipici includono:
PLAN– interpreta la richiesta e produce un piano passo‑passoCALL_TOOL– invoca uno strumento specifico o un batch di strumentiVERIFY– controlla gli output rispetto a regole semplici o controlli secondariRECOVER– gestisce errori con retry, fallback o escalationDONE/FAILED– esiti terminali
Eventi (es.: ToolCallSucceeded, TimeoutExceeded) insieme allo stato corrente determinano il prossimo stato. Questo rende retry, timeout e gestione degli errori espliciti invece che nascosti nei prompt o nel glue code.
Come dovrei progettare i contratti degli strumenti per i miei agenti?
Progetta gli strumenti come vere API di produzione, non come descrizioni in prosa. Ogni tool dovrebbe avere:
- Schema di input: campi obbligatori, tipi, vincoli e valori di default
- Schema di output: struttura chiara per successi, risultati parziali e "nessun risultato"
- Errori tipizzati: es.
InvalidInput,NotFound,RateLimited,TransientFailure - Aspettative operative: target di latenza e limiti di rate
Valida gli input prima della chiamata (per catturare errori del modello) e gli output dopo (per intercettare regressioni nello strumento). Versiona i contratti e fissa gli agenti su versioni specifiche in modo che i cambiamenti di schema non rompano i flussi silenziosamente.
Come gestisco i fallimenti, i retry e l'idempotenza nei workflow degli agenti?
Dai per scontato che ogni chiamata esterna fallirà talvolta e progetta di conseguenza.
Pattern chiave:
- Idempotenza: gli strumenti che hanno effetti accettano un
request_idstabile o una chiave di business e restituiscono lo stesso risultato se vengono richiamati con lo stesso ID. - Retry mirati: ritenta solo sui fallimenti transitori (timeout, 5xx, limiti di rate) con backoff esponenziale, jitter e un numero massimo di tentativi.
- Circuit breaker: interrompi temporaneamente le chiamate a uno strumento che fallisce ripetutamente e passa a un fallback o a una modalità degradata.
- Superfici di errore strutturate: restituisci tipi di errore espliciti in modo che l'agente possa decidere se ritentare, ripianificare o chiedere all'utente.
Questo mantiene alta l'affidabilità senza creare loop incontrollati, effetti collaterali duplicati o costi fuori controllo.
Qual è il modo corretto per gestire memoria e stato negli agenti?
Separa lo stato a breve termine dalla memoria a lungo termine e mantieni l'LLM stateless.
- Usa lo stato a breve termine per tutto ciò che serve a completare il task corrente: obiettivo attivo, passi, output degli strumenti e contatori di retry.
- Conserva la memoria a lungo termine (profilo utente, cronologia progetto) in uno store esterno con schemi strutturati, non come trascrizioni grezze.
- Tratta l'LLM come una funzione pura che opera su un oggetto stato esplicito: carica lo stato pertinente, costruisci il prompt, chiama il modello e poi persisti lo stato aggiornato.
Evita di usare log grezzi o cronologie conversazionali come “memoria”; deriva invece record compatti e strutturati con regole chiare di retention e privacy.
Come dovrei affrontare concorrenza, limiti di velocità e backpressure nei sistemi agentici?
Considera il sistema agente come un sistema distribuito sotto carico, anche se ogni flusso appare sequenziale.
Per restare affidabili:
- Metti i passi di lunga durata o con effetti collaterali dietro code per controllare la concorrenza con pool di worker.
- Applica limiti di velocità per modelli e strumenti con quote per utente, tenant e globali.
- Usa backpressure: scarta traffico non critico, degrada funzionalità o metti in pausa code a bassa priorità quando la saturazione aumenta.
- Combina contratti idempotenti con locking ottimistico/pessimistico a livello dati per evitare lavoro duplicato e condizioni di race.
Monitora profondità delle code, percentili di latenza e tassi 429/503 per intercettare il sovraccarico prima che diventi un outage.
Quale osservabilità mi serve per eseguire agenti in produzione in sicurezza?
Devi poter rispondere a “cosa ha fatto?” e “perché l'ha fatto?” per qualsiasi task.
Requisiti pratici:
- Trace: una traccia end-to-end per task che copra transizioni di stato, chiamate a strumenti e invocazioni del modello.
- Log strutturati: registra decisioni chiave (selezione strumenti, revisioni del piano, trigger dei guardrail) con correlation ID.
- Metriche: tasso di successo dei task, tasso di errore per stato, latenza (end-to-end e per tool/model), e costo per risultato riuscito.
- Redaction: maschera PII e segreti in prompt, input e output prima del logging; controlla la retention in base alla sensibilità.
Con questo, il triage degli incidenti passa da “l'agente sembra instabile” a identificare esattamente stato, strumento e modifica che hanno causato la regressione.
Come dovrebbero i team distribuire e gestire i sistemi agentici nel tempo in modo sicuro?
Tratta gli agenti come servizi in evoluzione: gestiscili con la stessa disciplina delle altre componenti di produzione.
Pratiche raccomandate:
- Usa shadow mode, canary e feature flag per distribuire gradualmente nuovi agenti o versioni di modelli.
- Definisci SLO per affidabilità, latenza e qualità, e collegali ad alert e runbook.
- Mantieni suite di regressione e replay offline per qualsiasi modifica a prompt, strumenti o policy.
- Separa la proprietà: i team prodotto gestiscono comportamento, prompt e strumenti di dominio; i team platform gestiscono framework macchina a stati, SDK comuni, osservabilità e enforcement policy.
Così puoi migliorare continuamente mantenendo i fallimenti contenuti, diagnostici e reversibili.