Herramientas de usuario

Herramientas del sitio


cursos:sdd:05-patrones-agenticos

Patrones agénticos

Un patrón agéntico es una forma conocida de conectar varias llamadas al modelo para resolver algo que una sola llamada no resuelve bien. En el tema anterior vimos que el grafo es el mapa y el bucle es el camino; los patrones de aquí son los mapas que ya se sabe que funcionan.

Cada patrón de esta página se le pide al agente con texto, y ese texto se deja escrito como una skill del proyecto (tema 2) para no reescribirlo cada vez. Los ejemplos de cada sección son eso: lo que hay que poner. El primero lleva la cabecera completa del SKILL.md; en el resto va solo el cuerpo, que es lo que cambia.
Empieza por lo más simple que pueda funcionar. Una llamada bien planteada con buen contexto resuelve la mayoría de las tareas. Cada patrón añade latencia, coste y sitios donde fallar, así que solo se sube de patrón cuando el anterior se queda corto de verdad, no porque quede mejor en el diagrama.
Patrón Cuándo se usa
Pipeline La tarea se parte en pasos fijos, conocidos de antemano
Enrutador Entradas de tipos distintos que se atienden mejor por separado
Planificador-Ejecutor Conviene revisar el plan antes de que se toque nada
Orquestador Las subtareas no se conocen hasta ver el problema
Ejecutor-Revisor Hay criterios claros y la crítica mejora el resultado
Adversarios Interesa encontrar el fallo, no pulir el acabado
Razonador-Refutador La causa de un fallo no está clara y conviene acertar antes de arreglar
Best-of-N Los intentos salen muy distintos y verificar es barato
Human-in-the-loop Hay acciones irreversibles o responsabilidad de por medio
Memoria persistente El trabajo dura más que una sesión

1. Pipeline (prompt chain)

La tarea se parte en pasos fijos y la salida de cada uno es la entrada del siguiente. Entre paso y paso se pone una puerta que comprueba que lo producido sirve antes de seguir.

paso 1pasa la puertasinopaso 2pasa la puertasinopaso 3corregir el paso 2corregir el paso 1

Es el patrón del spec-driven development: requisitos → diseño → tareas → código, con una aprobación en cada salto (tema 10). Cada paso ve un problema más pequeño y mejor acotado que el original, y por eso lo hace mejor.

  • A favor: cada paso es simple, verificable y depurable por separado.
  • En contra: latencia, y un error del paso 1 se arrastra hasta el final. De ahí que la puerta entre pasos sea obligatoria, no decorativa.
  • Condición: los pasos tienen que ser siempre los mismos. Si cambian según el problema, lo que hace falta es un orquestador.

Ejemplo. Una funcionalidad nueva, de la petición al código. Como skill del proyecto, con su cabecera:

---
name: pipeline-funcionalidad
description: Implementa una funcionalidad nueva por fases. Usar cuando se
  pida añadir algo que no existe todavía.
---

Implementa la funcionalidad en cuatro pasos. No empieces uno hasta cerrar
el anterior.

1. REQUISITOS: escribe en docs/specs/<nombre>/requirements.md qué tiene que
   pasar y cómo se comprueba. Para y espera el visto bueno.
2. DISEÑO: a partir de ese fichero, escribe design.md: ficheros que se
   tocan, contratos, modelo de datos. Para y espera el visto bueno.
3. TAREAS: parte el diseño en tareas de menos de una hora en tasks.md,
   cada una con su criterio de hecho.
4. CÓDIGO: implementa las tareas en orden. Después de cada una lanza los
   tests; si fallan, arregla antes de pasar a la siguiente.

Si en el paso 3 o en el 4 descubres que el diseño estaba mal, vuelve al
paso 2 y corrígelo antes de seguir.

2. Enrutador

Una primera llamada clasifica la entrada y la manda a la ruta adecuada: otro prompt, otro modelo u otra herramienta.

clasificar la peticionbugtipoflujo de depuracionfuncionalidad nuevatipootroflujo completo de specruta por defecto

Los dos usos que de verdad aparecen:

  • Por especialidad: un bug va al flujo de depuración; una funcionalidad nueva, al flujo de especificación completo.
  • Por coste: lo fácil al modelo barato, lo difícil al caro. El ahorro es grande y la calidad no baja, porque lo que se enruta mal es lo que estaba en la frontera.

