Agent Mentor Learn
Verificação e garantia de qualidade: não deixe passar o que só “parece certo” · Lição 3 de 6

Lição 3: Verificadores determinísticos: só valem verificações que produzem passa/falha

Objetivos de aprendizado:

  • Ordenar verificadores candidatos por “mais rápido, mais confiável, mais escalável” e escolher o certo para uma saída específica
  • Escrever um script de verificação determinística que devolve passa/falha em uma forma que o agente consegue ler e sobre a qual consegue iterar
  • Reconhecer falsos negativos em que verificadores estritos demais rejeitam saídas corretas, corrigi-los com normalização, e entender onde as verificações determinísticas encontram seu teto

Pré-requisitos: Lições 1 e 2 (sem uma verificação que rode, “parece pronto” é o único sinal; verifique o estado final primeiro, o processo como rede de segurança; critérios de sucesso precisam ser mensuráveis) | Anterior: << Lição 2 | Próxima: Lição 4 >>

De “o que verificar” para “como verificar”

Ao final da Lição 2, você já deveria ter um critério de sucesso concreto escrito. Pegue o exemplo que esta lição usa: o agente lê um lote de CSVs de vendas e os agrega em um report.json com um título, itens por canal e um total. A Lição 2 ensinou você a enquadrar o critério como estado final — o arquivo existe, os campos estão presentes, o total é igual à soma das contagens dos itens — em vez de verificações de processo turno a turno do tipo “ler arquivo, depois calcular a soma, depois escrever o arquivo”.

Você tem o critério. A próxima pergunta é prática: o que você usa para conferir contra esse critério?

Você poderia bater o olho. Poderia fazer outro modelo ler e dar um retorno. Ou poderia escrever uma dúzia de linhas de Node que carrega o JSON, soma as contagens e sai com código não zero se elas não baterem. As três chegam a uma conclusão, mas o custo e a confiabilidade variam radicalmente.

A documentação oficial oferece um princípio de ordenação: escolha o método de correção mais rápido, mais confiável e mais escalável1. Por essa régua, as três categorias caem em uma ordem clara:

  • Correção baseada em código — a mais rápida e mais confiável, extremamente escalável; a fraqueza é que lhe falta nuance para julgamentos complexos que precisam de menos rigidez baseada em regras1.
  • Correção baseada em LLM — rápida e flexível, escalável e adequada a julgamento complexo, mas teste para garantir a confiabilidade antes de escalar1. (Isso é a Lição 4.)
  • Correção humana — a mais flexível e de mais alta qualidade, mas lenta e cara; evite se possível1.

“Confiável” aqui significa mesma entrada, mesmo veredicto toda vez. A correção baseada em código fica em primeiro não por ser inteligente, mas por ser previsivelmente burra — ela não vai te aprovar hoje no feeling e te reprovar amanhã por causa de uma formulação. Em computação, sistemas determinísticos produzem a mesma saída toda vez, dadas entradas idênticas, enquanto sistemas não determinísticos — como os agentes — podem gerar respostas variadas mesmo com as mesmas condições iniciais2. Os “verificadores determinísticos” desta lição são esse tipo de verificação previsivelmente burra: uma coisa determinística avaliando uma coisa não determinística.

Um princípio relacionado para desenhar tarefas de eval: estruture as questões de modo a permitir correção automatizada (por exemplo, múltipla escolha, correspondência de string, corrigida por código, corrigida por LLM)1. Onde você tem chance de automatizar, automatize.

O cardápio de verificadores: qualquer coisa que retorne um sinal

“Verificador” soa como um framework especializado, mas a barra é bem mais baixa. A definição da documentação oficial é quase seca: a verificação é qualquer coisa que retorne um sinal que o Claude consiga ler na conversa: uma suíte de testes, o código de saída de um build, um linter, um script que compara a saída com um fixture, ou uma captura de tela do navegador comparada com um design3.

