Compare dois LLMs de forma justa na sua tarefa: troca de lados e log de custos
Faça um confronto direto de dois modelos nos seus próprios tickets: configurações iguais, cada par julgado nas duas ordens, e um log calls.jsonl que dá o custo da execução.

Nesta página
Você rodou três prompts por dois modelos, e o segundo venceu. Antes de confiar nisso, verifique qual deles foi listado em segundo. Uma comparação justa precisa de três coisas: configurações idênticas para ambos os modelos, cada par julgado nas duas ordens, e um log de tokens por chamada, para você saber quanto custou a execução inteira. Abaixo está um pequeno script Python que faz as três coisas na sua própria tarefa.
O que um torneio público faz que você pode aproveitar#
O Clocktower Bench de Dwarkesh Patel é um projeto de pesquisa não oficial em que modelos de linguagem jogam o jogo de dedução social Blood on the Clocktower uns contra os outros. O jogo não é o ponto aqui; o harness é. Segundo a página About do projeto (verificada em 8 de novembro de 2026), ele faz algumas coisas que qualquer confronto direto deveria fazer:
- um modelo ocupa todos os assentos do bem e outro todos os assentos do mal, e cada distribuição é jogada duas vezes com os lados trocados, para que um assento sortudo não decida o resultado;
- todos os modelos recebem as mesmas configurações: o mesmo esforço de raciocínio, um teto de 8.000 tokens por turno e um limite de 150 palavras na fala;
- o Storyteller é roteirizado, então nenhum modelo arbitra o jogo;
- cada chamada de API é gravada em
calls.jsonlcom seus tokens e custo. A página diz que um único jogo custa de cerca de $2 a $50, dependendo dos modelos (nas APIs da OpenAI e da Anthropic).
O harness é feito para modelos da OpenAI e da Anthropic, e sua tarefa provavelmente não é um jogo de tabuleiro. Mas os três hábitos são transferíveis: configurações iguais, lados trocados e um log de custos. O restante deste guia os aplica a algo comum.

