Reflection en agentes IA: self-reflection y cross-reflection

Cómo un agente IA evalúa y mejora su propio output con self-reflection y cross-reflection. Guía con código paso a paso para developers que empiezan.

Colaboradores: Esther Aznar, Ivan Garcia Villar

Imagina que escribes un email importante, lo envías, y unas horas después lo relees y te das cuenta de que te has dejado algo importante o has cometido un error importante aparentemente fácil de detectar. Ese momento de “¿cómo no lo vi antes?” lo conocemos todos. Ahora imagina que antes de mandarlo alguien te lo hubiera leído en voz alta. Casi seguro que habrías pillado el error.

Reflection es exactamente eso, pero aplicado a agentes de IA. En lugar de generar una respuesta y devolverla tal cual, el agente delega una tarea de revisión, normalmente a otra instancia del mismo modelo o a uno diferente con unos criterios de evaluación . Es el “espera, déjame releerlo” antes de darle a enviar.

Si vienes del mundo del desarrollo, la idea te va a sonar: es lo mismo que un code review (que alguien más lo lea antes del merge). Una capa de evaluación antes de dar el output por bueno.

Prerrequisito: Para seguir este post necesitas tener claro qué es un agente IA y cómo encadenarlos. Si no lo tienes, empieza por el post de prompt chaining — ahí se explica cómo dividir una tarea compleja en pasos encadenados, que es la base de todo esto.

¿Qué es reflection en un agente IA?

Reflection es un “AI coding pattern” que añade una fase explícita de evaluación y corrección antes de devolver el resultado final.

Un LLM, por lo general, no tiende a preguntarse si el proceso que está siguiendo es correcto, si está completo o si tiene sentido una vez ha tomado un camino determinado. Dado que los modelos no son deterministas, es posible que el resultado de un prompt termine en resultados inesperados debido a pequeños errores que se amplifican en tareas largas y flujos complejos. Reflection añade ese paso que falta: antes de devolver el resultado, el agente lo evalúa contra unos criterios. Si no los cumple, itera. Si los cumple, lo devuelve. Permite revisar su resultado una segunda o tercera vez sin verse influenciado por el proceso de razonamiento anterior y atendiendo a unos criterios concretos de calidad que tú defines.

Hay tres niveles de evaluación:

VarianteQuién evalúaCuándo usarla
Self-reflectionEl mismo agenteMejorar estilo, estructura y completitud
Cross-reflectionUn segundo agente críticoReducir puntos ciegos del generador usando un evaluador separado
Human reflectionUna personaDecisiones que requieren criterio humano real

Self-reflection: el agente evalúa su propio output

Self-reflection es la variante más directa: el mismo modelo que genera el output también es el que lo evalúa, pero cambiando el rol que tiene en cada fase.

La versión más simple es definir en su prompt inicial fases diferenciadas, que influyan en cómo se ejecuta la inferencia y pongan el foco en revisar aspectos de su propio trabajo. Este enfoque puede ser útil en ocasiones, pero es muy limitado porque el modelo sigue influido por los razonamientos que le han llevado a los resultados previos. Si no entendió bien la tarea al generar, es posible que tampoco detecte el fallo al revisarla.

Para solucionar esta limitación, necesitamos separar el primer resultado de la segunda inferencia en procesos separados, usando la salida del primer proceso para generar el prompt del segundo proceso, junto con instrucciones precisas de cómo enfocar la revisión. De esta manera conseguimos influenciar al modelo a tener un comportamiento diferente y crítico sobre su primer resultado.

Imagina que nuestra tarea es generar artículos para un blog. Y que nuestro evaluador está especializado en detectar desviaciones de nuestra guía de estilo de comunicación:

Eres un evaluador de estilo editorial. 

Tu tarea es revisar si la respuesta generada cumple con los requisitos de estilo editorial de la empresa. 
No debes mejorar directamente la respuesta. Solo debes evaluarla y devolver feedback estructurado.

Evalúa la respuesta según estos criterios:

[... Descripción detallada de los criterios de evaluación ...]

Reglas:

- Usa "approved": true solo si la respuesta cumple con los estándares de calidad.
- Incluye en "blockingIssues" solo los problemas que deberían corregirse
antes de aceptar la respuesta por ir en contra de nuestra guía de estilo
- Incluye en "suggestions" problemas menores que no incumplan directamente
ninguna regla pero creas que no concuerdan exactamente con nuestro tono y vocabulario
- approved debe ser true solo si blockingIssues está vacío.
- Si approved es false, debes incluir al menos un blockingIssue o una suggestion accionable.

