Go to main content
Observability, evaluations, security, and cost control - GinnyTech header image with an editorial cosmic visual

Observability, evals, security and cost control

Observability, evaluations, security, and cost control on GinnyTech: decide if an agentic workflow is sufficiently observable and secure for recurring use with controls, ownership, and revisable outputs.

AD
Created byAndrii Dyshkantiuk
Lesson 234 / 236Level: AdvancedDuration: 35 minPrerequisites: 1

What you will learn

  • Design AI workflows for data with controls, owners, and reviewable outputs
  • Apply AI, AutoML or agentic AI to business analytics cases without losing rigor
  • Recognize risks of leakage, drift, cost, privacy, and ungoverned automation

Observability, evals, security and cost control

Mettere in produzione un workflow agentico sui dati significa accettare una scomodità: ogni risposta che l’agente produce deve restare verificabile settimane dopo, da qualcun altro, senza dover credere sulla parola né all’agente né a chi l’ha lanciato. Finché il workflow gira in demo, basta che l’output sembri giusto. Quando gira ogni lunedì mattina sui numeri che muovono budget e roadmap, servono trace complete, valutazioni ripetibili, permessi espliciti e un costo prevedibile. Questa è la differenza tra un esperimento interessante e un servizio di cui un team si fida. La lezione appartiene al binario sistemi-llm.

La lezione riprende il workflow multi-agente visto in precedenza, analista, engineer, reviewer, owner, e aggiunge lo strato che lo rende esercibile in modo ricorrente: observability per ricostruire cosa è successo, evals per sapere se sta peggiorando, controlli di sicurezza per limitare i danni, budget per evitare sorprese in fattura. Il filo conduttore è uno solo: la decisione resta umana e revisionabile, l’agente accelera i passaggi intermedi senza mai diventare l’unico testimone del proprio lavoro.

Perché un agente sui dati diventa ingestibile senza trace

Un’analisi manuale lascia tracce quasi gratis: la query sta nella cronologia del warehouse, il foglio ha una versione, la mail con la raccomandazione riporta data e destinatari. Un agente cancella tutto questo per default. Ragiona in una finestra di contesto effimera, chiama tool in sequenza, scarta i rami che non portano da nessuna parte e restituisce solo il risultato finale. Se nessuno registra i passaggi intermedi, il lunedì successivo nessuno sa rispondere alle tre domande che contano: su quali dati ha lavorato, quali controlli ha superato, perché ha scelto quella raccomandazione e non un’altra.

Il problema si vede nei numeri. Prendi un agente di reporting settimanale che gira su un dataset da qualche milione di righe: in una run tipica esegue 8-12 chiamate al modello, 4-6 query al warehouse e 2-3 scritture intermedie. Senza trace, quando il report di marzo contraddice quello di febbraio, l’unica opzione è rilanciare tutto e sperare di riprodurre il percorso. Con un modello non deterministico, la speranza è una cattiva strategia di debugging. Con una trace che registra input, prompt, parametri, query eseguite, righe lette, errori e retry, il confronto tra le due run richiede minuti: si vede subito se è cambiato il dato, il codice o il comportamento del modello.

C’è un secondo motivo, meno ovvio. La trace è la materia prima di tutto il resto: senza run registrate non esistono eval storiche, senza eval storiche non si misura il drift, senza misura del drift ogni reclamo di uno stakeholder diventa un’indagine da zero. L’osservabilità non è un optional da aggiungere quando il sistema scala: è il prerequisito che rende possibili le altre tre gambe, valutazione, sicurezza e controllo dei costi. Un team che parte registrando tutto, anche in modo grezzo, spende qualche euro di storage e si compra la possibilità di diagnosticare qualsiasi problema futuro. Un team che parte senza, al primo incidente paga in giorni-uomo quello che avrebbe speso in megabyte.

Cosa registrare in ogni run: l’anatomia di una trace utile

Una trace utile non è un dump indiscriminato del contesto: è una struttura minima che permette di ricostruire, confrontare e attribuire. Il nucleo è sempre lo stesso, indipendentemente dal framework, LangGraph, Agents SDK o codice orchestrato a mano, e conviene fissarlo in uno schema prima di scrivere la prima eval.