A tarefa: redigir respostas para 30 tickets de suporte#
Digamos que você administra um pequeno suporte e quer saber se o DeepSeek-V4-Flash ou o GLM-5.3-Flash escreve melhores primeiros rascunhos. Você tem um arquivo tickets.jsonl com 30 tickets reais, um objeto JSON por linha: {"id": 1, "text": "..
A API é compatível com OpenAI, então o pacote oficial openai funciona com a CheapTokens em https:/. Use os ids completos dos modelos da documentação: deepseek- e zai-. Ambos os modelos também recebem um terceiro modelo como juiz, MiniMaxAI/, para que nenhum concorrente avalie o próprio trabalho.
Comece pela parte que torna o log possível: uma função pela qual toda chamada passa.
import json, os, re
from openai import OpenAI
client = OpenAI(base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"])
CONTESTANTS = {"deepseek": "deepseek-ai/DeepSeek-V4-Flash-0731",
"glm": "zai-org/GLM-5.3-Flash"}
JUDGE = "MiniMaxAI/MiniMax-M2.7"
SETTINGS = {"temperature": 0.2, "max_tokens": 800} # same for both
def call(model, messages, role, ticket_id, **settings):
r = client.chat.completions.create(model=model, messages=messages, **settings)
with open("calls.jsonl", "a", encoding="utf-8") as f:
f.write(json.dumps({
"role": role, "ticket": ticket_id, "model": model,
"input_tokens": r.usage.prompt_tokens,
"output_tokens": r.usage.completion_tokens,
"finish": r.choices[0].finish_reason}) + "\n")
return r.choices[0].message.content or ""
def draft(model, ticket):
msgs = [{"role": "system", "content": "You answer support tickets. "
"Reply in under 80 words, politely, with no promises about refunds."},
{"role": "user", "content": ticket["text"]}]
return call(model, msgs, "draft", ticket["id"], **SETTINGS)Os campos em calls.jsonl são os que este script escolhe; os números de uso vêm do objeto usage da resposta da API. Ambos os modelos recebem o mesmo system prompt, o mesmo temperature e o mesmo max_tokens. Mude um deles para um modelo e você estará comparando configurações, não modelos.
Troque os lados: julgue cada par duas vezes#
Agora o juiz. Um modelo juiz pode tender para a resposta em um determinado slot, então um único veredito mistura qualidade com posição. O truque do Clocktower se encaixa perfeitamente: mostre cada par nos dois papéis. Uma resposta ganha um ticket apenas se o juiz a escolher nas duas ordens. Se o veredito seguir o slot em vez da resposta, o ticket é um empate.
def judge(ticket, first, second):
prompt = (f"Ticket:\n{ticket['text']}\n\nReply A:\n{first}\n\nReply B:\n{second}\n\n"
"Which reply is better for the customer? Finish with a last line: Verdict: A or Verdict: B.")
out = call(JUDGE, [{"role": "user", "content": prompt}], "judge",
ticket["id"], temperature=0, max_tokens=1000)
letters = re.findall(r"Verdict:\s*([AB])\b", out)
return letters[-1] if letters else None
tickets = [json.loads(l) for l in open("tickets.jsonl", encoding="utf-8")]
wins = {"deepseek": 0, "glm": 0, "tie": 0}
for t in tickets:
a = draft(CONTESTANTS["deepseek"], t)
b = draft(CONTESTANTS["glm"], t)
v1 = judge(t, a, b) # DeepSeek's reply shown as A
v2 = judge(t, b, a) # GLM's reply shown as A
pick1 = {"A": "deepseek", "B": "glm"}.get(v1)
pick2 = {"A": "glm", "B": "deepseek"}.get(v2)
wins[pick1 if pick1 and pick1 == pick2 else "tie"] += 1
print(wins)Dois detalhes são fáceis de passar despercebidos. O juiz roda em temperature=0 para manter seus vereditos mais estáveis; nem todo modelo respeita essa configuração, então rode novamente alguns pares e compare os vereditos antes de confiar neles. O veredito é lido apenas de uma linha final Verdict:, e os limites são generosos (800 para rascunhos, 1000 para o juiz) para que um modelo que raciocina antes de responder não seja cortado no meio do pensamento; a estimativa abaixo usa tamanhos típicos, não os limites. Uma resposta não interpretável gera None, que cai em um empate em vez de travar a execução.
Experimente você mesmoExperimente: o juiz disse "A" nas duas ordens para o ticket 7. Quem ganha esse ticket?Mostrar resposta
Ninguém. Na primeira ordem, A era a resposta do DeepSeek; na segunda ordem, A era a resposta do GLM, então o juiz preferiu o slot, não um modelo. O código conta isso como empate. Uma pilha desses empates é, por si só, um resultado: os dois modelos estão próximos na sua tarefa, ou seu prompt de juiz é vago demais.
A ordem é apenas uma forma de enviesar um teste. Configurações, prompts e logging também podem enviesá-lo, então verifique a configuração inteira, não apenas quem vem primeiro. Aqui está o mesmo experimento antes e depois.
Comparar
O que muda quando você deixa de comparar de forma casual
Casual
Um prompt por modelo, padrões mantidos, o par mostrado em uma única ordem, e a conta lida no dashboard no fim do mês. O vencedor pode ser o modelo que por acaso foi listado primeiro.
Justo
Um system prompt compartilhado e o mesmo temperature e max_tokens, cada par julgado nas duas ordens, e uma linha em calls.jsonl para cada chamada. O vencedor é quem vence dos dois assentos.
Registre cada chamada, depois precifique a execução#
O log é o que transforma a comparação em um orçamento. Após a execução, some os tokens por modelo:
from collections import defaultdict
totals = defaultdict(lambda: [0, 0, 0])
for line in open("calls.jsonl", encoding="utf-8"):
c = json.loads(line)
s = totals[c["model"]]
s[0] += c["input_tokens"]; s[1] += c["output_tokens"]; s[2] += 1
for model, (tin, tout, n) in totals.items():
print(model, n, "calls", tin, "input", tout, "output")Você terá uma linha por modelo com contagem de chamadas, tokens de entrada e tokens de saída. Multiplique pelos dois preços do modelo (entrada e saída são cobrados separadamente) ou, mais fácil, cole os totais em um marcador de custo ao vivo.
Você também pode estimar antes de rodar. Suponha que cada ticket mais o system prompt tenha cerca de 300 tokens de entrada, um rascunho tenha cerca de 250 tokens de saída, e o juiz veja aproximadamente 800 tokens de entrada (ticket mais ambas as respostas mais a pergunta) e responda com cerca de 200 tokens (a linha de veredito mais um pouco de espaço para pensar):
- rascunhos: 30 tickets × 2 modelos = 60 chamadas, 30 × 300 = 9000 de entrada e 30 × 250 = 7500 tokens de saída por concorrente;
- vereditos: 30 tickets × 2 ordens = 60 chamadas, 60 × 800 = 48000 de entrada e 60 × 200 = 12000 tokens de saída no juiz.
Nos preços atuais, os 30 rascunhos de um concorrente custam $0.00089 no DeepSeek-V4-Flash e $0.00048 no GLM-5.3-Flash. Os 60 vereditos no MiniMax-M2.7 custam $0.0042. Some os três e você tem o preço da comparação inteira antes de enviar uma única requisição. A mesma carga de trabalho de redação em todos os três modelos, com entrada e saída contadas juntas:
| Modelo | Entrada $/1M | Saída $/1M | Custo | OpenRouter |
|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.00048 | $0.00092 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.00089 | $0.0017 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.0016 | $0.0189 |
9.000 tokens de entrada e 7.500 tokens de saída no total. Os preços por 1M de tokens são atuais e mudam junto com nossas tarifas; a coluna OpenRouter mostra preços de referência para a mesma carga de trabalho.
A tabela também mostra a referência do OpenRouter, para você ver como o total se compara. As suposições de tokens são suas para mudar: se seus tickets forem longos, a entrada do juiz cresce mais rápido, porque carrega ambas as respostas duas vezes. Se seu juiz pensa em voz alta antes de responder, o número de saída dele também cresce, e seu log vai mostrar isso. Para o hábito de contar tokens antes de uma requisição, veja conte tokens antes de enviar uma requisição.
O mesmo limite que o Clocktower usa vale a pena copiar. Ele define 8.000 tokens por turno, e você já tem max_tokens. Coloque-o em SETTINGS e uma resposta descontrolada não pode multiplicar silenciosamente sua estimativa.
Antes de lançar a execução completa#
Lista de verificação
Pré-voo para uma comparação justa
Esse último item importa. Rode três tickets, abra calls.jsonl e leia. Verifique se finish diz stop em vez de length; se um rascunho foi cortado por max_tokens, um modelo pode estar perdendo apenas porque sua resposta foi truncada. O mesmo vale para as chamadas do juiz: um length ali vira um empate silencioso, então conte também os vereditos None. Se sua tarefa tem uma única resposta certa, você pode nem precisar de um juiz: teste seu prompt em 20 casos com avaliação por correspondência exata.
Lendo o resultado sem se enganar#
Olhe os três números em wins. Trinta tickets é uma amostra pequena: uma divisão como 12 a 9 com 9 empates diz que os modelos estão próximos, não que um é melhor. O log ajuda aqui. Se um modelo vence por pouco mas usa muito mais saída, a verdadeira questão é o preço da tarefa, não a contagem de vitórias; preço por 1M de tokens versus custo por tarefa percorre essa comparação.
Se o resultado for próximo, escolha o modelo mais barato para a tarefa, ou adicione mais tickets e rode novamente. Ambos são baratos de tentar porque toda a maquinaria já é um loop. Quando quiser repetir o exercício em outro modelo, os três modelos acima ficam atrás de uma API e uma chave: a configuração na documentação é a mesma para cada um, e a página de preços lista quanto cada um custa agora. A própria descoberta de Dwarkesh Patel sobre modelos da OpenAI versus Anthropic (detecção versus engano) é o resultado dele para o jogo dele; o seu virá dos seus tickets.



