Skip to content

1. 一条评估任务的解剖 ​

先看实物。下面是一条完整的评估任务,为便于阅读做了删节。

jsonc
{
  "id": "[mobile_data_issue]airplane_mode_on|user_abroad_roaming_enabled_off",

  // 提供给 Agent 的工单
  "ticket": "用户手机无法上网,状态栏显示 'No Service'。客户 John Smith,
             号码 555-123-2002,当前在法国。速度测试结果为 excellent 才算解决。
             不更换套餐,必要时愿意充值 2.0 GB 流量。",

  // 提供给用户模拟器的行为规范
  "user_scenario": { "instructions": {
      "known_info": "You are John Smith with phone number 555-123-2002.
                     You are currently abroad in France.",
      "unknown_info": null,
      "task_instructions":
        "…express mild frustration after the first unsuccessful attempt.
         You will consider the issue resolved only when speed test returns
         excellent internet speed and nothing else. …
         Whenever the agent asks you about your device, always ground your
         responses on the results of tool calls. …
         Never make up the results of tool calls."
  }},

  // 运行前将两侧状态重置到同一起点
  "initial_state": { "initialization_actions": [
      { "env_type": "user",      "func_name": "turn_airplane_mode_on" },
      { "env_type": "user",      "func_name": "turn_roaming_off" },
      { "env_type": "assistant", "func_name": "enable_roaming",
        "arguments": { "customer_id": "C1001", "line_id": "L1002" } }
  ]},

  // 评分标准
  "evaluation_criteria": {
      "actions": [
        { "requestor": "user", "name": "toggle_airplane_mode" },
        { "requestor": "user", "name": "toggle_roaming" }
      ],
      "env_assertions": [
        { "func_name": "assert_mobile_data_status", "expected_status": true },
        { "func_name": "assert_internet_speed",
          "expected_speed": 200, "expected_desc": "excellent" }
      ],
      "communicate_info": null,
      "nl_assertions": null,
      "reward_basis": ["ENV_ASSERTION"]
  }
}

这条任务跑一次会经历这些:Agent 收到工单,看到"用户说上不了网";它查运营商侧的数据库,发现漫游已开通、套餐正常——故障不在它能看到的地方;它必须通过提问引导用户查看设备;用户模拟器按 known_info 只知道自己叫 John Smith、在法国,它不知道飞行模式开着;Agent 问到设备状态,模拟器按事实锚定要求必须先调用工具查询再回答;最后测速结果达到 excellent,env_assertions 通过,任务算解决。

四处设计值得说明。

用户的认知范围被显式建模。 known_info 只有姓名、号码、所在国家三项。飞行模式开启、数据漫游关闭这两个真正的故障原因不在其中——用户并不知情,无法主动陈述,Agent 只能通过提问和引导用户查询来获取。

这是渐进式信息透露在任务定义层面的实现:它不靠一句「不要一次说完」的提示词去约束模拟器,而是把用户的知识范围建模为独立字段。多数基准在任务开始时给出完整需求,而真实用户的初始表述往往只是「我上不了网」。把需求澄清到可执行的程度,本身就是 Agent 必须具备的能力。

模拟用户被约束,防止被 Agent 说服。 task_instructions 含三类约束:情绪设定(首次修复失败后表现轻度不满)、验收口径(仅当测速为 excellent 才认定解决,poor、fair、good 均不接受)、事实锚定(关于设备状态的任何回答必须以工具返回为依据)。

第三类最关键。缺少事实锚定约束时,模拟用户会顺应 Agent 的引导确认问题已解决,评估随之退化为两个模型之间的相互确认。

两侧信息都不完整。 飞行模式与漫游开关属于用户侧,运营商侧的漫游开通属于 Agent 侧。这一划分决定了故障的形态——运营商侧已开通,用户设备侧关闭,Agent 查询数据库只能得到「配置正常」。故障位于数据库不可见的一侧,只有引导用户查询才能发现。