Desmonte essa lista. Rodar npm test e ficar tudo verde é um passa, perfeito para entregas de código — soluções de código são verificáveis por testes automatizados4. Códigos de saída de build são os mais fáceis: a toolchain já escreveu as asserções para você. Um linter sozinho não basta, mas é ótimo como piso para “erros que não se deveria cometer”. Um script de comparação confronta a saída desta execução com um arquivo de fixture que você preparou (uma amostra sabidamente boa), adequado quando a saída é estável e o formato é fixo. Comparação de captura de tela serve para quando você está mexendo em estilos de frontend.

Uma nota de prática de engenharia: compiladores e verificadores estáticos de tipo (como tsc --noEmit) são muitas vezes usados assim em projetos reais, porque eles também emitem códigos de saída e localizações de erro legíveis. Isso é acréscimo meu, não está na lista oficial — não trate como endossado oficialmente.

Esses métodos não são mutuamente exclusivos. Verificadores formam um espectro — uma ponta é “correspondência exata de string com um fixture”, a outra ponta é “pedir ao Claude que julgue”2. Esta lição cobre a metade da esquerda; a Lição 4 vai para a direita.

Forma mínima: output == golden_answer

A ponta esquerda do espectro é assim1:

text
output == golden_answer

Só uma checagem de igualdade. Isso se chama correspondência exata. Ela mede se a saída do modelo bate com uma resposta correta predefinida, normalmente depois de normalizar espaços em branco e caixa; é uma métrica simples e sem ambiguidade, perfeita para tarefas com respostas categóricas e bem delimitadas, como análise de sentimento (positivo, negativo, neutro)1.

“Normalização” aqui é simples: antes de comparar, apague as diferenças que não carregam significado. Em código:

O resultado da segunda chamada merece um olhar demorado. trim() e toLowerCase() resgatam quebras de linha e caixa, mas não aquele ponto final. Até onde a normalização deveria ir depende de quais diferenças são irrelevantes para a sua tarefa — um julgamento que nenhuma biblioteca faz por você. A seção de armadilhas mais adiante nesta lição é sobre o que acontece quando você erra esse julgamento.

Deixe a saída do verificador legível

Aquela definição tinha uma cláusula que costuma ser pulada: o sinal precisa ser algo que o Claude consiga ler na conversa3. Essa cláusula determina como o seu script de verificação deveria escrever a saída dele. Compare duas mensagens de falha:

text
FAIL: a validação não passou
text
FAIL  report.json  - total não bate: declarado 48, a soma dos count dos itens é 50

A primeira diz ao agente “você está errado” e depois o deixa adivinhando. A segunda diz qual campo falhou, o que era esperado, o que veio de fato — no turno seguinte ele consegue ir direto corrigir aquele número. O mesmo passa/falha, uma diferença de ordem de grandeza em informação. Outra parte da orientação oficial diz a mesma coisa: faça o Claude mostrar evidências em vez de afirmar sucesso — a saída dos testes, o comando que ele rodou e o que ele retornou, ou uma captura de tela do resultado; revisar evidências é mais rápido do que refazer a verificação você mesmo, e funciona para sessões que você não estava acompanhando3. O seu script de verificação é o produtor dessa evidência. Se ele for vago, a evidência é vaga.

Duas regras práticas: use o código de saída 0 para passa e não zero para falha (CI e o && do shell conseguem usar isso direto). No stdout, uma falha por linha, informando “qual campo, o que era esperado, o que veio”.

Com passa/falha, o comportamento do agente muda

Durante a execução, agentes precisam de “ground truth” do ambiente a cada etapa (como resultados de chamadas de ferramenta ou execução de código) para avaliar o progresso4. Sem um verificador, o único sinal de progresso que ele consegue obter é o parágrafo que ele acabou de escrever — se ele acha que terminou, terminou. Com um verificador, o ambiente contém uma fonte de fato independente do julgamento dele.