Ogni run deve registrare l’identità della richiesta, chi l’ha lanciata, con quali parametri, su quale snapshot dei dati, perché la stessa domanda su dati diversi è una domanda diversa. Poi la sequenza di passi: per ogni chiamata al modello, il nome del modello, i token in input e output, la latenza; per ogni tool call, il nome del tool, gli argomenti effettivi, il risultato o l’errore, il numero di righe toccate. Infine l’esito: output consegnato, approvazioni richieste e ottenute, errori bloccati dai guardrail, costo totale. Questo è il minimo che consente di rispondere a cosa è successo nella run del 12 marzo senza rieseguire nulla.

# Schema minimo di trace per un workflow agentico sui dati
# Ogni run produce un record così: ricostruibile e confrontabile.
trace = {
    "run_id": "run_2026-03-12_001",      # identificativo univoco della run
    "richiesta": {                        # chi ha chiesto cosa, su quali dati
        "utente": "m.rossi",
        "domanda": "perche il churn q1 sale nel segmento smb?",
        "snapshot_dati": "warehouse prod, snapshot 2026-03-11",
    },
    "passi": [                            # sequenza ordinata di azioni
        {"tipo": "llm", "modello": "gpt-4o", "token_in": 3200,
         "token_out": 450, "latenza_s": 6.2},
        {"tipo": "tool", "nome": "query.warehouse", "righe_lette": 184000,
         "esito": "ok", "durata_s": 11.4},
        {"tipo": "tool", "nome": "write.prod", "esito": "bloccato",
         "motivo": "manca approvazione owner"},  # il guardrail ha funzionato
    ],
    "esito": "consegnato con 1 blocco",   # esito finale leggibile
    "costo_usd": 0.42,                    # costo pieno della run
}

Tre campi meritano attenzione particolare. Lo snapshot dei dati è quello che quasi tutti dimenticano: se la run punta a una tabella viva che nel frattempo è stata ricaricata, la riproducibilità è persa anche con la trace perfetta. La soluzione economica è registrare l’identificativo dello snapshot o, dove il warehouse lo permette, usare time travel così che la query storica restituisca gli stessi dati. Il secondo campo critico è il motivo dei blocchi: un guardrail che ferma una scrittura senza spiegare perché genera ticket, non fiducia. Il terzo è il costo per run, che deve essere calcolato dentro la trace stessa, non ricostruito a fine mese dalla fattura del provider, altrimenti il controllo dei costi arriva sempre tardi.

La granularità giusta si trova con una regola pratica: registra tutto ciò che servirebbe a un reviewer per arrivare alla stessa decisione partendo dagli stessi dati, e niente altro. Il contenuto integrale di ogni prompt di sistema a ogni passo è rumore; la versione del prompt template usata basta. Il full dump di un dataframe da 180.000 righe è rumore; conteggio, schema e hash del risultato bastano. Le trace devono essere economiche da scrivere e veloci da interrogare, perché il loro valore emerge nelle query aggregate, success rate per settimana, latenza mediana per tool, top delle query più costose, non nella lettura di una singola run.

Come costruire eval che misurano davvero la qualità

Le eval sono il punto dove i team sbagliano più spesso, in una direzione precisa: misurano se l’output sembra buono invece di misurare se il workflow prende la decisione giusta. Una eval utile parte sempre da casi con risposta nota, un eval set versionato, e definisce in anticipo cosa conta come successo. Per un workflow agentico sui dati, tre famiglie di eval coprono quasi tutto: correttezza fattuale (i numeri citati corrispondono a una riesecuzione indipendente delle query), qualità del processo (il workflow ha eseguito i controlli previsti, test sul grain, confronto con baseline, verifica di leakage, o li ha saltati) e comportamento sui casi avversi (di fronte a dati mancanti, a una richiesta ambigua o a un tool che fallisce, si ferma e chiede, oppure inventa).

La metrica aggregata più onesta per un eval set è il tasso di successo con il suo intervallo di incertezza, non il singolo numero secco. Su eval set piccoli, 30 o 50 casi che sono la norma, l’intervallo conta più della stima: un 86 per cento di successo su 30 casi ha un margine di diversi punti percentuali, e una regressione da 86 a 80 tra due versioni potrebbe essere rumore. La pratica che funziona è fissare una soglia di allarme sulla differenza, per esempio un calo di oltre 5 punti su almeno 50 casi fa scattare la review, e non promuovere mai una modifica al workflow, nuovo modello, nuovo prompt, nuovo tool, senza averla fatta passare sull’eval set completo.

