Ugo Lattanzi
Questa pagina è disponibile anche inEnglishDeutsch
Pubblicato il

Dalle traces ai casi di test

Quasi tutti strumentano per debuggare latenza e costo. Quasi nessuno strumenta perché un fallimento di produzione possa diventare un test permanente. È un altro elenco di campi, e che fosse quello sbagliato lo scopri solo quando ti serve.

Quarto articolo della serie sul testing dei sistemi AI. Nel primo abbiamo costruito il metro, nel secondo l'abbiamo dato in mano a un modello, nel terzo è rimasta la persona. Manca la domanda da cui parte tutto: i casi da dove arrivano?

Una mano con una matita rossa cerchia tre fotogrammi su un provino a contatto, appoggiato su un tavolo di legno scuro accanto a una lente d'ingrandimento.
Un provino a contatto: decine di scatti di ciò che è successo davvero, e una persona che ne cerchia tre. La selezione e la promozione sono lo stesso gesto, ed è l'unico passaggio che nessuna macchina fa al posto tuo.
Il ciclo che chiude la serie: produzione, traces, selezione, promozione umana, golden set, CI, di nuovo produzione.
I primi tre articoli descrivono la metà destra. Questo descrive come si torna a sinistra.

Il caso che non si riusciva a riprodurre

Un utente segnala che l'assistente ha risposto male a una domanda su una policy interna. Arriva il ticket, qualcuno conferma il problema, si sistema il prompt. Fin qui è una giornata normale.

Tre settimane dopo, durante la review mensile, qualcuno si ricorda della regola che ci eravamo dati: ogni incidente diventa un caso permanente. Apre il ticket per trasformarlo in una riga del golden set, e scopre che non si può.

Il documento da cui era stato recuperato il contesto nel frattempo è stato aggiornato — quel paragrafo oggi non dice più la stessa cosa. Il template del prompt è cambiato due volte. Nessuno sa con quale versione del modello girasse quel giorno, perché il provider nel frattempo ha spostato l'alias. Resta la domanda dell'utente e lo screenshot della risposta sbagliata.

Cioè: resta un aneddoto. E un aneddoto non è un test, perché non c'è modo di rieseguirlo e vedere se oggi va meglio.

Il caso non era andato perso. Non era mai stato registrato in una forma che permettesse di riviverlo.


Una trace non è un caso di test

I due oggetti si somigliano abbastanza da far pensare che uno sia il formato compresso dell'altro. Non lo sono, e la differenza è quella che rende difficile il passaggio.

TraceCaso di test
Naturadescrittiva: cos'è successonormativa: cosa doveva succedere
Origineautomatica, la produce il sistemadecisa, la produce una persona
Volumetutto il trafficodecine o centinaia
Veritàè il fattoè un'assunzione che scegli di trattare come vera
Vitaretention di giorni o settimaneappend-only, anni
Costoinfrastrutturaleattenzione umana

La riga che conta è la prima. Una trace registra un comportamento; un caso di test dichiara un'aspettativa. Fra le due c'è un giudizio, e il giudizio è esattamente ciò che il sistema non possiede — se lo possedesse non avresti bisogno di testarlo.

Questa è anche la ragione per cui "genera automaticamente i tuoi eval dalle traces" è una promessa che va letta con attenzione. Generare i candidati dalle traces si fa, ed è utile. Ma è il passo facile, e viene venduto con il nome del passo difficile.


Strumentare per rieseguire non è strumentare per debuggare

Quasi tutti i sistemi in produzione hanno già dell'instrumentazione. Il problema è che è stata scritta per rispondere a domande diverse.

Chi strumenta per il debugging vuole sapere dove si è rotto e quanto è costato: latenza per step, token, errori, throughput. È materiale utile, e nessuno dei campi che servono a rieseguire un caso ci compare per caso.

Ecco l'elenco che serve, con il perché di ognuno.

Il prompt risolto e il template, separati. Il solo prompt risolto ti dice cosa ha visto il modello ma non ti dice cosa è cambiato quando il comportamento cambia. Il solo template è il contrario: sai cosa hai scritto, non cosa è arrivato. Servono entrambi, con la versione del template accanto. È la differenza fra "la risposta è peggiorata" e "la risposta è peggiorata da quando abbiamo aggiunto quella frase alla v7 del template".

Gli argomenti della tool call. Questo è il punto in cui i due estremi della serie si toccano. Le assertion dell'articolo da cui è nata la serie — verificare che il modello abbia prodotto i filtri di retrieval giusti, senza mai eseguire la query — sono esattamente ciò che una trace ben strumentata contiene già. Se registri gli argomenti, quel test si scrive da solo a partire dalla produzione, invece che a mano da un'immaginazione di laboratorio. È il singolo campo con il ritorno più alto di tutto l'elenco.

