Bloga dön

Modeller ve karşılaştırmalarKılavuz

İki LLM'i görevinizde adilce karşılaştırın: taraf değiştirme ve maliyet günlüğü

İki modeli kendi biletlerinizde kafa kafaya çalıştırın: eşit ayarlar, her çiftin her iki sırada değerlendirilmesi ve çalıştırmanın maliyetini veren bir calls.jsonl günlüğü.

Yazan: CheapTokens TeamYayınlanma tarihi: 7 dk okuma

Kapak: İki modeli adilce karşılaştırın, üç adımlı bir akışla: aynı istem, tarafları değiştir, her çağrıyı günlüğe kaydet
Bu sayfada
  1. Genel bir turnuvanın yapıp da ödünç alabileceğiniz şeyleri
  2. Görev: 30 destek bileti için yanıt taslakları hazırlamak
  3. Tarafları değiştirin: her çifti iki kez değerlendirin
  4. Her çağrıyı günlüğe kaydedin, sonra çalıştırmayı fiyatlandırın
  5. Tam çalıştırmayı başlatmadan önce
  6. Kendinizi kandırmadan sonucu okumak

Üç istemi iki modelden geçirdiniz ve ikincisi kazandı. Buna güvenmeden önce, hangisinin ikinci sırada listelendiğini kontrol edin. Adil bir karşılaştırma üç şey gerektirir: her iki model için aynı ayarlar, her çiftin her iki sırada da değerlendirilmesi ve çağrı başına token günlüğü, böylece tüm çalıştırmanın maliyetini bilirsiniz. Aşağıda, kendi görevinizde bu üçünü de yapan küçük bir Python betiği var.

Genel bir turnuvanın yapıp da ödünç alabileceğiniz şeyleri#

Dwarkesh Patel'in Clocktower Bench'i, dil modellerinin birbirine karşı Blood on the Clocktower sosyal çıkarım oyununu oynadığı resmi olmayan bir araştırma projesidir. Burada asıl mesele oyun değil; test düzeneği. Projenin Hakkında sayfasına göre (8 Kasım 2026'da kontrol edildi), herhangi bir ikili karşılaştırmanın yapması gereken birkaç şeyi yapıyor:

  • bir model tüm iyi koltukları, diğeri tüm kötü koltukları oynar ve her dağıtım taraflar değiştirilerek iki kez oynanır, böylece şanslı bir koltuk sonucu belirlemez;
  • tüm modeller aynı ayarları alır: aynı akıl yürütme çabası, tur başına 8.000 token sınırı ve konuşma için 150 kelime sınırı;
  • Hikâye Anlatıcı betiklenmiştir, yani hiçbir model oyuna hakemlik etmez;
  • her API çağrısı tokenları ve maliyetiyle birlikte calls.jsonl dosyasına yazılır. Sayfa, tek bir oyunun modellere bağlı olarak (OpenAI ve Anthropic API'lerinde) yaklaşık 2 ila 50 dolar arasında maliyetli olduğunu söylüyor.

Test düzeneği OpenAI ve Anthropic modelleri için kurulmuş ve sizin göreviniz muhtemelen bir masa oyunu değil. Ancak üç alışkanlık aktarılabilir: eşit ayarlar, değiştirilmiş taraflar ve maliyet günlüğü. Bu kılavuzun geri kalanı bunları sıradan bir şeye uyguluyor.

Her bilet her iki modele gider; hakem çifti iki kez, zıt sıralarda görür
Tek bir bilet için taraf değiştirme döngüsü

Görev: 30 destek bileti için yanıt taslakları hazırlamak#

Küçük bir destek masası işlettiğinizi ve DeepSeek-V4-Flash'in mi yoksa GLM-5.3-Flash'in mi daha iyi ilk taslaklar yazdığını bilmek istediğinizi varsayalım. Elinizde 30 gerçek bilet içeren bir tickets.jsonl dosyası var, satır başına bir JSON nesnesi: {"id": 1, "text": "..."}.

API, OpenAI uyumludur, bu nedenle resmi openai paketi CheapTokens ile https://api.cheaptokens.dev/v1 adresinde çalışır. Belgelerdeki tam model kimliklerini kullanın: deepseek-ai/DeepSeek-V4-Flash-0731 ve zai-org/GLM-5.3-Flash. Her iki model de hakem olarak üçüncü bir model alır, MiniMaxAI/MiniMax-M2.7, böylece hiçbir yarışmacı kendi işini notlandırmaz.

Günlüğü mümkün kılan kısımla başlayın: her çağrının içinden geçtiği tek bir fonksiyon.

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)

calls.jsonl içindeki alanlar bu betiğin seçtiği alanlardır; kullanım sayıları API yanıtının usage nesnesinden gelir. Her iki model de aynı sistem istemini, aynı temperature ve aynı max_tokens değerini alır. Bunlardan birini bir model için değiştirirseniz, modelleri değil ayarları karşılaştırıyorsunuz demektir.

Tarafları değiştirin: her çifti iki kez değerlendirin#

Şimdi hakem. Bir hakem modeli belirli bir konumdaki yanıta eğilim gösterebilir, bu nedenle tek bir karar kaliteyi konumla karıştırır. Clocktower hilesi buraya tam oturur: her çifti her iki rolde de gösterin. Bir yanıt, yalnızca hakem onu her iki sırada da seçerse bileti kazanır. Karar yanıtı değil konumu izlerse, bilet berabere kalır.

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)

