Nota · quality-gate
Sonnet superou o Opus nos meus evals, com menos da metade do custo
Adicionei model evals ao meu quality gate: contenders, uma tabela de preços em US$ por 1M de tokens, datasets de prompt e de código e três de segurança. O Sonnet 5.5 passou em 30 de 30 tarefas médias onde o Opus 5.5 passou em 27, e uma rubrica minha estava errada.
O Sonnet 5.5 passou em 30 de 30 tentativas nas minhas tarefas de segurança de nível médio. O Opus 5.5 passou em 27 e custou US$ 0,077 por tarefa, contra US$ 0,034.
Os dois números saíram do leaderboard que adicionei ao quality gate esta semana. Ele ranqueia modelos em tarefas que escrevi a partir dos meus próprios projetos e coloca preço em cada tentativa, então "qual eu devo usar" deixa de ser achismo.

Por que um gate ganha um leaderboard
O quality gate avalia o que um agente de IA entregou contra o ticket. Depois de algumas semanas usando, comecei a fazer outra pergunta: se eu trocar o modelo por trás do agente, a nota muda, e quanto isso me custa? Benchmark público não responde, porque ele não usa meus repositórios, minhas convenções nem meus hábitos.
Então o gate ganhou uma área de Model evals. Ela roda um conjunto de setups de agente contra um conjunto de tarefas, várias vezes cada, e ranqueia.
Um contender é mais que um modelo
O que entra no ranking é um contender: um harness (Claude Code ou Codex), um modelo e as configurações dele. Dois contenders que diferem só no modelo comparam modelos. Dois que diferem só no harness comparam ferramentas.

As configurações são a parte que mais uso. Um contender pode ter o próprio system prompt, ligar as skills e ligar o "use my setup", que entrega a ele minhas instruções globais, hooks e skills. Tudo vem desligado. O agente roda num diretório de config limpo, com só as credenciais copiadas, porque uma comparação em que um lado carrega o meu setup pessoal não é comparação. Eu queria que o próprio setup fosse testável, então ele vira mais um contender: "Sonnet 5.5, skills e meu setup ligados" aparece na tabela ao lado do Sonnet puro.
O tier é a outra configuração. O Codex tem um tier priority que cobra o dobro da tarifa padrão, e um contender pode ficar fixado nele.
O dinheiro vem de uma tabela de preços em US$ por 1M de tokens, com input, output, cache read e cache write de cada modelo, mais as tarifas priority quando o modelo tem. O Opus 5.5 custa US$ 4 de entrada e US$ 20 de saída, o Haiku 5.5 custa US$ 0,10 e US$ 0,50. Cada tentativa guarda os tokens que usou e as tarifas com que foi cobrada, então o custo é o preço equivalente de API mesmo quando o agente rodou numa assinatura. Na assinatura o custo real é zero, e um ranking por custo não serviria para nada.

Dois tipos de tarefa
Hoje são 7 datasets e 56 tarefas, de dois tipos.
Uma tarefa de código é um repositório, um commit de partida e um ticket. O agente trabalha num clone sem nenhum remote, e o gate avalia a entrega do mesmo jeito que avalia qualquer outra. Elas vêm de commits reais meus que se sustentaram.
Uma tarefa de prompt não tem repositório nem ferramentas. É uma pergunta com gabarito, e o gabarito vem de três formas: uma rubrica aplicada por um juiz fixo (Haiku 5.5, aprova com 70), uma resposta exata ou por regex, ou um script que executa o código da resposta. Cada tarefa tem limite de tempo e de dinheiro. Uma tentativa que bate em qualquer um dos dois é marcada como timeout ou estouro de orçamento, nunca como falha de qualidade, porque uma tentativa interrompida não diz nada sobre o quão bom é o modelo.

Cada tarefa roda k vezes, 3 por padrão. Uma resposta de sorte não deve ranquear ninguém. O leaderboard ordena por taxa de aprovação, depois por nota média, e mostra a consistência ao lado: o quanto a nota de uma tarefa se mexe entre as repetições.
Uma confissão: os datasets de código quase não rodaram. A única tentativa registrada é uma do Haiku numa tarefa de backend, nota 94,8, US$ 0,023 e 2m 16s. Tentativas de código instalam, compilam e testam um checkout inteiro, e eu ainda não rodei um dataset de código completo. Tudo abaixo é de tarefas de prompt.
Tarefas de segurança com uma armadilha em cada
Escrevi três datasets de segurança, fácil com 10 tarefas, médio com 10 e difícil com 8, todos em código sintético de Node e Supabase. A maioria é avaliada por scripts que executam a resposta contra payloads de ataque, em vez de um juiz, então o modelo não consegue chegar ao pass na conversa.
No médio, cada tarefa planta uma isca segura ao lado do furo de verdade: uma checagem de ID que parece faltar mas está coberta por um filtro confiável, injeção em LIKE, ORDER BY e LIMIT, um zip que escapa por um symlink, um SSRF por redirecionamento. O difícil vai atrás de SQL injection de segunda ordem, uma chave de cache que perde o tenant, uma corrida de saldo que precisa ser testada com duas instâncias do módulo e uma prompt injection dividida ao longo de um diff.
Cada check precisou falhar no código vulnerável e nas correções pela metade, e passar numa correção de referência, antes de eu confiar nele. As execuções de prova passaram 42 de 42 no médio e 27 de 27 no difícil.

