Ir al contenido

Depurar un test fallido con un agente

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

La herramienta debug controla una sesión real de 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 permanece en la superficie para que el agente pueda preguntar por qué no hay nada utilizable, en lugar de adivinar. Un adaptador solo puede arrancar de verdad cuando se cumplen las dos condiciones siguientes:

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

Pide al agente que compruebe:

debug(op: "adapters")

Responde con lo que ha encontrado o explica que un adaptador necesita las dos mitades.

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 el punto de interrupción, luego el lanzamiento

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

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.

Pide al agente algo como:

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

Ejecutará algo así:

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

El lanzamiento informa de dónde se ha detenido:

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 profundiza en él:

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 de [expand: reference N] —pero solo cuando hay algo real detrás—, de modo que nunca se ofrece una expansión que el agente no pueda usar.

Para cualquier cosa que no sea visible como variable local:

debug(op: "evaluate", expression: "config.timeout.inSeconds", frame_id: 1)
Qué quieres Operación
Ejecutar hasta la siguiente parada 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

Adjuntarse a algo que ya se está ejecutando

Sección titulada «Adjuntarse a algo que ya se está ejecutando»

Para un proceso bloqueado en lugar de uno que estás iniciando:

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

Cierra 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 con la ejecución a la que estaba ayudando.