KI-Agent-Runs debuggen: Timeouts, Wiederholungen und prüfbare Belege

Fehlgeschlagene Agent-Runs mit geordneten Ereignissen, wiederholten Tool-Aufrufen, sicherem Replay und einem Python-Beispiel ohne externe APIs debuggen.
Auf dieser Seite
  1. Idee in 30 Sekunden
  2. Das Hauptproblem
  3. So funktioniert es
  4. Typische Production-Signale für debugging runs
  5. Wie man den Debugging-Layer liest
  6. Wann verwenden
  7. Implementierungsbeispiel
  8. Ausführbare Testdaten: Timeout und wiederholter Tool-Aufruf
  9. Wann die Belege einen Fix stützen
  10. Investigation
  11. Typische Fehler
  12. Analyse beginnt bei irgendeinem Log-Fehler
  13. Keine Korrelation von trace + logs + metrics
  14. repeated calls und stop_reason werden ignoriert
  15. Kein Vergleich mit vorherigem Release
  16. Incident wird ohne replay und Verifikation geschlossen
  17. Selbst-Check
  18. FAQ
  19. Verwandte Seiten

Idee in 30 Sekunden

Debugging von Agent-Runs hilft, vom Symptom zur Ursache zu kommen: was genau kaputt ist, in welchem Schritt, und warum.

Dafür müssen Tracing, Logs und Metriken eines problematischen Runs zusammengeführt werden.

Ohne das sieht das Team oft nur den finalen Fehler, aber nicht den gesamten Weg dorthin.

Das Hauptproblem

In Agent-Systemen hat ein Incident selten nur eine offensichtliche Ursache.

Der finale Fehler kann nur eine Folge sein: Das eigentliche Problem kann früher begonnen haben, zum Beispiel durch einen langsamen tool call, ein fehlerhaftes retry oder eine Regression nach dem Release. Ohne systematisches debugging ist das schwer schnell zu lokalisieren.

Als Nächstes schauen wir, wie man diese Signale liest und stabil root cause findet.

In Production sieht das oft so aus:

  • in Logs gibt es viele Events, aber keine klare Reihenfolge;
  • die Ursache vermischt sich mit Begleitfehlern;
  • der Incident wurde "gefixt", kommt aber nach dem Release zurück;
  • MTTR steigt, weil das Team den Incident-Kontext jedes Mal neu aufbauen muss.

Genau deshalb sollte debugging eines Runs ein eigener operativer Prozess sein und keine manuelle Suche nach dem "ersten error".

So funktioniert es

Ein praktischer debugging run besteht meist aus drei Ebenen:

  • Run-Kontext (run_id, trace_id, release, workflow);
  • Evidence -> Analyse (spans, logs, metrics, stop_reason);
  • Entscheidung (Hypothese -> Fix -> Verifikation via replay und Tests).

Diese Ebenen beantworten: wo das Problem liegt, warum es entstanden ist und ob der Fix es wirklich entfernt. Tracing zeigt den Pfad, Logs zeigen Ereignisse, und Metriken zeigen Ausmaß und Trend.

Viele Logs != schnelles debugging. Geschwindigkeit entsteht nicht aus Datenmenge, sondern aus Korrelation rund um einen Run.

Typische Production-Signale für debugging runs

SignalWo prüfenWarum es wichtig ist
first_error_spanTracingPunkt finden, an dem der Fehler zuerst erschien
slowest_spanTracing + MetrikenBottleneck-Kandidat (muss verifiziert werden)
stop_reasonLog run_finishedverstehen, wie der Run beendet wurde
error_classtool_result / llm_result LogsTimeout von Logikfehler trennen
repeated_tool_callstool_call Logs + tool metricswiederholte Aufrufe erkennen (loops, retries, tool spam)
run_latency_p95Metrikenprüfen, ob Incident bereits systemisch ist
release_diffRelease-VergleichsdashboardRegression nach Änderungen erkennen
synthetic_run_statushealth checksAuswirkung auf kritischen workflow prüfen

Damit debugging stabil bleibt, werden diese Signale typischerweise nach release, workflow, model und tool segmentiert.

Wichtig: keine high-cardinality Labels (run_id, request_id, user_id) in Metriken aufnehmen. Dafür sind Logs und Tracing besser geeignet.

