--- name: run-eval description: 用 Twinkle Eval 跑一次評測——建立 config.yaml、下載或指定資料集、驗證設定、執行並讀結果。當使用者說「跑 benchmark」「跑評測」「建 config」「evaluate 這個模型」「twinkle-eval 怎麼跑」「評測結果怎麼看」「為什麼分數是 0」時使用。涵蓋 15 種 evaluation_method 的 config 差異、27 個內建可下載 benchmark、--validate / --dry-run / --resume,以及用 unparsed_rate 診斷 extractor 失效。 --- # 跑一次評測 ## 前提:本專案不啟動模型服務 Twinkle Eval 只做一件事——**拿題目去呼叫已經在外部運行的 OpenAI 相容端點**。 模型要自己先起好(vLLM、Ollama、OpenAI、NVIDIA Build 都行),再把 `base_url` 填進 config。 端點沒回應時,本專案的正確行為是依 `max_retries` 重試後報錯退出,**不會**也**不該**嘗試 重啟服務(CLAUDE.md 原則 G)。 ## 1. 產生 config ```bash twinkle-eval --init # 列出全部 11 個範本 twinkle-eval --init multiple_choice # 產生 configs/multiple_choice.yaml twinkle-eval --init all # 全部產生到 configs/ ``` 範本存放在 `twinkle_eval/templates/`,`--init` 直接掃該目錄。 ### config 骨架 ```yaml llm_api: base_url: "http://localhost:8000/v1" # 必填 api_key: "EMPTY" # 必填(本地 vLLM 隨便填) api_rate_limit: -1 # QPS,-1 為不限 max_retries: 3 timeout: 600 disable_ssl_verify: false model: name: "my-model" # 必填,會寫進結果路徑與紀錄 temperature: 0.0 top_p: 0.9 max_tokens: 4096 extra_body: # 傳給 API 的額外參數 evaluation: dataset_paths: # 必填,list(即使只有一個) - "datasets/example/tmmluplus/" evaluation_method: "box" # 必填,見下表 repeat_runs: 1 # >1 時算平均與標準差 shuffle_options: false # 選項隨機排列 logging: level: "INFO" ``` **每個 evaluation_method 的必填欄位與 `strategy_config` 參數不同**, 完整對照見 `references/config-reference.md`。 ### 挑 evaluation_method | 方法 | 用在 | 備註 | |------|------|------| | `pattern` | 選擇題,通用首選 | 正則比對,含中英文預設模式 | | `box` | 選擇題,推理模型 | 提取 `\boxed{}` / `\box{}`;**需要 `system_prompt`** | | `logit` | 多選項題 | 比較各選項 log-probability,不依賴輸出格式;選項數不限 | | `math` | 數學推理 | `\boxed{}` + MathRuler;需 `[math]` | | `regex_match` | BBH 之類自由格式 | ⚠️ `system_prompt` **不生效**,見下方說明 | | `custom_regex` | 自訂格式 | 必須設 `strategy_config.patterns` | | `ifeval` / `ifbench` | 指令遵循 | 需 `[ifeval]` / `[ifbench]` + nltk 資料 | | `bfcl_fc` / `bfcl_prompt` | 函式呼叫 | FC 走 tools API,prompt 走注入 | | `niah` | 長文本大海撈針 | | | `ragas` | RAG 品質 | | | `text2sql` | Text-to-SQL | 需設 `text2sql_db_base_path` | | `asr` | 語音辨識 | 需 `[asr]`;`llm_api.type: whisper` 或多模態 | | `vision_mcq` | 視覺多選題 | 需 `[vision]`(縮放用) | > ⚠️ **`evaluation.system_prompt` 只對 `box` 與 `math` 生效。** > `models/openai.py` 的 `_build_messages()` 以白名單決定是否送出 system message > (`method in {"box", "math"}`),其他方法即使在 config 設了 `system_prompt` 也不會進 request。 > `regex_match` 這類需要指定輸出格式的方法,格式要求必須寫進資料集的 `question` 欄位裡。 > (專案的 `templates/regex_match.yaml` 目前有同樣的誤導,見 [#144](https://github.com/ai-twinkle/Eval/issues/144)) ```bash twinkle-eval --list-strategies # 執行期確認可用方法 ``` ## 2. 準備資料集 ```bash twinkle-eval --download-dataset list # 列出 27 個內建 benchmark twinkle-eval --download-dataset mmlu # 短名稱 twinkle-eval --download-dataset tmmluplus gsm8k twinkle-eval --download-dataset all ``` 內建短名稱:`mmlu` `mmlu_pro` `mmlu_redux` `tmmluplus` `supergpqa` `gpqa` `formosa_bench` `gsm8k` `aime2025` `bbh` `ifeval` `ifbench` `bfcl` `needlebench` `longbench` `wikieval` `librispeech` `aishell1` `fleurs` `common_voice` `mmbench` `mmstar` `mmmu` `pope` `spider` `bird` `spider2_lite` `gpqa` 是 gated dataset,會互動式要求 HuggingFace token。 **先用 example 資料集驗證流程跑得通**再下載完整 benchmark: `datasets/example/` 底下每個 benchmark 都有 10–30 筆的子集,跑一次只要幾秒。 ## 3. 本機 config 的命名規則(重要) 含真實 API 金鑰的 config **絕對不得 commit**(CLAUDE.md 原則 E)。這三種前綴/後綴已被 `.gitignore` 全局排除,本機測試一律用其中之一: ``` config_local_*.yaml config_test_*.yaml *.local.yaml ``` 寫入任何含金鑰的 config 前,**先確認該路徑已在 `.gitignore` 中**。不確定就檢查: ```bash git check-ignore -v config_local_myrun.yaml # 有輸出 = 已被忽略 git diff --staged | grep -i "api_key" # commit 前確認 ``` ## 4. 執行 ```bash twinkle-eval --validate --config config_local_myrun.yaml # 只驗設定與資料集,不呼叫 API twinkle-eval --dry-run --config config_local_myrun.yaml # 顯示評測計畫,不呼叫 API twinkle-eval --config config_local_myrun.yaml # 正式跑 twinkle-eval --config config_local_myrun.yaml --export json csv html excel ``` **永遠先跑 `--validate` 再跑正式評測。** 資料集路徑錯、缺必填欄位、格式不符都會在這一步 抓到,省下一整輪 API 費用與時間。 中斷後續跑(⚠️ 目前失效,見 [#145](https://github.com/ai-twinkle/Eval/issues/145)): ```bash twinkle-eval --resume 20260825_1430 --config config_local_myrun.yaml ``` ## 5. 讀結果 ``` results/ ├── results_{timestamp}.json # 整體摘要 └── eval_results_{timestamp}_run{N}.jsonl # 各題明細(append 模式) ``` 摘要含 `dataset_results`(各資料集的 `average_accuracy`、`average_std`、 `average_pass_at_k`、`total_unparsed_count`)與 `duration_seconds`。 `config` 欄位是**移除 api_key 後**的設定。 明細每行含 `question_id` / `sample_id` / `question` / `correct_answer` / `predicted_answer` / `is_correct` / `llm_output` / `llm_reasoning_output` / token 用量。 各路徑另有增補欄位(`logit` 的 `logprob_scores`、`ifeval` 的四個指標、`vision_mcq` 的 `image_path`、`asr` 的 `wer` / `cer`)。 > ⚠️ 實際輸出**沒有** `file` 與 `timestamp` 欄位(CLAUDE.md §10 的規範與實作不符)。 > 這也讓 `--resume` 目前無法運作:它以 `{file}|{question_id}` 作為已完成紀錄的 key, > `file` 缺失使得 key 變成 `|{idx}`,與比對端的 `{檔案路徑}|` 永遠不匹配, > 結果是一題都不會跳過、整輪重跑並 append 出重複列。見 [#145](https://github.com/ai-twinkle/Eval/issues/145)。 ```bash # 快速看正確率 jq -r '.dataset_results | to_entries[] | "\(.key): \(.value.average_accuracy)"' \ results/results_*.json # 撈出所有答錯的題目 jq -c 'select(.is_correct == false) | {question_id, predicted_answer, correct_answer}' \ results/eval_results_*_run0.jsonl | head ``` ## 診斷:分數異常低 先看 **`unparsed_rate`**——這是判斷「模型答錯」還是「extractor 沒抓到」的關鍵。 ```bash # 資料集層級(摘要 JSON 用 average_unparsed_rate,per-file 才叫 unparsed_rate) jq -r '.dataset_results | to_entries[] | "\(.key): \(.value.average_unparsed_rate)"' \ results/results_*.json ``` | 症狀 | 多半是 | |------|--------| | `unparsed_rate` 高(>10%) | extractor 沒對上輸出格式 | | `unparsed_rate` ≈ 0 但分數低 | 模型是真的答錯 | | 全部 0 分且無 unparsed | ground truth 欄位或正規化對不上 | | 「所有資料集評測均失敗」 | 資料集路徑、格式,或 API 端點問題 | extractor 沒抓到時,撈幾筆 `llm_output` 出來看實際格式: ```bash jq -r 'select(.predicted_answer == null) | .llm_output' \ results/eval_results_*_run0.jsonl | head -3 ``` 常見成因: - **`box` 方法但沒設 `system_prompt`** → 模型不知道要用 `\boxed{}`,自然抓不到 - **推理模型的 think tag** → evaluator 會剝離完整的 `...` / `` / `` 標籤對;只有結尾 tag 而無開頭 tag 會被視為格式不合格而原樣保留 - **`content` 為 null**(vLLM `skip_special_tokens=true`)→ 會回退讀 `reasoning`, 再回退 `reasoning_content`(vLLM 0.18+ 改名,兩者都支援) - **選項超過 4 個**(MMLU-Pro A–J、SuperGPQA)→ 確認 extractor 支援多字母選項 ## 效能 並行度由 `ThreadPoolExecutor` 預設值決定,用 `llm_api.api_rate_limit` 節流: - 本地 vLLM:`-1`(不限) - 有 QPS 限制的商用 API:設成實際上限,否則會大量 429 `repeat_runs > 1` 會線性放大時間,但能得到標準差——量化模型穩定性時才開。