Skip to content
Guides14 min read

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.

Potato Team

Das Problem: Zu wissen, dass ein Agent gescheitert ist, reicht nicht

Eine hierarchische Fehlertaxonomie für Agenten mit vier Kategorien und einer Schweregrad-SkalaEine 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:

  1. Der Annotator sieht den Inhalt des aktuellen Schritts (Thought, Action, Observation, Code usw.)
  2. Er markiert den Schritt als korrekt oder inkorrekt
  3. Bei inkorrekt wählt er einen Fehlertyp aus der hierarchischen Taxonomie
  4. Er vergibt einen Schweregrad (gering, schwer oder kritisch)
  5. Optional schreibt er eine Begründung, die den Fehler erklärt
  6. 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:

Trajektorien-Evaluation mit laufendem PunktestandJeder 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.

FehlertypBeschreibungBeispiel
logical_errorUngültiger logischer Schluss„Da A B impliziert und B wahr ist, muss A wahr sein" (Bejahung des Konsequens)
incorrect_assumptionNimmt etwas an, das die Evidenz nicht decktSetzt voraus, dass eine Datei existiert, ohne nachzusehen
over_generalizationZieht aus dünner Evidenz einen zu weiten Schluss„Diese Funktion ist einmal fehlgeschlagen, also ist die ganze API kaputt"
circular_reasoningDie Schlussfolgerung dient als Prämisse„Die Antwort ist X, weil X korrekt ist"
incorrect_calculationRechen- oder LogikfehlerOff-by-one-Fehler beim Überlegen der Schleifengrenze

Wahrnehmungsfehler

Diese treten auf, wenn der Agent Informationen in seinen Beobachtungen falsch liest, falsch deutet oder übersieht.

FehlertypBeschreibungBeispiel
missed_elementBemerkt relevante Information nichtÜbersieht eine Fehlermeldung in der Terminalausgabe
misidentified_elementDeutet das Gesehene falschLiest einen 404-Fehler als erfolgreiche Antwort
hallucinated_elementBezieht sich auf etwas, das nicht da istVerweist auf einen Funktionsparameter, den es nicht gibt
outdated_referenceVerwendet veraltete Information aus einem früheren SchrittVerwendet 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.

FehlertypBeschreibungBeispiel
wrong_toolWählt ein für die Aufgabe unpassendes WerkzeugNimmt grep, wo find nötig wäre
wrong_argumentsRichtiges Werkzeug, falsche ParameterÜbergibt einem Edit-Kommando den falschen Dateipfad
premature_terminationHört auf, bevor die Aufgabe erledigt istGibt eine Antwort zurück, nachdem nur Teilinformationen gefunden wurden
unnecessary_actionFührt eine Handlung ohne Nutzen ausLiest eine gerade eben gelesene Datei erneut
destructive_actionFührt eine schädliche Handlung ausLöscht eine Datei ohne Backup

Kommunikationsfehler

Diese zeigen sich in den Antworten des Agenten an Nutzer oder darin, wie er die eigene Arbeit schildert.

FehlertypBeschreibungBeispiel
unclear_explanationDie Erklärung ist verwirrend oder mehrdeutigBeschreibt eine Korrektur, ohne zu sagen, was kaputt war
missing_contextLässt entscheidenden Kontext in der Antwort wegMeldet Erfolg, ohne die Einschränkungen zu erwähnen
incorrect_summaryDie Zusammenfassung passt nicht zu den tatsächlichen HandlungenBehauptet, 3 Dateien bearbeitet zu haben, obwohl nur 2 geändert wurden
overconfident_claimStellt 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:

SchweregradGewichtBeschreibung
minor-1Kleine Probleme, die den Trace nicht entgleisen lassen (z. B. unnötige Handlung, unklare Erklärung)
major-5Erhebliche Fehler, die Aufwand verschwenden oder teilweise falsche Ergebnisse erzeugen (z. B. falsches Werkzeug, falsche Annahme)
critical-10Fehler, 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:

yaml
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:

yaml
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:

json
{
  "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:

bash
python -m potato.trace_converter \
  --input raw_traces/ \
  --output data/traces.jsonl \
  --input-format react

2. 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:

yaml
- 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:

Evaluation eines Coding-Agenten mit Diff-DarstellungCodingTraceDisplay rendert Diffs, Terminalblöcke und Dateizugriffe neben den Bedienelementen der Trajektorien-Evaluation

3. Annotationsserver starten

bash
potato start config.yaml -p 8000

http://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:

  1. Den Inhalt des Schritts lesen, im Kontext der vorherigen Schritte
  2. Korrektheit markieren, per Klick auf „Korrekt" oder „Inkorrekt"
  3. Bei inkorrekt die Fehlerkategorie wählen (etwa „Denkfehler") und danach den konkreten Typ (etwa „Falsche Annahme")
  4. Schweregrad vergeben: gering, schwer oder kritisch
  5. 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/"
  6. 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

python
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

python
# 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:

python
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

python
# 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

python
# 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.