Una evaluación de LLM sin dependencias con un juez en quien realmente se puede confiar
Construya una pequeña evaluación de LLM a partir de registros reales, verificaciones de código y un juez basado en un único criterio; luego calibre a dicho juez con etiquetas humanas para que sus puntuaciones tengan sentido.
Las pruebas unitarias funcionan porque el código determinista da siempre la misma respuesta: se verifica que una función devuelva cuatro y así lo hace en todo momento. La salida de los modelos de lenguaje cambia la redacción en cada llamada, por lo que no existe una cadena esperada con la que comparar, y los equipos suelen solucionarlo adoptando una plataforma de evaluación que utiliza otro modelo para calificar los resultados. Es en el modelo de calificación donde la mayoría de las evaluaciones dejan de medir algo sin que nadie haya confirmado que esté de acuerdo con una evaluación humana. Esta guía permite crear una evaluación completa en Python puro en unas pocas horas, y dedica su sección más importante a calibrar al evaluador para que el número resultante pueda ser defendido.
Las tres partes de cualquier evaluación
Si se eliminan las herramientas, una evaluación consta de tres componentes, ninguno de los cuales necesita una biblioteca:
- Un conjunto de datos de entradas, preferiblemente extraído del uso real y no inventado.
Los paneles de control, el seguimiento y los conjuntos de datos alojados son comodidades creadas a partir de estas partes. Algunos de ellos son realmente útiles, pero el núcleo se resume en aproximadamente ochenta líneas de Python, y un código tan pequeño seguirá funcionando mucho tiempo después de que cualquier plataforma en particular cambie o desaparezca.
Paso 1: Recopilar cien entradas reales
Tome los datos de sus registros de producción. Evite ejemplos sintéticos, evite los casos que ya sabe que funcionan y evite muestras ya procesadas. Cien casos son suficientemente numerosos como para revelar problemas reales y lo bastante pocos como para poder etiquetarlos a mano posteriormente, lo cual resulta esencial. Incluya intencionadamente datos de entrada difíciles y complicados: un conjunto de casos fáciles genera una puntuación estable incluso cuando su sistema empeora.
Cargarlos es sencillo:
import json
with open("requests.jsonl") as f:
cases = [json.loads(line) for line in f][:100]
Tenga en cuenta que al extraer las primeras cien líneas se obtiene todo lo que esté en la parte superior del archivo, lo cual puede provenir de un mismo día o de un único cliente. Mezclar los datos antes de extraerlos, o tomar muestras de diferentes períodos de tiempo, proporciona un conjunto más representativo.
Si aún no cuenta con registros, cree usted mismo esos cien casos, pero espere que los resultados parezcan mejores de lo real hasta que el tráfico real los reemplace.
Paso 2: Separar las verificaciones de código de las decisiones basadas en juicio
Este es el paso que los equipos suelen omitir, y determina todo lo que viene después. Muchas propiedades pueden verificarse de manera determinística, y estas nunca deben enviarse a un modelo:
- La salida es JSON válido.
- Una categoría pertenece a un conjunto conocido.
- Un número está dentro de un rango permitido.
- Están presentes los campos obligatorios.
- La respuesta cumple con el límite de longitud.
Cada uno de estos puntos representa una afirmación. La siguiente función (en Python, independientemente de cómo esté etiquetada) ejecuta algunas de ellas y devuelve un diccionario con los resultados correspondientes:
def code_checks(output):
checks = {}
try:
parsed = json.loads(output)
checks["valid_json"] = True
checks["has_fields"] = all(k in parsed for k in ("answer", "confidence"))
except json.JSONDecodeError:
checks["valid_json"] = False
checks["has_fields"] = False
checks["under_limit"] = len(output) < 2000
return checks
Devolver verificaciones nombradas en lugar de un único valor booleano permite saber qué propiedad falló cuando algo sale mal. Lo que queda después de las verificaciones deterministas es la parte que requiere juicio: ¿la respuesta abordó realmente la pregunta planteada?, ¿introdujo hechos no presentes en el origen?, ¿se negó cuando debería haber respondido? Esas propiedades necesitan un juez, y un juez necesita validación.
Paso 3: Escribir un juez que evalúe una sola cosa
Asigne a cada llamada al juez exactamente un criterio. Una rúbrica que solicite cinco cualidades a la vez genera un veredicto combinado, y cuando falla no se puede determinar cuál calidad faltó. El juez que se muestra a continuación verifica una sola propiedad, las afirmaciones sin sustento y exige un formato de respuesta fijo:
JUDGE = """You are grading one property of a response.
PROPERTY: Does the response contain any claim that is not supported by the
source text provided?
SOURCE:
{source}
RESPONSE:
{response}
Answer with exactly one word, PASS or FAIL, then a new line, then one
sentence explaining your verdict. Do not explain anything else."""
def judge(source, response, call_model):
out = call_model(JUDGE.format(source=source, response=response))
verdict = out.strip().split("\n")[0].strip().upper()
return verdict == "PASS", out
La restricción de formato es tan importante como el criterio en sí. Requerir que la primera línea contenga una sola palabra, PASS o FAIL, facilita el análisis de los resultados. Los veredictos en formato libre varían en estructura de una evaluación a otra, y si un fallo en el análisis se considera como aprobado, todas las puntuaciones que se informen posteriormente estarán infladas sin que nadie se dé cuenta. Esta implementación opta por la prudencia: cualquier cosa que no sea exactamente PASS, incluyendo PASS. o un veredicto con formato adicional, se considera un fallo. Vale la pena registrar con qué frecuencia ocurre esto, ya que un aumento en las respuestas imposibles de analizar es en sí mismo una señal.
Paso 4: Ejecutar todo y conservar la salida bruta
Guarde la salida completa y la explicación del evaluador para cada caso, no solo el veredicto. Cuando cambie una puntuación, querrá saber qué es lo que realmente ha cambiado, y un valor booleano guardado no le permitirá averiguarlo.
results = []
for case in cases:
output = call_model(case["prompt"])
passed, reasoning = judge(case["source"], output, call_model)
results.append({
"id": case["id"],
"output": output,
"code_checks": code_checks(output),
"judge_pass": passed,
"judge_reasoning": reasoning,
})
rate = sum(r["judge_pass"] for r in results) / len(results)
print(f"{rate:.1%} pass on {len(results)} cases")
El bucle registra las verificaciones deterministas junto con el resultado del juez, aunque la tasa general solo tiene en cuenta al juez. En la práctica se deberían reportar ambos, ya que una respuesta que falla en la validación JSON constituye un fallo independientemente de lo que piense el juez. También hay que tener en cuenta que la misma función call_model genera y califica aquí; es común utilizar un modelo diferente o más potente para el juez, pero sea cual sea la elección, el siguiente paso es lo que garantiza su fiabilidad.
En este punto se dispone de un porcentaje. Por ahora no significa nada.
Paso 5: Calibrar al juez con sus propios etiquetados
Este es el paso que la mayoría de las guías omiten, y dura aproximadamente treinta minutos. Elija cincuenta de los cien casos y etiquete cada uno usted mismo como PASS o FAIL, sin consultar primero el veredicto del juez. Luego compare los dos conjuntos de etiquetados:
agree = sum(1 for r, human in zip(results[:50], human_labels)
if r["judge_pass"] == human)
print(f"judge agrees with me on {agree}/50 = {agree/50:.0%}")
both_fail = sum(1 for r, h in zip(results[:50], human_labels)
if not r["judge_pass"] and not h)
judge_fails = sum(1 for r in results[:50] if not r["judge_pass"])
human_fails = sum(1 for h in human_labels if not h)
print(f"judge caught {both_fail}/{human_fails} of the failures I found")
print(f"judge flagged {judge_fails - both_fail} things I considered fine")
El script muestra tres cifras: el acuerdo general, cuántos de tus errores también fueron detectados por el juez y cuántos casos fallidos consideraste aceptables. El código asume que human_labels es una lista de valores booleanos en el mismo orden que los primeros cincuenta resultados, y dividirá entre cero si no encontraste ningún error en absoluto, lo cual es una señal de que tu conjunto de datos es demasiado fácil.
Por qué el acuerdo bruto engaña
El acuerdo general es la cifra menos fiable, aunque sea la que más se menciona. Si el noventa por ciento de tus casos son aprobados, un juez que responde “APROBADO” a todo alcanza un noventa por ciento de acuerdo sin detectar nada en realidad.
La cifra importante: errores detectados
Lo que merece su atención es la proporción de sus propias etiquetas FAIL que también fue marcada por el juez. En términos de clasificación, esto representa la capacidad de recuperación del juez respecto a la clase de errores. Un juez que detecta dos de sus once errores en realidad no está calificando nada; simplemente aprueba todo junto con una explicación convincente. La tercera cifra, las alarmas falsas, también es importante, ya que un juez que marca respuestas correctas lo llevará a “arreglar” cosas que nunca estuvieron defectuosas.
Cuando usted y el juez no están de acuerdo
Lea cada caso en el que su etiqueta difiera de la del juez. Solo hay tres explicaciones posibles, y cada una tiene su propio remedio.
La rúbrica es ambigua
El juez se equivoca porque la descripción de la propiedad deja espacio para interpretaciones. Defínala con mayor precisión. Una buena prueba es si puede expresar esa propiedad en una sola oración que un nuevo colega pueda aplicar exactamente como usted lo haría.
Tus propias etiquetas están equivocadas
Esto ocurre con más frecuencia de lo que la gente espera. La etiquetación humana varía en unos cincuenta casos, especialmente en los ejemplos ambiguos al final de una sesión. Vuelve a etiquetar los casos en disputa con perspectiva fresca antes de culpar al juez.
La propiedad es inherentemente subjetiva
Algunas cualidades no pueden ser juzgadas de manera consistente por nadie. Si esa es la situación, ningún juez será fiable. O bien divide la propiedad en subpropiedades más pequeñas y verificables, o acepta que no se puede puntuar.
Itera: ajusta la rúbrica, vuelve a etiquetar lo necesario y ejecuta el proceso nuevamente hasta que el juez detecte suficientes errores tuyos como para que tengas que defender la cifra obtenida en una reunión. Luego, congela la rúbrica y almacena su versión junto con cada resultado futuro. No se pueden comparar resultados obtenidos con rúbricas diferentes. Para saber cómo mantener al juez en buen estado cuando se utiliza de forma continua en producción, consulta cómo gestionar a un LLM como juez en un sistema de producción dinámico.
Qué te ofrece la evaluación calibrada
El resultado es un número que puedes colocar junto a un cambio y en el que puedes confiar. Antes de modificar un prompt, cambiar de modelo o alterar una etapa de recuperación de información, tomas una medición; posteriormente, tomas otra. La diferencia solo contiene información porque primero confirmaste que el instrumento coincide con la opinión de una persona.
Dado que el código no depende de nada más que de la función que llama a su modelo, sigue funcionando cuando cambia de proveedor o cuando el ecosistema de herramientas se reorganiza, lo cual es más de lo que se puede decir de la mayoría de las soluciones recomendadas para esta tarea.
Límites de una evaluación con cien casos
Hay tres limitaciones que vale la pena entender antes de utilizar este enfoque.
Las pequeñas regresiones son invisibles
Una disminución del 91 al 89 por ciento corresponde a dos casos de cada cien, lo cual es indistinguible del ruido con este tamaño de muestra. Para detectar efectos pequeños se necesita mucho más datos o una comparación emparejada: ejecutar ambas versiones con los mismos datos e contar solo aquellos casos cuyo resultado cambió. Las comparaciones emparejadas eliminan la mayor parte de la variación que proviene de la dificultad de los casos.
El etiquetado manual no es escalable
Las etiquetas manuales dejan de ser prácticas después de unas pocas centenas de filas, y la calidad disminuye a medida que avanza la sesión. Tu juicio sobre el caso cuarenta y ocho no es el mismo que sobre el caso dos, lo cual introduce errores reales en la propia calibración. Etiquetar en varias sesiones cortas en lugar de una sola larga reduce esa desviación.
La calibración vence
Un juez validado con los resultados de hoy puede no ser válido para mañana. Cuando cambias el modelo detrás de tu característica, sus modos de fallo también cambian, por lo que el juez debe ser revalidado en lugar de reutilizado. Es un trabajo tedioso, y es precisamente lo que distingue una medición de un ritual.
Dos adiciones una vez que se ejecuta
- Versione todo. Almacene la versión del modelo y la versión de la rúbrica junto con cada resultado. Una calificación que falte en alguno de estos elementos no puede compararse con ninguna otra, por la misma razón una cifra de referencia sin su marco de referencia dice muy poco.
- Mantenga el conjunto de desacuerdos. Los casos en los que usted y el evaluador no estuvieron de acuerdo son las filas más informativas de todo el ejercicio. Volver a ejecutarlos después de cada cambio en la rúbrica es la forma más rápida de saber si ese cambio fue útil.
Los ejemplos aquí presentados priorizan la claridad sobre la robustez en producción. Úselos como plantilla para leer y adaptar, añadiendo intentos repetidos, manejo de errores y persistencia según lo requiera su configuración.
Puntos clave
- Una evaluación consiste en un conjunto de datos, un generador y un calificador; todo lo demás son herramientas opcionales.
Lecturas relacionadas
- Gestionar LLM como evaluador como sistema de producción vivo — Aprenda cómo el ciclo de vida de cuatro etapas de Netflix —datos reales, entrenamiento ajustado con rúbrica, despliegue seguro y monitoreo continuo— mantiene preciso a un evaluador LLM a gran escala.
- Dimensionamiento adecuado de LLMs: Enrutamiento, recuperación y evaluación según el tamaño bruto del modelo — Aprenda cómo elegir entre modelos de lenguaje pequeños y grandes según la carga de trabajo, medir el costo por tarea completada con éxito y utilizar primero el enrutamiento, RAG, caché y validación.
- Cambio seguro de modelos de LLM: Etiquetas humanas, métricas por paso y ajustes de esfuerzo — Cómo migrar un pipeline de LLM de múltiples pasos a modelos más recientes sin enfrentarse a regresiones invisibles: datos reales proporcionados por humanos, métricas por paso, prompts obsoletos y análisis del esfuerzo invertido.