跳到主要内容

03 · 打分内核:任务、奖励门与 pass^k

这是全库最该弄懂的一章。tau2-bench 的可信度全压在「凭什么算对」上,而它的答案和多数人的直觉不一样。

3.1 一个任务的答案卷长什么样

任务 Task(tasks.py:560)里管打分的是 evaluation_criteriaEvaluationCriteria,tasks.py:366)。它有四种「期望」,外加一个总开关:

字段期望什么对应 RewardType
actions一条能解题的参考工具调用序列ACTION
env_assertions在终态环境上跑的布尔断言ENV_ASSERTION
communicate_infoAgent 必须对用户说出的字符串COMMUNICATE
nl_assertions交给 LLM 主观判真假的自然语言断言NL_ASSERTION(实验性)
reward_basis总开关:上面哪几项真正参与算分

reward_basis 默认是 [DB, COMMUNICATE](tasks.py:435),和最初的 τ-bench 对齐。

最反直觉的一点(必须记住): actions 不是「Agent 必须逐个调对的动作清单」。它是一条参考轨迹,评测器把它重放出一个「标准终态」用来比对。只要 Agent 用别的路径达到等价终态,DB 分照样满分。actions 只有在 reward_basis 里显式包含 ACTION 时才变成硬性逐调用要求——标准的 airline/retail/telecom 任务都不用 ACTION(tasks.py:237-260 的注释、domains/README.md:38-47 反复强调这点)。

3.2 打分总流程:一个乘法门

入口 evaluate_simulation(evaluator.py:88)。先过两道闸,再算分:

① 这次模拟是「正常结束」吗(AGENT_STOP / USER_STOP)?
否 ──▶ reward = 0.0,附终止原因,结束 (evaluator.py:113-123)
是 ▼
② 有 evaluation_criteria 吗? 没有 ──▶ reward = 1.0
有 ▼
③ 分别算 env / action / communicate (/ nl) 四个分量

④ reward = 1.0;对 reward_basis 里的每一类,reward *= 该类分量
(只要有一类是 0,总分就是 0) (evaluator.py:215-248)

第 ④ 步是乘法不是加权平均,含义很硬:任何一个被选中的维度不达标,整题 0 分。客服场景要的就是这种「木桶效应」——改错了库、或漏说了必须说的话,都算失败。

每个分量各自也是 0/1:action/communicate 都是「所有期望全满足才 1,否则 0」(evaluator_action.py:101-103、evaluator_communicate.py:39-41)。

3.3 DB 分:整场评测的皇冠

RewardType.DB 这一分量,是 tau2「只看终态不看过程」哲学的落点。看 EnvironmentEvaluator.calculate_reward(evaluator_env.py:22)。它同时搭两个环境:

预测环境 predicted 标准环境 gold
──────────────── ──────────────
set_state( 开局 + 整段真实轨迹 ) set_state( 开局对话历史 )
│ 重放 Agent 实际调的工具 │
│ └─ 再逐个执行 answer 的 actions
▼ ▼
predicted DB hash gold DB hash
└──────────── 相等? ───────────┘
是→db_reward=1 否→0

关键点:

  • 比的是 Agent 库哈希 AND User 库哈希都相等(evaluator_env.py:116-118)——双控领域里用户侧状态也要对。
  • 标准终态是现算的:把 actions 逐个 make_tool_call 打进 gold 环境(evaluator_env.py:99-105),而不是硬编码一个期望 DB。所以出题人只要给「一种解法」,框架自动推导出「正确终态」。
  • 这就是为什么 3.1 说 actions 是「参考轨迹」——它的作用是生成 gold 终态,不是逐条卡 Agent。

env_assertions 是补充:在预测环境上跑布尔函数(如「这条线现在是停用状态吗」),全过才给 ENV_ASSERTION 分(evaluator_env.py:128-142)。

3.4 另外两个分量(轻量但有坑)

COMMUNICATE:子串匹配。 检查 communicate_info 里每个字符串是否原样出现在 Agent 某条文字消息里——小写化、去逗号后做子串包含(evaluator_communicate.py:69)。够用但脆:源码自己标了 TODO: This could be improved!。典型用途是「Agent 有没有把退款金额 $150 报给用户」。

ACTION:逐调用匹配。 只在 reward_basisACTION 时才真正卡分。判定靠 Action.compare_with_tool_call(tasks.py:178):工具名要一致,参数按 compare_args 指定的子集比对(不指定就全比)。主要用在少数 banking_knowledge 任务上。

NL_ASSERTION:LLM 当裁判。 把自然语言断言丢给一个评判 LLM 判真假,实验性质,默认不进 reward_basis

3.5 pass^k:一次过不算过

单次 0/1 分噪声很大。tau2 沿用 τ-bench 的 pass^k(agent_metrics.py:113)衡量稳定性——同一题跑 num_trials 次,随机抽 k全部成功的概率:

C(success_count, k)
pass^k = ───────────────────── (组合数之比)
C(num_trials, k)

直觉:k=1 就是普通成功率;k 越大越苛刻——pass^4 高,说明这题 Agent 不是碰运气过的,而是四次抽签次次都对。上层 compute_metrics(agent_metrics.py:202)对所有任务的 pass^k 求平均,连同 avg_reward、平均成本、DB 命中、鉴权成功率、终止原因分布一起汇总成 AgentMetrics

细节:INFRASTRUCTURE_ERROR(API 断连等基础设施故障,非 Agent 的错)会被剔除出指标,不算失败(agent_metrics.py:138-145)——避免把网络抖动记到模型头上。

3.6 EvaluationType:想测哪几维自己挑

EvaluationType(evaluator.py:24)是命令层的开关,决定跑哪些评测器:ENV / COMMUNICATE / ACTION 各测一维;ALL(默认)按 reward_basis 组合;带 IGNORE_BASIS 的变体则无视 reward_basis 把所有维度都乘进去(调试/全面体检用)。有个安全检查:如果任务的 reward_basis 要某维、但这次没评它,会直接抛错而不是悄悄少乘一项(evaluator.py:225-230)。

3.7 代码地图

主题文件符号名
打分总入口/乘法门src/tau2/evaluator/evaluator.pyevaluate_simulation
评测维度开关src/tau2/evaluator/evaluator.pyEvaluationType
DB 终态比对src/tau2/evaluator/evaluator_env.pyEnvironmentEvaluator.calculate_reward
动作匹配src/tau2/evaluator/evaluator_action.pyActionEvaluator.calculate_reward
动作比对规则src/tau2/data_model/tasks.pyAction.compare_with_tool_call
沟通子串匹配src/tau2/evaluator/evaluator_communicate.pyCommunicateEvaluator.evaluate_communicate_info
奖励类型语义src/tau2/data_model/tasks.pyRewardType
评测标准结构src/tau2/data_model/tasks.pyEvaluationCriteria
pass^k 公式src/tau2/metrics/agent_metrics.pypass_hat_k
指标汇总src/tau2/metrics/agent_metrics.pycompute_metrics