Siempre tiene que haber ruta por defecto. El fallo típico del enrutador no es equivocarse de ruta, es no tener ninguna para lo que no encaja en ninguna categoría.

Ejemplo. Clasificar lo que llega antes de ponerse a trabajar:

Antes de tocar nada, clasifica la petición en una de estas rutas y di en
una línea cuál has elegido:

- BUG: algo que ya debería funcionar y no funciona.
  -> Reproduce el fallo con un test que falle, arregla, comprueba que el
     test pasa. No refactorices nada más por el camino.
- FUNCIONALIDAD: algo que no existe todavía.
  -> Flujo completo: requisitos, diseño, tareas, código.
- CAMBIO MECÁNICO: renombrar, mover, subir una dependencia.
  -> Hazlo y enseña el diff. Sin especificación.
- NO ENCAJA: si dudas entre dos rutas, o no es ninguna, pregunta.
  No elijas por tu cuenta.

3. Planificador-Ejecutor

Se separa decidir qué hacer de hacerlo. El planificador produce un plan escrito; el ejecutor lo cumple paso a paso.

La ventaja es que el plan es un artefacto revisable antes de que se toque una sola línea, que es justo cuando corregir sale barato. Es también el patrón que explota el modo Plan de las herramientas del tema 3: el modelo caro planifica, uno más barato ejecuta lo ya decidido.

  • Cuándo: cambios que tocan varios ficheros, o donde el enfoque importa más que el teclear.
  • Riesgo: un plan malo contamina todo lo que viene detrás, y el ejecutor no suele darse cuenta porque su trabajo es obedecer. Por eso el plan se lee.
  • Detalle práctico: si el plan queda escrito en un fichero del repositorio, el ejecutor puede ir marcando lo hecho y una sesión nueva retoma por donde iba.

Ejemplo. Un cambio que toca varios ficheros:

FASE 1, PLAN. No escribas código todavía.
Lee el código afectado y escribe un plan con:
  - los ficheros que vas a tocar y qué cambia en cada uno
  - el orden de los cambios
  - lo que has descartado, y por qué
  - lo que no tienes claro, en forma de preguntas

Para y espera a que apruebe el plan.

FASE 2, EJECUCIÓN. Cumple el plan aprobado, y nada más que el plan.
Si a mitad descubres que no se sostiene, para y dilo en lugar de
improvisar otra solución por tu cuenta.

4. Orquestador

Un agente central mira el problema, decide sobre la marcha en qué subtareas se parte, se las reparte a trabajadores especializados y funde los resultados.

orquestador: partir el trabajotrabajador Atrabajador Btrabajador Corquestador: fundir resultados

La diferencia con el pipeline es quién decide los pasos: en el pipeline están escritos de antemano; aquí los decide el orquestador en tiempo de ejecución, al ver el caso concreto.

En desarrollo con IA la ventaja que más se nota no es el paralelismo, es el contexto: cada trabajador arranca con su propia ventana limpia y solo con lo suyo, así que el orquestador puede abarcar un problema que no cabría en una sola conversación.

  • A favor: control centralizado, trazable y reproducible; subtareas en paralelo.
  • En contra: el orquestador es un punto único de fallo —si parte mal el trabajo al principio, todo lo de debajo sobra— y el coste se multiplica por el número de trabajadores.
  • Lo difícil está en la fusión: juntar resultados parciales que se contradicen es más trabajo del que parece al dibujarlo.

Ejemplo. Diseño, implementación y calidad, cada uno en su agente. Un subagente empieza de cero y no sabe nada de esta conversación, así que el encargo lleva dentro quién es y qué tiene que devolver:

Eres el orquestador: no escribes código, repartes el trabajo y unes el
resultado. Cada subagente que lances arranca en blanco, así que dile en
el propio encargo quién es, qué le das, qué tiene que devolver y qué no
puede tocar.

1. Lanza un subagente con este encargo:

   "Eres el diseñador. Tienes el repositorio en solo lectura: no
    escribes ni modificas nada. Dado este enunciado <enunciado>,
    devuelve el enfoque, la lista de ficheros que hay que tocar y los
    contratos entre las partes. No implementes."

