Localizzazione degli errori passo per passo: usare la valutazione delle traiettorie per capire dove falliscono gli agenti
Usa lo schema trajectory_eval di Potato per localizzare gli errori passo per passo, con tassonomie gerarchiche degli errori, punteggi di gravità e monitoraggio del punteggio corrente lungo le tracce degli agenti.
Il problema: sapere che un agente ha fallito non basta
Una tassonomia degli errori di traiettoria
Fai girare il tuo agente su un benchmark. Ottiene il 63% sul completamento dei task. E adesso?
Un numero pass/fail ti dice che l'agente ha fallito sul 37% dei task, e nient'altro. Non ti dice dove nella traccia le cose sono andate storte, che tipo di errore ha commesso l'agente, né quanto fosse grave. Era un unico errore catastrofico al passo 2, oppure quindici passi di piccoli errori di ragionamento accumulati? L'agente ha usato male uno strumento, o ha ragionato a partire da una premessa falsa?
Senza la localizzazione degli errori passo per passo non puoi diagnosticare le modalità di fallimento, decidere cosa correggere per primo o costruire dati di addestramento per i process reward model. Stai regolando iperparametri al buio.
Lo schema di annotazione trajectory_eval di Potato risolve questo problema. Gli annotatori attraversano ogni passo di una traccia e registrano:
- Correttezza: questo passo è corretto o sbagliato?
- Tipo di errore: scelto da una tassonomia gerarchica che definisci tu
- Livello di gravità: minore, maggiore o critico, con pesi di punteggio configurabili
- Motivazione: spiegazione dell'errore in testo libero (facoltativa)
- Punteggio corrente: un punteggio cumulativo che scende in base alla gravità e ti dà una curva di qualità per ciascuna traccia
Questa guida copre tutta la configurazione: definire la tassonomia degli errori, condurre l'annotazione e analizzare i dati raccolti. Per il riferimento di configurazione dello schema, vedi la documentazione sorgente.
Panoramica dello schema trajectory_eval
Lo schema trajectory_eval è pensato per valutare in sequenza le tracce di agenti a più passi. Invece di un unico giudizio complessivo di qualità, produce un'annotazione strutturata degli errori per ogni passo, così ti ritrovi con una mappa dettagliata di dove e perché l'agente ha fallito.
Ecco cosa fa l'interfaccia di annotazione a ogni passo:
- L'annotatore vede il contenuto del passo corrente (thought, action, observation, code, ecc.)
- Marca il passo come corretto o sbagliato
- Se è sbagliato, sceglie un tipo di errore dalla tassonomia gerarchica
- Assegna un livello di gravità (minore, maggiore o critico)
- Se vuole, scrive una motivazione che spiega l'errore
- Il punteggio corrente in cima all'interfaccia si aggiorna da solo
L'annotatore attraversa la traccia un passo alla volta, costruendo un profilo completo degli errori.
L'interfaccia di valutazione della traiettoria mostra ogni passo con il suo punteggio:
Ogni passo riceve un giudizio di correttezza, un tipo di errore e un livello di gravità, con un punteggio corrente che scende in base alla gravità
Progettare una tassonomia gerarchica degli errori
È la tassonomia a rendere utile la valutazione delle traiettorie. Se la imposti bene puoi aggregare gli errori su più tracce e individuare schemi di fallimento sistematici; se la imposti male le tue etichette non sommeranno a niente. Ecco una tassonomia da cui partirei, con quattro categorie di primo livello.
Errori di ragionamento
Capitano quando il ragionamento dell'agente è difettoso, anche se ciò che vede e fa è per il resto corretto.
| Tipo di errore | Descrizione | Esempio |
|---|---|---|
logical_error | Inferenza logica non valida | «Poiché A implica B, e B è vero, allora A deve essere vero» (affermazione del conseguente) |
incorrect_assumption | Dà per assodato qualcosa che le prove non sostengono | Dà per scontato che un file esista senza controllare |
over_generalization | Trae una conclusione troppo ampia da prove limitate | «Questa funzione è fallita una volta, quindi tutta l'API è rotta» |
circular_reasoning | La conclusione viene usata come premessa | «La risposta è X perché X è corretta» |
incorrect_calculation | Errore di calcolo matematico o logico | Errore off-by-one nel ragionamento sul limite di un ciclo |
Errori di percezione
Capitano quando l'agente legge male, interpreta male o si lascia sfuggire informazioni nelle sue osservazioni.
| Tipo di errore | Descrizione | Esempio |
|---|---|---|
missed_element | Non nota informazioni rilevanti | Si lascia sfuggire un messaggio di errore nell'output del terminale |
misidentified_element | Interpreta male ciò che vede | Legge un errore 404 come una risposta andata a buon fine |
hallucinated_element | Fa riferimento a qualcosa che non c'è | Cita un parametro di funzione che non esiste |
outdated_reference | Usa informazioni superate da un passo precedente | Usa il valore di una variabile che era già stata sovrascritta |
Errori di azione
Capitano quando l'agente compie l'azione sbagliata, o quella giusta nel modo sbagliato.
| Tipo di errore | Descrizione | Esempio |
|---|---|---|
wrong_tool | Sceglie uno strumento inadatto al compito | Usa grep quando serve find |
wrong_arguments | Strumento corretto ma parametri sbagliati | Passa il percorso di file sbagliato a un comando di modifica |
premature_termination | Si ferma prima che il compito sia finito | Restituisce una risposta dopo aver trovato informazioni parziali |
unnecessary_action | Compie un'azione che non aggiunge nulla | Rilegge un file appena letto |
destructive_action | Compie un'azione che provoca danni | Cancella un file senza backup |
Errori di comunicazione
Si vedono nelle risposte dell'agente agli utenti o nel modo in cui racconta il proprio lavoro.
| Tipo di errore | Descrizione | Esempio |
|---|---|---|
unclear_explanation | La spiegazione è confusa o ambigua | Descrive una correzione senza dire cosa fosse rotto |
missing_context | Omette dalla risposta un contesto essenziale | Riporta un successo senza menzionare le riserve |
incorrect_summary | Il riepilogo non corrisponde alle azioni davvero compiute | Sostiene di aver modificato 3 file quando ne sono cambiati solo 2 |
overconfident_claim | Presenta l'incertezza come certezza | «Questo risolverà sicuramente il problema» per una modifica non testata |
Livelli di gravità e pesi del punteggio
Ogni errore riceve un livello di gravità. I pesi predefiniti sono:
| Gravità | Peso | Descrizione |
|---|---|---|
minor | -1 | Piccoli problemi che non fanno deragliare la traccia (per esempio azione non necessaria, spiegazione poco chiara) |
major | -5 | Errori significativi che sprecano lavoro o producono risultati in parte sbagliati (per esempio strumento sbagliato, assunzione errata) |
critical | -10 | Errori che rompono la traccia alla radice (per esempio azione distruttiva, terminazione prematura con risposta sbagliata) |
Il punteggio corrente parte da 100 e scende del peso della gravità a ogni errore. Una traccia che finisce a 85 ha avuto qualche problema minore; una che finisce a 40 ha avuto diversi fallimenti gravi.
Puoi cambiare questi pesi nella configurazione:
severity_levels:
- name: minor
weight: -1
description: "Small issue, does not derail the overall trace"
- name: major
weight: -5
description: "Significant error that wastes effort or produces wrong intermediate results"
- name: critical
weight: -10
description: "Fundamental failure that breaks the trace or causes harm"Configurazione YAML completa
Ecco un config.yaml completo per la valutazione delle traiettorie, con la tassonomia intera:
annotation_task_name: "Agent Trajectory Error Localization"
data_files:
- "data/traces.jsonl"
item_properties:
id_key: "trace_id"
text_key: "task"
# Display agent traces with step-by-step rendering
display:
type: "agent_trace"
trace_key: "trace"
step_display:
thought: { label: "Thought", color: "#E8F0FE" }
action: { label: "Action", color: "#FFF3E0" }
observation: { label: "Observation", color: "#F1F8E9" }
code: { label: "Code", color: "#F3E5F5" }
annotation_schemes:
- annotation_type: "trajectory_eval"
# Per-step correctness check
# Hierarchical error taxonomy (shown when step is marked incorrect)
- category: "perception"
label: "Perception Error"
types:
- name: "missed_element"
label: "Missed Element"
description: "Fails to notice relevant information in observations"
- name: "misidentified_element"
label: "Misidentified Element"
description: "Misinterprets what it observes"
- name: "hallucinated_element"
label: "Hallucinated Element"
description: "Refers to something not present in the context"
- name: "outdated_reference"
label: "Outdated Reference"
description: "Uses stale information from a previous step"
- category: "action"
label: "Action Error"
types:
- name: "wrong_tool"
label: "Wrong Tool"
description: "Selects an inappropriate tool for the task"
- name: "wrong_arguments"
label: "Wrong Arguments"
description: "Correct tool but incorrect parameters"
- name: "premature_termination"
label: "Premature Termination"
description: "Stops before the task is complete"
- name: "unnecessary_action"
label: "Unnecessary Action"
description: "Takes an action that adds no value"
- name: "destructive_action"
label: "Destructive Action"
description: "Takes an action that causes harm or data loss"
- category: "communication"
label: "Communication Error"
types:
- name: "unclear_explanation"
label: "Unclear Explanation"
description: "Explanation is confusing or ambiguous"
- name: "missing_context"
label: "Missing Context"
description: "Omits critical context from the response"
- name: "incorrect_summary"
label: "Incorrect Summary"
description: "Summary does not match the actual actions taken"
- name: "overconfident_claim"
label: "Overconfident Claim"
description: "States uncertain outcomes as certainties"
# Severity levels with score weights
# Running score configuration
# Optional rationale field
# Overall trace quality (optional, shown at the end)
- annotation_type: "radio"
# Annotator settings
annotator_config:
allow_back_navigation: true
require_all_steps: true
# Output settings
output:
path: "output/"
format: "jsonl"Configurazione passo per passo
1. Prepara le tracce dell'agente
I dati delle tracce vanno in formato JSONL, una traccia per riga. Ogni traccia ha bisogno di un ID, della descrizione del task e di un elenco di passi:
{
"trace_id": "trace_042",
"task": "Find the bug in the calculate_discount function and fix it",
"trace": [
{
"type": "thought",
"content": "I need to look at the calculate_discount function to find the bug. Let me search for it."
},
{
"type": "action",
"content": "search",
"action_input": "def calculate_discount"
},
{
"type": "observation",
"content": "Found in pricing.py line 45:\ndef calculate_discount(price, discount_pct):\n return price * discount_pct / 100"
},
{
"type": "thought",
"content": "The function calculates the discount amount but doesn't subtract it from the price. It should return price - (price * discount_pct / 100)."
},
{
"type": "action",
"content": "edit_file",
"action_input": "pricing.py:45: return price - (price * discount_pct / 100)"
},
{
"type": "observation",
"content": "File edited successfully."
},
{
"type": "action",
"content": "finish",
"action_input": "Fixed the calculate_discount function. It was returning the discount amount instead of the discounted price."
}
]
}Se le tue tracce sono in un altro formato (messaggi OpenAI, run di LangChain, log di conversazione di Claude), usa il convertitore di tracce di Potato:
python -m potato.trace_converter \
--input raw_traces/ \
--output data/traces.jsonl \
--input-format react2. Configura la tassonomia
Parti dalla tassonomia completa qui sopra, poi accorciala o estendila per il tuo agente. Per un coding agent, per esempio, potresti aggiungere una categoria code_quality:
- category: "code_quality"
label: "Code Quality Error"
types:
- name: "syntax_error"
label: "Syntax Error"
description: "Generated code has syntax errors"
- name: "runtime_error"
label: "Runtime Error"
description: "Code runs but produces an error"
- name: "logic_bug"
label: "Logic Bug"
description: "Code runs without errors but produces wrong output"
- name: "style_violation"
label: "Style Violation"
description: "Code works but violates project conventions"Per le tracce di coding agent, la valutazione mostra i diff e l'output del terminale accanto ai controlli di punteggio:
CodingTraceDisplay mostra diff, blocchi di terminale e letture di file accanto ai controlli di valutazione della traiettoria
3. Avvia il server di annotazione
potato start config.yaml -p 8000Apri http://localhost:8000 nel browser. Vedrai la prima traccia con la visualizzazione passo per passo.
4. Scrivi le linee guida di annotazione
Dai agli annotatori istruzioni chiare. Come minimo, documenta:
- Quando marcare un passo come sbagliato e quando invece come corretto ma subottimale
- Come scegliere tra le categorie di errore quando ne valgono più di una (usa la più specifica)
- Quando assegnare ciascun livello di gravità, con esempi concreti
- Se valutare i passi in base alle informazioni disponibili in quel momento oppure con il senno di poi
Il flusso di annotazione
Quando un annotatore apre una traccia, la descrizione del task sta in cima e il primo passo subito sotto. Il punteggio corrente segna 100 nell'angolo in alto a destra.
Per ogni passo, l'annotatore:
- Legge il contenuto del passo nel contesto dei passi precedenti
- Marca la correttezza cliccando su «Corretto» o «Sbagliato»
- Se è sbagliato, sceglie la categoria di errore (per esempio «Errore di ragionamento») e poi il tipo specifico (per esempio «Assunzione errata»)
- Assegna la gravità: minore, maggiore o critica
- Scrive una motivazione (se è abilitata): «L'agente dà per scontato che il file sia nella directory corrente senza controllare, ma i risultati della ricerca mostravano che sta in src/utils/»
- Passa al passo successivo cliccando su «Passo successivo» o premendo la freccia destra
Il punteggio corrente si aggiorna dopo ogni errore. Marca il passo 3 come errore maggiore (-5) e il punteggio scende da 100 a 95. Marca il passo 7 come critico (-10) e arriva a 85.
Alla fine della traccia l'annotatore dà il giudizio complessivo success/partial/failure e invia.
Analizzare i risultati
Caricare i dati di annotazione
import json
import pandas as pd
from collections import Counter
from pathlib import Path
# Load all annotation files
annotations = []
output_dir = Path("output/")
for f in output_dir.glob("*.jsonl"):
with open(f) as fh:
for line in fh:
annotations.append(json.loads(line))
print(f"Loaded {len(annotations)} annotated traces")Analisi della distribuzione degli errori
# Extract all errors across all traces
errors = []
for ann in annotations:
for step_ann in ann.get("error_localization", []):
if step_ann["correctness"] == "incorrect":
errors.append({
"trace_id": ann["trace_id"],
"step_index": step_ann["step_index"],
"category": step_ann["error_category"],
"error_type": step_ann["error_type"],
"severity": step_ann["severity"],
"rationale": step_ann.get("rationale", ""),
})
error_df = pd.DataFrame(errors)
print(f"Total errors found: {len(error_df)}")
print()
# Error distribution by category
print("Errors by category:")
print(error_df["category"].value_counts())
print()
# Most common specific error types
print("Top 10 error types:")
print(error_df["error_type"].value_counts().head(10))
print()
# Severity distribution
print("Severity distribution:")
print(error_df["severity"].value_counts())Analisi della posizione degli errori
Vedere dove in una traccia tendono a cadere gli errori spesso rivela schemi sistematici:
import matplotlib.pyplot as plt
import numpy as np
# Normalize step positions to [0, 1] range
for ann in annotations:
trace_length = len(ann.get("error_localization", []))
for step_ann in ann["error_localization"]:
if step_ann["correctness"] == "incorrect":
step_ann["normalized_position"] = step_ann["step_index"] / max(trace_length - 1, 1)
# Collect normalized positions
positions = [
step_ann["normalized_position"]
for ann in annotations
for step_ann in ann.get("error_localization", [])
if step_ann["correctness"] == "incorrect"
and "normalized_position" in step_ann
]
plt.figure(figsize=(10, 4))
plt.hist(positions, bins=20, edgecolor="black", alpha=0.7)
plt.xlabel("Normalized Position in Trace (0 = start, 1 = end)")
plt.ylabel("Error Count")
plt.title("Where Do Agent Errors Occur?")
plt.tight_layout()
plt.savefig("error_position_distribution.png", dpi=150)
print("Saved error_position_distribution.png")Distribuzioni del punteggio corrente
# Extract final running scores
final_scores = []
for ann in annotations:
score = 100
severity_weights = {"minor": -1, "major": -5, "critical": -10}
for step_ann in ann.get("error_localization", []):
if step_ann["correctness"] == "incorrect":
score += severity_weights.get(step_ann["severity"], 0)
score = max(score, 0)
final_scores.append({
"trace_id": ann["trace_id"],
"final_score": score,
"overall_success": ann.get("overall_success", "unknown"),
})
score_df = pd.DataFrame(final_scores)
print("Score statistics:")
print(score_df["final_score"].describe())
print()
# Score distribution by overall success
for label in ["success", "partial", "failure"]:
subset = score_df[score_df["overall_success"] == label]
if len(subset) > 0:
print(f"{label}: mean={subset['final_score'].mean():.1f}, "
f"median={subset['final_score'].median():.1f}, "
f"n={len(subset)}")Le modalità di fallimento più comuni
# Group errors by category + type for a failure mode analysis
failure_modes = (
error_df.groupby(["category", "error_type"])
.agg(
count=("severity", "size"),
avg_severity_weight=("severity", lambda x: x.map(
{"minor": 1, "major": 5, "critical": 10}
).mean()),
)
.sort_values("count", ascending=False)
)
print("Top failure modes (by frequency):")
print(failure_modes.head(15).to_string())
print()
# Impact-weighted failure modes (frequency x average severity)
failure_modes["impact"] = failure_modes["count"] * failure_modes["avg_severity_weight"]
print("Top failure modes (by impact):")
print(failure_modes.sort_values("impact", ascending=False).head(10).to_string())Contesto di ricerca
La localizzazione degli errori passo per passo si allinea con alcuni filoni recenti della valutazione degli agenti.
TRAIL (Patronus AI, 2025) ha annotato 148 tracce di agenti da GAIA e SWE-bench Lite rispetto a una tassonomia di oltre 20 tipi di errore, 841 errori in tutto. Il risultato da tenere a mente è quanto sia difficile la localizzazione: il miglior modello di ragionamento a contesto lungo che hanno testato ha raggiunto l'11% di accuratezza congiunta su categoria dell'errore e posizione. È esattamente il lavoro che trajectory_eval affida agli annotatori umani, ed è il motivo per cui queste etichette valgono la spesa.
AgentRewardBench (McGill NLP, 2025) è andato invece a controllare i giudici. Raccoglie 1.302 traiettorie di agenti web su cinque benchmark, fa rivedere ciascuna a una persona esperta su successo, effetti collaterali e ripetizioni, e poi valuta dodici giudici LLM rispetto a quelle revisioni. Nessun giudice è in testa su tutti i benchmark, e le valutazioni basate su regole fornite con i benchmark sottostimano quanto spesso gli agenti riescano davvero. Se pensi di automatizzare una parte di questa tassonomia con un modello, è questa la forma del controllo che ti serve.
Le etichette di correttezza e gravità per singolo passo prodotte da trajectory_eval alimentano anche direttamente l'addestramento dei modelli di ricompensa di processo: ogni passo annotato è un esempio di addestramento con un segnale di qualità di riferimento.
L'articolo di Anthropic Demystifying evals for AI agents propone la versione operativa dello stesso argomento. Valuta la trascrizione e non solo l'esito, e usa grader basati su modelli con rubriche esplicite su come l'agente ha chiamato gli strumenti e ha parlato con l'utente. Mette anche in guardia dal punteggiare rispetto a una sequenza di passi prescritta, perché gli agenti continuano a trovare percorsi validi che chi ha progettato la valutazione non aveva previsto. Tienilo presente quando applichi questa tassonomia: un passo è un errore perché era sbagliato, non perché era inatteso.
Anche il punteggio corrente pesato per gravità si mappa sui segnali di reward usati nell'RLHF. Una curva di punteggio che crolla al passo 5 di una traccia da 20 passi ti dice esattamente dove l'agente ha bisogno di lavoro, il che è molto più azionabile di un unico reward a fine traccia.
In sintesi
Lo schema trajectory_eval trasforma la valutazione degli agenti da un controllo pass/fail in una diagnosi. Con una tassonomia gerarchica, il punteggio di gravità e il punteggio corrente puoi vedere quale passo è andato storto, che tipo di errore fosse, quanto fosse grave e in quali punti gli errori tendono ad addensarsi lungo le tracce. Le etichette a livello di passo sono anche pronte per essere usate come dati di addestramento per i process reward model.
Parti dalla tassonomia completa di questa guida, poi affinala per il tuo agente e per gli schemi di errore che vedi davvero. La tassonomia migliore è quella che indica correzioni che puoi effettivamente fare.