# Valutazione di regressione: confronta due versioni sullo stesso eval set
# Si esegue prima di promuovere qualsiasi modifica al workflow.
def confronta_versioni(risultati_base, risultati_nuovi, soglia_calo=0.05):
    # risultati_*: liste di booleani, una voce per caso dell'eval set
    p_base = sum(risultati_base) / len(risultati_base)    # successo versione attuale
    p_nuovi = sum(risultati_nuovi) / len(risultati_nuovi)  # successo versione candidata
    calo = p_base - p_nuovi                               # positivo = peggioramento
    if calo > soglia_calo:
        return f"BLOCCATO: calo di {calo:.1%}, serve review umana"
    return f"OK: {p_nuovi:.1%} contro baseline {p_base:.1%}"

Due avvertenze contano più del codice. La prima riguarda gli eval automatici con un modello giudice: sono economici e veloci, ma il giudice condivide i bias del modello valutato e tende a premiare risposte fluenti e sicure anche quando i numeri sono sbagliati. La regola è usarli come filtro di prima linea e mantenere un campione valutato da umani, anche solo il 10-20 per cento dei casi, come ancoraggio: se giudice automatico e revisori umani divergono sistematicamente, il problema è l’eval, non il workflow. La seconda riguarda il leakage dell’eval set: se i casi di test finiscono nei prompt di esempio o nel training, il punteggio sale e la misura perde senso. L’eval set va versionato e trattato come materiale riservato, con una quota di casi tenuti fuori da qualsiasi contesto mostrato al modello.

Quando le eval girano in continuo, a ogni deploy e su un campione delle run di produzione, smettono di essere un esercizio e diventano un sistema di allarme. Il segnale da monitorare non è solo il punteggio medio ma la distribuzione degli errori per categoria: un aumento improvviso dei casi con dati mancanti gestiti male dopo una modifica al prompt di sistema indica esattamente dove guardare, senza dover rileggere decine di trace a mano.

Quanto costa un workflow agentico: budget, scomposizione e tetti

Il costo di un agente sorprende sempre nella stessa direzione: mai per il prezzo unitario dei token, sempre per il numero di passi che nessuno aveva stimato. Una singola domanda sul churn nel segmento SMB può generare 10 chiamate al modello con contesti da migliaia di token ciascuna, più 5 query al warehouse che scansionano centinaia di gigabyte. Il costo per run è la somma di tre voci, inferenza, dati, storage delle trace, e solo la prima è visibile nella dashboard del provider di modelli. Le altre due emergono nella fattura del warehouse a fine mese, quando è tardi.

La scomposizione che rende il costo governabile è la somma di token in ingresso e uscita per ogni chiamata valorizzati ai prezzi del modello, più byte scansionati sul warehouse al loro costo, più costo ammortizzato di trace ed eval set. Il valore della scomposizione non è la precisione contabile ma il fatto che ogni termine suggerisce una leva: i token si riducono con contesti più corti e meno retry, le scansioni con query che leggono partizioni invece di tabelle intere, lo storage dividendolo su molte run.

Tre meccanismi trasformano la scomposizione in controllo reale. Il budget per run con hard stop è il primo: ogni esecuzione riceve un tetto in dollari o in passi, superato il quale il workflow si ferma e consegna un risultato parziale dichiarato come tale. Senza hard stop, un loop di retry su un tool che fallisce può bruciare in un’ora il budget del trimestre, ed è il failure mode economicamente più comune. Il secondo è la cache semantica e operativa: le query al warehouse con gli stessi parametri non si rieseguono, e le sotto-attività deterministiche (profilo dati su uno snapshot già profilato, embedding già calcolati) si riusano. Il terzo è il routing per complessità: non ogni domanda merita il modello più costoso. Classificare la richiesta, lookup semplice, analisi standard, indagine aperta, e instradarla al modello adeguato riduce il costo medio del 40-60 per cento senza toccare la qualità dei casi difficili, che sono gli unici dove il modello grande paga il suo prezzo.

Voce di costoDove si nascondeLeva più efficace
Token di inputContesti lunghi trascinati a ogni passoCompattare il contesto, passare solo diff e sintesi
Token di outputRetry e verbosità non richiestaLimiti di lunghezza, temperature basse sui passi tecnici
Scansioni warehouseQuery su tabelle intere invece di partizioniFiltri di partizione obbligatori, tabelle aggregate
Retry e loopTool che falliscono e vengono richiamati identiciBackoff con limite, fallback dichiarati, hard stop