Wie man den Debugging-Layer liest

Welcher Run ist fehlgeschlagen -> in welchem Schritt -> warum genau. Diese drei Ebenen sollte man immer gemeinsam betrachten.

Wichtig ist, auf Trends und Release-Deltas zu schauen, nicht nur auf ein einzelnes Notfall-Event.

Typische Signalkombinationen:

  • first_error_span=tool_call + tool_error_rate ↑ -> Problem in einem konkreten tool-layer;
  • run_latency_p95 ↑ + tool_latency_p95 stabil -> wahrscheinlich Problem in LLM oder runtime-Logik;
  • repeated_tool_calls ↑ + stop_reason=max_steps -> Agent steckt in einer Schleife;
  • error_rate ↑ nach Release + positives release_diff -> Änderungsregression, kein einmaliger Incident;
  • synthetic_run_status=fail + health_score ↓ -> Problem beeinflusst bereits kritischen workflow.

Wann verwenden

Ein formaler debugging-flow ist nicht immer nötig.

Für ein einfaches single-shot Szenario ohne tools reichen manchmal Basis-Logging und manuelle Fehlerprüfung.

Ein systematischer debugging-Ansatz wird aber kritisch, wenn:

  • Runs mehrere reasoning-Schritte und tool calls enthalten;
  • Incidents Latenz, Kosten oder SLO beeinflussen;
  • Releases häufig kommen und Regressionen früh erkannt werden müssen;
  • es einen on-call Prozess gibt und ein planbares MTTR nötig ist.

Implementierungsbeispiel

Unten ist eine vereinfachte Funktion, die Evidence für einen Run sammelt und eine Basis-Hypothese bildet. Das ersetzt kein vollständiges incident tooling, zeigt aber einen praktischen debugging-Prozess.

PYTHON
from collections import Counter


def debug_run(run_id, trace_events, log_events, debug_metrics_snapshot):
    run_spans = sorted(
        [s for s in trace_events if s.get("run_id") == run_id],
        key=lambda s: s.get("started_at_ms", 0),
    )
    run_logs = sorted(
        [e for e in log_events if e.get("run_id") == run_id],
        key=lambda e: e["event_seq"],
    )

    first_error_span = next((s for s in run_spans if s.get("status") == "error"), None)
    # slowest_span kann None sein, wenn der Run keine spans enthält
    slowest_span = max(run_spans, key=lambda s: s.get("latency_ms", 0), default=None)

    stop_reason = "unknown"
    for event in reversed(run_logs):
        if event.get("event") == "run_finished":
            stop_reason = event.get("stop_reason", "unknown")
            break

    seen_signatures = set()
    repeated_tools = Counter()
    unclassified_tool_calls = 0
    for event in run_logs:
        if event.get("event") != "tool_call":
            continue
        if not event.get("tool") or not event.get("args_hash"):
            unclassified_tool_calls += 1
            continue
        signature = (event["tool"], event["args_hash"])
        if signature in seen_signatures:
            repeated_tools[event.get("tool")] += 1
        else:
            seen_signatures.add(signature)

    hypotheses = []
    if first_error_span and first_error_span.get("step_type") == "tool_call":
        hypotheses.append("Wahrscheinlicher Ausfall im tool-layer: Tool-Verfügbarkeit und timeout policy prüfen.")

    if repeated_tools:
        hypotheses.append("Wiederholte tool calls erkannt: dedupe/cache und stop conditions prüfen.")

    if slowest_span and debug_metrics_snapshot.get("run_latency_p95_ms", 0) > debug_metrics_snapshot.get("slo_latency_ms", 2500):
        hypotheses.append("p95 latency liegt über SLO: Bottleneck über slowest_span lokalisieren.")

    if debug_metrics_snapshot.get("release_error_rate_delta", 0) > 0:
        hypotheses.append("error_rate ist nach Release gestiegen: Änderungen in prompt/runtime/tool routing prüfen.")

    return {
        "run_id": run_id,
        "first_error_span": first_error_span,
        "slowest_span": slowest_span,
        "stop_reason": stop_reason,
        "repeated_tools": dict(repeated_tools),
        "unclassified_tool_calls": unclassified_tool_calls,
        "hypotheses": hypotheses,
    }