2. Parte el diseño en trozos independientes y lanza un subagente por
   cada trozo:

   "Eres quien implementa <parte>. Este es el trozo de diseño que te
    toca: <trozo>, y estos los contratos que tienes que respetar:
    <contratos>. Implementa solo eso. No toques ficheros fuera de esta
    lista: <ficheros>. Devuelve el diff y los tests que añadas."

3. Junta los resultados. Si dos se contradicen, resuélvelo tú y di cómo.

4. Sobre el resultado ya unido, lanza un subagente que no haya escrito
   nada de esto:

   "Eres el revisor de calidad de un código que no has escrito. Aquí
    tienes el diff completo <diff> y los requisitos <requisitos>. Busca
    requisitos sin cumplir, casos límite sin tratar y errores que se
    tragan. Devuelve cada hallazgo como BLOQUEANTE o MENOR."

5. Si vuelve algún BLOQUEANTE, repite el punto 2 solo con la parte
   afectada, añadiendo el hallazgo al encargo.

A cada subagente pásale el texto que necesita, nunca la conversación
entera: repartir el contexto es justamente para lo que sirve esto.

5. Ejecutor-Revisor

Un agente produce, otro distinto lo critica contra unos criterios, y se repite hasta que pase o hasta agotar las vueltas.

ejecutor: producir o corregirrevisor: evaluar contra los criteriossiaprobadonodevolver con los comentariosentregar

Es la capa 5 del harnés (tema 4) convertida en patrón. Funciona cuando se cumplen las dos condiciones:

  1. Hay criterios claros contra los que evaluar. Sin eso el revisor opina, y opinar no converge.
  2. La crítica mejora el resultado, igual que a una persona le sirve que le revisen el código.
El revisor tiene que ser otro agente con el encargo explícito de buscar problemas, y con límite de vueltas. Un modelo revisando su propia salida tiende a aprobarla; y sin tope de iteraciones, dos agentes se pueden pasar la tarde puliendo un detalle.

Ejemplo. Uno programa y otro revisa. El revisor se lanza en ese momento y recibe en el encargo su papel y el material, porque no ha visto nada de lo anterior:

Implementa la tarea. Al terminar, di qué has hecho y qué has dejado
fuera a propósito.

Después, sin revisarlo tú, lanza un subagente con este encargo:

  "Eres el revisor de este cambio, que no has escrito. Aquí tienes el
   diff <diff> y el requisito que dice lo que tenía que hacer
   <requisito>. Revisa en este orden:
     1. ¿Hace lo que pide el requisito? Cítalo.
     2. ¿Casos límite sin tratar: nulos, lista vacía, concurrencia?
     3. ¿Errores que se tragan sin registrar?
     4. ¿Los tests comprueban el comportamiento o la implementación?
   Marca cada hallazgo como BLOQUEANTE o MENOR. No comentes estilo: de
   eso se encarga el formateador. Si no encuentras nada, dilo; no
   inventes hallazgos para justificar la revisión."

Arregla los BLOQUEANTES y lanza un revisor **nuevo** con el diff ya
corregido. Tres vueltas como máximo; a la tercera, para y resume lo que
queda abierto.

6. Adversarios

La vuelta de tuerca del anterior: el segundo agente no pule, ataca. Su encargo no es «¿está bien?» sino «encuentra el caso en el que esto se rompe».

Revisor Adversario
Pregunta ¿Cumple los criterios? ¿Por dónde falla?
Produce Una lista de mejoras Un contraejemplo concreto
Termina cuando Aprueba No encuentra por dónde entrar

Dónde se usa de verdad:

  • Contra la especificación: buscar el requisito ambiguo, el caso que no se contempló, la contradicción entre dos apartados. Sale mucho más barato aquí que en producción.
  • Contra la implementación: generar los tests que la rompen, no los que la confirman. Emparejado con el mutation testing del tema 7, que mide exactamente eso.
  • Contra la seguridad: el papel de red team.

El valor está en que el criterio de éxito de los dos agentes es opuesto, y eso impide el acuerdo cómodo al que llegan dos modelos con el mismo objetivo.