Tarea original:

{{task}}

Respuesta generada:

{{output}}

El modelo es el mismo, pero el contexto cambia completamente cómo responde y eso permite que detecte problemas que ha cometido en otro hilo. Estamos modificando su enfoque y objetivo (generar artículo vs corregir el estilo editorial). Esto permite dar un segundo “punto de vista”.

En el código de abajo, generate() y evaluate() son simplemente dos llamadas al mismo LLM, cada una con su system prompt.

1.00

type Critique = {
  approved: boolean;
  blockingIssues: string[];
  suggestions: string[];
};

// Loop de self-reflection: el mismo modelo genera y evalúa,
// pero en llamadas separadas y con prompts distintos.
async function selfReflect(task: string, maxRounds = 3): Promise<string> {
  let output = await model.generate(task);

  for (let round = 0; round < maxRounds; round++) {
    const critique: Critique = await model.evaluate(output, task);

    if (critique.approved) break;

    output = await model.generate(task, {
      previousOutput: output,
      critique: [
        ...critique.blockingIssues,
        ...critique.suggestions,
      ].join("\n"),
    });
  }

  return output;
}

Este método tiene un límite claro. Si el modelo tiene una comprensión incorrecta de algo, va a usar esa misma comprensión equivocada para evaluarse. Es como pedirle a alguien que corrija un examen sin saber la respuesta correcta. Si el problema está en un sesgo propio del modelo o en la falta de capacidades, probablemente no lo detectará y volverá a caer en sus propias limitaciones.

El criterio de parada basado en la criticidad de los problemas encontrados evita rondas innecesarias.

Para eso existe cross-reflection con modelos diferentes.

Cross-reflection: un segundo modelo como crítico

Cross-reflection separa los roles en dos agentes distintos, ejecutados normalmente por modelos diferentes: uno genera el output y otro lo evalúa.

Si has leído el post del modelo como juez, esto te va a sonar parecido. En este patrón usamos un modelo dentro de un loop de feedback operativo antes de generar una respuesta final. Normalmente, cuando hablamos de un modelo como juez, lo usamos para evaluar, puntuar o comparar resultados existentes. En este caso, el juez está embebido dentro del proceso y se ejecuta antes de generar el resultado final.

1.00

Si el generador usa un modelo rápido, el crítico puede usar uno más conservador y detallista. Dos modelos de familias distintas pueden tener patrones de error diferentes. Eso no garantiza que el crítico tenga razón, pero sí puede aumentar la diversidad de criterio y ayudar a detectar fallos que el generador ha pasado por alto. Dependiendo del tipo de tarea, este patrón puede ahorrarte costes usando modelos más baratos para generar y modelos más caros para revisar, reduciendo la cantidad de tokens consumidos por el modelo caro en tareas sencillas.

Human reflection: cuándo entra el humano en el loop

Hay decisiones que ningún agente debería tomar. Recordemos que los modelos de IA no son infalibles, y dependiendo de lo crítico que sea fallar en el proceso que estés intentando automatizar, una revisión humana es obligatoria.

Human reflection añade un punto de espera en el loop: el agente pausa hasta recibir validación de una persona real. El código es igual que el de cross-reflection, pero la llamada de evaluación es asíncrona y no continúa hasta que alguien responde.

// Loop con checkpoint humano: el agente espera antes de continuar
async function humanReflect(task: string): Promise<string> {
  let output = await generate(task);

  // El agente pausa aquí hasta que una persona responda
  const humanFeedback = await waitForHumanApproval({
    output,
    task,
    // Guía para que el revisor sepa exactamente qué evaluar
    reviewGuide: "¿El output cumple los requisitos de negocio y es factualmente correcto?",
  });

  if (humanFeedback.approved) return output;

  // Si el humano rechaza, se regenera incorporando sus comentarios
  return generate(task, { feedback: humanFeedback.comments });
}

La regla para saber cuándo usarlo es simple: si el coste de un error supera con claridad el tiempo que cuesta revisarlo, la revisión humana no es opcional, es la decisión correcta. Un contrato, una respuesta a un cliente importante, código que va directo a producción.

Utilizar Cross-reflection puede mejorar mucho la calidad de respuesta de tus agentes, pero siempre recuerda que los modelos de IA nunca son deterministas y que el último responsable de un fallo eres tú, no el modelo.

