· 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.

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

  1. Accuracy é o piso, não o teto do que seu eval suite precisa
  2. Multi-dimensional scoring > single score
  3. Regression detection em prod é tão importante quanto eval pré-deploy
  4. Guardrails numéricos são non-negotiable em domínios onde os números importam
  5. LLM-as-judge é útil mas precisa de calibração contra experts humanos periodicamente
  6. 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.

Compartir:
Back to Blog

Related Posts

View All Posts »