Ejemplo. Dos agentes defienden decisiones de arquitectura distintas. Cada uno recibe su papel al lanzarlo, y el papel es precisamente lo que los hace adversarios:

Hay que decidir si el envío de correos va dentro de la petición o a una
cola. No lo decidas tú: monta el debate.

Lanza un subagente:

  "Defiende que el envío vaya dentro de la petición. Explica cómo se
   implementa, qué cuesta y qué se rompe el día que haya diez veces más
   tráfico. Este es el código de hoy: <contexto>. No te cures en salud
   reconociendo las ventajas de la otra opción: tu trabajo es la mejor
   versión de esta."

Lanza otro con el mismo encargo, cambiando la opción por la cola.

Con las dos propuestas encima de la mesa, lanza un subagente por cada
una:

  "Aquí tienes una propuesta de arquitectura <propuesta>. Tu único
   trabajo es encontrar el escenario concreto en el que falla: carga,
   fallo parcial, reintentos, despliegue, operación. No vale opinar ni
   pedir mejoras: hace falta el caso, con números si los hay."

Y con todo eso, un último subagente que no haya participado:

  "Aquí tienes dos propuestas de arquitectura y los ataques que ha
   recibido cada una <material>. Decide una y escribe en docs/adr/ la
   decisión, el motivo y la opción descartada con su razón. Si las dos
   aguantan igual, dilo: entonces se elige la más fácil de deshacer."

7. Razonador-Refutador

Antes de arreglar nada, un agente escribe su razonamiento paso a paso —qué ha visto, qué deduce de ello y qué piensa hacer— y otro distinto busca el paso que no se sostiene. Solo cuando el razonamiento aguanta se toca el código.

razonador: explicacion con hechos, deducciones y planrefutador: buscar el paso que no se sostienesirazonamiento solidonola prueba en contra pasa a ser un hechoimplementar el plan

Se parece a los dos anteriores, pero mira otra cosa y en otro momento:

Revisor Adversario Refutador
Pregunta ¿Cumple los criterios? ¿Por dónde falla? ¿Se deduce esto de lo que hemos visto?
Mira El resultado terminado Una propuesta o una implementación La explicación del problema
Cuándo Después de hacerlo Antes o después Antes de tocar nada

El fallo que evita es muy común: una solución impecable para un problema que no era el problema. El modelo ve un síntoma, se convence de una causa y la arregla con todo detalle. El Refutador hace las preguntas incómodas: ¿eso lo has comprobado o lo supones?, ¿de verdad se sigue de lo anterior?, ¿hay otra explicación para lo mismo?

Por qué gana el Refutador aunque tenga menos pruebas. Para que una explicación sea cierta tiene que cumplirse todo lo que exige; para que sea falsa basta con que falle una sola cosa. Por eso mil pistas a favor no pesan nada frente a un hecho que la hace imposible.

Por ejemplo, fallan 30 tests con NullPointerException dentro de PedidoService.calcularTotal(), y el Razonador concluye que el pedido llega sin cliente. El Refutador lee el código: el constructor de Pedido lanza una excepción si no recibe cliente, así que un pedido sin cliente nunca llega hasta ahí. La explicación es falsa, da igual cuántos tests fallen.

Qué pasa cuando gana. El trabajo no termina ahí: vuelve al Razonador, y lo que ha tumbado la explicación pasa a ser un hecho comprobado. Con esa pista nueva, el Razonador propone otra explicación —«el cliente nunca es nulo, así que el nulo sale de otro campo que usa calcularTotal(): el descuento, que solo se rellena cuando hay cupón»— y el Refutador intenta tumbarla también. Esta vez no puede: los 30 tests que fallan crean pedidos sin cupón y los que pasan, con cupón. Se repite hasta que una explicación aguanta, y solo entonces se arregla. Cada vuelta descarta una causa y deja un hecho más, así que el círculo se va cerrando.

  • Cuándo: tests que unas veces pasan y otras no, código lento sin causa clara, errores cuyo origen no se ve a simple vista.
  • En contra: más tiempo y más coste. Si la causa era obvia desde el principio, sobra.
  • Regla del Refutador: puede comprobar hechos —leer el código, lanzar un test—, pero no propone soluciones ni escribe código. Solo señala qué paso no se sostiene y con qué prueba.