El coste real de cada iteración

Cada llamada al modelo consume tokens, más iteraciones equivalen directamente a más dinero.

El patrón de crecimiento es peor de lo que parece. Cada iteración no solo incluye el nuevo output, sino también el output anterior y el feedback de la ronda previa. La segunda llamada es más cara que la primera, la tercera, más cara que la segunda. Si empiezas con 1.000 tokens de prompt base, tenlo en cuenta a la hora de estimar tus costes. Algunos proveedores aplican descuentos por prompt caching cuando parte del contexto se repite entre llamadas. Si tu proveedor lo soporta y reutilizas prefijos idénticos, puede amortiguar parte del coste.

La pregunta que tienes que hacerte antes de añadir más rondas es económica: ¿el error que evita esta iteración vale más que lo que cuesta corregirlo después? En la mayoría de casos, dos o tres rondas es el techo razonable. Si la tercera iteración apenas cambia el output respecto a la segunda, ahí está tu límite real.

En ocasiones, más rondas pueden significar más desviación del objetivo. Los modelos que revisan no están enfocados en el objetivo original de la tarea sino en encontrar errores. Esto, a su vez, puede influir en que el modelo generador se desvíe de su objetivo. Abusar de rondas de cross-reflection no suele mejorar el resultado. Si tus modelos revisores no consiguen encontrar todos los errores, valora generar varios diferentes, con objetivos distintos y enfocados, cada uno en un tipo de problema. Después, sintetiza todos esos errores y devuélvelos al generador o arréglalos uno por uno.

Hay además un límite técnico que no puedes ignorar: los modelos tienen un techo de contexto. Si acumulas output y feedback de varias rondas sin controlar el tamaño, la llamada falla con un error de API cuando superas esa ventana.

Errores comunes

Loop sin criterio de parada anticipada

El error más típico en una primera implementación: el loop siempre ejecuta todas las rondas, aunque el primer output ya sea perfectamente válido. El resultado es pagar más sin obtener nada a cambio.

La solución es simple: añade un criterio de aprobación explícito. Una puntuación mínima, una lista de sugerencias vacía, lo que encaje con tu caso. Si el crítico no tiene nada concreto que señalar, para.

Sycophancy del crítico

Sycophancy es cuando el crítico aprueba el output aunque tenga problemas evidentes. El modelo prefiere dar feedback positivo antes que generar fricción.

El síntoma es fácil de detectar: el crítico aprueba en la primera ronda casi siempre, incluso con outputs mediocres. La corrección es añadir al system prompt del crítico algo como “Antes de aprobar el output, identifica el punto más débil de la respuesta. Si aun así decides aprobarla, explica por qué ese punto no bloquea la entrega.”. Obligar al crítico a justificar sus aprobaciones suele aumentar el nivel de detalle de los errores que detecta a costa de consumir mas tokens.

Usar el mismo modelo sin cambiar el prompt

Self-reflection con el mismo system prompt que el generador no sirve de casi nada. El modelo reproduce exactamente los mismos sesgos que tuvo al generar, así que evalúa con los mismos puntos ciegos.

El system prompt del crítico tiene que llevar activamente al modelo a cuestionar el output, no a confirmarlo.

Feedback vago que no sirve de nada

Si el crítico devuelve “podría mejorar” o “añade más detalle”, el generador no sabe qué hacer con eso. Vago produce otro vago. El feedback tiene que ser accionable: “el tercer párrafo repite la idea del primero, elimínalo” o “falta un ejemplo concreto de código en la sección de costes”. Cuanto más específico, más útil es la siguiente iteración.

Checklist de implementación

  • El loop tiene un número máximo de iteraciones definido

  • Hay criterio de parada anticipada: el loop para antes del máximo si el output ya es satisfactorio

  • El system prompt del crítico obliga a señalar problemas, no a validar pasivamente

  • El feedback del crítico es accionable: especifica qué cambiar y dónde

  • Las llamadas al modelo tienen manejo de errores para rate limits, timeouts y respuestas inesperadas

Preguntas Frecuentes

¿Reflection reemplaza los tests y las validaciones programáticas?

No. Son capas distintas. Reflection mejora la calidad subjetiva del output: claridad, completitud, coherencia. Los tests y los guardarrailes verifican condiciones objetivas: ¿el JSON tiene el formato correcto?, ¿el código compila?, ¿están presentes los campos obligatorios? Son complementarios.