· Hernán Pérez Rodal · Engineering · 6 min read
LLM evaluation em domínios regulados: além de accuracy
Quando uma resposta incorreta do seu LLM impacta uma auditoria FDA, accuracy não basta. Contamos como avaliamos LLMs e agents na Darwin — golden sets, LLM-as-judge, regression detection e guardrails numéricos.

TL;DR — “Accuracy” é uma métrica insuficiente quando o output do seu LLM influencia decisões regulatórias. Na Darwin combinamos golden sets + LLM-as-judge + regression detection + guardrails numéricos. Este post conta como montamos e o que aprendemos em produção com milhares de casos por dia.
O problema: accuracy não é enough
Quando você avalia um classificador tradicional, accuracy + confusion matrix te dizem bastante. Quando você avalia um LLM em um domínio regulado:
- Accuracy parece alta em benchmarks genéricos mas falha em casos edge do domínio
- Casos críticos pesam diferente — um erro em um caso “rotineiro” vs. um caso “alto risco” não é a mesma coisa
- Os outputs são texto livre — não há uma única resposta correta
- Os modelos mudam sem avisar — OpenAI/Anthropic atualizam modelos, seu eval do dia 1 não serve para o dia 90
- As alucinações numéricas são silent killers — o modelo inventa um dado e o apresenta com confiança total
Na Darwin, quando o sistema diz “este lote tem 3 gaps de compliance FSMA 204”, essa resposta vai para uma auditoria. Errar ali é legal liability. Precisamos de evaluation que capture isso.
Os 4 níveis de evaluation que usamos
Nível 1: Golden sets curados manualmente
Um set de 300-500 casos representativos com respostas ground truth curadas por experts humanos (compliance officers internos + advisors externos).
Cobre:
- Casos rotineiros — a maioria, que o modelo deveria acertar sem problema
- Casos edge conhecidos — situações ambíguas, regras que se contradizem, lotes incompletos
- Casos adversariais — tentativas de fazer o modelo falhar (trick questions, dados contraditórios)
Metrics neste nível:
- Exact match — para respostas estruturadas (JSON, enum values)
- Semantic similarity — para respostas text-based (BERTScore, embeddings)
- Fact extraction recall — extraiu todos os facts relevantes?
Frequência: rodamos este set em CI antes de cada deploy. Quebra o deploy se cair do threshold.
Nível 2: LLM-as-judge para text outputs
Para respostas longas (reports, gap analyses, explicações) não há um único “correto”. Usamos outro LLM como juiz — com um prompt específico que avalia:
- Factualidade — os claims são verificáveis com a evidência mostrada?
- Completeness — cobre todos os aspectos que deveria?
- Citação correta — cita as regulamentações/fontes corretas?
- Tone/style — é profissional, preciso, não-alarmista?
O chave: o judge LLM usa outro modelo (ex: modelo A gera, modelo B julga). Isso evita o bias de “o modelo julgando a si mesmo”.
Pitfalls: LLM-as-judge tem seus próprios biases. Calibramos com amostras onde experts humanos também avaliaram, e ajustamos o prompt do judge iterativamente.
Nível 3: Regression detection em produção
Entre evals de golden sets, o modelo em produção processa milhares de casos por dia. Não podemos curar todos — mas podemos detectar regressões.
Técnicas:
- Score distributions over time — se a confidence média, os scores por dimensão de eval, ou os patterns de outputs mudam de repente, alerta
- Shadow mode para deploys — o modelo novo roda em paralelo, comparamos seus outputs contra o atual em casos reais
- Sampling + human review — 1-2% dos outputs são amostrados semanalmente e um compliance officer humano os revisa
- Feedback from operators — cada vez que um operador corrige uma resposta do sistema, é um data point
Quando algo muda sistematicamente (ex: o score de “completeness” cai 10% em uma semana), investigamos antes que cause um incidente.
Nível 4: Numerical guardrails
Este é o mais crítico. Os LLMs inventam números quando não os têm explícitos no contexto. Cobri isso parcialmente no post de RAG, mas vale a pena aprofundar.
Cada vez que nosso sistema retorna um output que contém um número (quantidade de lotes, datas, porcentagens, counts), passamos por um 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)Se um número do LLM não pode ser verificado contra a fonte estruturada, falhamos fast — devolvemos um erro em vez de uma resposta potencialmente incorreta. Melhor dizer “não sei” do que mentir com confiança.
O stack de eval
- Pytest + custom fixtures — golden sets como arquivos YAML, asserts em tests
- LangSmith + OpenTelemetry — tracing de cada invocação com input, output, metadata
- PromptLayer — versioning de prompts + history
- Custom dashboards — Grafana com score distributions, drift detection
- Postgres + dbt — persistência de eval runs, comparação histórica
O que não funcionou
V0: uma única metric global de “accuracy” — reduzia as decisões técnicas a “melhorou / piorou” sem matiz. Passamos para dashboards multi-dimensionais (factualidade, completeness, citação, etc.) com trade-offs explícitos.
Golden set estático sem updates — o set se desatualizou com novas versões de regulamentações. Agora temos um processo mensal de curadoria/review.
Eval só em staging antes do deploy — algumas regressões só apareciam com distribuição real de produção. Adicionamos o nível 3 (regression detection em prod) para compensar.
Confiar 100% no LLM-as-judge sem calibração humana — o judge tinha biases que não víamos. Agora calibramos contra human-rated samples mensalmente.
O que sim funcionou
Golden sets pequenos mas bem curados — 300 casos bem pensados > 3000 casos random
Dashboards de trend, não só de snapshot — detectar drift é mais importante que o score absoluto
Guardrails numéricos como defensive coding — zero alucinações numéricas que cheguem ao usuário final desde que implementamos
Feedback loops curtos — cada correção do operador volta ao sistema de eval como potential golden case
Tracing por cada LLM call — quando algo dá errado em produção, vamos do trace à causa raiz em minutos
Lessons learned
- Accuracy é o piso, não o teto do que seu eval suite precisa
- Multi-dimensional scoring > single score
- Regression detection em prod é tão importante quanto eval pré-deploy
- Guardrails numéricos são non-negotiable em domínios onde os números importam
- LLM-as-judge é útil mas precisa de calibração contra experts humanos periodicamente
- Operator feedback é o eval set mais valioso a longo prazo
E agora?
Estamos explorando automated adversarial testing — geração programática de casos edge que estressam o modelo. Combinado com fuzz testing de inputs (dados parciais, contraditórios, formatos raros), amplifica nosso eval coverage sem requerer curadoria manual.
Se você está construindo LLMs em produção e seu eval suite é “rodar um eval.py com 50 exemplos”, provavelmente está subestimando o problema. O crítico não é o número no report — é que você capture regressões antes que cheguem ao usuário.
Está montando seu eval suite para LLMs em produção? Vamos conversar — podemos compartilhar templates, dashboards e aprendizados.




