모델 & 비교가이드
당신의 과제에서 두 LLM을 공정하게 비교하기: 진영 교체와 비용 로그
당신의 티켓으로 두 모델을 일대일 비교하라: 동일한 설정, 모든 쌍을 양쪽 순서로 판정, 그리고 실행 비용을 알려주는 calls.jsonl 로그.

이 페이지의 내용
두 모델에 세 개의 프롬프트를 실행했더니 두 번째 모델이 이겼다. 그것을 믿기 전에 어느 쪽이 두 번째로 나열되었는지 확인하라. 공정한 비교에는 세 가지가 필요하다. 두 모델에 동일한 설정, 모든 쌍을 양쪽 순서로 판정, 그리고 호출당 토큰 로그로 전체 실행 비용을 알 수 있어야 한다. 아래는 당신의 과제에서 이 세 가지를 모두 수행하는 작은 Python 스크립트다.
공개 토너먼트에서 빌려올 수 있는 것#
Dwarkesh Patel의 Clocktower Bench는 언어 모델들이 서로 Blood on the Clocktower라는 사회적 추리 게임을 하는 비공식 연구 프로젝트다. 여기서 게임은 핵심이 아니다. 하네스가 핵심이다. 프로젝트의 About 페이지(2026년 11월 8일 확인)에 따르면, 어떤 일대일 비교든 해야 할 몇 가지를 수행한다:
- 한 모델이 모든 선한 자리를, 다른 모델이 모든 악한 자리를 맡고, 각 딜은 진영을 바꿔 두 번 플레이되므로 운 좋은 자리가 결과를 결정하지 않는다;
- 모든 모델이 동일한 설정을 받는다: 동일한 추론 노력, 턴당 8,000토큰 상한, 발언 150단어 제한;
- 스토리텔러는 스크립트로 정해져 있어 어떤 모델도 게임을 심판하지 않는다;
- 모든 API 호출은 토큰과 비용과 함께
calls.jsonl에 기록된다. 페이지에 따르면 모델에 따라(OpenAI 및 Anthropic API에서) 한 게임 비용은 약 $2에서 $50이다.
하네스는 OpenAI와 Anthropic 모델용으로 만들어졌고, 당신의 과제는 아마 보드 게임이 아닐 것이다. 하지만 세 가지 습관은 전이된다: 동일한 설정, 진영 교체, 비용 로그. 이 가이드의 나머지 부분은 이를 평범한 작업에 적용한다.