多维度的过程与结果校验。 env_assertions 检验终态,actions 检验关键动作是否发生,communicate_info 与 nl_assertions 检验必要信息是否已告知用户。每条任务可以检查四个维度中一个或多个。


2. 从这次运行看评估对象 ​

上面这条任务跑一次,涉及的东西比"输入加输出"多得多。

对象在这次运行里是什么少了它会怎样
任务那份 JSON每次跑的任务不同,结果无法比较
尝试这一次运行无法区分"偶尔成功"与"稳定成功"
轨迹每一步的工具调用与返回只知道失败,不知道哪一步开始错
结果终态:数据可用、测速达标只能看模型自己说做完了没有
评分器env_assertions 与 actions只能人工看输出,无法规模化
套件这类任务与其他任务的分组只能看单条,看不出分布

六个对象之外还有一层:判定这些对象的三层验证。

结果层读环境状态:数据可用、测速达标。不依赖模型自述,最可靠。

过程层读动作序列:该查设备的时候查了吗,该问的时候问了吗。它区分"达成"与"以允许的方式达成"。

质量层读表达:是否耐心、是否解释清楚、是否给了合规的替代方案。

三层的可靠性依次下降,成本依次上升,而且上层依赖下层。构建顺序应当自下而上——常见错误是一开始就做质量层,因为"它最能反映体验",结果量表分数无法解释:涨了不知道是不是真的好。

2.1 与输出评测的根本差别 ​

传统模型评测是「一次输入对一次输出」。上面这条任务不是这样:它是一个任务、多次尝试、一条轨迹、一个业务结果。两者的分子分母都不同。

差别的直接后果是三件事无法回答:为什么失败、从哪一步开始失败、换个环境还成不成立。

要回答这三件事,评估必须从「比较输出」变成「重复运行一台装置」。装置这个词是准确的:给定同一个初始状态,同一个 Agent 应当得到可比较的结果。这要求环境、工具、评分、协议都可控。


3. 四个组成部分各自的职责 ​

第 1 节那份 JSON 有四个顶层字段,它们不是随意分组的,各自回答一个问题。

字段回答什么谁读它
工单要解决什么Agent
模拟器规范对面那个人知道什么、怎么表现用户模拟器
初始状态从哪开始环境
评分标准什么算成功评分器

四者的独立性很重要:改评分标准不影响模拟器行为,改模拟器规范不影响初始状态。这个独立性让"调整难度"与"调整判据"变成两件可以分开做的事。

3.1 工单要写到什么程度 ​

工单不能太具体。如果它把"先查运营商侧再问用户设备"也写进去,任务就不再考察诊断能力,只考察照做能力。

工单也不能太模糊。"用户上不了网,帮他解决"这种写法在多数实现里会导致 Agent 漫无目的地试。第 1 节那份工单的写法值得参考:它给了验收口径(测速 excellent)、约束(不换套餐)、以及用户的授权范围(必要时愿意充值 2.0 GB)。这三样让 Agent 知道"能做什么"与"做到什么程度",但没有告诉它"怎么做"。

判据是:工单应当让一个有能力的人能独立完成,但不告诉他方法。

3.2 模拟器规范的三分法 ​

known_info、unknown_info、task_instructions 三分不是形式。

known_info 是用户「知道且会主动说」的。 第 1 节里只有姓名、号码、所在国家。这三项在对话里会被用户自然提到。

unknown_info 是用户「不知道」的。 第 1 节里是 null,因为故障原因(飞行模式开着)用户确实不知道。这一栏留空不是省略,是明确声明"这一部分不能由用户提供"。

task_instructions 是行为规则。 情绪、验收口径、事实锚定都在这里。

三栏分开的价值在于:如果只写一段自然语言描述用户,写的人很容易把"用户知道故障原因"混进去,因为写的人自己知道。分成三栏强制写的人对每项信息做一次归属判断。

3.3 初始状态要能被两次重置 ​

