Lección 5: El bucle de revisión, y componer patrones en un grafo
Objetivos de aprendizaje:
- Implementar un bucle de revisar y refinar (generar un borrador, evaluarlo, corregirlo según la retroalimentación), y usar dos criterios de decisión para determinar si vale la pena construir este bucle
- Escribir condiciones de parada más inteligentes que «máximo N rondas», y colocar los chequeos deterministas antes del juez
- Componer cinco patrones en lo que esta lección llama un «grafo», y dejar documentado en tus propios materiales que este sistema visual es una metáfora propia anclada a una cita concreta de una fuente primaria
Requisitos: Leíste las Lecciones 1–4 (quién tiene el plan, encadenamiento y enrutamiento, paralelización, orquestador-trabajadores), puedes escribir a mano un bucle de arnés impulsado por stop_reason | Anterior: << Lección 4 | Siguiente: Lección 6 >>
El borrador al que siempre le falta un paso
Le pides a un agente que escriba un plan de migración de base de datos. El primer borrador vuelve con aspecto razonable: contexto, pasos, ventana de tiempo, todo presente. Pero de un vistazo detectas dos huecos—la sección de reversión apenas dice «revertir si es necesario», y no hay clasificación de riesgo por ninguna parte. Escribes dos líneas de retroalimentación señalándolos, vuelve el segundo borrador, los dos huecos están llenos, y el conjunto sube un escalón de calidad.
Has repetido este proceso decenas de veces. Cada vez es lo mismo: la salida se queda corta, una persona aporta dos frases de retroalimentación, la salida mejora notablemente.
El problema no es que el modelo escriba mal. El problema es que tus dos frases de retroalimentación no son difíciles de producir. «Los pasos de reversión deben incluir comandos concretos», «Cada paso necesita una clasificación de riesgo»—estas son cosas que una lista de verificación podría cubrir. Si tú lo puedes articular con claridad, el modelo probablemente también. Entonces, ¿por qué tienes que ser tú quien lo dice cada vez?
Esta forma vale la pena escribirla como un bucle.
Revisar y refinar: escribir «corrígelo una vez más» en el flujo de control
La fuente primaria lo define en una frase: una llamada a un LLM genera una respuesta mientras otra aporta evaluación y retroalimentación en un bucle1.
Los cuatro patrones de las primeras cuatro lecciones tienen cada uno su propia topología: el encadenamiento descompone una tarea en una secuencia de pasos, donde cada paso procesa la salida del anterior1; el enrutamiento clasifica y luego despacha hacia tareas de seguimiento especializadas1; la paralelización ejecuta cosas simultáneamente y agrega los resultados por programa1; orquestador-trabajadores hace que un LLM central descomponga dinámicamente las tareas, delegue en trabajadores y sintetice los resultados1. Todos comparten un rasgo: los datos fluyen hacia adelante. El bucle de revisión es el primer patrón con una arista de retorno—la salida vuelve en bucle al nodo de generación.
¿Cómo se ve en un producto? La documentación de flujos de trabajo dinámicos de Claude Code da una descripción en lenguaje llano: ejecutar un verificador, corregir lo que falló, y repetir hasta que pase o deje de haber progreso2. Otra descripción cubre un uso distinto de la misma división del trabajo: hacer que agentes independientes revisen de forma adversarial los hallazgos de los demás antes de que se reporten2. Esa es una revisión cruzada única antes de reportar, sin arista de retorno y sin iteración—meterla dentro del bucle de revisión es una categorización de esta lección, no que el texto original describa la misma topología.
Un concepto, tres nombres
Aquí necesitamos tender un puente explícito, o vas a creer que estás aprendiendo tres cosas distintas.
El curso 6 de esta serie, que enseña colaboración multiagente, llama a esta división de «uno hace el trabajo, otro lo critica» productor-revisor. Ese es vocabulario didáctico nuestro. En los materiales primarios, la documentación de flujos de trabajo de Claude Code describe su uso de producto en una frase: hacer que agentes independientes revisen de forma adversarial los hallazgos de los demás (la abreviatura de esta lección para esa frase es «revisión cruzada adversarial»). La misma forma tiene dos nombres en las fuentes primarias: la referencia de patrones de Anthropic la llama evaluator-optimizer1; la documentación de flujos de trabajo de Claude Code no le da nombre, solo describe el uso—esa frase sobre revisar de forma adversarial los hallazgos de los demás2.
Tres nombres, una forma. La diferencia está en desde qué ángulo lo miras: al hablar de roles de colaboración ves dos actores, al hablar de patrones de orquestación ves una arista de retorno, al hablar de capacidades de producto ves una técnica de calidad reutilizable.
Hay una división del trabajo más que aclarar. El curso 10 de esta serie dedica un curso entero a enseñarte cómo ser un buen juez: cómo escribir rúbricas, cómo restringir el formato de salida del juez, por qué el juez necesita su propio contexto independiente, por qué el trabajador no debería ser también el juez. Ese curso enseña la calidad del juez en sí. Esta lección no repite esos temas. Esta lección enseña cómo cablear al juez dentro del flujo de control—en qué parte del bucle se sienta, cuándo se ejecuta, cuántas rondas se ejecuta, cuándo se detiene.
Cuándo vale la pena construir este bucle
Los criterios de aplicabilidad de la fuente primaria: este flujo de trabajo es particularmente efectivo cuando tenemos criterios de evaluación claros, y cuando el refinamiento iterativo aporta valor medible; las dos señales de buen encaje son, primero, que las respuestas del LLM se pueden mejorar demostrablemente cuando una persona articula su retroalimentación; y segundo, que el LLM puede aportar esa retroalimentación1.
Ya viste esta cita antes. El curso 10 de esta serie citó exactamente esta frase al responder la pregunta de «¿vale la pena construir un bucle de revisar y corregir?». Los mismos criterios, replanteados en un contexto de orquestación—salvo que esta vez estás implementando la respuesta como un bucle en el flujo de control.
Desglosadas, estas dos señales protegen cada una contra modos de fallo distintos:
La primera señal protege contra «corregir no ayuda». Algunas tareas no mejorarán en un segundo borrador por más clara que enuncies la retroalimentación—porque el problema son datos de entrada faltantes o una tarea definida de forma vaga, no la redacción de la salida. En este caso, construir un bucle solo significa que pagas dos veces para obtener dos versiones igual de inutilizables. El método de validación es tosco pero efectivo: hazlo tú mismo a mano tres veces. ¿Cuántas de esas tres fueron «claramente mejor después de la retroalimentación humana»? Si dos de tres fueron «la retroalimentación no ayudó», no construyas el bucle.
La segunda señal protege contra «el juez no puede dar ese tipo de retroalimentación». Aun si la retroalimentación humana funciona, todavía debes preguntar: ¿puede el modelo mismo aportar el mismo tipo de retroalimentación? Si tu retroalimentación depende de cosas que solo tú sabes (de qué se quejó este cliente el trimestre pasado, qué informó legal de palabra la semana pasada), el modelo no tiene esa información, así que la retroalimentación que dé será algo completamente distinto. En ese caso, o alimentas esa información dentro del prompt del juez—conviértela en criterios que el modelo pueda evaluar—o aceptas que este paso necesita a una persona.
Hay una precondición que entra en juego incluso antes que estas dos señales: los criterios de evaluación deben ser claros. Cuando los criterios no son claros, el bucle produce de forma confiable un tipo específico de fallo—el juez da retroalimentación que apunta en direcciones distintas o incluso contradictorias en cada ronda, la salida rebota entre dos versiones, las rondas se agotan, y el borrador final es peor que el primero. Esto no es culpa del bucle. Es que los criterios todavía no se han definido.
Los chequeos deterministas van antes del juez
La distinción definicional de la fuente primaria entre sistemas deterministas y no deterministas: los sistemas deterministas producen la misma salida cada vez dadas entradas idénticas, mientras que los sistemas no deterministas—como los agentes—pueden generar respuestas variadas incluso con las mismas condiciones iniciales3.
Los jueces son no deterministas. Cada regla que «el código puede decidir de forma definitiva» y que le entregas a un juez significa que estás usando algo que podría dar resultados distintos cada vez para evaluar algo que debería dar el mismo resultado siempre—y además pagando por una llamada extra al modelo.
El curso 10 de esta serie llama a esta disciplina puntuación por capas: usa código para lo que el código puede decidir, y entrégale al modelo solo lo que el código no puede. Esta lección la copia tal cual al ordenamiento de nodos del bucle. La cita de la Lección 2 sigue aplicando aquí—puedes añadir chequeos programáticos en cualquier paso intermedio para asegurar que el proceso sigue en curso1. Cada borrador del bucle es un paso intermedio.
Aplicado al ejemplo del plan de migración: «¿cada paso tiene su comando de reversión correspondiente?» se puede decidir de forma definitiva con expresiones regulares o parseo estructurado—eso es una compuerta. «¿Están escritos de forma creíble los comandos de reversión?» necesita un juez. Que falle el primero ni siquiera despierta al juez; apenas dile a quien escribe qué pasos faltan y sigue adelante.
El esqueleto de código del bucle
Vale la pena señalar unos cuantos detalles por separado.
runAgent es un bucle de arnés completo. Esto no ha cambiado desde la Lección 2: detrás de cada await runAgent(...) de este script está en ejecución el bucle impulsado por stop_reason del curso 7 de esta serie. Esto es apenas otra capa de flujo de control escrito en código envuelta alrededor del bucle.
Estos valores de reason son desenlaces distintos, no los colapses en un booleano (los sistemas reales a menudo necesitan subdividir más—por ejemplo, que la compuerta falle repetidamente merece su propio balde). passed se puede entregar directamente; max-rounds significa que las rondas se agotaron sin pasar, y probablemente necesita traspaso a una persona; no-progress significa que el modelo se atascó, y quemar más dinero no lo va a mejorar. Estos tres desenlaces deberían ser tres líneas separadas en tus datos de observabilidad—el enfoque de registro que enseña el curso 11 de esta serie debería aterrizar aquí, sobre este campo reason.
Que falle la compuerta también cuenta como ronda. Antes del continue, rounds ya se incrementó. Esto es intencional: fallar la compuerta repetidamente significa que el prompt de quien escribe tiene un problema, y dejarlo reintentar indefinidamente solo quema dinero en el mismo hueco.
Necesitas los dos tipos de condiciones de parada. La fuente primaria, al hablar de bucles de agente, dice: la tarea a menudo termina al completarse, pero también es habitual incluir condiciones de parada (como una cantidad máxima de iteraciones) para mantener el control1. De ahí viene «máximo N rondas»—es un fusible, que garantiza que este código se detiene bajo cualquier circunstancia. El mecanismo de parada de «no hay más progreso» viene de otra parte: ejecutar un verificador, corregir lo que falló, y repetir hasta que pase o deje de haber progreso2. Es más inteligente que el fusible porque vigila si esta ronda superó a la anterior, no cuántas rondas se han ejecutado.
Que el puntaje no suba significa salir—esa es la implementación más fácil, pero no la única. Si tu juez no emite un puntaje, puedes cambiar a vigilar si bajó la cantidad de ítems que fallan; si la tarea misma tiene mucha varianza, puedes cambiar a «salir solo tras dos rondas consecutivas sin mejora» con un contador stalled. Cuál elijas depende de qué tan estable sea tu juez, no de cuál suena más sofisticado. (Fíjate en el bestDraft del esqueleto: salir cuando el puntaje no sube presupone que siempre estás reteniendo el borrador con puntaje más alto; llevar solo bestScore sin bestDraft significa que las salidas no-progress y max-rounds entregarán la versión actual, que es peor.)
Componer patrones: esta lección lo llama un «grafo»
Cinco patrones, todos cubiertos. La siguiente pregunta es cómo disponerlos juntos.
Primero la postura oficial: estos bloques de construcción no son prescriptivos, son patrones comunes que los desarrolladores pueden moldear y combinar para encajar con distintos casos de uso; la clave del éxito, como con cualquier funcionalidad de LLM, es medir el desempeño e iterar sobre las implementaciones1.
Dicho de otro modo, para «cómo componer», la fuente primaria da permiso, no una receta. La receta la escribes tú.
Una declaración honesta sobre la terminología de grafos
El uso de «grafo / nodos / aristas» a lo largo de esta lección es una metáfora de ingeniería propia de esta lección, no terminología oficial (las secciones anteriores ya usaron «nodo» y «arista de retorno» con este significado).
Copia esta frase a tu propia documentación de arquitectura. Las palabras «grafo», «nodo», «arista», «DAG», «máquina de estados» aparecen cero veces en todas las fuentes primarias que cita esta lección. El vocabulario de las fuentes primarias es flujos de trabajo, patrones, orquestador-trabajadores, fan-out—está hablando de un catálogo de patrones, no de estructura topológica.
Aun así necesitamos usar la palabra «grafo», porque cuando cinco patrones se sientan juntos necesitas un lenguaje para hablar de ellos con claridad, y «grafo» es la opción de menor esfuerzo. Pero esta metáfora debe tener un punto de anclaje, o es apenas jerga inventada. El ancla es esta frase que sí existe de verdad en los materiales primarios: el script del flujo de trabajo mismo retiene el bucle, las bifurcaciones y los resultados intermedios, mientras que el contexto del modelo retiene solo la respuesta final2.
Esa frase ya nombra los tres elementos de un grafo: bucle (arista de retorno), bifurcación (punto de bifurcación), resultados intermedios (estado). Todo lo que hacemos es ponerle un nombre a cada uno.
Convenciones de dibujo
En el sistema visual de esta lección:
- Nodo = un bucle
runAgent, o una pieza de código puro (compuerta, clasificación, agregación, agrupamiento en lotes). Etiquetar el tipo de cada nodo es la acción más valiosa al dibujar—te obliga a responder «¿este paso necesita de verdad al modelo?».
- Arista = «quién alimenta a quién con su salida». Una arista no es una estructura de datos, apenas la siguiente línea de código que lee la variable de la línea anterior.
- Estado = variables del script. El ancla primaria también tiene una frase aquí: los resultados intermedios se quedan en variables del script en vez de aterrizar en el contexto del modelo2. No existe ningún concepto oficial de «un objeto de estado que se pasa entre nodos»—esa es una expresión que tomamos prestada de otros dominios; esta lección no construye esa abstracción, apenas pasa las variables que necesites.
Los cinco patrones en las formas de este sistema visual
Vale la pena releer la anotación de esa cuarta forma. La paralelización y orquestador-trabajadores son topográficamente similares en forma, y la diferencia clave es que las subtareas no vienen predefinidas sino que las determina el orquestador según la entrada concreta1. Dibujada en papel, la diferencia es «yo dibujé tres aristas» frente a «A dibujó tres aristas»—indistinguibles en papel, pero muy distintas en código.
Un ejemplo de composición
Hilando enrutamiento, fan-out, fusión y bucle de revisión:
La Lección 6 implementa una variante de este grafo: esa tanda de tickets resulta tener criterios de aceptación que se pueden escribir todos como reglas, así que la capa de [revisión] degrada a una {compuerta}, y el fan-out pasa de «un ítem complejo hacia tres trabajadores» a «una tanda de tickets, cada uno despachado a un manejador». Qué partes cambiaron y por qué—la apertura de la Lección 6 las enumera punto por punto. Mira primero la forma que hay aquí; el código espera hasta la lección siguiente.
Beneficios de ingeniería que trae la composición
Mover el flujo de control al código entrega más que solo «se entiende». Unos cuantos beneficios tienen respaldo primario:
El seguimiento paso a paso trae recuperabilidad. El runtime rastrea el resultado de cada agente a medida que avanza la ejecución, que es lo que hace que una ejecución se pueda reanudar dentro de la misma sesión2. Traducido al sistema visual de esta lección: cada nodo del grafo es naturalmente una ubicación de punto de control—el nodo termina, el resultado aterriza en una variable del script, y esa variable es el registro de «hasta dónde llegamos». El diseño de puntos de control que enseña el curso 9 de esta serie no necesita una base aparte aquí; las fronteras de los nodos son puntos de aterrizaje naturales.
El fan-out de grano fino preserva más progreso. Las palabras de la fuente primaria: un flujo de trabajo que reparte el trabajo entre muchos agentes pequeños preserva más progreso que un solo agente largo2. Un agente largo de cuarenta minutos se cae, y se fueron cuarenta minutos; cuarenta nodos pequeños de un minuto y uno se cae, pierdes un minuto y sabes cuál.
Las técnicas de calidad repetibles se vuelven reutilizables. Mover el plan al código también le permite a un flujo de trabajo aplicar un patrón de calidad repetible, no solo ejecutar más agentes: puede hacer que agentes independientes revisen de forma adversarial los hallazgos de los demás antes de que se reporten, o redactar un plan desde varios ángulos y sopesarlos entre sí, de modo que obtienes un resultado más confiable que con una sola pasada2. La palabra clave aquí es «repetible»—hacer una revisión cruzada a mano es una operación, escribirla en un script es una capacidad.
Las barreras deterministas envuelven a los agentes no deterministas. La retrospectiva del sistema multiagente de investigación de Anthropic declara: combinan la adaptabilidad de los agentes de IA construidos sobre Claude con salvaguardas deterministas como lógica de reintento y puntos de control periódicos4. Traducido al sistema visual de esta lección: el esqueleto del grafo es determinista (quién llama a quién, cuándo parar, qué arista tomar ante un fallo), y el interior de los nodos es no determinista. Esta estratificación no es preferencia estética, es un prerrequisito para hacer el sistema operable.
Disciplina de composición: cada capa añadida debe pasar una compuerta
Dichos los beneficios, ahora las restricciones.
Cada capa añadida debe pasar la compuerta de la «mejora medible». La fuente primaria dice lo mismo en dos lugares, y el segundo lo marca explícitamente como una reiteración: habría que considerar añadir complejidad solo cuando mejora demostrablemente los resultados1. Esto es especialmente crítico para esta lección—con cinco patrones desplegados frente a ti, el error más fácil es usarlos todos. Añadir un nodo significa una llamada más al modelo, un lugar más donde puede fallar, una cosa más que diagnosticar. Antes de añadirlo, pregunta: si lo quito, ¿bajan las métricas? No poder responder significa que todavía no has medido.
El reintento y el tiempo límite a nivel de nodo son práctica de ingeniería, no diseño oficial. Las fuentes primarias mencionan esto solo como una oración subordinada (salvaguardas deterministas como lógica de reintento y puntos de control periódicos4). Así que lo que sigue está escrito como práctica de ingeniería; no vas a encontrar su respaldo en ninguna documentación primaria: envuelve cada nodo runAgent en un tiempo límite, y tras agotarse o bien reintenta o bien marca ese nodo como fallido y continúa; la cantidad de reintentos depende de la naturaleza del nodo (los nodos de recuperación de solo lectura pueden reintentar varias veces, los nodos con efectos secundarios idealmente no se reintentan solos ni una vez); cuando un nodo falla, distingue «esta arista se puede saltar» de «el grafo entero debe detenerse», y no dejes que el fallo de un nodo opcional arrastre la ejecución completa. Esto es sentido común corriente de sistemas distribuidos, apenas aplicado a los agentes—no lo trates como algo nuevo.
La profundidad está acotada. La referencia a nivel de producto está ahí mismo: por omisión, un subagente puede lanzar subagentes propios, hasta tres capas por debajo de la conversación principal5. Tres capas no es un umbral que inventó esta lección, pero el mensaje es claro—la profundidad de anidamiento en productos reales no es ilimitada, y alguien pensó en serio dónde parar. Tu grafo debería tener una respuesta parecida. Si el grafo que dibujaste tiene cinco capas de anidamiento, sospecha primero que la tarea está descompuesta demasiado fino; no te pongas a pensar en cómo soportar más profundidad.
El grafo no es el objetivo, es una descripción de la forma de la tarea
Hay un modo de fallo que vale especialmente la pena prevenir al final de este curso: elegir primero una topología vistosa, y después buscar tareas que meterle adentro.
El orden debería invertirse. Dibuja primero la forma de dependencias propia de la tarea—qué pasos deben hacer fila (la salida del paso anterior es la entrada del siguiente), qué pasos no se afectan entre sí (da igual cuál se ejecute primero), qué paso necesita ver la entrada antes de saber en cuántas piezas partirse, qué paso necesita que alguien critique su salida antes de que sea confiable. Terminado ese dibujo, qué patrones usar queda básicamente decidido: los lugares donde se hace fila son cadenas, los lugares mutuamente independientes son abanicos, los lugares de ver-y-luego-decidir son orquestadores, los lugares que necesitan crítica son bucles.
Los patrones son nombres para formas de tareas, no un menú del que puedes elegir arbitrariamente.
Y por encima de esa disciplina hay una que entra en juego incluso antes: encontrar la solución más simple posible, y aumentar la complejidad solo cuando haga falta1. Esta frase apareció en la Lección 1, y al final de esta lección sigue siendo la misma frase. Después de aprender cinco patrones, «con una sola llamada a un LLM alcanza» sigue siendo una respuesta completamente válida—las fuentes primarias mismas dicen que para muchas aplicaciones, optimizar llamadas individuales a LLM con recuperación y ejemplos en contexto suele ser suficiente1.
El código de composición completo y ejecutable está en la Lección 6. Esta lección se detiene aquí. Lo que tienes ahora: cinco patrones, un sistema visual, y una lista de cuándo no usarlos.
💻 Ejercicios
Resumen
- Revisar y refinar es una llamada a un LLM que genera una respuesta mientras otra aporta evaluación y retroalimentación en un bucle1; su forma de producto es «ejecutar un verificador, corregir lo que falló, repetir hasta que pase o deje de haber progreso»2, y la revisión cruzada adversarial es otro uso de la misma división del trabajo (revisión cruzada única, sin arista de retorno); meterla dentro del bucle de revisión es una categorización de esta lección2. El curso 6 de esta serie la llama productor-revisor, ese es vocabulario didáctico nuestro, y el vocabulario primario es evaluator-optimizer1.
- Que valga la pena construirlo depende de dos señales: las respuestas del LLM se pueden mejorar demostrablemente cuando una persona articula su retroalimentación, y el LLM también puede aportar esa retroalimentación; es particularmente efectivo cuando los criterios de evaluación son claros y el refinamiento iterativo aporta valor medible1. Cuando los criterios no son claros, define criterios primero, no construyas el bucle primero.
- Las condiciones de parada no son de un solo tipo: las condiciones de parada como una cantidad máxima de iteraciones se usan para mantener el control1, y «no hay más progreso» es otro mecanismo de parada más rentable2; tres desenlaces (pasa / rondas agotadas / sin progreso) mapean a tres acciones distintas aguas abajo, no los colapses en un booleano. Las compuertas deterministas van antes de los jueces.
- Los cinco patrones se pueden componer: estos bloques de construcción no son prescriptivos, son patrones comunes que los desarrolladores pueden moldear y combinar para encajar con distintos casos de uso, y la clave del éxito es medir el desempeño e iterar sobre las implementaciones1.
- «Grafo / nodos / aristas» es el sistema visual propio de esta lección, no terminología oficial; su ancla primaria es apenas una frase—el script del flujo de trabajo mismo retiene el bucle, las bifurcaciones y los resultados intermedios, mientras que el contexto del modelo retiene solo la respuesta final2, más que los resultados intermedios se quedan en variables del script2. Cuando uses este vocabulario en tus propios documentos, incluye esta declaración junto con él.
- Los beneficios de la composición están documentados: el runtime rastrea el resultado de cada agente a medida que avanza la ejecución, que es lo que hace que una ejecución se pueda reanudar dentro de la misma sesión2; un flujo de trabajo que reparte el trabajo entre muchos agentes pequeños preserva más progreso que un solo agente largo2; mover el plan al código también le permite a un flujo de trabajo aplicar técnicas de calidad repetibles (revisión cruzada adversarial, redactar desde varios ángulos y luego sopesar)2; las salvaguardas deterministas (lógica de reintento y puntos de control periódicos) envuelven a los agentes no deterministas4.
- Las restricciones son igual de claras: habría que considerar añadir complejidad solo cuando mejora demostrablemente los resultados1; el reintento y el tiempo límite a nivel de nodo son práctica de ingeniería corriente, y las fuentes primarias ofrecen apenas una oración subordinada4; la profundidad también está acotada, y la referencia a nivel de producto es que los subagentes se anidan hasta tres capas por debajo de la conversación principal5; la solución más simple primero1.
>> Lección 6: Manos a la obra: convertir tu arnés en un grafo pequeño