과제: 30개 지원 티켓에 대한 답변 초안 작성#
작은 지원 데스크를 운영하며 DeepSeek-V4-Flash와 GLM-5.3-Flash 중 어느 쪽이 더 나은 첫 초안을 쓰는지 알고 싶다고 하자. 30개의 실제 티켓이 담긴 tickets.jsonl 파일이 있고, 한 줄에 하나의 JSON 객체다: {"id": 1, "text": "..
API는 OpenAI 호환되므로 공식 openai 패키지가 https:/의 CheapTokens와 함께 작동한다. 문서의 전체 모델 id를 사용하라: deepseek-과 zai-. 두 모델 모두 세 번째 모델을 심판으로 받는다, MiniMaxAI/, 그래서 어느 참가자도 자기 작업을 채점하지 않는다.
로그를 가능하게 하는 부분부터 시작하라: 모든 호출이 통과하는 하나의 함수.
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를 받는다. 한 모델에 대해 이 중 하나를 바꾸면 모델이 아니라 설정을 비교하는 것이다.
진영 교체: 모든 쌍을 두 번 판정#
이제 심판이다. 심판 모델은 특정 슬롯의 답변 쪽으로 기울 수 있으므로 단일 판정은 품질과 위치를 섞는다. 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)놓치기 쉬운 두 가지 세부 사항이 있다. 심판은 판정을 더 안정적으로 유지하기 위해 temperature=0에서 실행된다; 모든 모델이 그 설정을 존중하지는 않으므로 몇 쌍을 다시 실행하고 판정을 비교한 후 신뢰하라. 판정은 마지막 Verdict: 줄에서만 읽히고, 상한은 넉넉하다(초안 800, 심판 1000) 그래서 답변 전에 추론하는 모델이 생각 중간에 잘리지 않는다; 아래 추정은 상한이 아니라 일반적인 크기를 사용한다. 파싱할 수 없는 답변은 None를 주고, 이는 실행을 중단시키는 대신 무승부로 떨어진다.
직접 해보기시도해 보라: 심판이 티켓 7에 대해 두 순서 모두에서 "A"라고 말했다. 그 티켓에서 누가 이기는가?정답 보기
아무도 아니다. 첫 번째 순서에서 A는 DeepSeek의 답변이었고, 두 번째 순서에서 A는 GLM의 답변이었다, 그래서 심판은 모델이 아니라 슬롯을 선호했다. 코드는 이를 무승부로 센다. 이런 무승부가 쌓이는 것 자체가 결과다: 두 모델이 당신의 과제에서 비슷하거나, 심판 프롬프트가 너무 모호하다.
순서는 테스트를 기울이는 한 가지 방법일 뿐이다. 설정, 프롬프트, 로깅도 기울일 수 있으므로 누가 먼저인지만이 아니라 전체 설정을 확인하라. 다음은 같은 실험의 전과 후다.
비교하기
대충 비교를 멈추면 무엇이 바뀌는가
대충
모델당 하나의 프롬프트, 기본값 그대로, 쌍은 한 순서로만 표시, 청구서는 월말에 대시보드에서 확인. 승자는 우연히 먼저 나열된 모델일 수 있다.
공정
하나의 공유 시스템 프롬프트와 동일한 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")모델별로 호출 수, 입력 토큰, 출력 토큰이 담긴 한 줄을 얻는다. 모델의 두 가격(입력과 출력은 별도 청구)을 곱하거나, 더 쉽게는 합계를 실시간 비용 마커에 붙여넣어라.
실행 전에 추정할 수도 있다. 각 티켓과 시스템 프롬프트가 약 300 입력 토큰, 초안이 약 250 출력 토큰, 심판이 약 800 입력 토큰(티켓 + 두 답변 + 질문)을 보고 약 200 토큰(판정 줄 + 생각할 약간의 여유)으로 답한다고 가정하자:
- 초안: 30 티켓 × 2 모델 = 60 호출, 참가자당 30 × 300 = 9000 입력 및 30 × 250 = 7500 출력 토큰;
- 판정: 30 티켓 × 2 순서 = 60 호출, 심판에서 60 × 800 = 48000 입력 및 60 × 200 = 12000 출력 토큰.
현재 가격으로, 한 참가자의 30개 초안 비용은 DeepSeek-V4-Flash에서 $0.00089, GLM-5.3-Flash에서 $0.00048이다. MiniMax-M2.7의 60개 판정 비용은 $0.0042이다. 세 가지를 더하면 단일 요청을 보내기 전에 전체 비교 비용을 알 수 있다. 세 모델 모두에 걸친 동일한 초안 작성 작업량, 입력과 출력을 합산하면:
| 모델 | 입력 $/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 참조도 있어 총액이 어떻게 비교되는지 볼 수 있다. 토큰 가정은 당신이 바꿀 수 있다: 티켓이 길면 심판 입력이 가장 빠르게 증가한다, 왜냐하면 두 답변을 두 번 담기 때문이다. 심판이 답변 전에 소리 내어 생각하면 출력 숫자도 증가하고, 로그에 나타날 것이다. 요청 전에 토큰을 세는 습관은 요청을 보내기 전에 토큰 세기를 참조하라.
Clocktower가 사용하는 동일한 상한은 훔칠 가치가 있다. 턴당 8,000 토큰을 설정하고, 당신에게는 이미 max_tokens가 있다. SETTINGS에 넣으면 폭주하는 답변이 추정치를 조용히 배로 늘릴 수 없다.
전체 실행을 시작하기 전에#
체크리스트
공정한 비교를 위한 사전 점검
마지막 항목이 중요하다. 세 개의 티켓을 실행하고, calls.jsonl을 열어 읽어라. finish가 length이 아니라 stop라고 표시되는지 확인하라; 초안이 max_tokens에 의해 잘렸다면 한 모델이 단지 답변이 잘려서 지고 있을 수 있다. 심판 호출도 마찬가지다: 거기의 length는 조용한 무승부로 바뀌므로 None 판정도 세어라. 과제에 단일 정답이 있다면 심판이 전혀 필요 없을 수 있다: 20개 사례로 프롬프트를 정확 일치 채점으로 테스트.
자신을 속이지 않고 결과 읽기#
wins의 세 숫자를 보라. 30개 티켓은 작은 표본이다: 12 대 9에 9 무승부 같은 분할은 모델이 비슷하다는 것을 알려줄 뿐, 하나가 더 낫다는 것은 아니다. 여기서 로그가 도움이 된다. 한 모델이 약간 이기지만 출력을 훨씬 많이 사용한다면, 진짜 질문은 승수(count)가 아니라 과제의 가격이다; 1M 토큰당 가격 대 과제당 비용이 그 비교를 안내한다.
결과가 비슷하면 과제에 더 저렴한 모델을 선택하거나, 티켓을 더 추가하고 다시 실행하라. 전체 기계가 이미 루프이므로 둘 다 시도하기 저렴하다. 다른 모델에서 실험을 반복하고 싶을 때, 위의 세 모델은 하나의 API와 하나의 키 뒤에 있다: 문서의 설정은 각각 동일하고, 가격 페이지는 각각의 현재 비용을 나열한다. Dwarkesh Patel 자신의 OpenAI 대 Anthropic 모델에 대한 발견(탐지 대 기만)은 그의 게임에 대한 그의 결과다; 당신의 결과는 당신의 티켓에서 나올 것이다.