Il confronto che chiude il discorso economico è contro la baseline manuale: se l’analista impiega 4 ore a 50 euro l’ora per un report e l’agente lo produce a 3 euro di costo vivo con 30 minuti di review, il margine assorbe anche un tasso di rilavorazione del 20 per cento. Ma il conto regge solo se il costo è misurato per run nella trace, non stimato a spanne: senza misura, il primo mese di produzione porta sempre la stessa sorpresa.

Dove si rompe la sicurezza: permessi, dati e prompt

La superficie di attacco di un workflow agentico sui dati ha tre ingressi, in ordine di frequenza reale degli incidenti: i permessi troppo ampi, i dati sensibili nel contesto, le istruzioni ostili nascoste nei dati. Il primo è banale da descrivere e diffusissimo da trovare: l’agente gira con le credenziali di un service account che può leggere tutto il warehouse e scrivere nelle tabelle di produzione, perché tanto è solo un esperimento. La regola è il privilegio minimo per ruolo del workflow, l’analista legge, solo l’engineer propone scritture, solo l’owner approva, con tre classi di azioni fissate prima di partire: permesse senza attrito (leggere tabelle aggregate, scrivere in sandbox), permesse con approvazione (scritture fuori dalla sandbox, export di dati granulari, merge), vietate sempre (drop, grant, lettura di tabelle con dati personali non mascherati).

Il secondo ingresso è la perdita di dati sensibili attraverso il contesto. Un agente che profila una tabella clienti riceve email, codici fiscali o note libere, e li trascina nei prompt, nei log e magari nell’output consegnato a chi non dovrebbe vederli. Le difese sono a strati: viste mascherate a livello di warehouse così che l’agente non veda mai il dato grezzo, redazione dei pattern riconoscibili prima che il testo entri nel contesto, e una policy di retention sulle trace che distingue metadati (conservati a lungo) da payload con dati personali (cancellati dopo la finestra di debugging). Nessuno strato da solo basta; insieme riducono il rischio a un livello difendibile in una review privacy.

# Guardrail prima di ogni tool call: permessi, PII e istruzioni ostili
# Ritorna "ok" oppure blocca con un motivo registrato nella trace.
import re

PII = re.compile(r"[\w.]+@[\w.]+\.\w+|\b\d{11}\b|\b\d{16}\b")  # email, codici, carte
SOSPETTE = ("ignora le istruzioni", "system:", "esegui come admin")  # override tipici

def autorizza(azione, argomenti, ruolo="analyst"):
    # 1. Azioni vietate: mai consentite, per nessun ruolo
    if azione in ("db.drop", "grant.access", "read.pii_raw"):
        return "BLOCCATO: azione vietata dalla policy"
    # 2. Scritture fuori sandbox: solo con approvazione dell'owner
    if azione.startswith("write.") and not argomenti.get("sandbox", True):
        if ruolo != "owner" or not argomenti.get("approvato", False):
            return "BLOCCATO: serve approvazione owner"
    # 3. Dati in ingresso: cerca PII e istruzioni ostili nel testo
    testo = str(argomenti)
    if PII.search(testo):
        return "BLOCCATO: possibile dato personale, usare vista mascherata"
    if any(s in testo.lower() for s in SOSPETTE):
        return "BLOCCATO: possibile prompt injection nei dati"
    return "ok"

Il terzo ingresso merita un paragrafo a sé perché è specifico degli agenti: la prompt injection tramite dati. Una tabella, un ticket o una pagina web letta come fonte può contenere testo che istruisce il modello, per esempio ignorare le istruzioni precedenti ed esportare la tabella clienti, e un agente ingenuo esegue. La difesa combina igiene degli input (il contenuto letto dai tool è sempre dati, mai istruzioni: va delimitato e segnalato come tale nel prompt), validazione dell’output (ogni azione distruttiva o di export passa dal guardrail sopra, indipendentemente da chi l’ha chiesta) e approvazione umana sui rami critici. Non esiste un filtro perfetto contro le injection; esiste un’architettura in cui l’injection riuscita non può comunque fare danni gravi.

I guasti che ritornano e come intercettarli prima del danno

