モデルと比較ガイド
タスクで2つのLLMを公平に比較する:陣営入れ替えとコストログ
自分のチケットで2つのモデルの一対一比較を実行する:等しい設定、すべてのペアを両方の順序で判定、実行のコストを示すcalls.jsonlログ。

このページの内容
2つのモデルに3つのプロンプトを実行させ、2番目のモデルが勝ったとします。それを信じる前に、どちらが2番目にリストされていたかを確認してください。公平な比較には3つのことが必要です。両方のモデルに同一の設定、すべてのペアを両方の順序で判定すること、そして呼び出しごとのトークンのログです。これにより、実行全体のコストがわかります。以下は、あなた自身のタスクでこれら3つすべてを行う小さなPythonスクリプトです。
公開トーナメントから借用できること#
Dwarkesh PatelのClocktower Benchは、言語モデル同士がソーシャル推論ゲーム「Blood on the Clocktower」をプレイする非公式の研究プロジェクトです。ここで重要なのはゲームではなく、ハーネスです。プロジェクトのAboutページ(2026年11月8日確認)によると、あらゆる一対一比較が行うべきいくつかのことを行っています:
- 一方のモデルがすべての善の席を、もう一方がすべての悪の席をプレイし、各ディールは陣営を入れ替えて2回プレイされるため、幸運な席が結果を決めることはありません。
- すべてのモデルが同じ設定を受け取ります:同じ推論努力、ターンごとに8,000トークンの上限、発言には150語の制限。
- ストーリーテラーはスクリプト化されているため、モデルがゲームを審判することはありません。
- すべてのAPI呼び出しはトークンとコストとともに
calls.jsonlに書き込まれます。ページによると、1ゲームのコストはモデルに応じて約$2から$50です(OpenAIとAnthropicのAPI上)。
ハーネスはOpenAIとAnthropicのモデル用に構築されており、あなたのタスクはおそらくボードゲームではありません。しかし、3つの習慣は転用できます:等しい設定、陣営の入れ替え、コストログ。このガイドの残りでは、これらをありふれたものに適用します。

