Ir al contenido

Depurar una prueba fallida con un agente

Esta guía te muestra cómo lograr que un agente use un depurador en un fallo que las instrucciones print no explican.

La herramienta debug controla una sesión real del Debug Adapter Protocol. Un frame detenido responde de una vez a todas las preguntas sobre ese momento —cada variable local, cada invocador y una expresión evaluada en el propio ámbito del frame—, mientras que una instrucción print solo responde a la pregunta que se te ocurrió hacer antes de ejecutar.

La herramienta se mantiene expuesta para que el agente pueda preguntar por qué nada es utilizable, en lugar de adivinar. Un adaptador solo puede arrancar de verdad cuando se cumplen ambas condiciones:

  • un marcador de proyecto en el checkout, y
  • que el binario del adaptador se pueda resolver.

Pídele al agente que compruebe:

debug(op: "adapters")

Responde con lo que encontró, o explica que un adaptador necesita ambas partes.

Lenguaje Adaptador Marcador Instalación
Dart / Flutter dart debug_adapter pubspec.yaml Viene con el SDK: no hay nada que hacer
Python python3 -m debugpy.adapter pyproject.toml, setup.py, requirements.txt pip install debugpy
Rust / C / C++ lldb-dap Cargo.toml, CMakeLists.txt Viene con LLVM
Go dlv dap go.mod go install github.com/go-delve/delve/cmd/dlv@latest

Primero establece el punto de interrupción, luego lanza

Sección titulada «Primero establece el punto de interrupción, luego lanza»

El orden importa y la herramienta lo impone: los puntos de interrupción pueden establecerse antes de que exista una sesión, porque esa es la secuencia que exige el protocolo.

Pídele al agente algo como:

La función compute en bin/main.dart devuelve el valor equivocado. Establece un punto de interrupción en la línea que asigna product, lánzala bajo el depurador y dime qué son en realidad a y b.

Ejecutará aproximadamente:

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

El lanzamiento informa dónde se detuvo:

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)

Luego avanza hacia dentro:

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

Un objeto con hijos informa [expand: reference N] —pero solo cuando de verdad hay algo detrás—, así que una expansión que el agente no puede usar nunca se ofrece.

Para cualquier cosa que no sea visible como variable local:

debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)
Lo que quieres Op
Ejecutar hasta la siguiente detención continue
Siguiente línea, mismo frame step_over
Entrar en la llamada step_in
Salir al invocador step_out
Detener un programa en ejecución pause

Adjúntate a algo que ya está en ejecución

Sección titulada «Adjúntate a algo que ya está en ejecución»

Para un proceso atorado y no para uno que estás iniciando:

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

Termina siempre la sesión. Un adaptador de depuración posee un proceso detenido que retiene todo lo que retiene ese proceso: un puerto, un bloqueo, una conexión a una base de datos.

Una expansión de variables sobre un grafo de objetos profundo es ilimitada por naturaleza —un solo widget de Flutter alcanza todo el árbol de elementos—. Los hijos por ámbito están limitados a 50, los valores a 400 caracteres y los frames de pila a 30. Un resultado de herramienta que llena la ventana de contexto es una herramienta que termina la ejecución que estaba ayudando.