大模型評測與基準測試
大模型評測的做法是把題集和提示詞模板固定下來,讓每個被測模型在相同條件下把每道題各答幾遍:有標準答案的題用規則判分,開放式回答交給另一家廠商的評審模型按評分細則打分,再統計每個模型的得分、耗時和花費。一把 API Key 就能呼叫全部被測模型。按每月完整跑 2 輪、每輪 1,000 道題、兩個被測模型每題各答 3 遍估算,用各模型最低價分組的即時價格計算,模型費用每月約 ¥776,每輪約 ¥388。
價格更新於 2026年11月13日 下午6:08
月成本估算
假設:每月完整跑 2 輪;每輪 1,000 道題,兩個被測模型每題各答 3 遍,每次輸入約 800 tokens、輸出約 1,200 tokens(含推理模型的思考 tokens);其中一半是開放式題目,評審模型逐個回答打分,每次輸入約 2,000 tokens(評分細則、題目、參考答案和回答),輸出約 200 tokens。
業務問題
選模型、寫論文、做產品選型,都要在同一套題上比較幾家模型的質量、成本和速度。公開榜單的題目和你的任務不一樣,分數也是別人用各自的提示詞和參數測出來的,很難直接拿來用。
自己跑評測,難在細節:各家介面和參數不一樣,推理模型往往不接受 temperature;同一個模型名背後的版本會更新;幾千個請求一起發出去會遇到限流;開放式回答還要有人或評審模型打分,而評審打出的分數本身也要經得起檢驗。
用一個 OpenAI 相容介面呼叫全部被測模型,同一份指令碼只改 model 參數。給每輪評測單獨建 API Key,這一輪花了多少錢、每次請求的 tokens 和耗時,在控制台的操作日誌裡按 Key 一查就知道。
推薦模型組合
推薦模型按即時目錄自動選取,價格取各模型最低價分組,更新於 2026年11月13日 下午6:08。點模型名可檢視全部分組價格。
被測模型:旗艦與推理模型
gpt-5.5OpenAI
輸入 ¥10 / 輸出 ¥80 每百萬 tokens標準分組
各家的旗艦和推理模型,常作為論文裡的強基線。單價高,推理模型的思考 tokens 計入輸出,通常是整輪評測裡花費最大的一項;正式開跑前先用幾十道題試跑,按實際 usage 估算。
被測模型:主力與輕量模型
deepseek-v4.1-flashDeepSeek
輸入 ¥2.8 / 輸出 ¥11.2 每百萬 tokens國產模型分組
便宜很多的模型在你的題目上能做到什麼程度,往往是選型時最關心的問題。備選裡有國產和海外的主力、輕量模型,同一份指令碼加一個 model 參數就能放進評測。
評審模型(LLM-as-a-judge)
Claude Sonnet 5.5Anthropic
輸入 ¥3.2 / 輸出 ¥16 每百萬 tokensClaude 經濟分組
只給沒有標準答案的開放式回答打分。選一家不在被測名單裡的廠商,減少評審偏向自家模型的可能;被測名單裡有它的同廠模型時,換成備選裡的模型。評審打分要先和人工評分對照,校驗過再用。
參考架構
- 1
凍結題集
題目、標準答案或評分細則、提示詞模板整理成帶編號的 JSONL,和指令碼一起納入版本管理,評測期間不再改動。公開基準的題目可能出現在模型的訓練資料裡,最好再加一部分自己新寫、沒有公開過的題。
- 2
固定呼叫條件
所有模型用同一份系統提示詞;支援的模型設 temperature 0 和固定 seed,不支援的去掉並在記錄裡寫明。給這一輪單獨建 API Key:額度上限設成這一輪的預算,模型限制只放行這一輪要呼叫的模型。分組決定線路和價格,每個模型在整輪裡固定用同一個分組,跑完在操作日誌裡核對每條記錄的分組。
- 3
逐條記錄
每次呼叫記下請求的模型 ID、響應裡返回的模型名、實際傳送的參數、呼叫時間、耗時、finish_reason、usage 和原始輸出。開跑前用 /v1/models 存一份這把 Key 可呼叫的模型列表。
- 4
規則判分在先
選擇題、數值題和能精確匹配的短答案,用程式對照標準答案判分,結果穩定、可復現,也不用額外花錢。
- 5
評審模型打分
開放式回答按書面評分細則和參考答案打分,隱去被測模型的名字;兩兩比較時把兩個回答交換順序各評一次。另取 100 條左右由人工獨立打分,和評審模型的分數對照,一致性達到你所在領域的要求再使用。
- 6
控制併發並彙總
用訊號量限制同時在跑的請求數,從 8 個開始,看 429 的比例再調整;SDK 會退避重試,仍然失敗的題單獨重跑,不要從統計裡直接去掉。最後按模型統計得分的均值和波動、耗時和 tokens;這一輪的總花費,在操作日誌裡按這一輪的 Key 名稱篩選,看「篩選時段消耗」。
成本估算
假設:每月完整跑 2 輪;每輪 1,000 道題,兩個被測模型每題各答 3 遍,每次輸入約 800 tokens、輸出約 1,200 tokens(含推理模型的思考 tokens);其中一半是開放式題目,評審模型逐個回答打分,每次輸入約 2,000 tokens(評分細則、題目、參考答案和回答),輸出約 200 tokens。
| 專案 | 模型 | 用量假設 | 費用 |
|---|---|---|---|
| 被測:旗艦模型 | gpt-5.5 | 6,000 次 × 輸入 800 + 輸出 1,200 tokens | ¥624 |
| 被測:主力與輕量模型 | deepseek-v4.1-flash | 6,000 次 × 輸入 800 + 輸出 1,200 tokens | ¥94.08 |
| 評審模型打分 | Claude Sonnet 5.5 | 6,000 次 × 輸入 2,000 + 輸出 200 tokens | ¥57.6 |
| 合計(每月) | ¥775.68 | ||
按各模型最低價分組的即時價格計算,價格更新於 2026年11月13日 下午6:08。
只含模型呼叫費用,不含人工評分。每多測一個模型,按它的單價再加一行;題目數量或重複次數翻倍,費用大致翻倍。各模型的思考 tokens 差別很大,正式開跑前先用 50 道題試跑,按實際 usage 推算。這裡按各模型最低價分組估算,其中可能包含混合渠道;評測結果要寫進論文、需要說清線路來源的,選標著「官方轉發」的分組,單價更高,見各模型詳情頁的分組價格。
示例程式碼
用訊號量控制併發,每個模型每題各答 3 遍;有標準答案的按規則判分,開放式回答隱去模型名後交給評審模型打分。不接受 temperature 的模型自動去掉這個參數,實際傳送的參數和失敗的題都寫進結果檔案。
import asyncio
import json
import os
import re
import time
from openai import AsyncOpenAI, BadRequestError
client = AsyncOpenAI(base_url="https://www.tutujin.com/v1", api_key=os.environ["TUTUJIN_API_KEY"], max_retries=4)
MODELS = ["gpt-5.5", "deepseek-v4.1-flash"]
JUDGE = "claude-sonnet-5-5" # from a vendor that is not under test
REPEATS = 3
SYSTEM = "Answer the question. End with a last line of the form 'Answer: <answer>'."
limit = asyncio.Semaphore(8) # requests in flight; raise it slowly while watching for 429
async def ask(model, messages, params):
async with limit:
started = time.time()
try:
resp = await client.chat.completions.create(model=model, messages=messages, **params)
except BadRequestError:
# many reasoning models reject temperature: retry without it and keep what was really sent
params = {k: v for k, v in params.items() if k != "temperature"}
resp = await client.chat.completions.create(model=model, messages=messages, **params)
return resp, params, round(time.time() - started, 2)
async def judge(item, answer):
"""Open-ended answers: 0-5 against a written rubric, without the name of the model that wrote them."""
prompt = (
f"Question:\n{item['prompt']}\n\nReference answer:\n{item['reference']}\n\nRubric:\n{item['rubric']}\n\n"
f"Answer to score:\n{answer}\n\n"
'Score the answer from 0 to 5 by the rubric only. Reply with JSON only: {"score": 0, "reason": ""}'
)
resp, _, _ = await ask(JUDGE, [{"role": "user", "content": prompt}], {"temperature": 0})
raw = resp.choices[0].message.content
return json.loads(raw[raw.find("{"): raw.rfind("}") + 1])["score"] / 5
async def run_one(model, item, rep):
messages = [{"role": "system", "content": SYSTEM}, {"role": "user", "content": item["prompt"]}]
resp, params, latency = await ask(model, messages, {"temperature": 0, "seed": 7})
text = resp.choices[0].message.content or ""
if "answer" in item: # questions with a key are scored by rule
m = re.search(r"Answer:\s*(.+?)\s*$", text)
score = float(bool(m) and m.group(1).strip(" *.").lower() == item["answer"].lower())
else:
score = await judge(item, text)
return {
"item": item["id"], "repeat": rep, "model": model, "returned_model": resp.model, "params": params,
"score": score, "scored_by": "rule" if "answer" in item else JUDGE, "latency_s": latency,
"finish_reason": resp.choices[0].finish_reason, "usage": resp.usage.model_dump() if resp.usage else None,
"called_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"), "output": text,
}
async def main(path="testset.jsonl"):
items = [json.loads(line) for line in open(path, encoding="utf-8")]
jobs = [(m, it, r) for m in MODELS for it in items for r in range(REPEATS)]
results = await asyncio.gather(*(run_one(*j) for j in jobs), return_exceptions=True)
with open("results.jsonl", "a", encoding="utf-8") as f:
for (m, it, r), res in zip(jobs, results):
row = res if isinstance(res, dict) else {"item": it["id"], "repeat": r, "model": m, "error": repr(res)}
f.write(json.dumps(row, ensure_ascii=False) + "\n")
for m in MODELS:
ok = [x for x in results if isinstance(x, dict) and x["model"] == m]
failed = sum(1 for (jm, _, _), x in zip(jobs, results) if jm == m and not isinstance(x, dict))
print(m, "mean score", round(sum(x["score"] for x in ok) / max(len(ok), 1), 3),
"| mean latency", round(sum(x["latency_s"] for x in ok) / max(len(ok), 1), 2), "s",
"| failed, rerun these:", failed)
asyncio.run(main())常見問題
跑一輪完整評測要多少錢?
按每輪 1,000 道題、兩個被測模型每題各答 3 遍、一半題目由評審模型打分估算,用最低價分組計算每輪約 ¥388。費用主要取決於題目數 × 模型數 × 重複次數 × 輸出長度,旗艦推理模型的思考 tokens 往往是大頭。
評審模型的打分可靠嗎?
不能直接當真,要先校驗。常見的偏差有:偏愛更長的回答,受回答出現的先後順序影響,可能偏向同一廠商的模型,在專業領域也會判錯。所以評分細則要寫具體,隱去模型名,兩兩比較時交換順序各評一次,並用一批人工打分的樣本檢驗一致性;能用規則判分的題,不要交給評審模型。
設了 temperature 0 和 seed,結果就能完全復現嗎?
不能。seed 只能讓相同輸入的輸出儘量接近,不保證完全一致;推理模型通常不支援 temperature。記錄實際傳送的參數,每題多跑幾遍,報告均值和波動,而不是單次結果。
併發開多少合適,會被限流嗎?
從 8 個併發開始,看 429 的比例逐步調高;收到 429 時退避重試,仍然失敗的題記下來單獨重跑,不要從統計裡直接去掉。不同分組走不同線路,限流情況也可能不同。長期需要更高的併發,可以加客服微信 JsnonoChat,說明模型、分組和預計的請求規模,我們評估後答覆。
論文裡怎麼寫清楚用的是哪個模型?
寫完整的模型 ID(帶日期字尾的通常指向固定的版本快照)、呼叫的日期區間、所用分組和參數、提示詞模板、每題重複次數和評分方式,響應裡返回的模型名也一併儲存。同一個模型名背後的版本可能更新,隔一段時間重跑要重新記錄。
模型對比頁能代替評測嗎?
不能。模型對比頁列的是價格、輸入輸出類型、可用介面和上線日期這些事實,不包含質量評分;質量要用你自己的題集來測。
