Mettere un LLM in produzione senza farsi male: evals e costi
C'è un momento che conoscono tutti quelli che hanno portato un'app con un LLM davanti a utenti veri. Il prototipo funziona: nella demo risponde bene, i colleghi applaudono, si decide di aprirlo. Due settimane dopo arrivano tre notizie insieme. La prima è la fattura, più alta del previsto di un ordine di grandezza. La seconda è un utente che segnala una risposta sbagliata su un caso che nella demo funzionava benissimo. La terza è la domanda a cui non sai rispondere: «era già così prima della modifica di martedì, o l'abbiamo rotto noi?».
Quelle tre notizie non sono sfortuna: sono la differenza tra un prototipo e un servizio. Un prototipo deve funzionare una volta, davanti a te. Un servizio deve funzionare mille volte al giorno, davanti a persone che non hai mai visto, a un costo che puoi sostenere, e deve dirti quando smette di funzionare. Questo articolo è la mappa delle quattro cose che rendono possibile quel salto: misurare la qualità (gli evals), misurare il consumo, tagliare i costi e limitare i danni. Nessuna richiede di essere un ingegnere di sistemi: richiedono solo di essere fatte prima e non dopo.
Perché il prototipo non basta
Il software tradizionale è deterministico: a input uguale, output uguale. Un LLM no. La stessa domanda posta due volte può ricevere due risposte diverse, entrambe accettabili — oppure una buona e una sbagliata. Questo rompe l'abitudine mentale su cui poggia tutto il collaudo classico: non puoi scrivere un test che dice «l'output deve essere esattamente questa stringa», perché fallirebbe anche quando il sistema si comporta bene.
Da qui nascono i due problemi tipici della produzione. Il primo è che non ti accorgi dei peggioramenti: cambi una riga del prompt per sistemare un caso e ne rompi tre che nessuno stava guardando. Il secondo è che il costo non è visibile nel codice: una funzione che passa da 2.000 a 20.000 token di contesto ha lo stesso aspetto di prima, ma costa dieci volte tanto, moltiplicato per ogni utente. Entrambi i problemi si risolvono con la stessa mossa: rendere numeri quello che oggi è un'impressione.
Prima cosa: gli evals, i test dell'AI
Un eval (da evaluation) è un test pensato per un sistema non deterministico. Non verifica che l'output sia identico a un atteso, ma che sia abbastanza buono secondo un criterio che hai dichiarato prima. È la differenza tra «la risposta è questa» e «la risposta cita almeno una fonte presente nei documenti forniti e non ne inventa altre».
La regola d'oro è che il criterio viene prima del test. Se non sai dire cosa significa «risposta buona» per il tuo caso — accuratezza? tono? assenza di informazioni inventate? formato rispettato? — nessuno strumento ti salverà. Le documentazioni ufficiali di Anthropic e OpenAI dicono la stessa cosa con parole diverse: si parte dai criteri di successo, e sono specifici del tuo prodotto.
Come si costruisce un eval set che serve
Un eval set è semplicemente una lista di casi con, per ciascuno, cosa vuoi ottenere. Tre indicazioni pratiche:
- Parti piccolo e vero. Venti casi presi dal tuo dominio valgono più di duecento inventati. La miniera migliore sono i log di produzione: le conversazioni reali, comprese quelle andate male, sono il tuo eval set naturale. Entrambe le piattaforme permettono di costruire dataset direttamente dai log.
- Metti dentro i casi difficili. Un eval set fatto solo di domande facili dà sempre il 100% e non serve a nulla. Devono esserci gli ambigui, i fuori tema, quelli con dati mancanti, quelli in cui la risposta giusta è «non lo so».
- Congelalo. L'eval set è il tuo metro: se lo cambi ogni volta che misuri, non stai misurando niente. Si aggiunge, non si riscrive.
Chi dà il voto
Ci sono tre modi per assegnare un punteggio, e conviene usarli in quest'ordine.
Il grader scritto in codice è il più affidabile ed economico: verifica proprietà controllabili in modo meccanico. Il JSON è valido? Contiene tutti i campi previsti? La cifra corrisponde a quella del documento? Se hai già lavorato su come ottenere JSON affidabile da un LLM, sai che rendere l'output strutturato non serve solo a integrarlo: serve anche a poterlo testare.
Il modello come giudice (LLM-as-judge) serve per ciò che il codice non sa valutare: la risposta è pertinente? il tono è quello giusto? è fedele alle fonti? Si fa dando a un modello una rubrica di valutazione esplicita. È potente ma ha un difetto da conoscere: il giudice può derivare nel tempo o essere sistematicamente generoso, quindi va ricalibrato ogni tanto confrontandolo con giudizi umani.
Il giudizio umano resta insostituibile su un campione piccolo. Non serve rileggere tutto: serve controllare abbastanza casi da sapere se gli altri due metodi stanno dicendo la verità.
Quando gli evals riguardano un agente che usa strumenti c'è un livello in più: non basta valutare la risposta finale, va guardato il percorso. Ha scelto lo strumento giusto? Ha fatto quindici chiamate dove ne bastavano due? Le piattaforme chiamano trace la registrazione completa di una singola esecuzione — chiamate al modello, chiamate ai tool, passaggi di consegna — e valutare le trace è il modo per scoprire che un agente dà la risposta giusta per il motivo sbagliato.
Seconda cosa: sapere quanto costa e dove va il tempo
Il costo di un'app LLM è quasi tutto in una voce: token. Token in ingresso (il prompt, il contesto, le definizioni dei tool, la cronologia) e token in uscita (la risposta). Gli output costano molto più degli input, ma sono gli input a crescere di nascosto: ogni turno di conversazione, ogni documento allegato, ogni tool aggiunto gonfia il prompt di tutte le chiamate successive. Se hai presente come funziona la finestra di contesto, hai già l'intuizione giusta: quello che entra nella finestra è quello che paghi, ogni volta.
I tre numeri da tenere sotto gli occhi
Per ogni chiamata, tre valori raccontano tutto: token in ingresso, token in uscita, durata. Aggregali per funzionalità e per utente, non solo in totale: la media nasconde sempre il caso patologico che consuma il 60% del budget. Le API espongono questi numeri nella risposta, quindi non serve stimarli — basta registrarli.
Lo standard per non legarsi a un fornitore
Nel 2026 esiste un modo condiviso di registrare queste misure: le convenzioni GenAI di OpenTelemetry, lo standard aperto di riferimento per l'osservabilità. Definiscono nomi fissi per le metriche (il consumo di token, la durata dell'operazione) e una struttura di span che rispecchia come lavora un'app agentica: un'operazione di alto livello (invoke_agent) con dentro le singole chiamate al modello e le esecuzioni dei tool.
Il vantaggio pratico è la libertà: strumenti il codice una volta secondo lo standard e puoi cambiare piattaforma di monitoraggio senza rifare il lavoro. Nota importante sulla privacy: per impostazione predefinita si registrano solo i metadati (modello, token, durate). La registrazione del contenuto di prompt e risposte è opt-in, e va attivata solo con consapevolezza di cosa stai archiviando e per quanto.
Terza cosa: tagliare i costi senza toccare la qualità
Le leve che seguono non peggiorano l'output: cambiano solo come e quando le richieste vengono elaborate. È il posto giusto da cui iniziare, prima di sacrificare la qualità.
Il caching del prompt: la leva più grossa
Se le tue chiamate condividono un inizio identico — istruzioni di sistema, definizioni dei tool, un documento di riferimento, un blocco di esempi — quel pezzo non ha motivo di essere rielaborato da zero ogni volta. Il prompt caching lo memorizza e lo riusa. L'economia è netta: leggere dalla cache costa un decimo del prezzo normale dell'input, mentre scriverla costa un po' più del normale (circa un quarto in più per la durata breve, il doppio per quella lunga). Migliora anche il tempo di prima risposta, perché il modello parte da lavoro già fatto.
Due dettagli fanno la differenza tra risparmiare e pagare un sovrapprezzo. Il primo: il punto di cache va messo sull'ultimo blocco che resta identico tra le richieste. È l'errore classico — mettere il marcatore dopo il messaggio dell'utente o dopo un timestamp: così ogni richiesta scrive una cache nuova che nessuno rileggerà mai, e paghi il sovrapprezzo di scrittura senza mai incassare lo sconto. Il secondo: esiste una lunghezza minima perché la cache si attivi (dell'ordine di qualche centinaio o qualche migliaio di token secondo il modello): sotto quella soglia non succede nulla e non ricevi errori. Per questo va verificato sul campo, leggendo nella risposta i campi che dicono quanti token sono stati scritti e quanti letti dalla cache.
Il batch: metà prezzo per ciò che può attendere
Non tutto ha bisogno di una risposta immediata. Classificare l'arretrato di ticket, generare riassunti notturni, ricalcolare etichette su un archivio: sono lavori che possono aspettare qualche ora. Per questi esiste l'elaborazione in batch, che costa la metà del prezzo standard: mandi un blocco di richieste, torni a prendere i risultati entro una finestra di ore. Il compromesso è esplicito — niente risposta in streaming, niente istantaneità — e le dimensioni ammesse sono generose (decine di migliaia di richieste per singolo batch). La domanda da porsi per ogni funzionalità è secca: questa cosa la sta aspettando una persona davanti allo schermo? Se no, è candidata al batch.
Il modello giusto per il compito giusto
L'errore più comune e più costoso è usare il modello più capace per tutto. Molte operazioni di un sistema reale sono semplici: classificare un'intenzione, estrarre tre campi da un testo, decidere se una domanda è pertinente. Un modello piccolo le fa bene a una frazione del prezzo. Tenere il modello grande per i passaggi che richiedono ragionamento e delegare il resto a uno piccolo è la ristrutturazione che, in genere, sposta più soldi. È anche l'unica che va verificata con gli evals: qui la qualità può cambiare davvero, e l'eval set serve esattamente a dirti di quanto.
Quarta cosa: limitare i danni
Un sistema in produzione può sbagliare: la domanda utile non è «come faccio a impedirlo», ma «quanto danno può fare quando succede». Più un'applicazione ha strumenti che agiscono sul mondo — scrivere su un database, inviare messaggi, spostare denaro — più questa domanda conta.
Tre pratiche coprono la maggior parte dei casi. Permessi espliciti per ogni strumento, distinguendo ciò che l'agente può fare da solo, ciò che richiede un'approvazione e ciò che non può fare in nessun caso. Approvazione del piano invece che del singolo passo: far dichiarare all'agente cosa intende fare e approvare quello, invece di confermare venti azioni di seguito — un'abitudine che rende il controllo umano sostenibile invece che una tortura da click. E limiti di consumo: un tetto sul numero di iterazioni e sul budget di token per esecuzione, così un ciclo impazzito costa un fastidio e non una fattura.
L'ordine giusto delle mosse
Se dovessi comprimere tutto in una sequenza: prima scrivi i criteri di successo, poi costruisci venti casi di eval dai dati reali, poi registra token e durate di ogni chiamata, poi attiva il caching sulle parti stabili del prompt, poi sposta in batch quello che non ha fretta, poi valuta i modelli più piccoli sui compiti semplici — misurando con gli evals che hai già. I guardrail vengono prima di aprire al pubblico, non dopo.
Il filo che tiene insieme queste mosse è sempre lo stesso: prima misurare, poi ottimizzare. Ottimizzare senza evals significa non sapere cosa hai rotto; ottimizzare senza numeri di consumo significa non sapere se hai davvero risparmiato.
Prova subito
Prendi un'app o uno script che hai già scritto e fai queste tre cose, nell'ordine, in mezz'ora:
- Scrivi cinque casi di eval a mano. Un file, cinque righe: input e cosa deve valere la risposta per essere accettabile. Includi un caso difficile e un caso dove la risposta corretta è «non lo so». Eseguili adesso e segna il risultato: quello è il tuo punto di partenza, il numero contro cui confronterai ogni modifica futura.
- Stampa i token di ogni chiamata. Aggiungi una riga di log che registri token in ingresso, in uscita e durata. Fai girare il flusso una volta e guarda i numeri: nella maggior parte dei casi la sorpresa è quanto pesa il contesto ripetuto rispetto alla risposta.
- Individua il tuo prefisso stabile. Guarda le tue chiamate e chiediti quale parte iniziale è identica ogni volta. Se supera qualche centinaio di token, è candidata al caching: attivalo mettendo il marcatore alla fine di quel blocco e ricontrolla i numeri del punto 2 — nella risposta dovrai vedere token letti dalla cache, non solo scritti.
Quando avrai un numero prima e un numero dopo, avrai smesso di sperare che il sistema funzioni e avrai iniziato a saperlo. È l'unica differenza sostanziale tra un prototipo e qualcosa che puoi mettere davanti a delle persone.