Ausführbare Testdaten: Timeout und wiederholter Tool-Aufruf

Speichere debug_run oben und den folgenden Code gemeinsam als debug_demo.py. Starte python3 debug_demo.py mit Python 3.10+: ohne zusätzliche Bibliotheken, Schlüssel oder Netzwerkanfragen. event_seq ordnet Ereignisse innerhalb eines Runs; die Runtime muss diese Reihenfolge vergeben, nicht der Log-Eingang. Das Beispiel liefert die Einträge absichtlich in umgekehrter Reihenfolge.

PYTHON
run_id = "demo-timeout"
spans = [{"run_id": run_id, "step_type": "tool_call",
          "started_at_ms": 100, "latency_ms": 2000, "status": "error"}]
logs = [
    {"run_id": run_id, "event_seq": 3, "event": "run_finished",
     "stop_reason": "tool_error"},
    {"run_id": run_id, "event_seq": 2, "event": "tool_call",
     "tool": "create_ticket", "args_hash": "fixture-a"},
    {"run_id": run_id, "event_seq": 1, "event": "tool_call",
     "tool": "create_ticket", "args_hash": "fixture-a"},
]
report = debug_run(run_id, spans, logs, {})
assert report["stop_reason"] == "tool_error"
assert report["repeated_tools"] == {"create_ticket": 1}
assert debug_run("other-run", spans, logs, {})["repeated_tools"] == {}
unknown = [{"run_id": run_id, "event_seq": i, "event": "tool_call",
            "tool": "create_ticket"} for i in range(2)]
report = debug_run(run_id, [], unknown, {})
assert report["repeated_tools"] == {}
assert report["unclassified_tool_calls"] == 2
print("debug fixture: 5 passed")

Erwartete Ausgabe: debug fixture: 5 passed. Wiederholt wird die Analyse aufgezeichneter Ereignisse, nicht die Agent-Ausführung. Der wiederholte Aufruf von create_ticket muss untersucht werden: Ein Timeout beweist nicht, dass der erste Versuch kein Ticket angelegt hat. Gleiche operation_id mit dem externen System ab; teste die Runtime mit aufgezeichneten Tool-Antworten und ohne Schreibzugriff auf Produktion. AWS erklärt Idempotenz für sichere Wiederholungen.

args_hash muss eine stabile Signatur normalisierter Argumente sein, ohne Geheimnisse in Logs. Gleiche Argumente beweisen keine gleiche Absicht. Fehlt die Signatur, zählt das Beispiel einen nicht klassifizierten Aufruf statt eines Duplikats.

Wann die Belege einen Fix stützen

Bewahre Runtime-Version, Prompt, Modell, bereinigte Eingaben und Tool-Ergebnisse auf. Vergleiche dasselbe Szenario vor und nach der Änderung. Ergänze einen Regressionstest, der Ergebnis und Anzahl externer Aktionen prüft, nicht nur das Ausbleiben einer Exception.

Der zuerst gestartete Error-Span ist ein Kandidat, keine garantierte Grundursache: Ein Parent-Span kann nur einen Child-Fehler widerspiegeln. Replay deckt das aufgezeichnete Szenario ab, nicht jede Race Condition oder externe Serviceänderung. Ist ein Incident nicht reproduzierbar, halte die Unsicherheit fest und prüfe die Hypothese mit zusätzlicher Telemetrie.

Es folgt ein illustrativer Debugging-Snapshot:

Runfirst_error_spanslowest_spanstop_reasonFazit
run_9fd2tool_call: search_docstool_call: search_docs (1.8s)tool_errortool degradiert + retries
run_a113llm_generatellm_generate (2.4s)step_errorModellfehler nach Release
run_d77c—reasoning (3.1s)max_stepsLoop ohne expliziten Fehler

Investigation

Wenn ein Incident-Signal auslöst:

  1. run_id, trace_id, release und betroffenen workflow festhalten;
  2. im Tracing first_error_span und slowest_span finden;
  3. in Logs stop_reason, error_class, repeated_tool_calls prüfen;
  4. in Metriken Ausmaß bestätigen (Spike oder Trend) und Release-Deltas vergleichen.

Typische Fehler