Gli identificativi e la versione dei chunk recuperati, non il testo. Salvare il testo sembra la scelta prudente ed è la peggiore delle tre disponibili: gonfia le span, porta dentro dati personali che non ti servono, e soprattutto è una copia. Con una copia non ti accorgi mai che il documento sotto è cambiato: il caso continua a girare contro un contesto congelato che non esiste più da nessuna parte. Con identificativo più versione lo vedi subito.

La versione del modello e i parametri di sampling. Temperature, top-p, e il seed se lo usi. Senza questi il confronto con la baseline è fra due cose diverse, e non lo sai. Attenzione agli alias dei provider: gpt-qualcosa-latest è un campo che cambia significato sotto di te senza che nessun tuo file venga modificato.

Il segnale di feedback, agganciato alla trace. Non in un ticket separato, non in una tabella a parte con un timestamp da cui sperare di risalire. È la differenza fra "raccogliamo il feedback" e "il feedback è utilizzabile": nel secondo caso da un pollice giù arrivi in un click all'input, al contesto e agli argomenti che l'hanno prodotto.

C'è una asimmetria da tenere presente, ed è la stessa dei log: quello che non registri al momento non lo recuperi dopo. Nessuno ha mai ricostruito una trace mancante. Questo rende la scelta dei campi una decisione più impegnativa di quanto sembri, perché il costo di sbagliarla si paga mesi dopo, quando serve un caso e non c'è.


Cosa deve essere versionato

Un caso è riproducibile solo se tutto ciò da cui dipende è identificato. Nei sistemi LLM le cose che si muovono sotto un test sono cinque: il corpus documentale, l'indice, lo schema dei tool, il template del prompt, il modello.

Se una qualsiasi si muove senza che tu lo sappia, il test continua a girare e continua a dare un numero. Non fallisce: misura un'altra cosa. È il fallimento silenzioso peggiore, perché non produce un errore da guardare, produce una metrica sbagliata con la faccia seria.

Nel primo articolo c'era una regola: una variazione dei numeri deve essere attribuibile o al sistema o al dataset, mai a entrambi insieme. Qui la regola va estesa: mai a tre cose insieme. Se il punteggio scende e nella stessa settimana hai aggiornato la knowledge base, la conversazione su cosa sia successo dura giorni e finisce quasi sempre con un'ipotesi.

Va detta anche la parte scomoda: il pinning completo spesso non è praticabile. Non congeli il corpus di produzione per far girare dei test, e in molti casi non hai nemmeno il controllo sulle versioni del modello che il provider ti serve. La risposta pragmatica non è rinunciare, è registrare la versione anche quando non puoi congelarla. Non ti dà la riproducibilità, ti dà l'attribuzione — che è il 90% del valore e costa un campo.


Quali traces diventano casi

Qui c'è una decisione da prendere, e la scelta di default è quella sbagliata.

Il campionamento casuale è quasi sempre lo spreco peggiore, per la stessa ragione già vista nel terzo articolo a proposito della sampled review: se il tasso di errore è il 2%, su cento casi estratti a caso ne trovi due, e hai speso attenzione umana su novantotto casi che andavano bene. Serve una selezione stratificata, costruita su segnali diversi che rispondono a domande diverse.

Fallimento esplicito. Pollice giù, ticket, escalation, correzione umana. È la fonte migliore e la più scarsa: gli utenti segnalano una frazione piccola di quello che va storto, e la segnalano quando è grave. Ogni elemento di questa categoria merita di essere guardato.

Fallimento implicito. L'utente riformula la stessa domanda in modo diverso, ripete, abbandona a metà. È un segnale rumoroso — a volte è semplicemente cambiata idea — ma è abbondante quanto l'altro è scarso, e quasi nessuno lo raccoglie. Una riformulazione entro pochi secondi dalla risposta è, nella maggior parte dei sistemi conversazionali, il modo più economico di sapere che la risposta non è servita.

Disaccordo. I casi in cui il judge automatico e la persona danno verdetti diversi sullo stesso output. Valgono doppio, perché non mettono in discussione solo il sistema: mettono in discussione la rubrica. Il terzo articolo raccontava un progetto in cui due revisori indipendenti erano d'accordo fra loro sul 68% dei casi, e la conclusione era che il problema non fosse il modello ma la definizione. Questi casi sono il modo di trovare quelle situazioni prima che diventino un mese di prompt engineering inutile.