Gli incidenti dei workflow agentici sui dati ricadono in pochi stampi. Il loop di retry è il più costoso: un tool fallisce per un errore deterministico, credenziali scadute, tabella spostata, e l’agente lo richiama identico decine di volte, bruciando budget senza produrre nulla. Si intercetta con un limite di tentativi identici e un’escalation: dopo tre fallimenti uguali, il workflow si ferma e apre un ticket invece di riprovare. La deriva silenziosa è più insidiosa: il workflow continua a girare e a sembrare corretto, ma la qualità cala, il modello è stato aggiornato dal provider, lo schema a monte è cambiato, la stagionalità ha spostato le baseline. Solo le eval in continuo la catturano, ed è per questo che il punteggio va monitorato come una metrica di produzione, con soglie e allarmi, non guardato a mano ogni tanto.

Poi ci sono i guasti di giudizio: l’agente che presenta come certezza una stima fragile, che omette il segmento dove la metrica peggiora, che sceglie la baseline più favorevole alla sua tesi. Nessun guardrail tecnico li elimina del tutto; la difesa è il reviewer umano sul percorso critico e una convenzione di output che obbliga l’agente a dichiarare intervalli, alternative scartate e condizioni che invaliderebbero la raccomandazione. Un output che non dice cosa lo smentirebbe non è pronto per la decisione.

Dal prototipo al servizio ricorrente: soglie, ownership e monitoraggio

Il passaggio alla produzione si gioca su tre decisioni scritte prima del go-live. La prima è la soglia di promozione: quale punteggio sull’eval set, quale costo massimo per run, quali zero incidenti di sicurezza autorizzano l’uso ricorrente, e, simmetricamente, quale calo fa scattare il rollback automatico alla versione precedente. La seconda è l’ownership: ogni workflow ha un owner nominato che risponde della qualità, approva le modifiche e firma le eccezioni ai guardrail. Senza un nome, alla prima anomalia tutti guardano altrove. La terza è il piano di monitoraggio: dashboard con success rate, costo per run, latenza, interventi umani e incidenti bloccati; review cadenzata delle trace anomale; riesecuzione dell’eval set a ogni cambio di modello, prompt o schema a monte.

Un’ultima distinzione evita l’errore più comune del go-live: non tutto il workflow deve essere automatico allo stesso grado. Le letture e i controlli girano da soli; le sintesi passano da una review leggera a campione; le scritture e le raccomandazioni che muovono budget passano sempre dall’owner. Questa automazione graduata è ciò che rende il sistema sostenibile: il controllo umano si concentra dove il rischio è alto e scompare dove è solo attrito. Il risultato è un workflow che un professionista può revisionare, un auditor può ricostruire e un manager può tenere acceso senza chiedersi ogni mese cosa stia facendo davvero, e quanto stia spendendo per farlo.

Essential technical references

Verdetto: registra ogni run con trace complete, promuovi solo sopra soglia di eval con owner nominato, e instrada ogni domanda al modello adeguato con hard stop sul budget.

Il livello di governo in una frase

Il livello di governo decide se un workflow agentico è abbastanza osservabile, valutato e sicuro per girare in modo ricorrente sui dati.

La procedura in cinque passi

  1. Registra ogni run con snapshot dati, passi, blocchi motivati e costo pieno.
  2. Valuta ogni modifica sull’eval set versionato con soglia di calo che blocca il deploy.
  3. Applica permessi minimi, viste mascherate e guardrail contro PII e injection.
  4. Imponi budget per run con hard stop, cache e routing per complessità.
  5. Monitora eval, costi e rigetti con owner nominato e rollback dichiarato.

Un caso reale: quando i quattro pilastri mancano tutti

Nel settembre 2017 Equifax ha rivelato una violazione che ha esposto i dati di circa 147 milioni di americani. Gli attaccanti avevano sfruttato per mesi una vulnerabilità nota non patchata, senza che monitoraggio e allarmi la segnalassero. Nel luglio 2019 la Federal Trade Commission ha chiuso un accordo fino a 700 milioni di dollari di risarcimenti e sanzioni. Il caso mostra cosa succede senza i quattro pilastri di questa lezione: nessuna trace utile, nessuna eval dei controlli, permessi e patch non governati, costi scoperti solo a danno fatto.

Domande per chiudere la lezione

  1. Cosa registra una trace per rendere una run ricostruibile settimane dopo?
  2. Quando una modifica al workflow va bloccata prima del deploy?
  3. Quali tre ingressi di attacco copri con permessi e guardrail?
  4. Come tieni il costo per run sotto controllo senza guardare la fattura?
Serve una mano concreta?

Bloccato su questo argomento o vuoi applicarlo al tuo caso? Prenota una call di 15 minuti con un analista esperto.

Book a call