Auch mit observability scheitert debugging oft an typischen Fehlern.

Analyse beginnt bei irgendeinem Log-Fehler

Ohne Bindung an eine konkrete run_id mischt das Team Symptome aus verschiedenen Incidents. So wird es schwer, lokales Problem von kaskadierendem Ausfall zu trennen.

Keine Korrelation von trace + logs + metrics

Wenn Tracing, Logs und Metriken getrennt betrachtet werden, widersprechen sich Hypothesen oft. Dadurch steigt MTTR selbst bei einfachen Tool-Ausfällen.

repeated calls und stop_reason werden ignoriert

Ohne diese Signale übersieht man leicht loops und retry storms. Das verdeckt oft die frühe Phase von Tool-Spam.

Kein Vergleich mit vorherigem Release

Ohne release_diff sieht das Team nicht, ob das Problem nach Änderungen aufgetreten ist. Dadurch bleibt Regression länger in Production.

Incident wird ohne replay und Verifikation geschlossen

Ein Fix kann nur das Symptom entfernen, nicht die Ursache. Das erhöht das Risiko einer wiederholten teilweisen Störung.

Selbst-Check

Unten ist eine kurze Baseline-Checkliste für debugging-flow vor dem Release.

Fortschritt: 0/9

⚠ Grundlegende Observability fehlt

Das System wird in production schwer zu debuggen sein. Starten Sie mit run_id, structured logs und tracing von tool calls.

FAQ

Q: Womit beginnt debugging eines problematischen Runs?
A: Starte mit run_id und trace_id: finde first_error_span, prüfe stop_reason und bestätige dann das Ausmaß in den Metriken. first_error_span ist der schnellste Weg zum Fehlerpunkt.

Q: Was ist wichtiger für debugging: Tracing oder Logs?
A: Beides zusammen: Tracing zeigt den Schrittpfad, Logs liefern Event-Details (error_class, args_hash, policy decision).

Q: Wie erkenne ich Release-Regression statt einmaligem Ausfall?
A: Vergleiche error_rate, latency_p95, repeated_tool_calls zwischen Releases. Wenn das Signal nach dem Release stabil schlechter ist, ist es Regression.

Q: Was ist das Minimum an Daten für Debugging in 10-15 Minuten?
A: Minimum: run_id, trace_id, first_error_span, stop_reason, error_class, latency_p95 und Release-Kontext.

Verwandte Seiten

Weiter zum Thema:

⏱️ 9 Min. Lesezeit • Aktualisiert 3. Oktober 2026Schwierigkeit: ★★★
Integriert: Production ControlOnceOnly
Make agent behavior observable with run timelines
Correlate actions and policy decisions using run events, timelines, logs, and metrics:
  • Budgets (Steps / Spend Caps)
  • Tool-Permissions (Allowlist / Blocklist)
  • Kill switch & Incident Stop
  • Idempotenz & Dedupe
  • Audit logs & Nachvollziehbarkeit
Integrierter Hinweis: OnceOnly ist eine Control-Layer für Production-Agent-Systeme.
Observability Hooks (Python SDK)
from onceonly import OnceOnly

client = OnceOnly(api_key="once_live_...")
run_id = "run_obs_042"

client.post_event(run_id=run_id, type="step_start", step="retrieve_context")
client.post_event(run_id=run_id, type="step_end", step="retrieve_context", status="ok")

timeline = client.get_run_timeline(run_id, limit=200, offset=0)
metrics = client.gov.agent_metrics("support-agent", period="day")

Autor

Nick — Engineer, der Infrastruktur für KI-Agenten in Produktion aufbaut.

Fokus: Agent-Patterns, Failure-Modes, Runtime-Steuerung und Systemzuverlässigkeit.

🔗 GitHub: https://github.com/mykolademyanov


Redaktioneller Hinweis

Diese Dokumentation ist KI-gestützt, mit menschlicher redaktioneller Verantwortung für Genauigkeit, Klarheit und Produktionsrelevanz.

Die Beispiele dienen Lernzwecken und können simulierte Tools und Daten verwenden. Prüfen Sie vor dem Produktionseinsatz Zuverlässigkeit, Sicherheit und Wiederherstellung in Ihrer eigenen Umgebung.