Evaluador-optimizador
Un modelo genera, otro evalúa con criterios explícitos y devuelve feedback, y el bucle se repite hasta aprobar o agotar los intentos.
- Coste
- 3 de 3
- Latencia
- 3 de 3
- Cuándo usarlo
- Cuando tienes criterios de evaluación claros y una crítica concreta mejora de forma medible el resultado.
- Cuándo no
- Si no sabes escribir el criterio de aprobado o si la primera versión ya suele ser buena.
El evaluador-optimizador monta un bucle entre dos papeles: un generador produce una respuesta y un evaluador la juzga contra criterios explícitos y devuelve feedback accionable. El generador reescribe con ese feedback y el ciclo se repite hasta que el evaluador aprueba o se acaban los intentos. Building effective agents (Anthropic, 2024) lo compara con el proceso de un escritor que corrige un texto varias veces.
Qué es
flowchart LR E[Tarea] --> G[LLM generador] G --> V[LLM evaluador] V -- aprobado --> S[Salida] V -- feedback --> G V -- intentos agotados --> H[Mejor versión o humano]
Los dos papeles pueden ser el mismo modelo con prompts distintos. Lo importante es que el evaluador tenga una rúbrica y devuelva dos cosas separadas: un veredicto que tu código pueda leer (aprobado o no) y un feedback que el generador pueda usar.
El patrón aplica en tiempo de ejecución la misma idea que un modelo juez en tus evals. Si ya tienes una rúbrica para evaluar tu sistema, tienes medio evaluador hecho.
Cuándo usarlo
El artículo da dos señales de que encaja:
- Las respuestas mejoran de forma demostrable cuando una persona les da feedback concreto.
- El modelo es capaz de dar ese feedback por sí mismo.
Ejemplos que cita: traducción literaria, donde el traductor puede pasar por alto matices que un evaluador detecta, y búsquedas complejas que necesitan varias rondas en las que el evaluador decide si hace falta buscar más.
Otros casos donde funciona bien: textos con requisitos verificables (longitud, términos obligatorios, estilo de la casa), código que tiene que pasar tests (aquí el evaluador puede ser el propio test) y respuestas que deben citar solo lo que está en las fuentes.
Cuándo no
- No puedes escribir el criterio de aprobado. “Que quede mejor” no es una rúbrica, y un evaluador sin criterio aprueba casi todo o nunca aprueba.
- La primera versión ya es buena casi siempre. Pagas al menos el doble para mejorar pocos casos. Mide primero.
- El criterio se comprueba con código. Si “aprobado” es “el JSON valida” o “los tests pasan”, usa esa comprobación: es más barata y fiable. Eso es una puerta de encadenado con reintento.
- El evaluador comparte los puntos ciegos del generador. Si ambos fallan en lo mismo, el bucle solo confirma el error. Cambia el prompt del evaluador, dale información que el generador no tenía o usa un evaluador humano en los casos críticos.
Ejemplo concreto
Fichas de producto para una tienda. La rúbrica exige: menos de 80 palabras, mencionar material y medidas, no usar superlativos sin dato, tono de la marca. El generador escribe la ficha a partir de los datos técnicos. El evaluador devuelve aprobado: false con «Falta la medida del asa; “el mejor bolso” es un superlativo sin dato». El generador corrige. Tras como mucho tres vueltas, la ficha se publica o pasa a revisión de una persona con el último feedback adjunto.
La longitud se comprueba en código antes de llamar al evaluador: ahorras una llamada cuando el fallo es obvio.
Esqueleto de código
import json
import anthropic
MODEL = "claude-sonnet-5-5"
client = anthropic.Anthropic()
RUBRICA = """1. Menciona material y medidas.
2. No usa superlativos sin un dato que los respalde.
3. Tono cercano y frases cortas."""
VEREDICTO = {
"type": "object",
"properties": {
"aprobado": {"type": "boolean"},
"feedback": {"type": "string"},
},
"required": ["aprobado", "feedback"],
"additionalProperties": False,
}
def llamar(system: str, prompt: str, **extra) -> str:
resp = client.messages.create(model=MODEL, max_tokens=1024, system=system,
messages=[{"role": "user", "content": prompt}], **extra)
return "".join(b.text for b in resp.content if b.type == "text")
def generar(datos: str, feedback: str | None) -> str:
previo = f"\n<feedback>\n{feedback}\n</feedback>\nCorrige todo lo señalado." if feedback else ""
return llamar(f"Escribes fichas de producto. Criterios:\n{RUBRICA}",
f"<datos>\n{datos}\n</datos>{previo}")
def evaluar(datos: str, ficha: str) -> dict:
salida = llamar(
f"Evalúas fichas de producto contra estos criterios:\n{RUBRICA}\n"
"Aprueba solo si cumple todos. Si no, explica qué falla y cómo arreglarlo.",
f"<datos>\n{datos}\n</datos>\n<ficha>\n{ficha}\n</ficha>",
output_config={"format": {"type": "json_schema", "schema": VEREDICTO}},
)
return json.loads(salida)
def ficha_producto(datos: str, max_vueltas: int = 3) -> tuple[str, bool]:
feedback = None
for _ in range(max_vueltas):
ficha = generar(datos, feedback)
if len(ficha.split()) > 80: # comprobación barata en código
feedback = "Supera las 80 palabras. Acorta sin perder material ni medidas."
continue
veredicto = evaluar(datos, ficha)
if veredicto["aprobado"]:
return ficha, True
feedback = veredicto["feedback"]
return ficha, False # a revisión humana con el último feedbackEl evaluador recibe también los datos de origen: así puede detectar que la ficha inventa algo que no estaba en ellos. Sin esa referencia solo juzgaría la forma.
Coste y latencia
Es el patrón con peor latencia entre los workflows: cada vuelta suma una generación y una evaluación en serie. El coste mínimo ya es el doble de una llamada simple y crece con cada vuelta. Para contenerlo: comprueba con código todo lo que puedas antes del evaluador, deja la rúbrica como prefijo estable para cachearla y registra cuántas vueltas necesita cada caso. Si casi todo se aprueba a la primera, quizá baste con evaluar sin bucle; si casi nada se aprueba, el problema está en el prompt del generador.
Patrones relacionados
- Encadenado de prompts: una puerta en código es un evaluador barato y determinista.
- Paralelización: varios evaluadores en paralelo, uno por criterio.
- Agente autónomo: un agente que comprueba su trabajo con tests aplica este bucle con feedback del entorno.
- Modelo juez con rúbrica: cómo escribir una rúbrica que discrimine.