Ejemplo. Encontrar la causa de un fallo antes de arreglarlo. El auditor se lanza en ese momento y no sabe nada del problema, así que el encargo lleva el contexto y el razonamiento:

Hay un fallo y no está claro de dónde viene. No toques código todavía.

FASE 1, RAZONAMIENTO. Escribe tres listas:
  - HECHOS: lo que has visto de verdad: errores, trazas, qué tests
    fallan y cuáles no. Solo lo comprobado.
  - DEDUCCIONES: qué concluyes, un paso por línea, diciendo de qué hecho
    sale cada uno.
  - PLAN: qué cambiarías y qué esperas que pase después.

FASE 2, REFUTACIÓN. Sin revisarlo tú, lanza un subagente con este encargo:

  "Eres un auditor de lógica. Aquí tienes el contexto del problema
   <contexto> y un razonamiento sobre su causa <razonamiento>.
   Comprueba solo esto:
     1. ¿Se da algo por cierto sin haberlo comprobado?
     2. ¿Hay algún paso que no se deduce del anterior?
     3. ¿Hay otra causa que explique los mismos hechos y se ha pasado
        por alto?
   Puedes comprobar lo que haga falta sin modificar nada. No propongas
   soluciones ni escribas código. Devuelve cada paso que no se sostiene
   con la prueba que lo tumba, o 'RAZONAMIENTO SÓLIDO'."

FASE 3. Si vuelve algún fallo, añade sus pruebas a HECHOS, formula otra
explicación que las respete y lanza un auditor nuevo. Tres vueltas como
máximo; a la tercera, para y resume lo descartado y lo que queda abierto.
Con 'RAZONAMIENTO SÓLIDO', implementa el plan.

Más informnación:

8. Best-of-N

Se lanza el mismo encargo N veces en paralelo —con temperatura alta o con modelos distintos— y se elige el mejor resultado. Sirve porque los intentos salen genuinamente distintos entre sí.

Lo difícil no es generar: es elegir. Y de eso depende que el patrón valga o no valga nada:

Cómo se elige Fiabilidad
Verificador objetivo: compila, pasa los tests, mide mejor en el benchmark Alta. Es el caso que interesa
Un modelo juez compara las N salidas Regular: hereda los sesgos del juez
Una persona mira las N Alta, pero no escala
  • Cuándo: verificar es mucho más barato que generar, y un intento suelto falla a menudo. El caso claro en código es tener N implementaciones y quedarse con la que pasa la batería de tests.
  • En contra: cuesta N veces. Se reserva para el trozo difícil, no para todo el trabajo.

Ejemplo. Tres diseños, y solo uno se implementa:

Diseña TRES soluciones distintas para esta tarea. Distintas de verdad:
si las tres son la misma con otros nombres, vuelve a empezar.

De cada una: el enfoque en cinco líneas, los ficheros que toca, qué se
gana, qué se paga, y qué pasa el día que haya que añadir un proveedor
más.

Compáralas en una tabla contra estos criterios, por orden:
  1. cumple el requisito
  2. esfuerzo de implementación
  3. lo que cuesta cambiarla dentro de un año
  4. cuánto código nuevo mete en el proyecto

Recomienda una, y di en qué caso cambiaría la recomendación.
No implementes ninguna hasta que te diga cuál.

9. Human-in-the-loop (HITL)

No es «que alguien lo mire»: es decidir en qué puntos concretos se para y se espera a una persona.

Los tres sitios donde tiene sentido poner la puerta:

  • Aprobar el plan, antes de que se escriba nada. El punto más rentable de todos: corregir aquí cuesta un párrafo.
  • Aprobar la acción irreversible, justo antes de ejecutarla.
  • Aprobar la entrega, antes de que salga de cara al cliente.

Lo que pasa por una persona siempre: borrar datos, desplegar, mandar correos, gastar dinero, tocar producción. Todo lo que el historial no deshace.

La persona va en la puerta, no en cada paso. Un flujo que pide confirmación de todo acaba aprobándose a ciegas, y eso es peor que no tener puerta: da la sensación de control sin el control. Las capas de abajo —límites, comprobaciones, revisión automática— están para reducir lo que llega al humano a lo que de verdad necesita criterio.

