Módulo 3: El eval como fitness function
3. El eval-set y el score
Descripción
Al terminar esta lección vas a tener construida la herramienta base de todo el módulo: el eval-set y el score que produce. En la lección 2 viste el cambio de forma —de igualdad exacta a criterio de propiedad, y de booleano por caso a score agregado— con tres corridas de una sola pregunta. Aquí lo formalizas: un eval-set no es un caso, es un conjunto de casos, cada uno con su input y su criterio de éxito; correrlo contra el componente significa calificar cada caso; y el resultado es un score —la fracción de casos que pasaron su criterio—. Vas a ver el eval-set del agente de soporte de Mercado ejecutado caso por caso: diez preguntas frecuentes, cada una con la frase que su respuesta debe contener, corridas contra una versión del agente, con el detalle de cuáles pasan y cuáles fallan, y el score final: 9 de 10, 0.90. Al terminar, tendrás en las manos la pieza sobre la que operan las cuatro lecciones restantes.
Esto importa porque el score es la moneda del módulo. La compuerta de la lección 4 compara un score contra un umbral; la detección de regresiones de la lección 5 compara el score de un cambio contra el de un baseline; el gate de CI de la lección 6 pone el score en un exit code. Todo se apoya en este número, y este número no aparece por magia: sale de una estructura muy concreta —casos con criterio— corrida de una forma muy concreta —cada caso se califica, los resultados se agregan—. Si entiendes bien la anatomía aquí, el resto del módulo es aplicar esta pieza en distintos contextos. Y hay una razón de reproducibilidad: a diferencia de "probar a ojo" —que da un resultado distinto según quién mire y qué casos elija—, un eval-set corrido produce el mismo score siempre, porque los casos y el criterio están fijos. Eso es lo que lo hace apto para una compuerta: una compuerta necesita un número confiable, no una impresión.
Conexión con el módulo: esta lección es la que fabrica la materia prima. La 2 te dijo por qué necesitas un score; esta te da cómo se construye. Lo que sigue lo consume: la lección 4 le pone un umbral y lo vuelve compuerta; la 5 compara scores entre versiones para atrapar regresiones; la 6 lo mete en el pipeline. Aquí también aparece, marcada con claridad, la frontera dura del módulo: verás que el eval-set viene dado —diez preguntas ya elegidas, con su criterio ya definido— y que cómo se eligen esos casos y ese criterio (cobertura, representatividad, sesgo) es diseño de evals, de AI Engineering. Nuestro trabajo empieza cuando el eval-set ya existe.
La analogía: el examen estandarizado con su clave de respuestas
Vuelve al examen estandarizado que millones de estudiantes toman el mismo día. Míralo por partes, porque cada parte es una pieza del eval-set. El examen tiene muchas preguntas —no una sola—, y cada pregunta trae en la clave del profesor su respuesta correcta. Cuando un estudiante lo responde, un calificador recorre pregunta por pregunta: compara la respuesta del estudiante contra la clave de esa pregunta y marca acierto o error. Al final, no reporta "el estudiante acertó la pregunta 7"; reporta una calificación: "85 de 100". Esa calificación es un solo número que resume el desempeño en todo el examen.
Traduce cada parte. El examen completo —el conjunto de preguntas con sus claves— es el eval-set. Cada pregunta con su clave es un caso (un input con su criterio de éxito). Calificar pregunta por pregunta es correr el componente contra cada caso y aplicar el criterio. Y la calificación final —"85 de 100", o 0.85— es el score. Fíjate en tres propiedades que el examen deja claras y que el eval-set hereda. Primera: la calificación es objetiva y reproducible —dos calificadores con la misma clave dan la misma nota—. Segunda: es un agregado —no importa qué tan bien redactó el estudiante una pregunta suelta, importa cuántas acertó en total—. Tercera: una calificación no aprueba ni reprueba por sí sola —hace falta un umbral, "se aprueba con 60", que es la compuerta de la lección 4—. El examen produce la nota; el umbral decide qué hacer con ella. Esta lección construye el examen y saca la nota; la siguiente pone el umbral.
Ejemplo trabajado: el eval-set del agente de soporte, caso por caso
Vamos a construir el eval-set del agente de soporte de Mercado y a correrlo contra una versión del agente, viendo el detalle caso por caso y el score final. El eval-set son diez preguntas frecuentes, cada una con la frase clave (must_contain) que su respuesta correcta debe contener. La versión del agente que probamos responde bien nueve de las diez —falla justo la del cupón—, para que veas un score realista, no un 1.00 de laboratorio.
Recuerda la frontera: estas diez preguntas y sus criterios vienen dados. Por qué estas diez y no otras, si cubren bien lo que importa, si el criterio contains es el adecuado para cada una —todo eso es diseño de evals, de AI Engineering—. Aquí el eval-set ya existe y lo corremos.
# Leccion 03 — la anatomia del eval-set y el score
# Todo SIMULADO. Cero red, cero API, cero claves. Salida determinista.
# --- EL EVAL-SET: cada caso = input (question) + criterio de exito (must_contain) ---
# NOTA DE FRONTERA: como se ELIGEN estos casos y criterios es AI Engineering.
# Aqui el eval-set YA existe; nuestro trabajo es correrlo y sacar el score.
EVAL_SET = [
{"id": "q1", "question": "donde esta mi pedido", "must_contain": "tracking"},
{"id": "q2", "question": "como devuelvo un producto", "must_contain": "devolucion"},
{"id": "q3", "question": "cuanto tarda el envio", "must_contain": "3 a 5 dias"},
{"id": "q4", "question": "puedo pagar en cuotas", "must_contain": "cuotas"},
{"id": "q5", "question": "el producto llego roto", "must_contain": "reembolso"},
{"id": "q6", "question": "como cambio mi direccion", "must_contain": "perfil"},
{"id": "q7", "question": "no recibi mi factura", "must_contain": "correo"},
{"id": "q8", "question": "quiero cancelar mi pedido", "must_contain": "cancelar"},
{"id": "q9", "question": "el cupon no funciona", "must_contain": "vigencia"},
{"id": "q10", "question": "como contacto a un vendedor", "must_contain": "mensajes"},
]
# La respuesta correcta ("gold") por caso; cada una contiene la frase del criterio.
GOLD = {
"q1": "Puedes ver el tracking de tu pedido en tu perfil.",
"q2": "Para una devolucion, entra a tu pedido y pulsa Devolver.",
"q3": "El envio estandar tarda de 3 a 5 dias habiles.",
"q4": "Si, puedes pagar en cuotas sin interes con tarjeta.",
"q5": "Lamentamos eso; puedes pedir un reembolso desde el pedido.",
"q6": "Cambia tu direccion en la seccion Perfil, Direcciones.",
"q7": "Te reenviamos la factura al correo de tu cuenta.",
"q8": "Puedes cancelar el pedido si aun no fue enviado.",
"q9": "Revisa la vigencia del cupon; quiza ya expiro.",
"q10": "Escribe al vendedor desde la seccion Mensajes.",
}
POOR_ANSWER = "Lo siento, no tengo informacion sobre eso."
def make_agent(competent_ids):
# STUB del LLM: responde bien los casos en competent_ids; para el resto,
# una respuesta pobre que falla el criterio. Determinista, sin API real.
def agent(question, case_id):
return GOLD[case_id] if case_id in competent_ids else POOR_ANSWER
return agent
def criterion_contains(output, must_contain):
# El criterio de exito: la salida contiene la frase esperada.
return must_contain.lower() in output.lower()
def run_eval(agent):
# Corre el agente contra CADA caso, califica, y agrega en un score.
passed, detail = 0, []
for case in EVAL_SET:
out = agent(case["question"], case["id"])
ok = criterion_contains(out, case["must_contain"])
passed += ok
detail.append((case["id"], ok))
total = len(EVAL_SET)
return passed, total, passed / total, detail
# La version bajo prueba: competente en 9 de 10 (falla q9, el cupon).
ALL_IDS = {c["id"] for c in EVAL_SET}
agent = make_agent(ALL_IDS - {"q9"})
passed, total, score, detail = run_eval(agent)
print(f"{'caso':<6}{'must_contain':<16}{'resultado'}")
for case in EVAL_SET:
out = agent(case["question"], case["id"])
ok = criterion_contains(out, case["must_contain"])
print(f"{case['id']:<6}{case['must_contain']:<16}{'PASS' if ok else 'FAIL'}")
print(f"\npassed = {passed}/{total} score = {score:.2f}")
Qué esperar. Al correrlo:
caso must_contain resultado
q1 tracking PASS
q2 devolucion PASS
q3 3 a 5 dias PASS
q4 cuotas PASS
q5 reembolso PASS
q6 perfil PASS
q7 correo PASS
q8 cancelar PASS
q9 vigencia FAIL
q10 mensajes PASS
passed = 9/10 score = 0.90
Aquí está el eval-set corrido, y aquí está la anatomía completa a la vista. Léelo en dos niveles, que es la clave de la lección.
El nivel del caso. Cada renglón es un caso calificado: la pregunta se le pasó al agente, la respuesta se comparó contra el criterio, y salió PASS o FAIL. Nueve renglones dicen PASS —el agente respondió con la frase clave— y uno, q9 (el cupón), dice FAIL —el agente dio la respuesta pobre "no tengo información", que no contiene "vigencia"—. Este nivel es diagnóstico: te dice exactamente qué falló. Si mañana quieres arreglar el agente, sabes que el problema está en las preguntas de cupones, no en las de envío. El detalle por caso es tu mapa de dónde duele.
El nivel del score. La última línea agrega los diez casos en un solo número: passed = 9/10, score = 0.90. Este nivel es decisorio: es el número que la compuerta comparará contra un umbral. Fíjate en lo que hizo el agregado: convirtió diez veredictos binarios en un grado —0.90— que resume la calidad del componente en una cifra comparable. Si otra versión sacara 0.60, sabrías al instante que es peor, aunque no miraras ningún caso individual. El score es lo que hace comparables dos versiones de un componente probabilístico.
Los dos niveles conviven y sirven para cosas distintas. El score decide (¿pasa la compuerta?); el detalle diagnostica (¿qué arreglo?). Un buen sistema de eval reporta los dos: el número para la compuerta y la lista de fallos para el equipo. Pero para el resto del módulo, el protagonista es el score —0.90—, porque es lo que la compuerta consume.
Profundización: la anatomía, la agregación y la frontera
Las tres piezas del eval-set. Un eval-set tiene exactamente tres piezas, y conviene nombrarlas para no confundirlas. Primera, los casos: los inputs con los que pruebas el componente (las diez preguntas). Segunda, el criterio de éxito: la regla que decide si la respuesta a un caso está bien (aquí, contains(must_contain)). Tercera, la agregación: cómo combinas los veredictos por caso en un score (aquí, la fracción que pasa). Cambia cualquiera de las tres y cambia el eval. En este módulo variamos sobre todo el criterio (lección 7, distintos tipos) y usamos siempre la agregación más simple —la fracción que pasa, o pass-rate—; la elección de los casos viene dada.
Por qué el pass-rate es la agregación por defecto (y no la única). Agregar por "fracción de casos que pasan" es la forma más simple y la que usaremos en todo el módulo: cada caso vale igual, pasa o no pasa, y el score es cuántos pasaron entre el total. Es intuitivo y suficiente para una compuerta. Pero no es la única forma de agregar, y conviene saberlo: podrías ponderar casos (los críticos valen más), promediar un score continuo por caso (cuando el criterio da un grado, no un binario —el LLM-as-judge de la lección 7 da 0.4, no solo 0 o 1—), o exigir que ningún caso crítico falle (un veto, no un promedio). Cuál agregación usar es, otra vez, diseño de evals (AI Engineering). Aquí usamos pass-rate porque es la que hace la mecánica de la compuerta más transparente.
Por qué el eval-set es reproducible y "a ojo" no. El valor de esta estructura, frente a revisar respuestas a mano, es que produce el mismo score siempre. Los casos están fijos (siempre las mismas diez preguntas), el criterio está fijo (siempre contains), y la agregación está fija (siempre pass-rate). Corre el eval hoy, mañana, en tu máquina o en CI: si el componente no cambió, el score es idéntico. "Probar a ojo" no tiene esa propiedad —cada persona mira casos distintos, con un criterio mental distinto, y saca una impresión distinta—. Una compuerta necesita un número en el que se pueda confiar para tomar una decisión de deploy, y solo una estructura fija lo da. Esta es la razón profunda por la que el módulo insiste en el eval-set: no es que "a ojo" no funcione nunca, es que no es reproducible, y una compuerta sin reproducibilidad no es una compuerta.
La frontera, otra vez, con precisión. Todo lo que esta lección hace es correr un eval-set dado y agregar un score. Lo que NO hace —y es de AI Engineering— es decidir el contenido del eval-set: qué preguntas incluir para que cubran los casos que importan y no solo los fáciles, cuántas bastan para que el score sea confiable, cómo evitar el sesgo de meter solo casos que ya sabes que el agente responde bien (lo que inflaría el score sin medir nada), y qué criterio de éxito captura de verdad la calidad de cada tarea. Un eval-set mal diseñado da un score que parece una medición pero no lo es —un examen con puras preguntas triviales aprueba a cualquiera—. Reconocer que el eval-set puede estar mal diseñado es parte de saber usarlo; diseñarlo bien es la otra guía. Este módulo asume un eval-set razonable y se concentra en lo que sigue: convertir su score en una compuerta.
Errores comunes
Confundir el detalle por caso con el score (de nivel). Qué pasa: el equipo mira la lista de PASS/FAIL y toma decisiones caso por caso —"arreglemos q9 y ya"— sin mirar el score agregado, o al revés, mira solo el score y pierde el diagnóstico de qué falló. Por qué pasa: los dos niveles conviven en la misma salida y es fácil quedarse en uno. Cómo detectarlo: si no puedes decir a la vez "el score es 0.90" y "lo que falla es la pregunta del cupón", estás usando solo un nivel. Cómo corregirlo: usa cada nivel para su propósito —el score para la compuerta (¿pasa el deploy?), el detalle para depurar (¿qué arreglo?)—; los dos son parte del reporte del eval.
Un eval-set con puros casos fáciles (de diseño, en la frontera). Qué pasa: el eval-set se llena de preguntas que el agente ya responde bien, el score sale altísimo (0.98) y el equipo concluye que la calidad es excelente —cuando en realidad no probó ningún caso difícil—. Por qué pasa: es natural meter casos que "sabemos que funcionan" y da la satisfacción de un score alto. Cómo detectarlo: si tu eval-set nunca da un score intermedio o bajo, sospecha que es demasiado fácil, no que tu componente es perfecto. Cómo corregirlo: esto es diseño de evals (AI Engineering) —cobertura, casos difíciles, representatividad—; pero a nivel de este módulo, la señal de alerta es un eval-set que siempre aprueba (lo verás como la "fitness function inútil" en la lección 4).
Agregar mal: mezclar casos que no valen igual (de agregación). Qué pasa: el eval-set mezcla casos triviales con casos críticos y los promedia por igual con pass-rate, de modo que fallar un caso crítico (un reembolso mal explicado) pesa lo mismo que fallar uno trivial, y el score no refleja el riesgo real. Por qué pasa: el pass-rate simple trata todos los casos como iguales, que es cómodo pero no siempre correcto. Cómo detectarlo: si un fallo grave y uno leve mueven el score exactamente lo mismo, tu agregación no distingue lo que importa. Cómo corregirlo: la agregación puede ponderar o vetar (diseño de evals, AI Engineering); en este módulo usamos pass-rate por simplicidad, pero sabiendo que no es la única forma de agregar.
Ejercicios
Ejercicio 1 — Calcula el score. Un eval-set tiene 8 casos. Al correrlo contra una versión del agente, el detalle es: PASS, PASS, FAIL, PASS, PASS, PASS, FAIL, PASS. (a) ¿Cuál es el score con la agregación pass-rate? (b) Si el umbral de la compuerta fuera 0.80, ¿pasaría? (c) ¿Qué te dice el detalle que el score solo no te dice?
Ver solución
- (a) Score: 6 PASS de 8 casos = 6/8 = 0.75.
- (b) ¿Pasa el umbral de 0.80? No: 0.75 < 0.80, la compuerta falla (deploy bloqueado). El score cabe justo por debajo del umbral.
- (c) Qué agrega el detalle: el score (0.75) te dice cuánto falla, pero el detalle te dice qué falla —los casos 3 y 7—. Para arreglar el componente necesitas el detalle: sabes que el problema está en esos dos casos concretos, no repartido por todo el eval-set. El score decide (no pasa la compuerta); el detalle guía la corrección (mira los casos 3 y 7). Los dos niveles, cada uno para su trabajo.
Ejercicio 2 — Cambia el criterio, cambia el score. Toma el caso q3 ("¿cuánto tarda el envío?", must_contain = "3 a 5 dias"). El agente responde: "El envío suele llegar bastante rápido, en pocos días." Di el veredicto de este caso bajo dos criterios: (a) contains("3 a 5 dias") y (b) contains("dias"). Explica por qué el score del eval-set depende no solo del componente, sino del criterio elegido.
Ver solución
- (a)
contains("3 a 5 dias"): la respuesta "en pocos días" no contiene "3 a 5 dias" → FAIL. El criterio exige el dato específico (el rango), y la respuesta fue vaga. - (b)
contains("dias"): la respuesta sí contiene "días" → PASS. El criterio solo exige que se mencione "días", y la respuesta lo hace, aunque no dé el rango correcto.
La moraleja: la misma respuesta del mismo componente da FAIL con un criterio y PASS con otro. Eso significa que el score del eval-set no mide solo qué tan bueno es el componente —mide qué tan bueno es según el criterio que elegiste—. Un criterio flojo (contains("dias")) aprueba una respuesta vaga e infla el score; un criterio estricto (contains("3 a 5 dias")) exige el dato correcto. Por eso elegir bien el criterio es tan importante —y es diseño de evals, AI Engineering—. En este módulo el criterio viene dado; pero entender que el score depende de él es clave para no confiar ciegamente en un número alto.
Ejercicio 3 — ¿Qué es del eval como gate y qué de su diseño? Tienes que poner una compuerta de calidad a la búsqueda semántica de Mercado. Para cada tarea, di si es de este módulo (usar el eval-set como gate) o de la frontera (diseñar el eval-set), y por qué. (a) Correr el eval-set de la búsqueda y calcular su score. (b) Elegir 100 queries representativas del tráfico real para el eval-set. (c) Comparar el score contra un umbral de 0.85 para decidir el deploy. (d) Definir que el criterio de éxito sea "el producto correcto está entre los tres primeros resultados".
Ver solución
- (a) Correr el eval-set y calcular el score → este módulo. Es ejecutar la herramienta y producir el número. La mecánica de esta lección.
- (b) Elegir las 100 queries representativas → frontera (AI Engineering). Es diseño del eval-set: qué casos lo componen para que cubra el tráfico real sin sesgo. No es de aquí.
- (c) Comparar el score contra el umbral → este módulo. Es convertir el score en una decisión de deploy: la compuerta (lección 4).
- (d) Definir el criterio de éxito ("entre los tres primeros") → frontera (AI Engineering). Es diseñar qué hace que una respuesta cuente como correcta para esta tarea. Elegir la métrica es diseño de evals.
La regla que separa: construir el eval-set —sus casos y su criterio— (b, d) es AI Engineering; correrlo y usar su score para decidir (a, c) es de aquí. Este módulo empieza cuando el eval-set ya existe.
Resumen y siguiente paso
En esta lección construiste la herramienta base del módulo: el eval-set y el score. Con la analogía del examen estandarizado viste sus tres piezas —los casos (las preguntas), el criterio de éxito (la clave de respuestas) y la agregación (la calificación final)— y las dos propiedades que hereda: es objetivo y reproducible (mismo eval-set, mismo score siempre), y es un agregado (importa cuántos casos pasan, no la redacción de uno suelto). Ejecutaste el eval-set del agente de soporte de Mercado caso por caso y viste los dos niveles de su salida: el detalle por caso (nueve PASS y un FAIL en el cupón), que diagnostica qué falla, y el score agregado (9/10 = 0.90), que decide —el número que la compuerta consume—. Y marcaste la frontera con precisión: correr y agregar es de aquí; elegir los casos y el criterio es diseño de evals, de AI Engineering.
Antes de avanzar deberías poder: nombrar las tres piezas de un eval-set (casos, criterio, agregación); explicar la diferencia entre el nivel del caso (diagnóstico) y el nivel del score (decisión); justificar por qué un eval-set es reproducible y "a ojo" no; y reconocer que el score depende del criterio, no solo del componente.
Lo que sigue es darle poder a ese score. Hasta ahora es solo un número —0.90— que describe el componente pero no decide nada. En la lección 4 vas a ponerle un umbral y a convertirlo en una compuerta: el corazón del módulo. Vas a ejecutar el eval_gate sobre dos versiones del agente —una que pasa (0.90 ≥ 0.80 → deploy permitido, en verde) y otra que regresó (0.60 < 0.80 → deploy bloqueado, en rojo)— y vas a ver por qué esa compuerta es, exactamente, una fitness function para la calidad de un componente probabilístico. Es el paso de "tengo un score" a "el score gobierna el deploy".
Recursos
- Anthropic — docs de Claude, crear y correr evaluaciones (conceptual) — la guía de cómo estructurar un conjunto de casos con criterio de éxito y correrlo para obtener una medición agregada; el respaldo de la anatomía eval-set → score de esta lección, sin fijar versión.
- Chip Huyen — AI Engineering (O'Reilly), capítulos de evaluación — el tratamiento sistemático de cómo se compone un conjunto de evaluación (cobertura, representatividad, agregación); la referencia para diseñar el eval-set que este módulo toma como dado.
- martinfowler.com — "Emerging Patterns in Building GenAI Applications", Bharani Subramaniam y Martin Fowler — el patrón de evals como conjunto de casos que produce una métrica agregada sobre la que se gobiernan los cambios; el marco de arquitectura del módulo.