共享的 Env 抽象
基准测试复用与 RL 训练相同的
Env 协议。两者的联系是直接的——BenchmarkBuilder.make_envs() 返回的 Env 实例与 RLDataset 中使用的完全一致:RL Training Benchmark Evaluation─────────── ────────────────────RLDataset BenchmarkBuilder└── EnvGroupBuilder └── make_envs()└── make_envs() → list[Env] └── list[Env]└── Env.step() → reward └── Env.step() → reward
两条路径都使用
EnvFromMessageEnv 把消息级环境适配到 token 级的 Env 接口。同一个 MessageEnv 实现可以同时驱动训练和评测——只需在任一场景下用 EnvFromMessageEnv 包装它。rollout 基础设施也是共享的:run_benchmark() 内部会调用 RL 模块中的 do_single_rollout()。系统提示词
提示词如何解析
每个基准测试都定义了一个能提升得分的
recommended_system_prompt(例如,数学基准测试会指示模型使用 \\boxed{})。运行器按以下顺序解析提示词:1.
BenchmarkConfig.system_prompt:如果设置了,始终优先2.
BenchmarkBuilder.recommended_system_prompt:当没有设置覆盖值时自动应用3. 无系统提示词:如果两者都没有设置
按模型覆盖
不同模型可能需要不同的提示词策略。可以在配置层面覆盖:
# Thinking model — needs explicit formatting instructionthinking_config = BenchmarkConfig.for_model("Qwen/Qwen3.6-35B-A3B",system_prompt="Think step by step. Put your final answer in \\\\boxed{}.",)result = await run_benchmark("gsm8k", client, renderer, thinking_config)print(f"thinking: {result.score:.1%}")
内置的推荐提示词
基准测试 | recommended_system_prompt |
gsm8k | "Put your final answer in \\boxed{}." |
math500 | "Put your final answer in \\boxed{}." |
aime*, hmmt* | "Put your final answer in \\boxed{}." |
mmlu_pro, gpqa, supergpqa | 无(选择题格式说明放在用户提示词中) |
ifeval | 无 |
其他大多数 | 无 |
答案解析与评分
内置抽取工具
框架在
tinker_cookbook.eval.benchmarks._common 中提供了常用的答案抽取函数:函数 | 用途 |
extract_boxed(text) | 从 \\boxed{...} 中抽取内容(支持嵌套花括号) |
extract_gsm8k_answer(text) | 抽取数值答案——依次尝试 \\boxed{}、####、answer is,最后取最后一个数字 |
extract_mcq_answer(text) | 抽取选择题选项字母(A/B/C/D) |
extract_python_code(text) | 从围栏代码块中抽取 Python 代码 |
check_gsm8k(response, expected) | 带容差的浮点数比较 |
每个基准测试的
MessageEnv.step() 都使用这些函数来评分。抽取出的答案和预期答案会存入 logs 以便查看。自定义评分函数
BenchmarkConfig.grade_fn 可以在不修改 Env 的情况下覆盖内置评分逻辑。运行器会在 rollout 完成后调用它,替换奖励值:def my_grader(response: str, logs: Logs) -> float:"""Custom grading function.Args:response: Last assistant turn (thinking stripped).logs: Benchmark-specific fields — e.g., "expected", "input", "extracted".Returns:Reward value (typically 0.0 or 1.0)."""expected = logs["expected"]# Use your own extraction logicextracted = my_custom_extract(response)return 1.0 if extracted == expected else 0.0config = BenchmarkConfig(grade_fn=my_grader)result = await run_benchmark("gsm8k", sampling_client, renderer, config)
这在以下情况下很有用:
内置抽取逻辑漏掉了你的模型使用的某种合法答案格式
你想要更宽松或更严格的评分标准
你需要特定领域的解析逻辑
无需重跑的重新评分
regrade_trajectories() 会用一个新评分函数处理已保存的轨迹——不需要再做模型推理:from tinker_cookbook.eval.benchmarks import regrade_trajectories# Try a more lenient grader on existing resultsdef lenient_grader(response: str, logs: Logs) -> float:expected = logs["expected"]# Accept answers with or without \\boxed{}return 1.0 if expected in response else 0.0regraded = regrade_trajectories("evals/run_01", "gsm8k", grade_fn=lenient_grader)print(f"Original: {original.score:.1%}, Regraded: {regraded.score:.1%}")
裁判模型
像
arena_hard 这样的基准测试使用 LLM 裁判来评分。裁判与候选模型完全独立——不同的模型、不同的渲染器、不同的规模。配置裁判
# Candidate model: the model being evaluatedcandidate_client = await sc.create_sampling_client_async(model_path="tinker://run-abc/weights/final")candidate_renderer = get_renderer("qwen3_5", candidate_tokenizer)# Judge model: a separate, typically stronger modeljudge_client = await sc.create_sampling_client_async(base_model="Qwen/Qwen3.5-397B-A17B")judge_renderer = get_renderer("qwen3_5", judge_tokenizer)config = BenchmarkConfig(judge_sampling_client=judge_client,judge_renderer=judge_renderer,)result = await run_benchmark("arena_hard", candidate_client, candidate_renderer, config)
评分如何工作
对于
arena_hard,流程如下:1. 候选模型针对基准测试问题生成回答
2. 裁判模型接收问题与回答并打分(1-10)
3. 得分 >= 7 被判为正确
如果没有显式设置,裁判渲染器默认使用候选渲染器。
更换裁判
比较不同裁判对同一个模型的评分:
for judge_name, judge_client in judges.items():config = BenchmarkConfig(judge_sampling_client=judge_client,judge_renderer=judge_renderer,)result = await run_benchmark("arena_hard", candidate_client, candidate_renderer, config)print(f"Judge={judge_name}: {result.score:.1%}")
了解更多
评测概览:可用的基准测试与整体架构
基准测试指南:配置、存储、pass@k
RL 环境:与基准测试共享的 Env 协议