initialization_actions 的三个动作分别属于用户侧与 Agent 侧。两边的状态都要重置。

一个容易被忽略的要求:重置要幂等。如果第一次运行后某个动作留下了残留,第二次重置时同一个动作可能失败。做法是让每个动作都写成"设置成某个值",不用"切换"。

第 1 节的写法是 turn_airplane_mode_on 与 turn_roaming_off——名字里带了目标状态,所以重复执行结果相同。如果写成 toggle_airplane_mode,第二次执行就会把它切回去。

3.4 评分标准的四层 ​

actions、env_assertions、communicate_info、nl_assertions 四层,reward_basis 决定用哪几层聚合。

层检查什么用代码还是模型
actions关键动作是否发生代码
env_assertions终态是否正确代码
communicate_info必须告知的信息是否告知代码(字段匹配)
nl_assertions自然语言表述是否满足要求模型

前三层都能用代码判,第四层只能靠模型。分层的价值在于:能代码判的部分不交给模型,减少偏差来源。

reward_basis 是聚合规则。第 1 节只用了 ENV_ASSERTION,意味着这条任务只看终态。如果要把"必须告知用户"也算进成功条件,就要把 communicate_info 加进去。


4. 五个组成要素 ​

从上面这条任务可以抽出评估装置的五个要素。

要素内容缺了会怎样
数据集初始状态、工单、模拟器规范、验收标准打包为一条每次运行任务不同,结果无法比较
环境状态数据库中的客户、线路、套餐、账单,加设备侧开关上次残留影响这次,同一用例有时过有时不过
工具接口两侧各自的原子操作Agent 只能用自然语言描述动作,规划被工具吸收
评分标准四层检查加聚合规则只能人工看输出,无法规模化
执行协议交互顺序与终止条件对话可能永不结束,或在不该结束时结束

第三项有一条要求容易被忽略:工具必须是原子操作,不存在「解决用户的上网问题」这类高层抽象。抽象层次过高会使评估退化为对单次函数调用的考察,规划与推理环节被工具本身吸收。

反过来说,工具也不能过细到把业务逻辑拆成十几个步骤——那样评估的是「能否照着清单执行」,也不考察规划。

合适的粒度是「一个有业务含义的操作」:查用量、改配置、发通知、转人工。每一步都有明确的业务语义,但都不包含「判断该做什么」这层。

4.1 两侧各有自己的工具 ​

对话型任务里,工具要备两套:Agent 侧能查询与修改业务状态,用户模拟器侧能修改设备状态。

这个划分不是形式上的对称,它决定了故障的形态。前面那条任务里,运营商侧已经开通漫游,用户设备侧的漫游开关是关的——Agent 查询数据库只能得到「配置正常」的结论。故障位于数据库不可见的一侧,只有引导用户查询才能发现。

如果只有 Agent 侧工具,这类故障无法构造。任务会退化成「查一下配置,发现正常,改一下配置」的线性流程,中间没有信息不对称。

双侧工具还带来一个验证维度:用户改变状态后,Agent 必须重新调用工具才能获知结果。这验证了「Agent 是否真的读到了用户侧的操作结果」,防止它凭之前的状态推断。

4.2 需要隔离的第三类环境 ​

前两类之外还有一类:任务会在真实系统上产生副作用,必须隔离。

隔离环境的关键是「重置成本」。一个能在一秒内重置的沙盒,可以支持每次尝试前都重置;一个需要五分钟准备的环境,只能支持一批任务重置一次,后者会让任务之间有残留影响。

判据是:如果一个任务失败了,下一次跑同一任务能否从同一状态开始。不能,说明重置成本过高,需要改环境设计。

第五项在对话型任务里尤其重要。正常终止信号是模拟用户输出结束标记,此外还有轮数上限,以及模拟用户因耐心耗尽而主动结束——沟通效率过低本身即计为失败。

4.3 执行协议为什么是独立要素 ​

