Modelle & VergleicheAnleitung
Vergleiche zwei LLMs fair auf deiner Aufgabe: Side-Swap und Kostenprotokoll
Führe einen Head-to-Head von zwei Modellen auf deinen eigenen Tickets durch: gleiche Einstellungen, jedes Paar in beiden Reihenfolgen bewertet und ein calls.jsonl-Protokoll, das die Kosten des Laufs liefert.

Auf dieser Seite
Du hast drei Prompts durch zwei Modelle laufen lassen, und das zweite hat gewonnen. Bevor du dem traust, prüfe, welches als zweites aufgeführt war. Ein fairer Vergleich braucht drei Dinge: identische Einstellungen für beide Modelle, jedes Paar in beiden Reihenfolgen bewertet und ein Protokoll der Tokens pro Aufruf, damit du weißt, was der ganze Lauf gekostet hat. Unten ist ein kleines Python-Skript, das alle drei auf deiner eigenen Aufgabe erledigt.
Was ein öffentliches Turnier macht, das du übernehmen kannst#
Dwarkesh Patels Clocktower Bench ist ein inoffizielles Forschungsprojekt, bei dem Sprachmodelle das Social-Deduction-Spiel Blood on the Clocktower gegeneinander spielen. Das Spiel ist hier nicht der Punkt; der Harness ist es. Laut der About-Seite des Projekts (geprüft am 8. November 2026) macht es ein paar Dinge, die jeder Head-to-Head tun sollte:
- ein Modell spielt alle guten Sitze und ein anderes alle bösen Sitze, und jede Runde wird zweimal mit getauschten Seiten gespielt, damit ein glücklicher Sitz nicht das Ergebnis entscheidet;
- alle Modelle bekommen die gleichen Einstellungen: die gleiche Reasoning-Anstrengung, eine Obergrenze von 8.000 Tokens pro Zug und ein Limit von 150 Wörtern für die Rede;
- der Storyteller ist geskriptet, sodass kein Modell das Spiel leitet;
- jeder API-Aufruf wird mit seinen Tokens und Kosten in
calls.jsonlgeschrieben. Die Seite sagt, dass ein einzelnes Spiel je nach Modellen etwa 2 bis 50 US-Dollar kostet (bei OpenAI- und Anthropic-APIs).
Der Harness ist für OpenAI- und Anthropic-Modelle gebaut, und deine Aufgabe ist wahrscheinlich kein Brettspiel. Aber die drei Gewohnheiten übertragen sich: gleiche Einstellungen, getauschte Seiten und ein Kostenprotokoll. Der Rest dieser Anleitung wendet sie auf etwas Alltägliches an.