Novità. Input lontani da tutto ciò che il golden set contiene già. È il segnale che attacca il buco che il primo articolo ammetteva di non poter coprire — duecento esempi non descrivono la coda lunga di quello che gli utenti chiedono davvero. Non la coprirai comunque, ma puoi almeno accorgerti quando compare una zona di traffico su cui non hai nessuna misura.

Outlier di costo e latenza. Non sono errori di qualità e vanno tenuti separati dagli altri, ma sono regressioni vere. L'articolo introduttivo aveva già stabilito che in questi sistemi il costo fa parte della conversazione sulla qualità, non di un'altra conversazione.

La trappola

Se selezioni solo sui fallimenti, costruisci un set che descrive i bug di ieri.

Il meccanismo è insidioso perché produce numeri in miglioramento: il set contiene i casi che erano rotti, tu li sistemi, il punteggio sale. Nel frattempo il traffico normale — quello che funzionava e che nessuno ha mai segnalato — non è rappresentato da nessuna parte, e se peggiora non se ne accorge nessuno finché non lo dicono gli utenti.

Serve una quota fissa di traffico ordinario che passa. È la stessa logica dell'holdout del primo articolo, applicata alla composizione del set invece che al suo utilizzo: una difesa strutturale contro una deriva che nessuno commette apposta.


La promozione è un atto umano

C'è una scorciatoia che a un certo punto viene in mente a tutti: prendere l'output di produzione e promuoverlo a output atteso. È il modo più veloce di riempire un golden set, e ne servono cinque minuti per mille casi.

È anche il modo di congelare il comportamento attuale e chiamarlo verità. Il primo articolo ha già il vocabolario per dirlo: quello che ottieni è un silver dataset con l'etichetta golden addosso, e il rischio è di confondere "il modello è coerente con sé stesso" con "il modello ha ragione". Da quel momento ogni regressione rispetto al comportamento di oggi risulta un errore, incluse le correzioni.

La divisione del lavoro è questa. La trace fornisce l'input, il contesto, l'output effettivo e i metadati: tutta la parte voluminosa e noiosa, gratis. Una persona aggiunge tre cose che non stanno da nessuna parte nella trace — i criteri di accettazione, la categoria, e il giudizio che quel caso valga la pena tenerlo per sempre.

Sono due o tre minuti a caso. È esattamente la ragione per cui il golden set resta piccolo, ed è un bene: un set di duecento casi in cui qualcuno ha pensato a ognuno vale più di uno da diecimila prodotto da uno script.

Vale la pena notare che questa è una delle attività di human-in-the-loop in fase di valutazione descritte nel terzo articolo, e ne eredita i problemi: se due persone scrivono criteri diversi per lo stesso caso, il disaccordo entra nel golden set e da lì in ogni misura che ci costruirai sopra. Il kappa fra chi promuove i casi è una metrica poco romantica e molto informativa.


Con cosa si fa, oggi

La domanda pratica è se questo flusso si compri o si scriva. La risposta è che si compra la metà noiosa e si scrive la metà che conta, e sono esattamente le due metà che questo pezzo ha separato fin qui.

Il passaggio dalla trace al caso è già un pulsante. Su Langfuse si chiama + Add to dataset ed è disponibile su qualunque observation di una trace di produzione; esiste anche in blocco, selezionando righe dalla tabella delle observation e mappando i campi. L'elemento che ne esce conserva il source_trace_id, quindi la provenienza che l'ottava regola chiede non la scrivi a mano: è un campo del prodotto. Su LangSmith il pulsante si chiama Add to Dataset e parte da una selezione filtrata di run. Sono gli stessi strumenti che più avanti ritroverai fra gli esportatori OTLP: la trace che scrivi una volta serve sia a guardare sia a promuovere.

Anche la revisione umana ha un posto dove avvenire. Le annotation queue di Langfuse sono la coda del paragrafo precedente, resa prodotto: si crea scegliendo le dimensioni di punteggio, le trace ci entrano una alla volta o in blocco, e chi rivede vede un compito per volta con punteggi e commenti, navigabile da tastiera. Non è un dettaglio estetico. Due minuti a caso moltiplicati per venti casi al mese sono la differenza fra una pratica che sopravvive e una che salta alla prima settimana piena.

Quello che nessuno di questi prodotti decide è il giudizio. I criteri di accettazione, la categoria, e se quel caso valga la pena tenerlo per sempre non stanno in nessun campo della trace, e non c'è pulsante che li produca. La divisione del lavoro descritta sopra non cambia perché hai comprato uno strumento: cambia solo che la parte voluminosa smette di costarti tempo.

