Evals: como parar de perguntar "parece bom?" para sua IA

“Parece bom” é a métrica mais usada e menos verificável em desenvolvimento de aplicação com LLM. Ela depende de quem está olhando, do humor da pessoa naquele momento, de quantos exemplos ela realmente conferiu antes de formar opinião. Evals existe para trocar essa impressão por alguma coisa que possa ser medida, repetida e comparada ao longo do tempo.

O que é um eval, estruturalmente

Um eval é um caso de teste estruturado: uma entrada específica, executada contra o sistema de IA, com uma ou mais asserções que determinam se o resultado passa ou falha segundo um critério definido de antemão. A diferença para um teste de software tradicional não está na estrutura, está no tipo de asserção disponível, porque comparar resposta de LLM com resposta esperada raramente é uma questão de igualdade exata de texto.

Três famílias de asserção, cada uma para um tipo de verificação

Correspondência exata e determinística funciona bem quando a resposta esperada realmente tem um formato fixo: um valor numérico específico, uma string de um conjunto fechado de opções, a presença ou ausência de determinado termo, validação contra um schema JSON. É a asserção mais barata e mais confiável, exatamente porque não depende de julgamento de qualidade nenhum, só de comparação estrutural.

Similaridade semântica entra quando a resposta esperada não tem formato fixo, mas tem um significado específico que precisa estar presente, independente da redação exata usada. Aqui, o próprio texto gerado e o texto esperado são convertidos em embeddings, e a similaridade de cosseno entre os dois decide se estão próximos o suficiente para passar, com um limiar configurável de tolerância.

Julgamento por rubrica (LLM-graded) entra quando nem correspondência exata nem similaridade semântica capturam o critério real, geralmente quando a qualidade avaliada é mais subjetiva: tom apropriado, completude da explicação, ausência de informação inventada. Aqui, um modelo avaliador recebe a resposta e uma rubrica específica, e devolve um veredito de aprovação ou reprovação com justificativa, funcionando como uma aplicação prática de LLM-as-a-Judge dentro do próprio processo de eval.

Um exemplo de configuração

Ferramentas como Promptfoo estruturam isso em arquivo de configuração, combinando as três famílias de asserção conforme o caso:

tests:
  - vars:
      pergunta: "Qual o prazo de garantia legal de um eletrônico no Brasil?"
    assert:
      - type: contains
        value: "90 dias"
      - type: similar
        value: "O prazo legal é de noventa dias para produtos duráveis"
        threshold: 0.85
      - type: llm-rubric
        value: "A resposta cita a base legal (Código de Defesa do Consumidor) sem inventar artigo incorreto"

Esse único caso de teste combina uma verificação exata (contains), uma verificação de significado (similar) e um julgamento mais subjetivo sobre fundamentação (llm-rubric), cada uma cobrindo um aspecto diferente do que “resposta boa” deveria significar naquele contexto específico.

Escolher a asserção certa é parte do trabalho

Usar correspondência exata onde a resposta naturalmente varia de redação gera falso negativo constante, reprovando respostas corretas só por não baterem palavra por palavra. Usar julgamento por rubrica onde correspondência exata resolveria adiciona custo e latência desnecessários a uma verificação que já era simples e barata. Parte real do trabalho de montar uma suíte de evals é decidir, para cada caso, qual das três famílias de asserção realmente representa o critério de sucesso daquele teste específico, em vez de aplicar a mesma abordagem para tudo.

De impressão para evidência

O ganho de estruturar isso como eval, em vez de continuar perguntando “parece bom” a cada mudança, é poder rodar o mesmo conjunto de casos repetidamente, comparar resultado ao longo do tempo, e apontar exatamente qual caso passou a falhar depois de uma mudança específica. “Parece bom” não sobrevive a essa comparação. Um eval bem definido sobrevive, porque não depende de impressão de ninguém no momento em que é executado.