前四个要素都是「静态配置」,只有执行协议定义「怎么跑」。它决定三件事:谁先说话、什么算一轮、什么情况下强制停止。

三个设计点容易被忽略。

轮数上限要有。 没有上限时,两个模型可能永远互相确认,评估跑不完。上限值要按任务复杂度定,不能取一个全局常数——一个三十步的任务与一个三步的任务用同一个上限,前者会被误判为失败。

耐心耗尽要算失败。 用户模拟器因为沟通效率过低而结束对话,这个结局应当记为失败,不能记为「未完成而排除」。否则评估会奖励那些反复追问的 Agent——它们把任务拖到超时而免于计分。

强制停止后的状态要保留。 轮数用尽时,环境状态是那一刻的快照。这个快照仍然可以按结果层判定——它可能刚好达成,也可能差一步。按快照判定比按「超时即失败」更准确。


5. 两类环境与四种形态 ​

对话型任务必须有用户模拟器。另一大类任务没有对话对方:代码生成、数据分析、数学求解,Agent 自始至终只与工具交互,正确性由能否通过执行验证决定。

这两类的结构差异:

维度人机交互型工具调用型
交互对象模拟用户无
考察重点沟通策略与引导可观测状态变更的正确性
评分主体环境断言加自然语言断言测试代码
终止条件用户确认或轮数耗尽给出答案或轮数耗尽

工具调用型环境按「是否保持跨轮状态」与「是否隔离」两个维度再分层:

形态状态工具调用典型用例
单轮无无单轮问答、数学题
工具无多轮搜索加信息综合
有状态工具有多轮修改数据库记录后验证
沙盒有加隔离多轮代码执行与测试

分层的实际用途是选型:任务只需要一问一答,不要引入沙盒;任务会改数据库,就必须有状态与重置。


6. 五个基准的设计对照 ​

同一个五要素框架,填法可以差很远。几个公开基准的填法对照,能看出取舍空间。

基准被测能力任务来源环境扮演者验证器
对话型客服人机交互与工具调用人工编写加组合生成用户模拟器加业务数据库四层检查按聚合规则判二元
代码修复软件开发真实议题,人工筛选代码仓库加测试套件修复前失败修复后通过,加未破坏原有
手机界面操作图形界面操作参数化模板实例化真实设备模拟器最终界面状态断言
桌面操作图形界面操作从预置中间状态启动真实虚拟机百余个独立评估函数
终端操作命令行编程人工编写容器文件系统检查加真实执行
信息搜集通用助手人工编写加专有附件开放网络精确字符串匹配

六个基准覆盖了四类环境形态(有交互对象、有状态、有隔离、开放环境)。它们的验证器深度也不同:从界面状态断言到百余个评估函数。

一条观察:验证器越深的基准,任务数越少。原因是每个任务的准备与验证成本都更高。这是评估集设计里的一个固定取舍——深度与广度不可兼得。


7. 难度分层与诊断价值 ​

评估集需要覆盖不同难度,不只是为了「区分度」,更是为了诊断。

一个公开基准把全部任务分三级,并给出人类与模型的表现对照:

级别所需能力人类模型
一级一到两个工具93.9%30.3%
二级多步思考91.8%9.7%
三级复杂组合87.3%0%

这一分层的价值在最后一列的形状:模型从三成掉到零,人类只掉六个百分点。三条曲线的差距不同,指向不同的改进方向——一级失败指向基础工具使用,二级指向多步规划与信息整合,三级指向长序列思考。

如果只看总体通过率,这三个方向会被平均成一个数。

7.1 难度分层的两个操作要求 ​

每级任务数不能太少。 三级只有五条任务时,一条的差别就是 20 个百分点,噪声淹没有效信号。

难度要按能力定义,不按步骤数定义。 「需要十步」不等于「困难」——十步重复操作可能很简单。「需要多步且每步依赖前一步的结论」才是困难。

后者与前者在实践中经常被混淆,导致某些基准的「困难」任务其实只是冗长。