İki ayrıntı kolayca gözden kaçar. Hakem, kararlarını daha istikrarlı tutmak için temperature=0 değerinde çalışır; her model bu ayarı dikkate almaz, bu yüzden birkaç çifti yeniden çalıştırıp kararları karşılaştırmadan onlara güvenmeyin. Karar yalnızca son bir Verdict: satırından okunur ve sınırlar cömerttir (taslaklar için 800, hakem için 1000), böylece yanıtlamadan önce akıl yürüten bir model düşüncesinin ortasında kesilmez; aşağıdaki tahmin sınırları değil, tipik boyutları kullanır. Ayrıştırılamayan bir yanıt None verir, bu da çalıştırmayı çökertmek yerine beraberliğe düşer.

Kendin deneDeneyin: hakem 7. bilet için her iki sırada da "A" dedi. O bileti kim kazanır?Cevabı göster

Hiç kimse. İlk sırada A, DeepSeek'in yanıtıydı; ikinci sırada A, GLM'in yanıtıydı, yani hakem modeli değil konumu tercih etti. Kod bunu beraberlik olarak sayar. Bu tür beraberliklerin birikmesi kendi başına bir sonuçtur: iki model görevinizde birbirine yakın ya da hakem isteminiz çok belirsiz.

Sıra, bir testi saptırmanın yalnızca bir yoludur. Ayarlar, istemler ve günlükleme de saptırabilir, bu yüzden yalnızca kimin önce gittiğini değil, tüm kurulumu kontrol edin. İşte aynı deney, öncesi ve sonrası.

Karşılaştır

Gelişigüzel karşılaştırmayı bıraktığınızda ne değişir

Gelişigüzel

Model başına bir istem, varsayılanlar açık bırakılmış, çift tek bir sırada gösterilmiş ve fatura ay sonunda panodan okunmuş. Kazanan, ilk sırada listelenen model olabilir.

Adil

Tek bir paylaşılan sistem istemi ve aynı temperature ve max_tokens, her çift her iki sırada değerlendirilmiş ve her çağrı için calls.jsonl dosyasına bir satır. Kazanan, her iki koltuktan da kazanandır.

Her çağrıyı günlüğe kaydedin, sonra çalıştırmayı fiyatlandırın#

Günlük, karşılaştırmayı bütçeye dönüştüren şeydir. Çalıştırmadan sonra model başına tokenları toplayın:

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")

Model başına çağrı sayısı, giriş tokenları ve çıkış tokenları içeren bir satır alırsınız. Modelin iki fiyatıyla (giriş ve çıkış ayrı faturalandırılır) çarpın ya da daha kolayı, toplamları canlı bir maliyet göstergesine yapıştırın.

Çalıştırmadan önce de tahmin edebilirsiniz. Her biletin artı sistem isteminin yaklaşık 300 giriş tokenı olduğunu, bir taslağın yaklaşık 250 çıkış tokenı olduğunu ve hakemin yaklaşık 800 giriş tokenı (bilet artı her iki yanıt artı soru) gördüğünü ve yaklaşık 200 tokenla (karar satırı artı düşünmek için biraz pay) yanıt verdiğini varsayalım:

  • taslaklar: 30 bilet × 2 model = 60 çağrı, yarışmacı başına 30 × 300 = 9000 giriş ve 30 × 250 = 7500 çıkış tokenı;
  • kararlar: 30 bilet × 2 sıra = 60 çağrı, hakemde 60 × 800 = 48000 giriş ve 60 × 200 = 12000 çıkış tokenı.

