Evals - qualidade medida em produção
Evals é o harness que mede se a resposta é boa: dataset de exemplos, métrica (fidelidade, relevância, groundedness) e runner que executa ponta a ponta. Sem eval, melhorar um agente é chute e qualquer ajuste vira rotina.
O que é
Eval é um experimento de software. Você junta um dataset de prompts com a saída esperada, define uma métrica (heurística, embedding ou juiz LLM) e roda tudo por um runner que pontua cada caso e devolve uma média. Quando a média sobe, você sabe que melhorou. Quando cai, você sabe que piorou, sem precisar discutir.
Três peças sustentam tudo: o dataset (pares input/expected ou trilhas de produção), a métrica (como comparar saída com esperado) e o runner (loop que chama o agente, captura a saída, aplica a métrica e agrega). A métrica é o ponto onde quase todo time tropeça: a primeira versão costuma ser vaga demais e aceita qualquer coisa.
Dataset, métrica e runner
O dataset precisa ter cobertura real: casos fáceis (smoke), casos de borda (contradição no histórico, ferramenta que falha) e casos adversariais (instrução maliciosa embutida num documento recuperado). 50 casos bem escolhidos vencem 5.000 casos gerados por template. Cobertura importa mais que volume.
A métrica define o que é 'bom'. Fidelidade pergunta se ela é fiel ao contexto recuperado (sem inventar fonte). Relevância pergunta se ela responde o que foi pedido. Groundedness pergunta se cada afirmação tem cabimento extraído, não inventado pelo modelo. Cada métrica vira uma função determinística ou um prompt de juiz; as duas moram no mesmo arquivo.
O runner é a cola. Recebe o dataset, percorre cada caso, chama o agente com timeout, captura a saída (e a cadeia de tool calls, se for agente), aplica a métrica e devolve um relatório em JSON ou CSV. É o componente que entra no CI.
Exemplo de caso avaliado
Antes de rodar 500 casos, vale rodar um único e ler o resultado a olho. Um caso para um agente RAG de políticas de férias pode ser:
id: ferias-elege-01
input:
pergunta: "Posso vender 5 dias de férias para pagar o plano de saúde?"
contexto_recuperado:
- "Política 7.2: é permitido vender até 10 dias de férias por ano."
- "Política 4.1: o adiantamento é depositado em conta corrente do titular."
expected:
tem_que_dizer:
- "Sim, é permitido vender até 10 dias"
nao_pode_dizer:
- "É proibido vender férias"
metricas:
- fidelidade: groundedness
- relevancia: resposta-cobre-pergunta
- completude: contem-valor-numericoMétricas: as três que importam
Fidelidade (groundedness) compara cada frase da resposta com o contexto recuperado. Se o modelo afirmar algo sem cabimento extraído, perde pontos. É a métrica que mais cedo pega alucinação.
Relevância mede se a resposta cobre o que foi perguntado. Um juiz LLM com rubrica binária (respondeu / não respondeu) já resolve. A pegadinha é a resposta tecnicamente correta que ignora a pergunta, e a métrica precisa pegar isso.
Qualidade geral (helpfulness, completeness) agrega tudo: tom, clareza, utilidade. Costuma ser a mais subjetiva e onde o juiz humano brilha. Reserve-a para amostras pequenas, não para 100% do dataset.
O runner mínimo viável
O esqueleto abaixo carrega um YAML de caso, chama o agente, aplica um juiz LLM e devolve um veredito. Em produção o runner é maior (paralelismo, cache, retries), mas o formato fica bem neste documento:
import fs from "node:fs";
import { runAgent } from "../src/agent";
import { judge } from "./judges";
type Case = {
id: string;
input: Record<string, unknown>;
expected: { tem_que_dizer?: string[]; nao_pode_dizer?: string[] };
metricas: string[];
};
async function evalCase(c: Case) {
const t0 = Date.now();
const out = await runAgent(c.input);
const scores: Record<string, number> = {};
for (const m of c.metricas) {
scores[m] = await judge(m, c, out);
}
return { id: c.id, ms: Date.now() - t0, scores, output: out };
}
export async function evalFile(path: string) {
const cases = fs.readFileSync(path, "utf8")
.split("\n---")
.map(parseYaml);
const results = await Promise.all(cases.map(evalCase));
const media = (k: string) =>
results.reduce((a, r) => a + (r.scores[k] ?? 0), 0) / results.length;
return { n: results.length, results, media: { fidelidade: media("fidelidade") } };
}Onde entra na pipeline
Duas vezes. Antes do merge: a suite de regressão roda em todo PR e bloqueia se cair. Depois do merge: amostras de produção são re-avaliadas para detectar drift (modelo mudou, retrieval degradou, vazou).
Sem o segundo estágio você acaba em autoengano. Métricas que olham só para o histórico do PR não pegam regressões de runtime, e o defeito aparece só quando o usuário reclama.
Pontos fortes
- substitui discussão por número: 'a fidelidade caiu 4 pontos' é um argumento que fecha thread
- cobre regressões invisíveis a olho nu, principalmente em chains longos de tool calling
- gera dataset corrigível que a própria suite usa, virando um loop virtuoso de melhoria
Limitações
- métricas ruins passam a iludir: juiz LLM não calibrado vira carimbo que aceita qualquer coisa
- dataset parado envelhece: o agente evolui, a suite não, e a métrica para de refletir a realidade
- custo: rodar 500 casos com juiz LLM cobra tempo, leva tempo e exige rate limit na API do juiz
conexões no mapa
- Benchmarks - referência públicafornece dadosEvals - qualidade medida em produção
- LLM as judge - rubrica automatizadaautomatiza a notaEvals - qualidade medida em produção
- Regression evals - suite fixa no PRprotege o baselineEvals - qualidade medida em produção
- Evals - qualidade medida em produçãomede a recuperaçãoRAG - retrieval-augmented generation
atualizado em 14/10/2026 · fontes para aprofundar · reportar erro
em outros domínios
- Logging & detection - sinais e alertascloudsecobservar em produção alimenta a avaliação
- Engenharia de detecçãosecopsdetecção e avaliação medem precisão e recall do mesmo jeito
Estude antes
Desbloqueia
teste o que entendeu
Carregando perguntas…