8. 指标口径:能力上限与可靠性 ​

上一节确定在哪里测,这一节确定怎么算成功。

同一个成功率数字,在不同场景下含义完全不同。若它对应退款客服,意味着每五名用户有一人未拿到退款;若它对应漏洞挖掘,五次中命中四次已属可观。差别在业务对成功率的要求。

两个指标分别回答两个问题。

能力上限。 同一任务运行 \(k\) 次,只要至少一次通过,任务就算通过:

\[\mathrm{Pass@}k = 1 - (1-p)^{k} \]

业务可靠性。 同一任务连续运行 \(k\) 次,要求每一次都通过,且不能触发安全、合规或幻觉等一票否决项:

\[\mathrm{Pass}^{k} = p^{k} \]

两者的差距在 \(p\) 中等时最惊人。取 \(p = 0.6\)、\(k = 5\):

\[\mathrm{Pass@}5 = 1 - 0.4^{5} \approx 99.0\%, \qquad \mathrm{Pass}^{5} = 0.6^{5} \approx 7.8\% \]

前一个数字说「几乎总能至少成功一次」,后一个说「连续五次都不出错仍然很难」。前者适合探索型任务的能力展示,后者才接近支付、退款、权限变更、生产部署的可靠性要求。

报告必须写清 \(k\) 的口径:是同一任务的 \(k\) 次独立采样,还是生产流水线上连续 \(k\) 个任务。对会产生副作用的操作,不能简单地重试直到成功,而应在沙盒或可回滚环境中采样,并把每一次失败都记入可靠性指标。


9. 采样预算:任务数与重复次数 ​

两笔独立的预算,不能互换。

任务数决定分辨力。设允许误差 \(\varepsilon\)、置信水平对应 \(z\)(95% 时约 1.96)、成功率先验 \(p\):

\[n = \frac{z^{2}\,p\,(1-p)}{\varepsilon^{2}} \]

取 \(p = 0.5\)(最保守)、\(\varepsilon = 0.05\):\(n \approx 384\)。

这个数字让很多团队吃惊——他们通常用几十个任务就下结论。几十个任务只能分辨 15 到 20 个百分点的差异,分辨不了 5 个百分点。\(\varepsilon\) 放宽到 0.1 时 \(n \approx 96\),所以任务数取决于要分辨多大的差异。

重复次数决定单任务结论的稳定性。采样温度、工具时序、环境状态都会带来随机性。

两个预算的差别:

配置知道什么不知道什么
任务少、每任务多次这批任务上很稳换一批是否成立
任务多、每任务一次覆盖广单任务结论是否靠运气

实用配比按场景分:

场景任务数重复理由
回归集多(200 以上)少(1–2)关注是否退化,覆盖优先
关键任务少(10–20)多(5–10)关注稳定性,方差优先
能力上限中(50)多(10 以上)需要 pass@k,样本要够

第三个场景的成本最高:50 个任务乘 10 次是 500 次运行。所以能力评测通常离线做,不进每次发布的回归。


10. 数据集构造 ​

四件事决定数据集能不能用。

任务族划分。 按任务形状分组(单轮问答、多轮检索、代码修改、文档生成),不按业务领域分。同一族内的任务在失败模式上相似,聚合才有意义。

难度分层。 每族内分易、中、难。只看总通过率会掩盖「难任务全崩」——总通过率 82% 可能是易题 100%、难题 20%。

保留集。 划出一部分任务,只用于发布前评测,不参与任何调优决策。保留集的独立靠使用纪律维持,不靠物理隔离:如果它被用来选择提示词、选阈值、选模型,它就不再是保留集。

检查方法两条:看它有多久没被用于决策,以及它与训练集的文本重叠度。重叠超过 10% 说明已被污染。

反例集。 专门收集已知的失败模式。它不追求通过率,追求「不能退化」——例如「曾经越权过一次的那类任务」必须永远通过。

10.1 任务从哪里来 ​

三个来源,各自的成本与代表性不同。