Então a cadeia de comportamento muda: dê ao Claude algo que produza um passa ou falha, e o loop se fecha sozinho; o Claude faz o trabalho, roda a verificação, lê o resultado e itera até a verificação passar3. Dito de outro jeito: agentes conseguem iterar sobre soluções usando resultados de testes como feedback4.

No tipo de loop de harness que você escreveu à mão no curso 7 desta série, a implementação é expor a verificação como uma ferramenta:

A saída do verificador volta para a conversa via tool_result. O modelo lê total não bate: declarado 48, a soma dos count dos itens é 50, e no turno seguinte vai corrigir. Você participou de zero etapas.

Duas extensões convenientes. Primeira, verificações não precisam ficar só no fim. A Lição 2 cobriu como fluxos complexos podem ser quebrados em checkpoints discretos onde mudanças de estado específicas deveriam ter ocorrido, em vez de validar cada etapa intermediária5. Esses checkpoints de verificação são pontos de pouso naturais para verificações determinísticas — algo como “depois que todos os CSVs forem lidos, a contagem de linhas deveria ser igual à soma das contagens de linhas dos arquivos individuais”. O mesmo retrospecto também menciona combinar a adaptabilidade dos agentes com salvaguardas determinísticas, como lógica de retentativa e checkpoints regulares5 (note que “checkpoints” ali se refere ao tipo de checkpoint de recuperação, de salvar estado, do curso 9 desta série).

Segunda, uma classe de verificação pode subir para a camada da API. Adicione strict: true às suas definições de ferramenta para garantir que as chamadas de ferramenta do Claude sempre batam exatamente com o seu schema6. O schema é a sua declaração da estrutura dos parâmetros.

Com essa única linha, questões estruturais como “typo no nome do campo” ou “count passado como string” saem de “algo para o qual você escreve código de verificação” e viram uma garantia da plataforma. Ferramentas são um contrato entre sistemas determinísticos e agentes não determinísticos2, e strict é como você escreve esse contrato dentro da interface.

Mas ele policia estrutura, não semântica. Se total de fato é igual à soma de todos os valores de count, o schema não tem nada a dizer — essa parte você continua verificando por conta própria.

A armadilha: verificadores estritos demais rejeitam saídas corretas

Esta é a forma mais comum de as verificações determinísticas falharem, e muitas vezes você não percebe quando acontece.

A orientação oficial para evals de ferramentas é afiada: evite verificadores excessivamente estritos que rejeitam respostas corretas por diferenças espúrias como formatação, pontuação ou formulações alternativas válidas2.

“Diferenças espúrias” é a expressão-chave. A mesma resposta correta pode carregar um espaço no fim, pode escrever positive como Positive, pode ter dois espaços entre palavras em vez de um. Essas diferenças não significam nada para a tarefa, mas para um verificador de comparação byte a byte são catastróficas. A direção da falha também é insidiosa: ela não deixa erros passarem, ela pune saídas corretas — isso é um falso negativo.

Aqui vai uma quebra concreta com sua correção. O título do fixture é Resumo de canais Q1 2026. O report.json do agente tem todos os dados corretos, só com espaço em branco antes e depois do título e dois espaços a mais entre palavras. O verificador estrito ingênuo é assim:

Rode; a saída real:

text
$ node strict.mjs spacey.jsonFAIL: title não bate, obtido "  Resumo de canais   Q1 2026\n"

Um relatório com conteúdo inteiramente correto, reprovado por uma quebra de linha no fim. Se esse resultado voltar para o agente, ele vai ficar mexendo nos espaços do título — uma direção sem relação com a tarefa.

A correção é uma função:

replace(/\s+/g, " ") funde espaços em branco consecutivos em um só, trim() remove os das pontas, toLowerCase() uniformiza a caixa. Depois da mudança, o mesmo arquivo passa — o script completo e os resultados reais de execução estão no Nível 2 dos exercícios, mais abaixo.

