Voltar ao blog

Modelos e comparaçõesGuia

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.

Por CheapTokens TeamPublicado em 8 min de leitura

Capa: Compare dois modelos de forma justa, com um fluxo de três etapas: mesmo prompt, troque os lados, registre cada chamada
Nesta página
  1. O que um torneio público faz que você pode aproveitar
  2. A tarefa: redigir respostas para 30 tickets de suporte
  3. Troque os lados: julgue cada par duas vezes
  4. Registre cada chamada, depois precifique a execução
  5. Antes de lançar a execução completa
  6. Lendo o resultado sem se enganar

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.jsonl com 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.

Cada ticket vai para os dois modelos; o juiz vê o par duas vezes, em ordens opostas
O loop de troca de lados para um ticket

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://api.cheaptokens.dev/v1. Use os ids completos dos modelos da documentação: deepseek-ai/DeepSeek-V4-Flash-0731 e zai-org/GLM-5.3-Flash. Ambos os modelos também recebem um terceiro modelo como juiz, MiniMaxAI/MiniMax-M2.7, 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.

python
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.

python
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:

python
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 Custo OpenRouter
GLM-5.3-Flash $0.00048 $0.00092
DeepSeek-V4-Flash $0.00089 $0.0017
MiniMax-M2.7 $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.