Ejemplo. La persona aprueba la especificación antes de que empiece el diseño:

Escribe la especificación en requirements.md y PARA.

No empieces el diseño, no escribas código, no propongas el paso
siguiente. Termina el documento con dos listas:

  - las decisiones que has tomado por tu cuenta porque el enunciado no
    las decía (esta es la parte importante: es donde se cuela lo que yo
    no quería)
  - las preguntas que necesitas que conteste una persona

Sigo yo. Cuando te diga "aprobado", pasas al diseño. Cualquier otra
respuesta es una corrección de los requisitos.

10. Memoria persistente

Un agente empieza de cero cada sesión. La memoria persistente es lo que le permite retomar el trabajo y acumular lo aprendido. Tres tipos, con papeles distintos:

Memoria Qué guarda Dónde vive
De trabajo El contexto de la tarea actual La ventana de contexto. Se pierde al cerrar
Episódica Qué se hizo, qué se intentó y qué falló El plan con lo hecho marcado, el historial de decisiones
Semántica Hechos estables del proyecto: convenciones, arquitectura, reglas CLAUDE.md, AGENTS.md, .kiro/steering/, los ADR
En desarrollo, la memoria que funciona son ficheros dentro del repositorio, no una base vectorial. Son legibles por una persona, van versionados, se revisan en el pull request y se corrigen a mano cuando dicen una tontería. Una base vectorial aporta cuando el volumen no cabe en ficheros; antes de eso es complejidad que solo estorba.

Lo que hay que decidir explícitamente es la política de actualización: qué se escribe, cuándo y quién lo borra. Una memoria a la que solo se añade se pudre: acumula decisiones viejas que ya no valen, y el agente las sigue a rajatabla porque no sabe que caducaron.

Ejemplo. Los ficheros que el agente lee al empezar y mantiene al terminar:

La memoria de este proyecto son ficheros. Antes de nada, lee:

  .kiro/steering/          reglas que valen para todo el proyecto
  docs/specs/<actual>/     requisitos, diseño y tareas de lo que hacemos
  docs/adr/                decisiones ya tomadas, y por qué

Reglas:
  - Si algo de lo que te digo contradice lo que hay escrito, gana lo
    escrito y me avisas.
  - Toda decisión que afecte a tareas futuras va a docs/adr/ el mismo día.
  - Al terminar una tarea, márcala en tasks.md. La sesión siguiente
    empieza leyendo ese fichero, no preguntándome por dónde ibas.
  - Si un documento ha dejado de ser verdad, corrígelo en el mismo cambio
    que lo desmiente. Nunca escribas encima dejando debajo la versión
    vieja.

11. Cómo elegir

Patrón Fuerte en Su límite Coste
Pipeline Pasos simples y verificables uno a uno Los pasos son rígidos Bajo
Enrutador Ahorro y especialización Se equivoca al clasificar Bajo
Planificador-Ejecutor Corregir cuando aún es barato Un plan malo contamina todo Medio
Orquestador Problemas grandes, contexto repartido Punto único de fallo; la fusión Alto
Ejecutor-Revisor Calidad con criterios claros Sin criterios, no converge Medio-alto
Adversarios Encontrar lo que falla No mejora el acabado Medio
Razonador-Refutador Acertar con la causa antes de arreglar Sobra si la causa era obvia Medio
Best-of-N Calidad cuando verificar es barato Se paga N veces Alto
HITL Responsabilidad y lo irreversible El humano es el cuello de botella Bajo, pero lento
Memoria persistente Trabajo que dura semanas Se pudre sin mantenimiento Bajo

Los patrones se combinan, y casi siempre se usan combinados. Las dos combinaciones que aparecen una y otra vez en desarrollo con IA:

  • Planificador → HITL → Pipeline → Ejecutor-Revisor: se planifica, una persona aprueba el plan, se ejecuta por fases y cada fase pasa por revisión. Es, punto por punto, lo que hacen los frameworks SDD del tema 11.
  • Orquestador → trabajadores → Adversario: se reparte el trabajo grande y al final un agente intenta romper el resultado unificado.

Fuentes

cursos/sdd/05-patrones-agenticos.txt · Última modificación: por claude