Die Aufgabe: Antwortentwürfe für 30 Support-Tickets#
Angenommen, du betreibst einen kleinen Support-Desk und willst wissen, ob DeepSeek-V4-Flash oder GLM-5.3-Flash bessere erste Entwürfe schreibt. Du hast eine Datei tickets.jsonl mit 30 echten Tickets, ein JSON-Objekt pro Zeile: {"id": 1, "text": "..
Die API ist OpenAI-kompatibel, also funktioniert das offizielle openai-Paket mit CheapTokens unter https:/. Verwende die vollständigen Modell-IDs aus der Dokumentation: deepseek- und zai-. Beide Modelle bekommen außerdem ein drittes Modell als Judge, MiniMaxAI/, damit keiner der Kontrahenten seine eigene Arbeit bewertet.
Beginne mit dem Teil, der das Protokoll möglich macht: eine Funktion, durch die jeder Aufruf läuft.
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)Die Felder in calls.jsonl sind die, die dieses Skript wählt; die Usage-Zahlen kommen aus dem usage-Objekt der API-Antwort. Beide Modelle bekommen denselben System-Prompt, dieselbe temperature und dieselbe max_tokens. Ändere eines davon für ein Modell, und du vergleichst Einstellungen, nicht Modelle.
Tausche die Seiten: bewerte jedes Paar zweimal#
Jetzt der Judge. Ein Judge-Modell kann zur Antwort in einem bestimmten Slot neigen, sodass ein einzelnes Urteil Qualität mit Position vermischt. Der Clocktower-Trick passt gut: Zeige jedes Paar in beiden Rollen. Eine Antwort gewinnt ein Ticket nur, wenn der Judge sie in beiden Reihenfolgen wählt. Wenn das Urteil dem Slot statt der Antwort folgt, ist das Ticket ein Gleichstand.
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)Zwei Details sind leicht zu übersehen. Der Judge läuft mit temperature=0, um seine Urteile stabiler zu halten; nicht jedes Modell beachtet diese Einstellung, also führe ein paar Paare erneut aus und vergleiche die Urteile, bevor du ihnen traust. Das Urteil wird nur aus einer abschließenden Verdict:-Zeile gelesen, und die Obergrenzen sind großzügig (800 für Entwürfe, 1000 für den Judge), damit ein Modell, das vor der Antwort nachdenkt, nicht mitten im Gedanken abgeschnitten wird; die Schätzung unten verwendet typische Größen, nicht die Obergrenzen. Eine nicht parsbare Antwort ergibt None, was zu einem Gleichstand führt, statt den Lauf abstürzen zu lassen.
Probier es selbstProbier es aus: Der Judge sagte bei Ticket 7 in beiden Reihenfolgen "A". Wer gewinnt dieses Ticket?Antwort anzeigen
Niemand. In der ersten Reihenfolge war A die Antwort von DeepSeek, in der zweiten Reihenfolge war A die Antwort von GLM, also bevorzugte der Judge den Slot, nicht ein Modell. Der Code zählt es als Gleichstand. Ein Haufen solcher Gleichstände ist selbst ein Ergebnis: Die beiden Modelle liegen bei deiner Aufgabe nah beieinander, oder dein Judge-Prompt ist zu vage.
Die Reihenfolge ist nur eine Möglichkeit, einen Test zu verzerren. Einstellungen, Prompts und Logging können ihn ebenfalls verzerren, also prüfe das ganze Setup, nicht nur, wer zuerst kommt. Hier ist dasselbe Experiment vorher und nachher.
Vergleichen
Was sich ändert, wenn du aufhörst, beiläufig zu vergleichen
Beiläufig
Ein Prompt pro Modell, Standardeinstellungen belassen, das Paar in einer Reihenfolge gezeigt und die Rechnung am Monatsende vom Dashboard abgelesen. Der Gewinner ist möglicherweise das Modell, das zufällig zuerst aufgeführt war.
Fair
Ein gemeinsamer System-Prompt und dieselbe temperature und max_tokens, jedes Paar in beiden Reihenfolgen bewertet und eine Zeile in calls.jsonl für jeden Aufruf. Der Gewinner ist, wer von beiden Sitzen aus gewinnt.
Protokolliere jeden Aufruf, dann bepreise den Lauf#
Das Protokoll ist das, was den Vergleich in ein Budget verwandelt. Summiere nach dem Lauf die Tokens pro Modell:
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")Du erhältst eine Zeile pro Modell mit Aufrufanzahl, Input-Tokens und Output-Tokens. Multipliziere mit den beiden Preisen des Modells (Input und Output werden getrennt abgerechnet) oder, einfacher, füge die Summen in einen Live-Kostenmarker ein.
Du kannst auch vor dem Lauf schätzen. Nimm an, dass jedes Ticket plus System-Prompt etwa 300 Input-Tokens sind, ein Entwurf etwa 250 Output-Tokens, und der Judge sieht ungefähr 800 Input-Tokens (Ticket plus beide Antworten plus die Frage) und antwortet mit etwa 200 Tokens (die Urteilszeile plus etwas Raum zum Nachdenken):
- Entwürfe: 30 Tickets × 2 Modelle = 60 Aufrufe, 30 × 300 = 9000 Input- und 30 × 250 = 7500 Output-Tokens pro Kontrahent;
- Urteile: 30 Tickets × 2 Reihenfolgen = 60 Aufrufe, 60 × 800 = 48000 Input- und 60 × 200 = 12000 Output-Tokens beim Judge.
Zu aktuellen Preisen kosten die 30 Entwürfe eines Kontrahenten $0.00089 bei DeepSeek-V4-Flash und $0.00048 bei GLM-5.3-Flash. Die 60 Urteile bei MiniMax-M2.7 kosten $0.0042. Addiere die drei, und du hast den Preis des gesamten Vergleichs, bevor du eine einzige Anfrage sendest. Dieselbe Entwurfslast über alle drei Modelle, mit Input und Output zusammengezählt:
| Modell | Input $/1M | Output $/1M | Kosten | 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 Input- und 7.500 Output-Tokens insgesamt. Die Preise pro 1 Mio. Tokens sind aktuell und ändern sich zusammen mit unseren Tarifen; die OpenRouter-Spalte zeigt Referenzpreise für dieselbe Arbeitslast.
Die Tabelle zeigt auch die OpenRouter-Referenz, damit du siehst, wie sich die Summe vergleicht. Die Token-Annahmen kannst du ändern: Wenn deine Tickets lang sind, wächst der Judge-Input am schnellsten, weil er beide Antworten doppelt enthält. Wenn dein Judge vor der Antwort laut nachdenkt, wächst auch seine Output-Zahl, und dein Protokoll wird es zeigen. Für die Gewohnheit, Tokens vor einer Anfrage zu zählen, siehe count tokens before you send a request.
Die gleiche Obergrenze, die Clocktower verwendet, ist es wert, übernommen zu werden. Es setzt 8.000 Tokens pro Zug, und du hast bereits max_tokens. Setze es in SETTINGS, und eine durchgehende Antwort kann deine Schätzung nicht still vervielfachen.
Bevor du den vollen Lauf startest#
Checkliste
Pre-Flight für einen fairen Vergleich
Der letzte Punkt ist wichtig. Führe drei Tickets aus, öffne calls.jsonl und lies es. Prüfe, ob finish stop statt length sagt; wenn ein Entwurf durch max_tokens abgeschnitten wurde, verliert ein Modell möglicherweise nur, weil seine Antwort abgeschnitten wurde. Dasselbe gilt für die Aufrufe des Judges: Ein length dort wird zu einem stillen Gleichstand, also zähle auch die None-Urteile. Wenn deine Aufgabe eine einzige richtige Antwort hat, brauchst du vielleicht gar keinen Judge: test your prompt on 20 cases with exact-match grading.
Das Ergebnis lesen, ohne sich selbst zu täuschen#
Schau dir die drei Zahlen in wins an. Dreißig Tickets sind eine kleine Stichprobe: Eine Aufteilung wie 12 zu 9 mit 9 Gleichständen sagt dir, dass die Modelle nah beieinander liegen, nicht dass eines besser ist. Das Protokoll hilft hier. Wenn ein Modell knapp gewinnt, aber viel mehr Output verbraucht, ist die eigentliche Frage der Preis der Aufgabe, nicht die Anzahl der Siege; price per 1M tokens versus cost per task geht diesen Vergleich durch.
Wenn das Ergebnis knapp ist, wähle das günstigere Modell für die Aufgabe oder füge mehr Tickets hinzu und führe den Lauf erneut aus. Beides ist günstig auszuprobieren, weil die ganze Maschinerie bereits eine Schleife ist. Wenn du die Übung mit einem anderen Modell wiederholen willst, liegen die drei Modelle oben hinter einer API und einem Schlüssel: Das Setup in der Dokumentation ist für jedes gleich, und die Preisseite listet, was jedes gerade kostet. Dwarkesh Patels eigener Befund zu OpenAI- versus Anthropic-Modellen (Erkennung versus Täuschung) ist sein Ergebnis für sein Spiel; deines wird aus deinen Tickets kommen.



