· Hernán Pérez Rodal · Engineering · 6 min read
LLM evaluation en dominios regulados: más allá de accuracy
Cuando una respuesta incorrecta de tu LLM impacta una auditoría FDA, accuracy no alcanza. Contamos cómo evaluamos LLMs y agents en Darwin — golden sets, LLM-as-judge, regression detection y guardrails numéricos.

TL;DR — “Accuracy” es una métrica insuficiente cuando el output de tu LLM influye decisiones regulatorias. En Darwin combinamos golden sets + LLM-as-judge + regression detection + guardrails numéricos. Este post cuenta cómo lo armamos y qué aprendimos en producción con miles de casos por día.
El problema: accuracy no es enough
Cuando evaluás un clasificador tradicional, accuracy + confusion matrix te dicen bastante. Cuando evaluás un LLM en un dominio regulado:
- Accuracy parece alta en benchmarks genéricos pero falla en casos edge del dominio
- Casos críticos pesan distinto — un error en un caso “rutinario” vs. un caso “alto riesgo” no es lo mismo
- Los outputs son texto libre — no hay una única respuesta correcta
- Los modelos cambian sin avisarte — OpenAI/Anthropic actualizan modelos, tu eval del día 1 no sirve para el día 90
- Las alucinaciones numéricas son silent killers — el modelo inventa un dato y lo presenta con confianza total
En Darwin, cuando el sistema dice “este lote tiene 3 gaps de compliance FSMA 204”, esa respuesta va a una auditoría. Equivocarse ahí es legal liability. Necesitamos evaluation que capture eso.
Los 4 niveles de evaluation que usamos
Nivel 1: Golden sets curados manualmente
Un set de 300-500 casos representativos con respuestas ground truth curadas por experts humanos (compliance officers internos + advisors externos).
Cubre:
- Casos rutinarios — la mayoría, que el modelo debería acertar sin problema
- Casos edge conocidos — situaciones ambiguas, reglas que se contradicen, lotes incompletos
- Casos adversariales — intentos de hacer que el modelo falle (trick questions, data contradictoria)
Metrics en este nivel:
- Exact match — para respuestas estructuradas (JSON, enum values)
- Semantic similarity — para respuestas text-based (BERTScore, embeddings)
- Fact extraction recall — ¿extrajo todos los facts relevantes?
Frecuencia: corremos este set en CI antes de cada deploy. Rompe el deploy si baja de threshold.
Nivel 2: LLM-as-judge para text outputs
Para respuestas largas (reports, gap analyses, explicaciones) no hay un único “correcto”. Usamos otro LLM como juez — con un prompt específico que evalúa:
- Factualidad — ¿los claims son verificables con la evidencia mostrada?
- Completeness — ¿cubre todos los aspectos que debería?
- Citación correcta — ¿cita las regulaciones/fuentes correctas?
- Tone/style — ¿es profesional, preciso, no-alarmista?
Lo clave: el judge LLM usa otro modelo (ej: modelo A genera, modelo B juzga). Esto evita el bias de “el modelo juzgándose a sí mismo”.
Pitfalls: LLM-as-judge tiene sus propios biases. Lo calibramos con muestras donde expertos humanos también evaluaron, y ajustamos el prompt del judge iterativamente.
Nivel 3: Regression detection en producción
Entre evals de golden sets, el modelo en producción procesa miles de casos por día. No podemos curar todos — pero podemos detectar regresiones.
Técnicas:
- Score distributions over time — si la confidence promedio, los scores por eval dimension, o los patterns de outputs cambian de golpe, alerta
- Shadow mode para deploys — el modelo nuevo corre en paralelo, comparamos sus outputs contra el actual en casos reales
- Sampling + human review — 1-2% de outputs se samplean semanalmente y un compliance officer humano los revisa
- Feedback from operators — cada vez que un operador corrige una respuesta del sistema, es un data point
Cuando cambia algo sistemáticamente (ej: el score de “completeness” baja 10% en una semana), investigamos antes de que cause un incidente.
Nivel 4: Numerical guardrails
Este es el más crítico. Los LLMs inventan números cuando no los tienen explícitos en contexto. Esto lo cubrí parcialmente en el post de RAG, pero vale la pena profundizar.
Cada vez que nuestro sistema retorna un output que contiene un número (cantidad de lotes, fechas, porcentajes, counts), pasamos por un guardrail validator:
def validate_numerical_claims(response: str, source_data: dict) -> ValidationResult:
"""Extract numerical claims from LLM response and verify against source data."""
claims = extract_numbers_from_text(response)
for claim in claims:
# Is this number present in our verified data?
if not verify_claim_in_source(claim, source_data):
return ValidationResult(
valid=False,
reason=f"Claim '{claim}' cannot be verified against source data",
)
return ValidationResult(valid=True)Si un número del LLM no se puede verificar contra la fuente estructurada, fallamos fast — devolvemos un error en vez de una respuesta potencialmente incorrecta. Mejor decir “no sé” que mentir con confianza.
El stack de eval
- Pytest + custom fixtures — golden sets como archivos YAML, asserts en tests
- LangSmith + OpenTelemetry — tracing de cada invocación con input, output, metadata
- PromptLayer — versioning de prompts + history
- Custom dashboards — Grafana con score distributions, drift detection
- Postgres + dbt — persistencia de eval runs, comparación histórica
Lo que no funcionó
V0: una sola metric global de “accuracy” — hacía bajar los decisiones técnicas a “mejoró / empeoró” sin matiz. Pasamos a dashboards multi-dimensional (factualidad, completeness, citación, etc.) con trade-offs explícitos.
Golden set estático sin updates — el set se desactualizó con nuevas versiones de regulaciones. Ahora tenemos un proceso mensual de curación/review.
Eval solo en staging antes de deploy — algunas regresiones solo aparecían con distribución real de producción. Agregamos el nivel 3 (regression detection en prod) para compensar.
Confiar 100% en LLM-as-judge sin calibración humana — el judge tenía biases que no veíamos. Ahora calibramos contra human-rated samples mensualmente.
Lo que sí funcionó
Golden sets chicos pero bien curados — 300 casos bien pensados > 3000 casos random
Dashboards de trend, no solo de snapshot — detectar drift es más importante que el score absoluto
Guardrails numéricos como defensive coding — zero alucinaciones numéricas que lleguen al usuario final desde que lo implementamos
Feedback loops corto — cada corrección del operador vuelve al sistema de eval como potential golden case
Tracing por cada LLM call — cuando algo sale mal en producción, vamos del trace a la causa raíz en minutos
Lessons learned
- Accuracy es el piso, no el techo de lo que tu eval suite necesita
- Multi-dimensional scoring > single score
- Regression detection en prod es tan importante como eval pre-deploy
- Guardrails numéricos son non-negotiable en dominios donde los números importan
- LLM-as-judge es útil pero necesita calibración contra expertos humanos periódicamente
- Operator feedback es el eval set más valioso a largo plazo
¿Y ahora?
Estamos explorando automated adversarial testing — generación programática de casos edge que estresan al modelo. Combinado con fuzz testing de inputs (datos parciales, contradictorios, formatos raros), amplifica nuestra eval coverage sin requerir curación manual.
Si estás construyendo LLMs en producción y tu eval suite es “correr un eval.py con 50 ejemplos”, probablemente estás subestimando el problema. Lo crítico no es el número en el report — es que captures regresiones antes de que lleguen al usuario.
¿Estás armando tu eval suite para LLMs en producción? Hablemos — podemos compartir templates, dashboards y aprendizajes.