E c'è una scorciatoia che ora te la offrono loro. LangSmith dichiara che il proprio motore può generare automaticamente esempi di ground truth dalle traces di produzione. Vale la pena rileggere il paragrafo sulla promozione con quella frase davanti: generare i candidati dalle traces è utile ed è il passo facile, promuovere l'output di produzione a output atteso è il modo di congelare il comportamento attuale e chiamarlo verità. Il pulsante non distingue le due cose. Devi distinguerle tu, e la distinzione è tutto il valore.

Se non vuoi un prodotto, non ti serve. I cinque campi di questo articolo si registrano con qualunque libreria di tracing, la selezione stratificata è una query sul tuo store, e la promozione è un file di testo in git con una revisione sopra. È la strada che consiglio a chi ha un sistema solo: la piattaforma la comprerai quando avrai tre sistemi e nessuna voglia di mantenerne tre versioni.


Il payoff: misurare quanto il set è scollato dalla realtà

Fin qui abbiamo parlato di come alimentare il golden set. La parte che rende questo lavoro qualcosa di più di una comodità è un'altra, e riguarda la manutenzione.

Nel primo articolo il problema della manutenzione era ammesso e la soluzione era la disciplina: una quota fissa di ogni review mensile dedicata a riallineare il set con il traffico reale. Nello stesso articolo, poche righe dopo, c'era anche la frase onesta: la manutenzione è la prima cosa che salta quando c'è una scadenza.

Con la distribuzione delle categorie del traffico da una parte e quella del golden set dall'altra, quella disciplina diventa due numeri.

Il gap di copertura. Categorie che pesano nel traffico e sono sotto-rappresentate nel set. È l'allarme che, senza questa misura, arriva sotto forma di utenti scontenti sei mesi dopo — con un punteggio eccellente sul cruscotto, perché il set stava misurando bene una cosa che nessuno chiedeva più.

I casi morti. L'opposto: casi nel set che non corrispondono più a niente che qualcuno chieda. Il primo articolo aveva una regola numero sette, «Datalo e potalo», che era un'intuizione: se un caso è lì da due anni e passa sempre, forse non sta più misurando niente. Con la distribuzione del traffico smette di essere un'intuizione e diventa una decisione difendibile in una riunione.

La differenza fra le due versioni non è tecnica, è organizzativa. Una metrica sopravvive a una scadenza; la buona volontà no. È lo stesso motivo per cui, nel primo articolo, il gate della CI è sopravvissuto solo quando ha smesso di essere un blocco da subire ed è diventato qualcosa con cui si poteva discutere.


Privacy, in concreto

Nel primo articolo il GDPR era una riga: gli esempi vanno anonimizzati prima di finire in un repository che legge tutto il team. Quando la fonte diventa il traffico reale la questione smette di essere una riga.

Il contenuto di prompt e completion è dato personale nel momento in cui l'utente ci scrive dentro qualcosa, e non è un'ipotesi di scuola: è il caso normale. Le persone incollano email, numeri di pratica, nomi di clienti, e nei contesti sanitari o legali cose ben più delicate. Nel momento in cui quel contenuto entra in una span, è entrato nel sistema di osservabilità, che tipicamente ha una superficie di accesso molto più larga del database di produzione.

Quattro conseguenze pratiche.

La cattura del contenuto va trattata come opt-in, non come default. È anche la scelta che ha fatto OpenTelemetry: per impostazione predefinita le convenzioni GenAI non catturano il contenuto dei prompt né gli argomenti dei tool, proprio perché possono contenere dati sensibili, e serve un flag esplicito per attivarli.

La redazione va fatta nel collector, non nell'applicazione. Se la fai nell'applicazione, ogni servizio deve ricordarsene, e prima o poi uno se ne dimentica — di solito quello scritto in fretta. Nel collector è un punto solo, verificabile, che vale per tutto ciò che passa.

La retention delle traces e quella del golden set sono due politiche diverse. Le prime scadono dopo giorni o settimane, il secondo è append-only e vive per anni. Il momento del passaggio dall'una all'altro è il punto in cui l'anonimizzazione deve essere già avvenuta, perché dopo non c'è più una scadenza a salvarti.

Il golden set resta privato. Nel primo articolo l'argomento era la contaminazione dei benchmark: un set pubblico è un set bruciato. Quando i casi vengono dal traffico reale la ragione si somma a quella sulla protezione dei dati, e le due insieme non lasciano molto spazio.


Cosa dà OpenTelemetry e cosa no

Vale la pena essere precisi, perché è un ambito in cui il marketing corre più della sostanza.