Lembrete do outro lado: normalização não é uma coisa de “quanto mais melhor”. Se você também remover a pontuação, pode acabar suavizando erros reais, como total: 48 contra total: 4.8. O padrão de julgamento é sempre o mesmo — esta diferença carrega significado? Se sim, seja estrito. Se não, normalize.

Onde as verificações determinísticas encontram seu teto

Verificadores determinísticos têm uma fronteira de aplicabilidade bem definida.

A primeira fronteira é texto livre. Saídas de pesquisa são difíceis de avaliar programaticamente, já que são texto de forma livre e raramente têm uma única resposta correta5. Você não consegue escrever output == golden_answer para um resumo — dado o mesmo material, dois resumos bem escritos podem usar formulações completamente diferentes. Esse tipo de julgamento vai para o domínio da Lição 4.

A segunda fronteira é “se encaixa nos requisitos mais amplos do sistema”. Soluções de código são verificáveis por testes automatizados, mas para garantir que as soluções se alinhem com requisitos mais amplos do sistema, a revisão humana continua sendo crucial4. Um patch pode passar em todos os testes e ainda assim ser um desenho ruim que torna o módulo inteiro impossível de manter. O retrospecto multiagente ecoa isso: mesmo em um mundo de avaliações automatizadas, o teste manual continua essencial5.

As verificações determinísticas são donas do piso de “o que não deveria estar errado não está errado”. Acima desse piso, você precisa de ferramentas diferentes.

Proporção: nem tudo merece um verificador

O erro oposto também é comum: construir uma suíte completa de verificação para um script de uso único.

O agente escreve um script de migração de dados que roda uma vez e depois é apagado, e você monta validação de estrutura, comparação com fixture, amostras de regressão — o tempo gasto escrevendo o verificador ultrapassa o tempo de simplesmente bater o olho na saída.

A régua de julgamento continua sendo aquela velha frase: você deveria considerar adicionar complexidade apenas quando ela comprovadamente melhora os resultados4. Especificamente para verificadores, faça a si mesmo uma pergunta:

  • Quantas vezes esta verificação vai rodar? Se for uma só e você estiver ali olhando, os seus olhos podem ser mais rápidos.
  • Sem ela, quanto tempo até os erros serem descobertos? “Na hora, estou olhando bem para a saída” contra “quando algo lá na frente quebrar, dois dias depois” levam a conclusões totalmente diferentes.
  • Quantas iterações de prompt você planeja para esta tarefa? Assim que passa de uma, você precisa de uma régua estável para comparar antes e depois, ou “isso melhorou?” será sempre um chute.

Quanto a quando você tem obrigação de escrever um, a orientação oficial é dura — sempre forneça verificação (testes, scripts, capturas de tela); se você não consegue verificar, não publique3.

💻 Exercícios

Recapitulação

  • O princípio de ordenação dos métodos de correção é “mais rápido, mais confiável, mais escalável”; a correção baseada em código fica em primeiro nos três, e o custo é lhe faltar nuance para julgamentos complexos que precisam de menos rigidez baseada em regras1.
  • Uma verificação pode ser qualquer coisa que retorne um sinal que o Claude consiga ler na conversa: uma suíte de testes, o código de saída de um build, um linter, um script que compara a saída com um fixture, ou uma captura de tela comparada com um design3.
  • Verificadores formam um espectro; a ponta esquerda é a correspondência exata de string com um fixture, a ponta direita é pedir ao Claude que julgue2; a forma mínima é só output == golden_answer, normalmente depois de normalizar espaços em branco e caixa, perfeita para tarefas com respostas categóricas e bem delimitadas1.
  • Com passa/falha, o loop se fecha sozinho: faça o trabalho, rode a verificação, leia o resultado, itere até passar3; isso porque agentes precisam de ground truth do ambiente a cada etapa para avaliar o progresso4, e é por isso que eles conseguem iterar usando resultados de testes como feedback4.
  • Fluxos complexos podem ser quebrados em checkpoints de verificação discretos onde mudanças de estado específicas deveriam ter ocorrido, em vez de validar cada etapa intermediária5; uma classe de validação de estrutura pode até subir para a camada da API com strict: true, para fazer as chamadas de ferramenta obedecerem estritamente ao schema6.
  • A maior armadilha são os verificadores estritos demais: eles rejeitam respostas corretas por diferenças espúrias como formatação, pontuação ou formulações alternativas válidas2. A correção é normalizar primeiro e depois comparar, guardando o rigor para as partes que de fato carregam significado.
  • As verificações determinísticas têm um teto: saídas de pesquisa são difíceis de avaliar programaticamente5; os testes automatizados verificam a funcionalidade, mas para garantir que as soluções se alinhem com requisitos mais amplos do sistema a revisão humana continua sendo crucial4, e mesmo com evals automatizados maduros o teste manual continua essencial5.
  • Não construa um verificador completo para um script de uso único — você deveria considerar adicionar complexidade apenas quando ela comprovadamente melhora os resultados4; mas na outra direção, se você não consegue verificar, não publique3.

