Fehlerlokalisierung Schritt für Schritt: Mit Trajektorien-Evaluation finden, wo Agenten scheitern
Mit dem trajectory_eval-Schema von Potato Fehler Schritt für Schritt lokalisieren, samt hierarchischer Fehlertaxonomie, Schweregraden und laufendem Punktestand über ganze Agent-Traces hinweg.
Das Problem: Zu wissen, dass ein Agent gescheitert ist, reicht nicht
Eine Trajektorien-Fehlertaxonomie
Der Agent läuft über einen Benchmark. Er erreicht 63 % bei der Aufgabenerfüllung. Und jetzt?
Eine Pass/Fail-Zahl sagt, dass der Agent bei 37 % der Aufgaben gescheitert ist, und sonst nichts. Sie sagt nicht, an welcher Stelle im Trace es schiefging, welche Art von Fehler der Agent gemacht hat oder wie schlimm er war. War es ein einzelner katastrophaler Fehltritt in Schritt 2 oder fünfzehn Schritte kleiner Denkfehler, die sich aufsummiert haben? Hat der Agent ein Werkzeug falsch benutzt oder aus einer falschen Prämisse heraus argumentiert?
Ohne Fehlerlokalisierung pro Schritt lassen sich Fehlermodi nicht diagnostizieren, Prioritäten für Korrekturen nicht setzen und keine Trainingsdaten für Process Reward Models bauen. Das Tuning läuft dann im Dunkeln.
Das Annotationsschema trajectory_eval in Potato löst das. Annotatoren gehen jeden Schritt eines Trace durch und halten fest:
- Korrektheit: Ist dieser Schritt korrekt oder inkorrekt?
- Fehlertyp: Ausgewählt aus einer hierarchischen Taxonomie, die man selbst definiert
- Schweregrad: Gering, schwer oder kritisch, mit konfigurierbaren Punktgewichten
- Begründung: Freitext-Erklärung des Fehlers (optional)
- Laufender Punktestand: Ein kumulativer Wert, der je nach Schweregrad sinkt und eine Qualitätskurve pro Trace ergibt
Dieser Leitfaden deckt die vollständige Einrichtung ab: die Fehlertaxonomie definieren, die Annotation durchführen und die gesammelten Daten auswerten. Die Konfigurationsreferenz des Schemas steht in der Quelldokumentation.
Überblick über das trajectory_eval-Schema
Das Schema trajectory_eval ist darauf ausgelegt, mehrstufige Agent-Traces der Reihe nach zu bewerten. Statt einer einzigen Gesamtqualitätsnote entsteht für jeden Schritt eine strukturierte Fehlerannotation, am Ende also eine detaillierte Karte davon, wo und warum der Agent gescheitert ist.
Das passiert bei jedem Schritt in der Annotationsoberfläche:
- Der Annotator sieht den Inhalt des aktuellen Schritts (Thought, Action, Observation, Code usw.)
- Er markiert den Schritt als korrekt oder inkorrekt
- Bei inkorrekt wählt er einen Fehlertyp aus der hierarchischen Taxonomie
- Er vergibt einen Schweregrad (gering, schwer oder kritisch)
- Optional schreibt er eine Begründung, die den Fehler erklärt
- Der laufende Punktestand oben in der Oberfläche aktualisiert sich automatisch
Der Annotator arbeitet sich Schritt für Schritt durch den Trace und baut so ein vollständiges Fehlerprofil auf.
Die Oberfläche für die Trajektorien-Evaluation zeigt jeden Schritt mit seinem Punktwert:
Jeder Schritt bekommt eine Korrektheitsbewertung, einen Fehlertyp und einen Schweregrad, dazu einen laufenden Punktestand, der je nach Schweregrad sinkt
Eine hierarchische Fehlertaxonomie entwerfen
Die Taxonomie ist das, was Trajektorien-Evaluation überhaupt lohnend macht. Sitzt sie, lassen sich Fehler über Traces hinweg aggregieren und systematische Muster erkennen; sitzt sie nicht, ergeben die Labels am Ende nichts. Hier ist eine Taxonomie als Ausgangspunkt, mit vier Kategorien auf oberster Ebene.
Denkfehler
Diese treten auf, wenn die Schlussfolgerungen des Agenten fehlerhaft sind, selbst wenn Wahrnehmung und Handlung im Übrigen stimmen.
| Fehlertyp | Beschreibung | Beispiel |
|---|---|---|
logical_error | Ungültiger logischer Schluss | „Da A B impliziert und B wahr ist, muss A wahr sein" (Bejahung des Konsequens) |
incorrect_assumption | Nimmt etwas an, das die Evidenz nicht deckt | Setzt voraus, dass eine Datei existiert, ohne nachzusehen |
over_generalization | Zieht aus dünner Evidenz einen zu weiten Schluss | „Diese Funktion ist einmal fehlgeschlagen, also ist die ganze API kaputt" |
circular_reasoning | Die Schlussfolgerung dient als Prämisse | „Die Antwort ist X, weil X korrekt ist" |
incorrect_calculation | Rechen- oder Logikfehler | Off-by-one-Fehler beim Überlegen der Schleifengrenze |
Wahrnehmungsfehler
Diese treten auf, wenn der Agent Informationen in seinen Beobachtungen falsch liest, falsch deutet oder übersieht.
| Fehlertyp | Beschreibung | Beispiel |
|---|---|---|
missed_element | Bemerkt relevante Information nicht | Übersieht eine Fehlermeldung in der Terminalausgabe |
misidentified_element | Deutet das Gesehene falsch | Liest einen 404-Fehler als erfolgreiche Antwort |
hallucinated_element | Bezieht sich auf etwas, das nicht da ist | Verweist auf einen Funktionsparameter, den es nicht gibt |
outdated_reference | Verwendet veraltete Information aus einem früheren Schritt | Verwendet einen Variablenwert, der längst überschrieben wurde |
Handlungsfehler
Diese treten auf, wenn der Agent die falsche Handlung wählt oder die richtige Handlung falsch ausführt.
| Fehlertyp | Beschreibung | Beispiel |
|---|---|---|
wrong_tool | Wählt ein für die Aufgabe unpassendes Werkzeug | Nimmt grep, wo find nötig wäre |
wrong_arguments | Richtiges Werkzeug, falsche Parameter | Übergibt einem Edit-Kommando den falschen Dateipfad |
premature_termination | Hört auf, bevor die Aufgabe erledigt ist | Gibt eine Antwort zurück, nachdem nur Teilinformationen gefunden wurden |
unnecessary_action | Führt eine Handlung ohne Nutzen aus | Liest eine gerade eben gelesene Datei erneut |
destructive_action | Führt eine schädliche Handlung aus | Löscht eine Datei ohne Backup |
Kommunikationsfehler
Diese zeigen sich in den Antworten des Agenten an Nutzer oder darin, wie er die eigene Arbeit schildert.
| Fehlertyp | Beschreibung | Beispiel |
|---|---|---|
unclear_explanation | Die Erklärung ist verwirrend oder mehrdeutig | Beschreibt eine Korrektur, ohne zu sagen, was kaputt war |
missing_context | Lässt entscheidenden Kontext in der Antwort weg | Meldet Erfolg, ohne die Einschränkungen zu erwähnen |
incorrect_summary | Die Zusammenfassung passt nicht zu den tatsächlichen Handlungen | Behauptet, 3 Dateien bearbeitet zu haben, obwohl nur 2 geändert wurden |
overconfident_claim | Stellt Unsicheres als sicher dar | „Das behebt das Problem garantiert" bei einer ungetesteten Änderung |
Schweregrade und Punktgewichte
Jeder Fehler bekommt einen Schweregrad. Die voreingestellten Gewichte sind:
| Schweregrad | Gewicht | Beschreibung |
|---|---|---|
minor | -1 | Kleine Probleme, die den Trace nicht entgleisen lassen (z. B. unnötige Handlung, unklare Erklärung) |
major | -5 | Erhebliche Fehler, die Aufwand verschwenden oder teilweise falsche Ergebnisse erzeugen (z. B. falsches Werkzeug, falsche Annahme) |
critical | -10 | Fehler, die den Trace grundlegend zerstören (z. B. destruktive Handlung, vorzeitiger Abbruch mit falscher Antwort) |
Der laufende Punktestand startet bei 100 und fällt bei jedem Fehler um das Gewicht des Schweregrads. Ein Trace, der bei 85 endet, hatte ein paar kleinere Probleme; einer, der bei 40 endet, mehrere schwere Fehlschläge.
Diese Gewichte lassen sich in der Konfiguration ändern:
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"Vollständige YAML-Konfiguration
Hier eine komplette config.yaml für die Trajektorien-Evaluation mit der vollständigen Taxonomie:
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"Einrichtung Schritt für Schritt
1. Agent-Traces vorbereiten
Trace-Daten liegen im JSONL-Format vor, ein Trace pro Zeile. Jeder Trace braucht eine ID, eine Aufgabenbeschreibung und eine Liste von Schritten:
{
"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."
}
]
}Liegen die Traces in einem anderen Format vor (OpenAI-Messages, LangChain-Runs, Claude-Konversationslogs), hilft der Trace-Konverter von Potato:
python -m potato.trace_converter \
--input raw_traces/ \
--output data/traces.jsonl \
--input-format react2. Taxonomie konfigurieren
Mit der vollständigen Taxonomie oben anfangen, dann für den eigenen Agenten kürzen oder erweitern. Für einen Coding-Agenten ließe sich etwa eine Kategorie code_quality ergänzen:
- 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"Bei Traces von Coding-Agenten rendert die Evaluation Diffs und Terminalausgabe direkt neben der Bewertung:
CodingTraceDisplay rendert Diffs, Terminalblöcke und Dateizugriffe neben den Bedienelementen der Trajektorien-Evaluation
3. Annotationsserver starten
potato start config.yaml -p 8000http://localhost:8000 im Browser öffnen. Zu sehen ist der erste Trace mit der schrittweisen Darstellung.
4. Annotationsrichtlinien schreiben
Annotatoren brauchen klare Anweisungen. Mindestens dokumentieren:
- Wann ein Schritt als inkorrekt gilt und wann als korrekt, aber suboptimal
- Wie zwischen Fehlerkategorien zu wählen ist, wenn mehrere passen (die spezifischste nehmen)
- Wann welcher Schweregrad zu vergeben ist, mit konkreten Beispielen
- Ob Schritte anhand der zu diesem Zeitpunkt verfügbaren Information oder im Nachhinein bewertet werden
Der Annotationsablauf
Öffnet ein Annotator einen Trace, steht die Aufgabenbeschreibung oben und der erste Schritt darunter. Der laufende Punktestand zeigt oben rechts 100.
Für jeden Schritt tut der Annotator Folgendes:
- Den Inhalt des Schritts lesen, im Kontext der vorherigen Schritte
- Korrektheit markieren, per Klick auf „Korrekt" oder „Inkorrekt"
- Bei inkorrekt die Fehlerkategorie wählen (etwa „Denkfehler") und danach den konkreten Typ (etwa „Falsche Annahme")
- Schweregrad vergeben: gering, schwer oder kritisch
- Eine Begründung schreiben (falls aktiviert): „Der Agent nimmt an, dass die Datei im aktuellen Verzeichnis liegt, ohne nachzusehen, aber die Suchergebnisse zeigten sie in src/utils/"
- Zum nächsten Schritt gehen, per Klick auf „Nächster Schritt" oder mit der Pfeiltaste nach rechts
Der laufende Punktestand aktualisiert sich nach jedem Fehler. Wird Schritt 3 als schwerer Fehler markiert (-5), fällt der Stand von 100 auf 95. Wird Schritt 7 als kritisch markiert (-10), geht es auf 85 herunter.
Am Ende des Trace vergibt der Annotator die Gesamtbewertung Erfolg/teilweise/Fehlschlag und schickt ab.
Die Ergebnisse auswerten
Annotationsdaten laden
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")Fehlerverteilung analysieren
# 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())Fehlerposition analysieren
Zu sehen, wo im Trace Fehler landen, deckt oft systematische Muster auf:
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")Verteilung der laufenden Punktestände
# 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)}")Die häufigsten Fehlermodi
# 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())Forschungskontext
Die Fehlerlokalisierung pro Schritt passt zu mehreren jüngeren Strängen in der Agent-Evaluation:
TRAIL (Patronus AI, 2025) hat 148 Agenten-Traces aus GAIA und SWE-bench Lite gegen eine Taxonomie mit mehr als 20 Fehlertypen annotiert, insgesamt 841 Fehler. Bemerkenswert ist, wie schwer die Lokalisierung ausfiel: Das beste getestete Long-Context-Reasoning-Modell kam auf 11 % gemeinsame Genauigkeit über Fehlerkategorie und Fundstelle. Genau diese Aufgabe gibt trajectory_eval an menschliche Annotierende weiter, und deshalb lohnen sich die Labels.
AgentRewardBench (McGill NLP, 2025) nahm sich stattdessen die Judges vor. Die Arbeit sammelt 1.302 Web-Agent-Trajektorien über fünf Benchmarks, lässt jede von einer Fachperson auf Erfolg, Nebenwirkungen und Wiederholungen prüfen und bewertet damit zwölf LLM-Judges. Kein Judge führte über alle Benchmarks hinweg, und die regelbasierten Auswertungen, die die Benchmarks mitbringen, meldeten zu selten Erfolg. Wer Teile dieser Taxonomie mit einem Modell automatisieren will, braucht eine Prüfung dieser Art.
Die Korrektheits- und Schweregrad-Labels auf Schrittebene aus trajectory_eval fließen außerdem direkt in das Training von Process Reward Models: Jeder annotierte Schritt ist ein Trainingsbeispiel mit einem Ground-Truth-Qualitätssignal.
Anthropics Demystifying evals for AI agents formuliert die operative Fassung desselben Arguments. Bewerte das Transkript und nicht nur das Ergebnis, und setze modellbasierte Grader mit expliziten Rubriken dafür ein, wie der Agent Werkzeuge aufgerufen und mit Nutzenden gesprochen hat. Der Text warnt zugleich davor, gegen eine vorgeschriebene Schrittfolge zu bewerten, weil Agenten immer wieder gültige Wege finden, die beim Entwurf der Eval niemand vorgesehen hatte. Behalte das im Blick, wenn du diese Taxonomie anwendest: Ein Schritt ist ein Fehler, weil er falsch war, nicht weil er unerwartet kam.
Der schweregradgewichtete laufende Punktestand entspricht außerdem den Reward-Signalen aus RLHF. Eine Punktekurve, die bei Schritt 5 eines 20-schrittigen Trace steil abfällt, zeigt genau, wo der Agent nachgebessert werden muss, und damit deutlich mehr als ein einzelner Reward am Ende des Trace.
Zusammenfassung
Das Schema trajectory_eval macht aus der Agent-Evaluation statt einer Pass/Fail-Prüfung eine Diagnose. Mit hierarchischer Taxonomie, Schweregraden und laufendem Punktestand wird sichtbar, welcher Schritt schiefging, um welche Art Fehler es sich handelte, wie schlimm er war und wo sich Fehler über Traces hinweg häufen. Die Labels auf Schrittebene lassen sich zudem direkt als Trainingsdaten für Process Reward Models verwenden.
Mit der vollständigen Taxonomie aus diesem Leitfaden anfangen, sie dann auf den eigenen Agenten und die tatsächlich auftretenden Fehlermuster zuschneiden. Die beste Taxonomie ist die, die auf Korrekturen zeigt, die sich auch umsetzen lassen.