来源代表真实使用的程度成本局限
人工编写低到中每条几十到几百元容易写出「教科书式」的任务
真实日志回放高需要脱敏与重放环境分布偏斜,需聚类去重
参数化模板中一次编写,多次实例化只覆盖模板考虑到的维度

第一个来源的问题是它反映作者的想象,而作者的想象往往比真实使用更规范——真实用户会写错字、给错信息、中途改需求。

第三个来源的价值在于可扩展,但它只能覆盖模板设计时想到的参数。模板里没有的维度永远不会出现。

实践中常见组合是「真实日志提供分布、人工补齐少见情形、模板扩展数量」。三者各补另一方的缺口。

10.2 参数化模板的三重收益 ​

模板化不只是省事。

回放失效。 参数每次不同,背诵固定操作序列没有用。

近乎无限实例。 一个模板可以生成成千上万个实例,适合做重复采样的稳定性检验。

可做对照实验。 固定部分参数、只改其余参数,可以精确测量某个因素的影响。这是模板化最被低估的用途——它让「某条件是否影响成功率」变成一个可测的问题。


11. 完整评测的配置 ​

把前面各节的参数合到一处,看一次可用的评测需要定下哪些东西。

yaml
评测配置:
  任务集:
    回归集: 240 条(四族各 60 条,每族内易中难 20/20/20)
    保留集: 80 条(冻结后不参与任何调优)
    反例集: 30 条(要求 100% 通过)
    陷阱集: 20 条(要求 0% 上当)
  采样:
    回归: 每任务 1 次
    关键任务: 每任务 5 次
    能力评测: 每任务 10 次(离线)
  判据:
    关键用例: 全过
    总体通过率: 不低于基线减 2 个百分点
    单任务族: 不低于基线减 10 个百分点
  成本与时延:
    单任务成本: 不超过基线 1.2 倍
    首词时延: 不超过基线 1.3 倍

四组参数各自的来历。

任务数 240。 按 \(\varepsilon = 0.065\) 反推(\(n \approx 1.96^2 \times 0.25 / 0.065^2 \approx 227\),取整到 240 便于四族均分)。这个精度能分辨 6 到 7 个百分点的差异。

保留集 80。 少于回归集,因为它的用途不同——它只需给出「有没有整体退化」的判断,不需要分辨细粒度差异。

重复 5 次。 关键任务的单次结果有随机性,5 次能看出波动范围。少于 3 次时,一次偶发失败与真实退化无法区分。

单族 10 个百分点。 四族各 60 条,单族的标准误比总体大。10 个百分点的判据比总体宽松,正是为了容纳这个更大的噪声。

11.1 配置之外的两件事 ​

配置文件写不进去的两件事,但它们决定这套配置有没有用。

谁来看结果。 如果没人按族看结果,那「单族不塌陷」这条判据永远不会触发。

保留集多久轮换。 保留集不是永久不变的。使用一段时间后,它的题目会泄漏到调优过程里(因为人看过结果)。可行做法是每季度换掉三分之一,换掉的题目降级进回归集。

失败模式症状成因修的位置
用测试集调参分数持续上升,线上无改善看到失败就改提示词,改完跑同一批保留集,调参只看训练子集
评分器与实现互相泄漏评测通过但真实失败评分器检查内部实现细节只检查外部可观察行为
只看总分不看维度总分不变,某族全崩另一族涨聚合时平均掉了按族分别报告,关键族设硬性要求
任务分布偏离真实使用通过率高但投诉集中用例以单轮为主,实际多轮按真实调用形状配比

第四行是最容易犯的:200 个用例里 180 个是单轮问答、20 个需要多轮工具调用,而真实使用里八成是后者。通过率的分母与真实分布严重不匹配。

第三行最隐蔽,因为它在总分上看不出来。一个具体的例子:某次改动让代码修改类任务全崩、文档生成类大幅改善,总分持平。只看总分,这次改动会被当成「无影响」合并进去。