>> Lição 4: LLM como juiz: rubricas, formatos e o que não deixar ele julgar

Footnotes

  1. Define success criteria and build evaluations — Claude API documentation — https://platform.claude.com/docs/en/test-and-evaluate/develop-tests 2 3 4 5 6 7 8 9

  2. Writing effective tools for agents — with agents — Anthropic Engineering — https://www.anthropic.com/engineering/writing-tools-for-agents 2 3 4 5 6

  3. Best practices for Claude Code — Claude Code official documentation — https://code.claude.com/docs/en/best-practices 2 3 4 5 6 7 8

  4. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2 3 4 5 6 7 8 9

  5. How we built our multi-agent research system — Anthropic Engineering — https://www.anthropic.com/engineering/multi-agent-research-system 2 3 4 5 6 7

  6. Tool use with Claude — Claude API documentation — https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview 2

Exercícios

01

Abaixo estão 6 saídas comuns de agentes. Para cada uma, responda: que verificador você usaria? Por que aquele?

Nível 1: escolha um verificador para 6 saídas (sem código)
  1. Um arquivo de configuração JSON (nome do serviço, porta, timeout)
  2. Um CSV ordenado por “valor decrescente”
  3. Um resumo em texto livre (300 palavras destiladas de um relatório longo)
  4. Um patch de código (corrigindo um bug conhecido)
  5. Uma lista de nomes de arquivo (o agente alega ter processado esses arquivos)
  6. Um rótulo de sentimento (produz positive / negative / neutral para um comentário)

Depois de responder às seis, responda a mais uma pergunta: quais dessas seis não dá para julgar puramente por verificações determinísticas? Para essas, que piso as verificações determinísticas ainda conseguem segurar? O que sobra para os métodos da Lição 4?

Critérios de conclusão · marcado localmente
02

Montagem da tarefa. O agente precisa produzir um report.json com estes requisitos:

Nível 2: escreva um `verify.mjs` de verdade
  • Tem title, do tipo string
  • Tem items, um array em que cada item tem name (string) e count (inteiro)
  • Tem total (inteiro), e total é igual à soma de todos os valores de count
  • title deveria bater com o título do fixture Resumo de canais Q1 2026

Escreva um script Node verify.mjs que faz três camadas de verificação: validação de estrutura, verificação de consistência de total, comparação de título depois de normalizar espaços em branco. O uso é node verify.mjs report.json, sai com 0 ao passar, sai com 1 ao falhar e imprime os motivos, um por linha.

Depois de escrevê-lo, crie três arquivos de amostra para validá-lo: um inteiramente correto, um em que total está genuinamente calculado errado, e um “todo o conteúdo correto, mas o título tem espaço em branco a mais”. O terceiro precisa passar — ele é a sua prova de que a normalização resgatou um falso negativo.

Critérios de conclusão · marcado localmente