Osservabilità degli agenti AI: come capire che cosa ha fatto l'agente e che cosa registrare
L'osservabilità degli agenti AI è la capacità di ricostruire che cosa ha fatto l'agente, perché e con quali prove, e di verificare che il risultato sia corretto. Si compone di due strati: runtime (registro delle azioni, tracce, output dei controlli) e processo (accordo sul compito, criteri di accettazione, report con i link alle prove). Senza entrambi, la frase «fatto» dell'agente non significa nulla.
Che cos'è l'osservabilità di un agente AI
L'osservabilità degli agenti AI è la possibilità di rispondere, sulla base di artefatti e non della memoria o delle parole dell'agente, a tre domande: che cosa ha fatto, perché proprio in quel modo e con quali prove, e se il risultato è corretto. Se dopo venti minuti di lavoro l'agente dice «fatto, ma due test cadono, forse per il timing», e alla domanda «quali percorsi sono stati toccati» risponde «fatemi vedere il codice», l'osservabilità manca. L'agente non è stupido: semplicemente non ha nulla da mostrare.
Il problema è sistemico. Lo stesso agente in una settimana può dire dieci volte in buona fede «fatto», e ogni volta questo significherà qualcosa di diverso: in un caso sono stati eseguiti tutti i test, in un altro solo uno, in un altro nessuno. Più un progetto vive a lungo, più costa ogni sessione successiva, perché inizia con gli scavi.
Due strati: runtime e processo
L'osservabilità si confonde con i log, ma i log ne sono solo una metà.
| Strato | Che cosa risponde | Artefatti |
|---|---|---|
| Runtime | Che cosa è successo | Registro delle azioni dell'agente, tracce, output di test e build, commit |
| Processo | Che cosa si voleva e perché è stato fatto così | Accordo sul compito (perimetro, criteri di accettazione), rubrica di valutazione, report dell'agente |
Solo il runtime: si vede che cosa è caduto, ma non è chiaro che cosa si volesse ottenere. Solo il processo: si capisce che cosa si voleva, ma non si vede perché non ha funzionato. Servono entrambi.
Perché l'agente non può farlo da solo
Dire all'agente «logga meglio» non funziona, per tre motivi.
- Non sa che cosa non sa. L'agente non registra un segnale di cui non sospetta l'importanza.
- Il formato cambia. In una sessione scrive in modo dettagliato, in un'altra in una riga sola. Confrontare le sessioni e calcolare aggregati diventa impossibile.
- Il processo non si chiude con i log. La risposta alla domanda «perché così» sta nei documenti creati prima del lavoro, non nell'output dei comandi.
Per questo l'osservabilità va progettata nell'harness — l'ambiente dell'agente: modelli, hook, script, formato obbligatorio del report.
Che cosa registrare: il minimo indispensabile
Un buon registro non è una trascrizione di tutto, ma un insieme di fatti con cui si può ricostruire il corso del lavoro e calcolare le statistiche.
- Identificatori: id della sessione, id del compito o della funzionalità, modello dell'agente.
- Azioni con effetti collaterali: quali comandi sono stati eseguiti, quali file sono cambiati. La lettura dei file di solito si può non scrivere.
- Esiti dei controlli: quale comando di verifica, codice di ritorno, quanti test sono passati e quanti sono caduti.
- Ritentativi: quanti tentativi sono serviti per un passo e in che cosa differivano.
- Consumo: token e tempo, se lo strumento li fornisce.
- Esito: il compito è stato accettato, respinto o abbandonato, e perché.
Che cosa non scrivere: segreti, token di accesso, dati personali presenti nell'output dei comandi. Il registro può finire in un accesso condiviso oppure di nuovo nel contesto del modello.
Registro delle azioni tramite gli hook di Claude Code
In Claude Code il registro conviene tenerlo con gli hook — comandi che lancia lo strumento stesso, non il modello, quindi scattano sempre. L'hook riceve in ingresso un JSON con la descrizione dell'evento, compresi l'id della sessione, il nome dello strumento e i suoi parametri. Esempio di configurazione in .claude/settings.json che, dopo ogni chiamata a Bash, Edit o Write, aggiunge una riga al registro:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash|Edit|Write",
"hooks": [
{ "type": "command", "command": ".claude/hooks/log-action.sh" }
]
}
]
}
}
#!/usr/bin/env bash
# .claude/hooks/log-action.sh — una riga JSON per ogni azione
mkdir -p .agent/logs
jq -c '{ts: (now | todate), session: .session_id, tool: .tool_name,
cmd: .tool_input.command, file: .tool_input.file_path}' \
>> .agent/logs/actions.jsonl || true
Il formato JSONL (un record JSON per riga) è comodo perché si filtra facilmente con jq e si carica in qualsiasi sistema di analisi. Inoltre Claude Code conserva in locale la cronologia completa delle sessioni nella cartella ~/.claude/projects/ — la si può aprire per vedere per intero i ragionamenti e l'output, ma per le statistiche un registro proprio e compatto è più pratico.
Tracciamento: una traccia per sessione, uno span per compito
Quando gli agenti sono molti o lavorano a orari pianificati, i registri testuali non bastano: servono gli aggregati. Qui si possono usare gli strumenti classici di tracciamento distribuito e lo standard OpenTelemetry.
- Traccia — una sessione dell'agente.
- Span — un compito all'interno della sessione.
- Span annidati — i passi di verifica: build, test, e2e.
Il minimo di attributi per ogni span: id della funzionalità e della sessione, modello, esito della verifica, token, numero di ritentativi. Questi dati si leggono in Jaeger, Grafana Tempo, Honeycomb e rispondono a domande che in una singola sessione non si vedono: quale tipo di compito richiede più ritentativi, dove l'agente spende più token, dopo quale modifica dell'harness è calata la quota di compiti accettati. Claude Code dispone di un'esportazione integrata di metriche ed eventi tramite OpenTelemetry — si attiva con variabili d'ambiente, i dettagli sono nella documentazione dello strumento.
I commit come registro
Il registro più economico e affidabile ce l'avete già: git. Basta renderlo leggibile.
- Un commit — un compito. Così
git logcoincide con l'elenco di ciò che è stato fatto, e annullare un compito è un sologit revert. - Nel messaggio — che cosa è stato fatto e come è stato verificato. Non «fix», ma «che cosa è cambiato, perché, con quale comando è stato verificato».
- Commit solo dopo una verifica positiva. Così ogni punto della storia è uno stato funzionante, e
git bisecttrova il colpevole.
fix(payments): arrotondamento dello sconto ai centesimi
Lo sconto veniva calcolato in float, su un importo 999.99 si perdeva un centesimo.
Il calcolo è stato portato a int in centesimi.
Verificato: make test (142 passed), make e2e checkout — ok.
Fuori perimetro: arrotondamento nei report, annotato in BACKLOG.md.
Il report dell'agente: prove invece di impressioni
Il messaggio finale dell'agente è anch'esso un artefatto di osservabilità, se ha una forma fissa. Definite il modello in AGENTS.md o in CLAUDE.md:
## Report a fine compito
- Che cosa è stato fatto: elenco delle modifiche (file — sostanza)
- Come è stato verificato: comandi esatti e relativo esito
- Che cosa NON è stato verificato e perché
- Scostamenti dall'accordo sul compito
- Domande aperte per la persona
Regola principale: ogni affermazione rimanda a una prova — un file e una riga, l'output di un test, una voce del registro. «Sembra a posto» e «mi pare ok» non sono un report. La voce «che cosa non è stato verificato» è particolarmente preziosa: mostra onestamente i confini della sicurezza.
Accordo prima del codice, rubrica dopo
Lo strato di processo inizia prima del lavoro. Un breve accordo sul compito (sprint contract) fissa il perimetro, i criteri di accettazione, la sezione «fuori perimetro» e le domande aperte che decide la persona. La sezione «fuori perimetro» evita metà delle discussioni sul risultato.
Dopo il lavoro il risultato si valuta con una rubrica, non con un'impressione. Altrimenti due revisori esperti daranno verdetti diversi, e la valutazione diventa una lotteria.
| Dimensione | Domanda | Prova |
|---|---|---|
| Correttezza | Sono stati soddisfatti i criteri di accettazione? | Output dei test, e2e |
| Architettura | Sono stati rispettati i confini dei moduli? | file:riga |
| Test | Il nuovo comportamento è coperto? | Elenco dei nuovi test |
| Errori | Sono gestiti i guasti e i casi limite? | Test o file:riga |
| Osservabilità | Si capisce dai log che cosa succede? | Estratto del registro |
Ogni dimensione si valuta con A/B/C/D, e un voto senza riferimento non è valido. Regola semplice per decidere: una D o due C — respingere, con un elenco di ciò che va corretto. La stessa rubrica può applicarla un agente verificatore separato.
Come ricostruire che cosa ha fatto l'agente in una sessione
Se il report manca o non è affidabile, percorrete gli artefatti in quest'ordine.
- Stato dell'albero:
git status— che cosa non è committato, se ci sono file spazzatura. - Storia:
git log --statdall'inizio della sessione — quali commit e quali file. - Portata delle modifiche:
git diff --stat main...HEAD, poi il diff completo dei file importanti. - Registro delle azioni: quali comandi sono stati lanciati e che cosa hanno restituito i controlli.
- Verifica ripetuta: lanciate voi build e test. Fidatevi dell'output di un comando, non del riassunto.
# quali comandi ha lanciato l'agente nell'ultima sessione
jq -r 'select(.tool=="Bash") | .cmd' .agent/logs/actions.jsonl | tail -n 30
# quali file sono stati modificati più spesso
jq -r '.file // empty' .agent/logs/actions.jsonl | sort | uniq -c | sort -rn | head
Errori tipici
- Registrare tutto. Gigabyte di trascrizioni che nessuno legge. Registrate le azioni con effetti collaterali e gli esiti dei controlli.
- Formato libero. Se il formato del registro lo decide l'agente, non ci saranno aggregati. Il formato lo stabilisce l'harness.
- Fidarsi del report senza l'output dei comandi. «Test verdi» senza output è un'opinione.
- Ritentativi alla cieca. Senza la causa del guasto l'agente modifica punti a caso. Pretendete prima una diagnosi con il riferimento al log, poi la correzione.
- «Lo togliamo dopo». I file di debug e le modifiche non committate confondono la sessione successiva: non distinguerà il temporaneo dal voluto.
Riepilogo
L'osservabilità degli agenti AI si costruisce su due strati: il runtime (registro delle azioni tramite hook, tracce, commit con la descrizione delle verifiche) e il processo (accordo sul compito prima del codice, rubrica e report con i link alle prove). Così «fatto» significa la stessa cosa in ogni sessione, e rivedere il lavoro dell'agente richiede minuti. Il tema è affrontato in pratica nel corso di harness engineering, su un progetto reale di backend per i pagamenti.
Domande frequenti
In che cosa l'osservabilità di un agente AI differisce dal monitoraggio classico?
Il monitoraggio risponde se il sistema funziona adesso. L'osservabilità di un agente deve rispondere anche al perché l'agente ha preso una decisione e su che cosa si basa il risultato, quindi ai log si aggiungono l'accordo sul compito, la rubrica e il report.
Dove Claude Code conserva la cronologia delle sessioni?
In locale, nella cartella ~/.claude/projects/, sotto forma di file JSONL per sessione. Per le statistiche conviene tenere un registro proprio e compatto tramite gli hook.
Ci si può fidare del report dell'agente sul lavoro svolto?
Solo se ogni affermazione è supportata da una prova verificabile: l'output di un comando, un riferimento file:riga, una voce del registro. I controlli critici conviene rilanciarli di persona.
Serve Jaeger o Grafana per un solo sviluppatore?
Di solito no: bastano un registro JSONL, la disciplina dei commit e un modello di report. I sistemi di tracciamento si ripagano quando gli agenti sono più d'uno o lavorano con regolarità senza supervisione.
Che cosa fare se l'agente non scrive il report secondo il modello?
Tenere il modello breve e in cima al file delle istruzioni, e verificarne il rispetto con un meccanismo: un hook alla conclusione oppure un agente verificatore separato che restituisce il lavoro senza report.
Questo argomento è trattato in modo approfondito, con pratica, nel corso «Harness Engineering: инженерия надёжности AI-агентов».
Vai al corso