chatcli eval é esse harness. Use-o para provar que uma mudança de prompt, modelo ou engine deixou o ChatCLI melhor, e não pior, e para comparar modelos e providers nas mesmas tarefas.
Evals gastam tokens de verdade: cada caso é uma chamada real ao modelo, e as checagens de juiz somam outras. Use
--max-cost para limitar uma execução e validate/list para conferir uma suíte sem gastar nada.Começo rápido
✗ testes passam: saiu com 1 (esperado 0): --- FAIL: TestSum ….
O repositório traz uma suíte inicial em evals/: idioma no chat, formato JSON, aritmética, não inventar uma flag, corrigir um bug em Go, criar um arquivo e responder sem mexer em arquivos.
Como uma tentativa roda
1
Sandbox
Um diretório temporário é criado. A
fixture do caso é copiada para ele, os files inline são escritos e os comandos de setup são executados (por exemplo git init). Se o setup falhar, a tentativa é marcada como erro e o candidato nem roda.2
Policy do coder
Em casos
coder, é gravado um coder_policy.json local (merge: true, ou seja, por cima da sua policy global). Por padrão ele libera o @coder, inclusive o exec, dentro do workspace descartável. Use policy: no caso para restringir. Operações safety-immune continuam pedindo aprovação, e como num eval não há ninguém para aprovar, elas são negadas. Não existe uma chave de “aprovar tudo” para isso.3
Candidato
O binário real roda
chatcli -p "<prompt>" --raw --no-anim (com /coder na frente nos casos coder), sem stdin e dentro do timeout do caso. Por padrão a execução é hermética: memória de longo prazo, bootstrap, recall de memória e de sessão, autosave de sessão, checkpoints do coder e histórico do REPL ficam desligados, então o resultado depende só da suíte. Com --with-memory, a avaliação usa a sua memória e o seu recall reais. Os seus hooks nunca disparam num candidato, com ou sem --with-memory: são efeitos colaterais na sua máquina, e um hook que roda um eval entraria em recursão.4
Registro
Pelo
CHATCLI_EVAL_RECORD, o one-shot informa a resposta final, as tool calls, os turnos, tokens, custo e transcript num arquivo fora do sandbox, que o candidato não consegue ler nem alterar.5
Avaliação
Cada checagem roda sobre a resposta e sobre o sandbox. Comandos como
go test ./... rodam dentro dele. A tentativa só passa se todas as checagens passarem, e a nota dela é a média ponderada das notas das checagens.6
Limpeza
O sandbox é apagado. Com
--keep ele fica no disco, e o relatório guarda o caminho.Formato da suíte
A suíte é um arquivo YAML. Passando um diretório, são carregados todos os*.yaml / *.yml que estão diretamente nele. Subdiretórios não são varridos, então as fixtures podem ter YAML próprio. As chaves são estritas: uma chave escrita errada é erro, e nunca vira uma checagem ignorada em silêncio.
Checagens
As checagens determinísticas vêm primeiro: são baratas, reprodutíveis e não abrem margem para discussão. Use o juiz só para o que não tem teste determinístico.
Toda checagem aceita
name: (aparece nos relatórios) e weight: (o peso dela na nota da tentativa, padrão 1). Os caminhos nas checagens e em files precisam ser relativos e ficar dentro do sandbox: caminhos absolutos e escapes com .. são recusados já ao carregar a suíte.
O juiz
O juiz recebe a tarefa, a rubrica, a resposta de referência (reference, opcional), as ferramentas que o candidato chamou e a resposta do candidato. Ele não fica sabendo qual modelo gerou a resposta. Responde com {"score": 0..1, "reasoning": "..."}, e a leitura é tolerante: aceita bloco de código e normaliza uma nota dada em escala de 0 a 10 ou de 0 a 100. Uma chamada ao juiz que falha ou não pode ser lida conta como amostra zero. Nunca vira aprovação silenciosa.
- Qual modelo julga:
--judge-provider/--judge-model; senão, o primeirojudge:declarado numa suíte; senão, o seu padrão configurado. - Autoavaliação é sinalizada: quando o juiz é o mesmo modelo do candidato, o relatório marca a execução como
self_judgede avisa. - O gasto do juiz entra no seu cost tracker real e aparece separado do gasto do candidato.
Tentativas, pass@k e pass^k
A saída de LLM varia, então um caso pode rodartrials vezes. Para cada caso o relatório traz:
O veredito do caso segue a
pass_policy. all (o padrão, pass^k) é rígido: um caso que passa duas vezes em três é instável e reprova, e o relatório mostra isso em vez de esconder. any e majority estão disponíveis por caso. Uma tentativa em que a execução quebrou ou estourou o tempo conta como erro, separado de reprovação, para que “o ChatCLI quebrou” nunca seja lido como “o ChatCLI errou”.
Baselines e o gate de CI
--out salva o relatório JSON. Uma execução posterior com --baseline compara caso a caso:
- Regressões: casos que passavam e agora não passam.
- Correções: casos que não passavam e agora passam.
- Casos novos e removidos.
- Taxa de aprovação, nota média e custo antes e depois, medidos só sobre os casos avaliados nas duas execuções. Assim, uma execução filtrada comparada com um baseline completo compara a mesma coisa.
--markdown grava o mesmo resultado em Markdown (tabela de resumo, delta contra o baseline e checagens que falharam), pronto para comentário de PR ou resumo de job de CI.
Referência de comandos
Ctrl+C para de agendar tentativas novas e encerra as que estão rodando. O relatório as marca como puladas.
CHATCLI_EVAL_RECORD é o contrato privado do harness com o binário que ele dirige: quando definida, uma execução one-shot grava o registro nesse caminho ao sair. Sem ela, que é o caso de toda execução que não vem do chatcli eval, o one-shot não grava nada.Evals e o pipeline de qualidade
Os padrões de qualidade mudam como o ChatCLI responde. Os evals mostram se a mudança valeu a pena. Para medir, por exemplo, se ligar o CoVe melhora um modelo nas suas tarefas, rode a mesma suíte duas vezes e compare:CHATCLI_* pode ser comparada assim, e o mesmo vale para o env: de um caso.