A rubrica que estava errada
O dataset fácil tem uma tarefa de row-level security do Supabase. Minha primeira rubrica listava quatro falhas, e uma delas era uma policy de UPDATE com USING e sem WITH CHECK, na teoria de que o dono poderia reescrever o id do usuário e entregar a linha para outra pessoa.
Está errado. Quando uma policy de UPDATE não tem WITH CHECK, o Postgres aplica a expressão do USING também à linha nova. A policy é segura. Respostas que contestavam a minha afirmação estavam sendo penalizadas.
A rubrica agora diz que a policy é segura, dá nota máxima para quem adiciona um WITH CHECK explícito mesmo assim e tira 10 pontos de quem afirma que o furo existe. Não refiz a medição das execuções antigas. A versão de um dataset é um hash do conteúdo, então uma correção gera uma versão nova e o ranking nunca mistura execuções de antes e de depois.

O que os primeiros números dizem
No médio, o Sonnet 5.5 passou 100% a US$ 0,034 por tarefa e 19s, o Opus 5.5 90% a US$ 0,077 e 25s, e o Codex com gpt-6-luna 70% a US$ 0,003 e 8,4s. A dispersão do Opus foi ±0,0: cada tarefa teve a mesma nota nas três repetições, então ele errou uma tarefa três vezes em três.
No difícil, Sonnet e Opus empatam em 92% de aprovação, com o Sonnet na frente na nota média (95,4 contra 93,1) e mais estável (±1,9 contra ±7,3). A tarefa de row-level security é onde todo mundo cai: na execução maior, cada um dos três contenders Claude puros passou nela uma vez em três tentativas.
Os setups não se pagaram. O Sonnet com skills e meu setup passou 87% em 15 tentativas e custou US$ 0,051 por tarefa, contra 92% e US$ 0,035 do Sonnet puro. Cuidado com essa comparação: os contenders com setup só rodaram 5 das 8 tarefas difíceis, e k é 3. Trato isso como motivo para olhar de novo, não como resultado.
O Luna é a ponta barata do gráfico. O leaderboard mostra tokens de saída no lugar de passos nos datasets de prompt, já que sem ferramentas não há passos, e o Luna escreve 583 tokens onde o Haiku escreve 4,6k.

Quantas tentativas ao mesmo tempo
O formulário de execução tem um campo de concorrência, de 1 a 8, e um padrão que depende do dataset: 2 para código, 4 para prompts. Uma tentativa de código roda o agente e depois a instalação, o build e os testes do gate no próprio checkout, o que pesa na CPU. Uma tentativa de prompt só espera a API. Passar de 2 ou 3 tentativas de código desacelera todas e distorce a coluna de tempo do ranking, então o formulário avisa isso ao lado do campo.

Coisas menores
- Duas execuções da mesma entrega agora podem ser comparadas: veredito e mudança de nota, cada dimensão, e os achados que apareceram, sumiram ou ficaram. Os achados são pareados por check, regra e arquivo, nunca por linha, então mover código não parece um problema novo.
- Uma execução que falhou pode ser rodada de novo na mesma branch e comparada com a anterior quando terminar.
- Uma execução reprovada ganha um prompt de correção: o que a entrega ainda precisa para passar, pronto para colar no agente.
- Um servidor MCP local deixa um agente pedir a nota ao gate enquanto ainda está trabalhando. Essas execuções aparecem na lista, mas ficam fora das baselines.
- Salvar um contender novo pelo dashboard respondia 400 até eu corrigir. Agora o servidor cria o id.
O que vem a seguir
A próxima rodada grande é um dataset de código, com os setups que perderam nos prompts. Se você ranqueia modelos no seu próprio trabalho: como você percebe que o gabarito é que está errado?
Algo a acrescentar, ou uma parte de que você discorda? Me responda.
Responder por e-mail