Mevcut fiyatlarla, bir yarışmacının 30 taslağı DeepSeek-V4-Flash'te $0.00089 ve GLM-5.3-Flash'te $0.00048 tutar. MiniMax-M2.7'deki 60 karar $0.0042 tutar. Üçünü toplayın ve tek bir istek göndermeden önce tüm karşılaştırmanın fiyatını elde edin. Üç modelin tamamında aynı taslak iş yükü, giriş ve çıkış birlikte sayılmış:

Model Maliyet OpenRouter
GLM-5.3-Flash $0.00048 $0.00092
DeepSeek-V4-Flash $0.00089 $0.0017
MiniMax-M2.7 $0.0016 $0.0189

Toplamda 9.000 giriş ve 7.500 çıkış token'ı. 1M token başına fiyatlar günceldir ve tarifelerimizle birlikte değişir; OpenRouter sütunu aynı iş yükü için referans fiyatları gösterir.

Tablo ayrıca OpenRouter referansını gösterir, böylece toplamın nasıl karşılaştırıldığını görebilirsiniz. Token varsayımları sizin değiştirebileceğiniz şeylerdir: biletleriniz uzunsa, hakem girişi en hızlı büyür çünkü her iki yanıtı iki kez taşır. Hakeminiz yanıtlamadan önce sesli düşünürse, çıkış sayısı da büyür ve günlüğünüz bunu gösterir. Bir istekten önce token sayma alışkanlığı için bir istek göndermeden önce tokenları sayın bölümüne bakın.

Clocktower'ın kullandığı aynı sınırı ödünç almaya değer. Tur başına 8.000 token belirliyor ve sizde zaten max_tokens var. Bunu SETTINGS içine koyun ve kontrolden çıkan bir yanıt tahmininizi sessizce çoğaltamaz.

Tam çalıştırmayı başlatmadan önce#

Kontrol listesi

Adil bir karşılaştırma için ön kontrol

Son madde önemlidir. Üç bilet çalıştırın, calls.jsonl dosyasını açın ve okuyun. finish değerinin length yerine stop dediğini kontrol edin; bir taslak max_tokens tarafından kesildiyse, bir model yalnızca yanıtı kesildiği için kaybediyor olabilir. Aynısı hakemin çağrıları için de geçerlidir: oradaki bir length sessiz bir beraberliğe dönüşür, bu yüzden None kararlarını da sayın. Görevinizin tek bir doğru yanıtı varsa, bir hakeme hiç ihtiyacınız olmayabilir: isteminizi tam eşleşme notlandırmasıyla 20 vakada test edin.

Kendinizi kandırmadan sonucu okumak#

wins içindeki üç sayıya bakın. Otuz bilet küçük bir örnektir: 12'ye 9 ve 9 beraberlik gibi bir bölünme, modellerin birbirine yakın olduğunu söyler, birinin daha iyi olduğunu değil. Günlük burada yardımcı olur. Bir model az farkla kazanıp çok daha fazla çıkış kullanıyorsa, asıl soru kazanma sayısı değil görevin fiyatıdır; 1M token başına fiyat ile görev başına maliyet bu karşılaştırmayı adım adım anlatır.

Sonuç yakınsa, görev için daha ucuz modeli seçin ya da daha fazla bilet ekleyip yeniden çalıştırın. Her ikisini denemek de ucuzdur çünkü tüm mekanizma zaten bir döngüdür. Alıştırmayı başka bir modelde tekrarlamak istediğinizde, yukarıdaki üç model tek bir API ve tek bir anahtarın arkasındadır: belgelerdeki kurulum her biri için aynıdır ve fiyatlandırma sayfası her birinin şu anda ne kadar tuttuğunu listeler. Dwarkesh Patel'in OpenAI ile Anthropic modelleri üzerine kendi bulgusu (tespit ile aldatma) onun oyunu için onun sonucudur; sizinki biletlerinizden gelecek.