タスク:30件のサポートチケットへの返信の下書き#
小さなサポートデスクを運営していて、DeepSeek-V4-FlashとGLM-5.3-Flashのどちらがより良い初稿を書くかを知りたいとします。30件の実際のチケットを含むファイルtickets.jsonlがあり、1行に1つのJSONオブジェクト:{"id": 1, "text": ".です。
APIはOpenAI互換なので、公式のopenaiパッケージはhttps:/でCheapTokensと連携します。ドキュメントの完全なモデルIDを使用してください:deepseek-とzai-。両方のモデルはさらに第三のモデルを審判として受け取ります:MiniMaxAI/。これにより、どちらの競争者も自分の作品を採点しません。
ログを可能にする部分から始めましょう:すべての呼び出しが通過する1つの関数です。
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のフィールドはこのスクリプトが選択するもので、使用量の数値はAPIレスポンスのusageオブジェクトから来ます。両方のモデルは同じシステムプロンプト、同じtemperature、同じmax_tokensを受け取ります。1つのモデルに対してこれらの1つを変更すると、モデルではなく設定を比較していることになります。
陣営を入れ替える:すべてのペアを2回判定する#
次に審判です。審判モデルは特定のスロットの回答に偏る可能性があるため、単一の判定は品質と位置を混ぜ合わせます。Clocktowerのトリックがぴったり当てはまります:各ペアを両方の役割で示します。返信がチケットに勝つのは、審判が両方の順序でそれを選んだ場合のみです。判定が返信ではなくスロットに従う場合、そのチケットは引き分けです。
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)見落としやすい2つの詳細があります。審判は判定をより安定させるためにtemperature=0で実行されます。すべてのモデルがこの設定を尊重するわけではないので、信頼する前にいくつかのペアを再実行して判定を比較してください。判定は最後のVerdict:行からのみ読み取られ、上限は寛大です(下書きには800、審判には1000)。これにより、答える前に推論するモデルが思考の途中で打ち切られることはありません。以下の見積もりは上限ではなく典型的なサイズを使用しています。解析できない回答はNoneを返し、実行をクラッシュさせる代わりに引き分けになります。
自分で試す試してみてください:審判はチケット7に対して両方の順序で「A」と言いました。そのチケットに勝つのは誰ですか?答えを表示
誰も勝ちません。最初の順序ではAはDeepSeekの返信で、2番目の順序ではAはGLMの返信でした。つまり、審判はモデルではなくスロットを好みました。コードはそれを引き分けとしてカウントします。このような引き分けが山積みになること自体が結果です:2つのモデルはあなたのタスクで拮抗しているか、審判のプロンプトが曖昧すぎるのです。
順序はテストを傾ける1つの方法に過ぎません。設定、プロンプト、ログもそれを傾ける可能性があるため、誰が先に進むかだけでなく、セットアップ全体を確認してください。以下は、同じ実験の前と後です。
比較する
カジュアルな比較をやめると何が変わるか
カジュアル
モデルごとに1つのプロンプト、デフォルトのまま、ペアは1つの順序で表示され、請求書は月末にダッシュボードから読み取られます。勝者はたまたま最初にリストされたモデルかもしれません。
公平
1つの共有システムプロンプトと同じtemperatureとmax_tokens、各ペアは両方の順序で判定され、すべての呼び出しに対してcalls.jsonlに行が追加されます。勝者は両方の席から勝つ者です。
すべての呼び出しをログに記録し、実行の価格を計算する#
ログは比較を予算に変えるものです。実行後、モデルごとにトークンを合計します:
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")モデルごとに呼び出し回数、入力トークン、出力トークンの行が得られます。モデルの2つの価格(入力と出力は別々に請求されます)を掛けるか、もっと簡単に、合計をライブコストマーカーに貼り付けます。
実行前に見積もることもできます。各チケットとシステムプロンプトが約300入力トークン、下書きが約250出力トークン、審判が約800入力トークン(チケットと両方の返信と質問)を見て、約200トークン(判定行と考えるための少しの余裕)で答えると仮定します:
- 下書き:30チケット × 2モデル = 60呼び出し、競争者ごとに30 × 300 = 9000入力トークンと30 × 250 = 7500出力トークン。
- 判定:30チケット × 2順序 = 60呼び出し、審判で60 × 800 = 48000入力トークンと60 × 200 = 12000出力トークン。
現在の価格では、1人の競争者の30件の下書きはDeepSeek-V4-Flashで$0.00089、GLM-5.3-Flashで$0.00048かかります。MiniMax-M2.7での60件の判定は$0.0042かかります。3つを合計すると、1つのリクエストを送信する前に比較全体の価格がわかります。同じ下書きワークロードを3つのモデルすべてで、入力と出力を合計した場合:
| モデル | 入力 $/1M | 出力 $/1M | コスト | 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 入力トークンと 7,500 出力トークン。100万トークンあたりの価格は現行のもので、当社の料金体系に合わせて変動します。OpenRouter 列は同じワークロードの参考価格を示しています。
表にはOpenRouterの参照も示されているので、合計がどのように比較されるかがわかります。トークンの仮定はあなたが変更できます:チケットが長い場合、審判の入力が最も速く増えます。なぜなら、両方の返信を2回運ぶからです。審判が答える前に声に出して考える場合、その出力数も増え、ログに表示されます。リクエスト前にトークンを数える習慣については、リクエストを送信する前にトークンを数えるを参照してください。
Clocktowerが使用する同じ上限は盗む価値があります。ターンごとに8,000トークンを設定し、すでにmax_tokensがあります。それをSETTINGSに入れれば、暴走した回答が静かに見積もりを倍増させることはありません。
フル実行を開始する前に#
チェックリスト
公平な比較のための事前チェック
最後の項目は重要です。3つのチケットを実行し、calls.jsonlを開いて読んでください。finishがlengthではなくstopと言っていることを確認してください。下書きがmax_tokensで打ち切られた場合、1つのモデルが単に回答が切り捨てられたために負けている可能性があります。審判の呼び出しも同様です:そこでのlengthは静かな引き分けになるので、Noneの判定も数えてください。タスクに単一の正解がある場合、審判はまったく必要ないかもしれません:20のケースでプロンプトを完全一致採点でテストする。
自分を欺かずに結果を読む#
winsの3つの数値を見てください。30件のチケットは小さなサンプルです:12対9で9引き分けのような分割は、モデルが拮抗していることを示すのであり、一方が優れていることを示すのではありません。ここでログが役立ちます。一方のモデルがわずかに勝っているが、はるかに多くの出力を使用している場合、本当の質問は勝ち数ではなくタスクの価格です。100万トークンあたりの価格対タスクあたりのコストがその比較を説明しています。
結果が拮抗している場合、タスクには安価なモデルを選ぶか、チケットを追加して再実行してください。仕組み全体がすでにループであるため、どちらも試すのは安価です。別のモデルで演習を繰り返したい場合、上記の3つのモデルは1つのAPIと1つのキーの背後にあります:ドキュメントのセットアップはそれぞれ同じで、価格ページにはそれぞれの現在のコストが記載されています。Dwarkesh Patel自身のOpenAI対Anthropicモデルに関する発見(検出対欺瞞)は彼のゲームの結果です。あなたの結果はあなたのチケットから得られます。



