EvalHarness/tests/error.md
sora 370953729b Fix perf stats (wrong import path), per-repeat checkpoints, README
- perf_stats aggregator lives in eval/, not model/: the import failed
  silently and EVERY perf column was empty (not just ttft). Now warns
  on stderr instead of swallowing.
- repeats > 1 get their own checkpoint key (:rep2, :rep3, ...): repeat 2
  previously restored repeat 1's predictions and finished instantly with
  identical scores. rep1 keeps the legacy key (existing checkpoints still
  resume).
- repeats summary: report the MEAN score and aggregate time/tokens over
  ALL runs (was: last run only).
- README: six-benchmark command as the primary example.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-11 13:38:04 +00:00

177 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# EvalHarness 对标 evalscope 全程踩坑记录
> 28 bench 双框架对比过程中发现的所有问题(我方 bug / es 方 bug / 口径差),按层归类。
> 每条含:现象 → 根因 → 修复 → 验证结果。
---
## 一.5、fix2 轮新增修复2026-09-02 晚prompt 逐字对齐 + 基建)
16.5. **es bfcl 单轮全 0 = 依赖版本崩溃**(已帮修,`evalscope/.../bfcl/v3/utils.py`
- 现象es bfcl simple/multiple/parallel 等 10 个单轮类全部 0 分irrelevance 类却 0.9+
- 根因utils.py `convert_format_language()` import `bfcl_eval.constants.enums`BFCL v4 新版枚举),环境装的是 bfcl_eval 2025.8.6.2旧版ast_checker 直接比较字符串 "Java"/"JavaScript"/"Python")→ ModuleNotFoundError → except 吞掉 → 全部判 `ast_decoder:decoder_failed`
- 修复utils.py 两个 convert 函数改为版本兼容try 新版枚举fallback 旧版字符串,注意 JavaScript 大写 S
- 验证es bfcl 20/类 0.1736 → **0.3563**simple 0.55、live_simple 0.7、parallel 0.45-0.5 全部起来)
- 遗留:我方 0.266 vs es 修复版 0.356 仍差 0.09——我方 bfcl_mock 的官方后端转换细节underscore_to_dot 等)待对齐
17. **多段 pool spec range 替换 bug**`model/runner.py::_make_adapter`
- 现象多段逗号池105+138+140+143 共 20 口)全军覆没 ConnectError单段池正常curl 同口 200
- 根因:`re.sub(r'\{..\}', port, u, count=1)` 每次只替换**最前**的 range → 20 个成员 URL 全带字面量 `{8200..8203}`httpx 连畸形 host 必败
- 修复:按逗号拆段,每段独立 search+替换自己的 range
- 验证20 成员展开正确4/4 请求成功;停滞的 mmlu_pro 恢复 1 samples/s
18. **网络抖动三层自愈**集群路由分钟级断通交替17:07-18:15 反复)
- adapterhttpx.Timeout(connect=15, read=300, write=30, pool=15)——半开 TCP 快速失败
- pool实例级 failover已有
- runner.run_one6 次退避重试10/20/40/60/90s跨过断网窗口——gather 不再被单点异常炸掉
19. **prompt 逐字对齐 10 处**(双侧渲染 diff 器验证,全部 IDENTICAL
- mmlu 域匹配失效subject/category 键名不一致 → 全局前 5es 是每科目 5 范例)
- mmlu_pro 三处header 应在范例前 / cot_content 变换单 ANSWER / 测试选项 `A) x`
- bbh 测试题缺 `Q:/A: Let's think` 包装
- aime 缺尾部 `Remember to put your answer inside \boxed{}.` + 前导 `\n`
- gsm8k `\boxed{}.` 句号math fewshot 单换行分隔imo/hmmt/cmmlu/trivia 尾换行
- lcb 缺 `### Format:` 前缀 + starter_code 进代码块 + expert 头移到 system role
- hle judge prompt 换 es 逐字版 + 三态契约移 system role
- trivia `list(str)` 拆字符隐患
20. **es limit 是 per-subset 语义**es mmlu limit=200 = 每科目 200 = 全量 14042同题跑法必须 per-subset kmmlu 4/科目=228、cmmlu 3、hle 25、imo 25、lb2 66/长度)
21. **gfc ckpt 版本漂移**:数据镜像重物化后 metadata 变化 → key_for 失配 restored 0同口径 f1 直接从本次 report.samples 算更稳
---
## 一、我方框架 bug16 个,全部已修)
### 判分 / 聚合层
1. **few_shot_hook 从未挂载**`data/registry.py`
- 现象bbh 全程 0-shot无官方 3-shot CoT15 个 MCQ 子集接近 0 分
- 根因:`register_dataset` 装饰器从不把模块级 `<name>_few_shot` 函数挂到 provider 上runner `getattr(prov, 'few_shot_hook')` 永远 None
- 修复:装饰器里 `factory.__globals__.get(f'{spec.name}_few_shot')` 自动拾取(注意 `__globals__` 是 dict 要用 `.get` 不是 `getattr`
- 验证bbh 27子集×10 = 0.819 vs es 0.8185
2. **MCQ 抽取格式不匹配**`eval/recipes/qa.py`
- 现象bbh 全部 MCQ 子集判分全 miss
- 根因:`mcq_letter` 返回裸字母 `'A'`target 是 `'(A)'`
- 修复bbh recipe 按 target 形态格式化为 `f'({val})'`
3. **llm_judge 标签子串扫描**`eval/scorer.py`
- 现象hle 虚高 1.0、simple_qa 解析错乱
- 根因:`label in upper` 全文子串匹配——judge 解释文本里 "CONSISTS" 命中 'C'、"ANSWER" 命中 'A'
- 修复:三级解析——`label_pattern` 正则(如 `GRADE:\s*([CI])`)→ 尾行词边界匹配 → 全文词边界兜底
- 验证hle 1.0→0.031es 0.050 同族、simple_qa 见下条
4. **dict 聚合器主指标被派生值均值覆盖**`eval/runner.py`
- 现象simple_qa 真值 0.035 被报成 0.403
- 根因:`simpleqa_official` 返回 5 个派生指标 dict代码取 `mean(全部值)` = mean(0.035, 0.945, 0.02, 0.98, 0.036) = 0.403
- 修复dict 展开时主指标取**同名键** `out[metric]`,无同名键才退化均值
- 验证simple_qa 同题 0.050 vs es 0.0503
5. **judge adapter 拿不到 API key**
- 现象judge 全 401
- 根因launcher 没 export OPENAI_API_KEYspec URL 里不含 key
- 修复:启动脚本统一带 key
### 配对 / 选样层
6. **双重 `_apply_limits` 依赖原地 shuffle 别名**`model/runner.py`
- 现象per-task 截断时判分配对错位mmlu 一度 0.275
- 根因run_eval 和 generate_predictions 各调一次 `_apply_limits`旧实现靠「in-place shuffle 使两处列表别名同步」侥幸正确per-group 重写返回新列表后别名断裂 → samples 顺序 ≠ preds 顺序
- 修复run_eval 用确定性重算得到与 generate_predictions 完全一致的 work 列表再 `zip` 判分
- 验证mmlu 恢复 0.730+
7. **checkpoint key 不含 subset**
- 现象lb2 三个长度互相污染缓存
- 修复ckpt 名加 `:{subset}` 后缀
8. **checkpoint key 不含 prompt / 生成参数**
- 现象:改 prompt 模板后重跑复用旧模板的预测hswag 渲染实验多次被坑humaneval 指令头实验同)
- 教训:**改任何影响 prompt 的东西必须删 ckpt**(此后每次模板修改都显式 `rm ckpt`
9. **`no_shuffle` 没管到 per_task 分支**
- 现象:以为在跑「同题前 N」实际 per-group 内仍 Random(42).shuffle 取前 Nhswag 同题实验一度假对齐)
- 修复per_task 分支尊重 `shuffle=False`(组内不洗直接前 N
10. **选样语义与 es 不同**`model/runner.py`
- es**每个 subset 独立** `Random(42).shuffle` 后取前 N
- 我方旧实现:全局 shuffle 后按组截断 → 同池不同题
- 修复per_task 时按组分组、组内独立 Random(42)、组序内取前 N
### 生成 / prompt 层
11. **`/no_think` 裸拼污染题面**
- 现象prompt 尾部最后一个选项后面跟 ` /no_think` 字样
- 根因Qwen3 软开关实现把标记 append 到最后一条 user 消息
- 修复:普通请求改 `chat_template_kwargs: {enable_thinking: false}`服务端验证支持tools 请求保留软开关(模板 400 规避)
- 验证gpqa 0.263 → 0.460es 0.480
12. **MCQ 选项渲染字符级差异**strict/cot_letter 分支)
- 现象hswag 同题下 `A.` 渲染 = 0.7825、`A)` 渲染 = 0.565——**两个字符差 22 分**no-think Qwen3 对格式极端敏感)
- 修复:所有 MCQ 分支逐字对齐 es`A) 选项` + `one of A,B,C,D`mmlu_pro 例外es 用 `A ` 空格分隔)
- 验证wino 0.655 = es 0.655hswag 对齐中
13. **prompt 模板逐 bench 缺失/走样**(最大一类,累计 12+ 处)
- mmlu/cmmlu/mmlu_pro我方 strict「整个回复只能是 ANSWER」 vs es「Think step by step + last line ANSWER」→ 全部对齐mmlu 0.730→0.795es 0.800
- few-shot 渲染es 范例=裸题+`A)`+`ANSWER:`,我方=Question:/A./Answer: → 对齐 + **按科目域匹配范例**es biology 题配 biology 范例)
- mmlu_proes 专属模板(`Question:/Options:/A x` + CoT 范例 + 按域 header→ 0.471→0.579es 0.629
- gsm8kes 范例=题+`Reasoning:`全文+`ANSWER: \boxed{}` → runner 加 reasoning 元数据渲染分支
- mathes 范例=`Problem:/Solution:` 裸答案 → 同上
- aime×3es=「Solve the following math problem step by step. Put your answer inside \boxed{}.」**指令前置**,我方是题后 suffix → 加 `aime_es`
- imo/hmmt26es=`Problem:\n{题}\n\nPlease reason boxed`**Problem: 前缀**),我方裸题 → 套 `imo_es`imo 0.138→0.19hmmt 验证中)
- simple_qaes=`Answer the question:\n\n{q}` 且**无 Answer 行契约**,我方自加契约 → `simple_qa_es`
- triviaes 开放卷(`Content: {wiki证据列表}` + es 模板),我方闭卷(镜像键名读错 `wiki_content``wiki_context`)→ 修键名+开模板0.555→0.700es 0.660
- lb2es=`<text>` 包装 + CoT 契约,我方裸文档+strict → `lb2_es`
- humanevales 指令头("Read the following function signature...")→ 加了头后 **harness 仍用 input 拼程序**导致指令进 main.py 全 0 → metadata 存原始 promptharness 用 metadata 版
- LCB我方无代码契约模型输出 LaTeX 数学推导!)→ 官方 system+`### Question:/### Answer:`+```python 格式 → `lcb_es`
- dropes=`Think step by step + "Answer: [ANSWER]"` → prompt_suffix范例=完整 Passage 式
14. **tau2 env 内部调用不传生成参数**`agent/envs/tau2_official.py`
- 现象agent 调用走 adapter 默认 4096 tokens/无温度
- 修复:`run_task(gen_kwargs=)` 透传temp 0/16k16k+长对话会超 131072 上下文 → 8192
15. **tau2 reward 读不存在的字段**
- 根因:读 `environment_reward/communication_reward`tau2 官方 reward_info 字段是 `reward/db_check/...` → 恒 0
- 修复:取 `rewards['reward']`es 同款官方综合分)
16. **HF 下载同步阻塞事件循环**
- 现象18 任务并发时全卡死py-spy 见主线程 `_hf_download`
- 修复:`asyncio.to_thread` 物化
### 基础设施
- 根盘写满Errno 28两次HF 缓存迁移 /data2、清 modelscope 196G、truncate docker 巨型日志
- `pkill -f <pattern>` 会误杀自己所在 shellcmdline 含 pattern→ 用 launcher 文件 + 短命令
- bash 工具 timeout 会杀未及 detach 的后台任务 → `nohup ... < /dev/null & disown` + 立即返回
- checkpoint 恢复打印 `restored N (M to generate)`N≠M 命中时警惕 key 失配(多次救场信号)
---
## 二、esevalscope侧问题代码级证据
1. **tau2 崩溃**`tau2_bench/generation.py:118` `openai_chat_choices(include_reasoning=False)` 丢弃 Qwen3 思考通道 → 空 content → 官方引擎 `message.py:116` 校验 raisetemp=0 确定性复现)
2. **gfc 误判**(已帮修):`general_fc_adapter.py` `is_call_tool = finish_reason=='tool_calls'`vLLM/Qwen3 输出 tool_calls 时 finish_reason 常为 'stop' → FN 虚高。patch`or bool(tool_calls)`
3. **BCB 执行器空跑**(已帮修):`bigcodebench_adapter.py` `passed = status=='success'`——沙箱命令完成≠测试通过198/200 空输出判对0.99 虚高。patchunittest 显式 runner + `__BCB_RESULT__ PASS` 标记 → 0.365(与我们 0.375、交叉判分三方一致)
4. **LCB 本地执行全体 -2**`use_sandbox=false` 本机跑 codegen_metrics 全失败环境缺依赖docker sandbox 修复后 0.413 有效
5. **gfc 0.0 失败运行残骸**:断连+空回复产物;重跑 0.456
6. **swe 无评测断点**198/200 评完后报告步骤遇断连作废,重跑=整段 4h我们用日志聚合出真值 0.0
---
## 三、口径差(非 bug须同口径才可比
| 类别 | 内容 |
|---|---|
| **选样** | 双方 shuffle42 的 base 行序不同 → 不同题集。统一方案:双侧 `shuffle=False` 取原始前 N数据源行序一致已验证 400/400 同题)|
| **金标集合** | dropes=answer+validated_answers 合并(任一算对),我们镜像无该字段 → 严 ~0.065 |
| **指标定义** | gfces 报 tool_call_f1我们 acc已重算同口径imojudge vs math_equal已统一 judge0.188 vs 0.198|
| **数据构成** | math 全量:我们随机 200易题占比高vs es 5 Level 均衡——对齐档 L×N 才可比 |
| **执行环境** | bfcl/gfc/tau2es+Qwen3 的 agent 栈兼容性差LCB判定器不同官方 codegen_metrics vs 自写 runner|
| **截断** | lb2 文档中位 41.7 万字符天然超 131072——head+tail 截断双方同构(非 bug|
---
## 四、方法论教训
1. **分数不一致时先做同题实验**(同 prompt 双跑 / 同题双侧——hswag 0.2 之谜三小时才定位到两个字符
2. **交叉判分**A 的输出 × B 的判分器)是分离「判分层差 vs 生成层差」的唯一利器
3. **隔离变量实验**:同题×同 prompt×只换渲染 → 定位 `A.` vs `A)`
4. **no-think 模型对 prompt 字符级格式极端敏感**22 分级别的摆动)
5. ckpt key 必须覆盖 prompt 语义,否则改模板=白改