Cosa dà. Un modello a trace e span che si adatta bene ai grafi di agenti: uno span per nodo, la relazione padre-figlio che ricostruisce il percorso, il contesto che si propaga attraverso le chiamate. Un formato di esportazione neutro, il che significa che il posto dove guardi le traces è una decisione reversibile. E il fatto che gli strumenti specializzati del settore — Langfuse, LangSmith e gli altri — sono esportatori OTLP: puoi usarli come implementazione senza adottarne il modello concettuale e il vocabolario.

Cosa non dà. La decisione su cosa sia corretto. Nessuna convenzione semantica ti dirà mai se quella risposta andava bene. Tutto ciò che sta a valle della trace — la selezione, i criteri, il giudizio — resta lavoro tuo, ed è il lavoro che conta.

E la parte da sapere prima di appoggiarcisi. Le convenzioni semantiche per la GenAI sono ancora in stato Development: a metà 2026 nessuno span, metrica o attributo specifico della GenAI risulta marcato come stabile. Nel 2026 sono state spostate in un repository dedicato, che al momento non ha né release né tag — cioè non c'è una versione contro cui fissarsi. E le rinomine ci sono state, non piccole: la 1.37.0 ha sostituito gli eventi per messaggio con gli attributi gen_ai.input.messages, gen_ai.output.messages e gen_ai.system_instructions, e ha rinominato gen_ai.system in gen_ai.provider.name; la 1.27.0 aveva già cambiato la nomenclatura dei token. Il risultato pratico è che nello stesso sistema convivono framework che emettono generazioni diverse della convenzione, e che le query vanno scritte tenendone conto.

Non è un argomento per non usarle: è un argomento per non costruirci sopra l'architettura del proprio pensiero. La domanda di questo articolo — cosa deve esserci nel record perché sia riutilizzabile — resta valida anche dopo la prossima rinomina. I nomi degli attributi no.

(Stato verificato a settembre 2026. Se stai leggendo molto dopo, controlla: è esattamente il tipo di informazione che invecchia in silenzio.)


Otto regole pratiche

  1. Registra il template e il risolto, separati e versionati. Uno solo dei due non serve a niente.
  2. Registra gli identificativi del contesto, non il testo. Il testo è una copia, e una copia nasconde i cambiamenti che stanno sotto.
  3. Aggancia il feedback alla trace, non a un ticket. Un feedback scollegato dalla sua esecuzione è un'opinione, non un dato.
  4. Fai la redazione nel collector. Perché l'applicazione, prima o poi, se ne dimenticherà.
  5. Nessun output di produzione diventa output atteso senza che una persona lo decida. La scorciatoia costa più di quello che fa risparmiare.
  6. Seleziona su cinque segnali, non solo sui fallimenti. Con una quota fissa di traffico che passa, altrimenti stai descrivendo i bug di ieri.
  7. Misura la divergenza fra la distribuzione del set e quella del traffico. È l'unica forma di manutenzione che sopravvive a una scadenza.
  8. Ogni caso porta con sé la sua provenienza. Identificativo della trace, data, segnale che l'ha selezionato. Il primo articolo lo chiedeva già; adesso c'è il modo di rispettarlo davvero.

Dove la serie si chiude

Fino a qui questa serie era una scala. Sposta le assertion dove costano poco, costruisci il metro, decidi chi lo tiene, tieni una persona nel punto in cui l'errore costa. Quattro gradini, ognuno più lento e più caro del precedente.

Con questo pezzo diventa un anello. La produzione genera le traces, le traces alimentano i casi, i casi misurano la produzione. E il punto in cui l'anello si chiude non è un tool: è una persona che guarda un caso reale e decide cosa sarebbe stato corretto — cioè il terzo articolo, che a questo punto si rilegge in un altro modo.

Una cosa sull'ordine. Questo articolo doveva essere il primo: senza traces non hai fallimenti reali da cui partire, e «parti dai fallimenti reali» era la prima regola del primo pezzo. Arriva per ultimo, che è esattamente l'ordine in cui succede nei progetti veri — l'osservabilità si mette quando serve, cioè quando è già tardi.

Rimane il consiglio pratico, che è meno ambizioso di quanto la parola osservabilità suggerisca: non serve una piattaforma. Servono cinque campi registrati bene, un posto dove il feedback si attacca all'esecuzione che l'ha prodotto, e qualcuno che una volta al mese guarda venti casi e ne promuove tre.


Fonti

← Tutti gli articoli
Ugo Lattanzi — Applied AI Architect Nessun cookie. Analitica aggregata e senza cookie tramite Cloudflare.