Phase 14 - Lesson 37

Loops de Feedback en Tiempo de Ejecución

Los agentes que no ven la salida real del comando adivinan. Un ejecutor de feedback captura stdout, stderr, el código de salida y el tiempo en un registro estructurado que la siguiente iteración puede leer. De este modo, el agente reacciona a los hechos en lugar de a su propia predicción de los hechos.

Tipo: Build Idiomas: Python (stdlib) Prerrequisitos: Phase 14 · 32 (Minimal Workbench), Phase 14 · 35 (Init Script) Tiempo: ~50 minutos

Objetivos de Aprendizaje

  • Distinguir el feedback en tiempo de ejecución de la telemetría de observabilidad.
  • Construir un ejecutor de feedback que envuelva comandos de shell y persista registros estructurados.
  • Truncar salidas grandes de forma determinista para que el loop se mantenga dentro del presupuesto de tokens.
  • Negarse a avanzar en el loop cuando falte el feedback.

El Problema

El agente dice "ejecutando pruebas ahora." El siguiente mensaje dice "todas las pruebas pasaron." La realidad es que no se ejecutó ninguna prueba. El agente imaginó la salida, o ejecutó el comando y nunca leyó el resultado, o leyó el resultado y truncó silenciosamente la línea de fallo.

Un ejecutor de feedback elimina esa brecha. Cada comando pasa a través del ejecutor. Cada registro contiene el comando, el stdout y stderr capturados, el código de salida, la duración de tiempo real y una nota de una línea del agente. El agente lee el registro en la siguiente iteración. La puerta de verificación lee los registros al final de la tarea.

El Concepto

flowchart LR
  Agent[Loop del Agente] --> Runner[run_with_feedback.py]
  Runner --> Shell[subprocess]
  Shell --> Capture[stdout / stderr / exit / duration]
  Capture --> Record[feedback_record.jsonl]
  Record --> Agent
  Record --> Gate[Puerta de Verificación]

Qué va en un registro de feedback

Campo Por qué importa
command Argv exacto, sin sorpresas de expansión de shell
stdout_tail Últimas N líneas, truncamiento determinista
stderr_tail Últimas N líneas, separado de stdout
exit_code La señal inequívoca de éxito
duration_ms Revela sondeos lentos y procesos descontrolados
started_at Marca de tiempo para reproducción
agent_note Una línea que el agente escribe sobre lo que esperaba

El truncamiento es determinista

Un log de 50 MB destruye el loop. El ejecutor trunca el inicio y el final con un marcador ...truncated N lines..., de forma determinista para que la misma salida siempre produzca el mismo registro. Sin muestreo; las partes que el agente necesita ver (error final, resumen final) residen al final (tail).

Feedback frente a telemetría

La telemetría (Phase 14 · 23, convenciones de OTel GenAI) es para operadores humanos que revisan ejecuciones a lo largo del tiempo. El feedback es para la siguiente iteración de esta ejecución. Comparten campos pero viven en archivos diferentes con diferentes tiempos de retención.

Negarse a avanzar sin feedback

Si el ejecutor falla antes de capturar la salida, el registro contiene exit_code: null y error: <reason>. El loop del agente debe negarse a declarar éxito con un código de salida null. Sin salida, no hay progreso.

Constrúyalo

code/main.py implementa:

  • run_with_feedback(command, agent_note) que envuelve subprocess.run, captura stdout/stderr/exit/duration, trunca de forma determinista y añade a feedback_record.jsonl.
  • Un cargador pequeño que transmite el JSONL a una lista de Python.
  • Una demostración que ejecuta tres comandos (éxito, fallo, lento) e imprime el último registro de cada comando.

Ejecútelo:

python3 code/main.py

Salida: tres registros de feedback añadidos a feedback_record.jsonl, el último de cada uno impreso en línea. Monitoree el final del archivo (tail) a lo largo de las reejecuciones para ver cómo se acumula el loop.

Patrones de producción en el mundo real

Tres patrones robustecen el ejecutor lo suficiente como para implementarlo.

Censurar al escribir, no al leer. Cualquier registro que toque stdout o stderr puede filtrar secretos. El ejecutor realiza un paso de censura antes de añadir al JSONL: elimina las líneas que coincidan con ^Bearer , password=, api[_-]?key=, AKIA[0-9A-Z]{16} (AWS), xox[baprs]- (Slack). La censura en el momento de la lectura es un peligro; el archivo en disco es lo que alcanza un atacante. Audite los patrones de censura trimestralmente en relación con los formatos de secretos observados en el entorno de ejecución de producción.

Política de rotación, no un solo archivo. Limite feedback_record.jsonl a 1 MB por archivo; en caso de desbordamiento, rote a .1, .2, descarte .5. El loop del agente solo lee el archivo actual, por lo que el coste de ejecución está limitado. El almacenamiento de artefactos de CI recibe el conjunto rotado completo. Sin rotación, el archivo se convierte en el cuello de botella en cada llamada del cargador.

ID de comando padre para cadenas de reintentos. Cada registro recibe un command_id; los reintentos llevan un parent_command_id que apunta al intento anterior. La lista de "intentos fallidos" del revisor (Phase 14 · 40) y la auditoría de la puerta de verificación siguen la cadena. Sin este enlace, los reintentos parecen éxitos independientes y la auditoría oculta el historial de fallos.

Úselo

Patrones de producción:

  • Herramienta Bash de Claude Code. La herramienta ya captura stdout, stderr, exit y duration. El ejecutor de esta lección es el equivalente agnóstico del framework para cualquier producto de agente.
  • Nodos de LangGraph. Envuelva cualquier nodo de shell en el ejecutor para que el registro persista fuera del estado del grafo.
  • Logs de CI. Transmita el JSONL a su almacenamiento de artefactos de CI; los revisores pueden reproducir cualquier comando sin tener que volver a ejecutar la sesión.

El ejecutor es un envoltorio delgado que sobrevive a cualquier migración de framework porque es el propietario de la estructura del registro.

Envíelo

outputs/skill-feedback-runner.md genera un run_with_feedback.py específico del proyecto con el presupuesto de truncamiento adecuado, un escritor JSONL conectado a la bancada de trabajo y un cargador que el agente lee en cada iteración.

Ejercicios

  1. Añada un campo cwd por registro para que se pueda distinguir el mismo comando ejecutado desde diferentes directorios.
  2. Añada un paso de censura que elimine las líneas que coincidan con ^Bearer o password=. Pruébelo con un registro de prueba (fixture).
  3. Limite el tamaño total de feedback_record.jsonl a 1 MB rotando a los archivos .1 y .2. Defenda la política de rotación.
  4. Añada un parent_command_id para que las cadenas de reintentos sean visibles: qué comando produjo la entrada que consumió el siguiente comando.
  5. Dirija el JSONL a una pequeña TUI que resalte el último código de salida diferente de cero. Enumere ocho características clave que la TUI debe mostrar para ser útil en una revisión.

Términos Clave

Término Qué dice la gente Qué significa realmente
Registro de feedback "Log de ejecución" Entrada JSONL estructurada con comando, salida, código de salida, duración
Truncamiento de cola "Recortar el log" Captura determinista de inicio + fin para que los registros quepan en el presupuesto de tokens
Denegación por nulo "Bloquear por datos faltantes" El loop no debe avanzar cuando exit_code es nulo
Nota del agente "Etiqueta de expectativa" La predicción de una línea que el agente escribe antes de leer el resultado
División de telemetría "Dos archivos de log" Feedback para la siguiente iteración, telemetría para el operador

Lecturas Adicionales

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).