Zum Inhalt springen

Einen fehlgeschlagenen Test mit einem Agenten debuggen

Diese Anleitung zeigt, wie du einen Agenten dazu bringst, einen Debugger zu benutzen, wenn Print-Anweisungen einen Fehler nicht erklären.

Das Tool debug steuert eine echte Sitzung des Debug Adapter Protocol. Ein angehaltener Frame beantwortet jede Frage zu diesem Moment auf einmal — jede lokale Variable, jeden Aufrufer und einen Ausdruck, der im eigenen Scope des Frames ausgewertet wird — während eine Print-Anweisung nur die Frage beantwortet, die du dir vor dem Lauf gestellt hast.

Das Tool bleibt an der Oberfläche, damit der Agent fragen kann, warum nichts brauchbar ist, statt zu raten. Ein Adapter kann tatsächlich erst starten, wenn beides gilt:

  • ein Projektmarker im Checkout, und
  • die Binärdatei des Adapters lässt sich auflösen.

Bitte den Agenten, das zu prüfen:

debug(op: "adapters")

Er antwortet mit dem, was er gefunden hat, oder erklärt, dass ein Adapter beide Hälften braucht.

Sprache Adapter Marker Installation
Dart / Flutter dart debug_adapter pubspec.yaml Kommt mit dem SDK — nichts zu tun
Python python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt pip install debugpy
Rust / C / C++ lldb-dap Cargo.toml, CMakeLists.txt Kommt mit LLVM
Go dlv dap go.mod go install github.com/go-delve/delve/cmd/dlv@latest

Die Reihenfolge zählt, und das Tool erzwingt sie: Haltepunkte dürfen vor einer Sitzung gesetzt werden, weil das Protokoll genau diese Folge verlangt.

Bitte den Agenten zum Beispiel so:

Die Funktion compute in bin/main.dart liefert den falschen Wert. Setze einen Haltepunkt auf die Zeile, die product zuweist, starte es unter dem Debugger und sag mir, was a und b wirklich sind.

Er führt ungefähr das aus:

debug(op: "breakpoints", file: "bin/main.dart", lines: [7])
debug(op: "launch", program: "bin/main.dart")

Der Start meldet, wo er angehalten hat:

Launched bin/main.dart on dart (session dart-1). Stopped: breakpoint. Use `stack` to see where.
debug(op: "stack")
→ #1 compute (bin/main.dart:7:3)
#2 main (bin/main.dart:2:18)

Dann gehst du hinein:

debug(op: "scopes", frame_id: 1)
→ Locals (reference 12)
debug(op: "variables", reference: 12)
→ a (int) = 6
b (int) = 7
product (int) = 42

Ein Objekt mit Kindern meldet [expand: reference N] — aber nur, wenn wirklich etwas dahintersteckt, damit eine Aufklappung, die der Agent nicht brauchen kann, nie angeboten wird.

Für alles, was nicht als lokale Variable sichtbar ist:

debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)
Was du willst Op
Bis zum nächsten Halt laufen continue
Nächste Zeile, derselbe Frame step_over
In den Aufruf hinein step_in
Hinaus zum Aufrufer step_out
Ein laufendes Programm anhalten pause

Für einen festgefahrenen Prozess, nicht für einen, den du selbst startest:

debug(op: "attach", port: 5858)
debug(op: "terminate")

Beende die Sitzung immer. Ein Debug-Adapter besitzt einen angehaltenen Prozess, der festhält, was dieser Prozess festhält — einen Port, eine Sperre, eine Datenbankverbindung.

Eine variables-Aufklappung auf einem tiefen Objektgraphen ist von Natur aus unbegrenzt — ein Flutter-Widget reicht bis in den ganzen Elementbaum. Kinder je Scope sind auf 50 begrenzt, Werte auf 400 Zeichen und Stack-Frames auf 30. Ein Tool-Ergebnis, das das Kontextfenster füllt, ist ein Tool, das den Lauf beendet, dem es helfen sollte.