一、共享的 Env 抽象
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()。
关于实现并注册基于 MessageEnv 的基准测试的完整示例,参见 添加新的基准测试。
二、提示词如何解析
每个基准测试都定义了一个能提升得分的 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 logic extracted = 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 协议