Capítulo 17 de 22 · avanzado
Construye un harness de evaluación
Qué cubre esta sesión
Cada cambio que le haces a un prompt, a un modelo o a un pipeline es una apuesta a que mejoró. Sin una forma de medir, estás apostando a una sensación — y las sensaciones son justo lo que una respuesta equivocada que suena segura está diseñada para engañar. Un harness de evaluación convierte "mejor" en un número: un conjunto fijo de casos de prueba, un scorer y una tasa de aprobación que puedes comparar antes y después. Una vez que lo tienes, dejas de discutir si el nuevo prompt es una mejora y simplemente lo corres.
El harness es casi vergonzosamente simple — casos, una función de check, un loop, un conteo. El criterio está en dos lugares: escribir casos de prueba que incluyan las trampas (no solo las victorias fáciles), y elegir el scorer correcto. Algunas salidas tienen una respuesta correcta que puedes checar en código; otras son abiertas y necesitan un modelo que las juzgue contra una rúbrica. Ambos pertenecen a tu caja de herramientas.
OpenAI
Empieza con el scorer más barato: un check de coincidencia exacta o de contención contra una respuesta esperada. Arma la lista de casos para que incluya los que atrapan regresiones — la capital de Australia es Canberra, y un prompt que "mejoró" volviéndose más seguro dirá encantado Sídney.
# The test set. A good one includes the obvious cases AND the traps — Canberra,
# not Sydney, is exactly the kind of case that catches a regression.
CASES = [
{"q": "What is the capital of France?", "expect": "Paris"},
{"q": "What is the capital of Japan?", "expect": "Tokyo"},
{"q": "What is the capital of Australia?", "expect": "Canberra"},
{"q": "What is 2 + 2?", "expect": "4"},
]
El harness corre cada caso, lo puntúa y reporta una tasa. Esa tasa es todo el punto: es el único número comparable que te dice si un cambio ayudó.
# Run every case through the model, score with a check function, report a rate.
# This "exact/contains" check is the cheapest scorer; use it whenever the right
# answer is unambiguous. The score is the number you compare across changes.
def check(output: str, expect: str) -> bool:
return expect.lower() in output.lower()
def run_eval(system: str) -> float:
passed = 0
for c in CASES:
out = ask(system, c["q"])
ok = check(out, c["expect"])
passed += ok
print(f" [{'PASS' if ok else 'FAIL'}] {c['q']} -> {out[:30]!r} (want {c['expect']})")
rate = passed / len(CASES)
print(f"score: {passed}/{len(CASES)} = {rate:.0%}")
return rate
Cuando no hay una respuesta correcta fija — un resumen, un tono, una explicación — el código no puede puntuarla, así que promueves un modelo a juez. Dale al juez un trabajo estrecho, una rúbrica y una salida estructurada.
# When the answer isn't a fixed string — a summary, a tone, an explanation — a
# code check can't score it. Use an LLM as the judge, with a rubric and a scale.
# Keep the judge's job narrow and its output structured so the score is usable.
def judge(question: str, answer: str) -> str:
return ask(
"You are a strict grader. Score the ANSWER to the QUESTION from 1-5 on "
"accuracy and clarity. Reply as 'score: N - reason'.",
f"QUESTION: {question}\nANSWER: {answer}",
)
Gemini
El harness es agnóstico al proveedor — casos, check, tasa — así que la versión de
Gemini cambia solo la llamada a ask. Eso es una ventaja: un buen harness te deja
evaluar entre proveedores sobre el mismo conjunto de pruebas, que es como de
verdad decidirías entre ellos.
def check(output: str, expect: str) -> bool:
return expect.lower() in output.lower()
def run_eval(system: str) -> float:
passed = 0
for c in CASES:
out = ask(system, c["q"])
ok = check(out, c["expect"])
passed += ok
print(f" [{'PASS' if ok else 'FAIL'}] {c['q']} -> {out[:30]!r} (want {c['expect']})")
rate = passed / len(CASES)
print(f"score: {passed}/{len(CASES)} = {rate:.0%}")
return rate
Unas cuantas advertencias honestas sobre los jueces LLM, porque son poderosos y fáciles de mal usar. Los jueces están sesgados — tienden a preferir respuestas más largas y su propio estilo, y pueden ser indulgentes. Amárralos: una rúbrica específica, una escala fija, una dimensión a la vez, y contrasta al juez contra calificaciones humanas antes de confiar en sus puntuaciones. Un juez que no has validado es solo otra opinión con un número pegado.
Ponlo a trabajar
Tres apps de docker-compose bajo code/showcase/<slug>/, una por idea. Misma
rutina: bash bootstrap-secrets.sh, docker compose up --build,
http://localhost:3000.
Showcase 1 — Corredor de evals
Un conjunto de pruebas fijo y un system prompt editable. Cambia el prompt, corre, observa la tasa de aprobación. Este es el loop central de la ingeniería de prompts hecha con honestidad — no "¿esto se ve mejor?" sino "¿subió el score?".
Showcase 2 — Juez LLM
Pega un borrador de respuesta de soporte y recíbelo calificado en tono, claridad y completitud con una razón para cada uno. Es el scorer para todo lo que no puedes checar con un match de string, y un ejemplo funcional de mantener a un juez estrecho y estructurado.
Showcase 3 — A/B de prompts
Dos system prompts, un conjunto de pruebas, dos tasas de aprobación, un ganador. Este es el artefacto que termina el debate: pega el prompt actual y el propuesto, y deja que el número decida.
Los tres son las mismas tres piezas — casos, un scorer, una tasa — acomodadas de tres maneras. Construye uno para cualquier cosa que pienses poner en producción.
Córrelo
El README de esta carpeta tiene la versión de Python, la línea de instalación,
las dos variables de entorno y los comandos exactos. Las keys vienen del .env
sin trackear en la raíz del curso.
Lo que te llevas
Un harness de evaluación es la diferencia entre ingeniería y vibes: un conjunto de pruebas fijo, un scorer, una tasa de aprobación que comparas en cada cambio. Es trivial de construir — casos, un check, un loop — y el oficio está en los casos de prueba (incluye las trampas que atrapan regresiones) y en el scorer (checks de código para respuestas exactas, un juez LLM para las abiertas). Trata al juez con sospecha hasta que lo hayas validado contra calificaciones humanas, porque un juez sesgado solo lava una adivinanza convirtiéndola en un score. Construye el harness antes de necesitarlo, y "¿es mejor la nueva versión?" se vuelve un comando que corres, no una discusión que tienes. La próxima semana, el harness se topa con su trabajo más importante: medir la seguridad — moderación y guardrails.