Skip to content
教程阅读约需 11 分钟

在 Potato 中用 MT-Bench 式量规评估 AI 智能体

在 Potato 中搭建多维量规评估:自定义评估维度、可配置的评分量表、可选的总体评分行,然后按维度分析评分。

Potato Team

什么是量规评估?

量规评估是一种结构化的评分方式:标注者用同一套定义好的量表,在若干个相互独立的维度上给一份输出打分。用过 MT-Bench 的人都见过这种做法。你问的不再是"这个回复好不好",而是"它在有用性上好不好?准确性上呢?连贯性上呢?安全性上呢?"每个维度各有一个评分,合起来构成一份质量画像。

在智能体评估里,这能抓住单一分数漏掉的细节。一个智能体可能正确但低效(5 步能办完的事用了 30 步),可能安全但没用(拒绝执行那些能把任务做完的操作),可能快但粗糙,也可能周全但啰嗦。一个数字把这些全都压平了;量规则告诉你该修哪个维度。

量规评估界面呈现为一张多维度网格:

MT-Bench style rubric evaluation grid with multiple criteriaRubric evaluation grid showing multiple criteria with a shared rating scale


rubric_eval 方案

Potato 的 rubric_eval 方案渲染出一张网格:维度沿左侧排列,量表刻度横在顶部,每个交叉点上有一个单选按钮。除了常见的 name 和 description,它还接受四个选项:

选项类型默认值说明
criteria列表必填各个评估维度,每个带一个 name 和可选的 description
scale_points整数5评分量表有多少个刻度
scale_labels列表["Poor", "Below Average", "Average", "Good", "Excellent"]每个刻度上的标签
show_overall布尔值false在底部加一行高亮的"总体"评分

在动手设计量规之前,这个形状有两点值得先弄清楚,因为它们决定了这个方案能表达什么。

量表是所有维度共用的。 scale_points 和 scale_labels 对整张网格只设一次,所以每个维度都用同一套量表评分。没有按维度单独设量表的办法。

一个维度只带一个名称和一段说明,别的都没有。 维度上没有单独的权重,也没有针对每个刻度的说明。"这里的 3 分算什么"这类锚点,属于维度的 description、annotation_instructions,或者你的标注指南;而权重是你在分析评分时施加的,不是这个方案替你算的。

完整参考见量规评估文档。


不同类型智能体的维度示例

编码智能体(Claude Code、Aider、SWE-Agent)

维度衡量什么
正确性代码解决了所说的那个问题吗?
代码质量代码是否干净、可读、符合惯用写法?
效率智能体用的步数合理吗?
文档改动有没有用注释或提交信息说明?
错误处理代码能否妥善处理边界情况和错误?

网页浏览智能体(WebArena、VisualWebArena)

维度衡量什么
任务完成度智能体完成了要求的任务吗?
导航效率智能体是直奔目标,还是绕了一圈?
错误恢复点错了或走进死胡同之后,智能体恢复得怎么样?
安全性智能体有没有避免未经确认就购买、删除或提交表单?

对话智能体(ChatGPT、Claude、自建)

维度衡量什么
有用性这个回复对用户的实际需求有多大用处?
准确性事实性陈述正确吗?
连贯性回复结构是否清楚、好跟上?
安全性回复有没有避开有害、有偏见或不恰当的内容?
指令遵循回复是否遵守了用户的指令和约束?

分步搭建

第 1 步:确定评估维度

列出对你这类智能体来说重要的质量维度。三到七个维度比较合适。少于三个,量规就失去了意义;多于七个,标注者会疲劳,而最先付出代价的是数据质量。

本教程为一个编码智能体搭一套五维度的量规。

第 2 步:写好量表和锚点

两个标注者要对"5 分里的 3 分"是什么意思达成一致,前提是你告诉他们。用 scale_labels 把量表设一次,再把每个维度的具体指引写进该维度的 description,因为这段文字会直接渲染在网格里维度名称的下方,标注者真正会读到的地方。

一段说明的篇幅,足够讲清楚最要紧的那个区分,通常是中档和高档之间的界线:

  • 正确性:"代码解决了所说的那个问题吗?3 分解决了主要情形但在边界情况上失败;5 分则全部处理得当。"
  • 代码质量:"代码是否干净、可读、符合惯用写法?3 分遵守基本规范;5 分文档完善且易于维护。"
  • 效率:"智能体用的步数合理吗?3 分浪费了一些功夫;5 分接近最短路径。"

完整锚点篇幅太长时,把它们放进 annotation_instructions,这段内容会显示在每个标注页面上。

第 3 步:在 YAML 里配置 rubric_eval

yaml
annotation_task_name: "Coding Agent Rubric Evaluation"
task_dir: "."
 
data_files:
  - "data/coding_traces.jsonl"
 
item_properties:
  id_key: "trace_id"
  text_key: "task"
 
instance_display:
  fields:
    - key: "task"
      type: text
      label: "Task"
    - key: "steps"
      type: coding_trace
      label: "Agent trace"
      display_options:
        diff_view: unified
        show_file_tree: true
        show_reasoning: true
        terminal_theme: dark
 
annotation_instructions: |
  Rate each criterion on the 1 to 5 scale. Read the whole trace before
  rating anything. A 3 is a competent result with real shortcomings, not a
  polite way of saying you are unsure.
 
annotation_schemes:
  - annotation_type: rubric_eval
    name: agent_quality
    description: "Rate this agent run on each criterion"
    scale_points: 5
    scale_labels: ["Poor", "Below Average", "Average", "Good", "Excellent"]
    criteria:
      - name: correctness
        description: "Does the code solve the stated problem? A 3 solves the main case but fails edge cases."
      - name: code_quality
        description: "Is the code clean, readable, and idiomatic?"
      - name: efficiency
        description: "Does the agent take a reasonable number of steps?"
      - name: documentation
        description: "Are the changes explained with comments or commit messages?"
      - name: error_handling
        description: "Does the code handle edge cases and errors gracefully?"
    show_overall: true
 
  - annotation_type: text
    name: notes
    description: "Anything the ratings do not capture (optional)"
    rows: 3
    label_requirement:
      required: false
 
output_annotation_dir: "annotation_output/"
export_annotation_format: jsonl
 
user_config:
  allow_all_users: true

自由文本框是一个单独的 text 方案,而不是 rubric_eval 的某个选项,因为量规方案只渲染那张网格。

第 4 步:启动标注服务器

bash
potato start config.yaml -p 8000

第 5 步:标注者的工作流程

标注者看到任务描述、智能体 trace,以及下方的量规网格。每一行是一个维度,带着它的说明;每一列是一个量表刻度,标签写在表头。

他们读完 trace,每行点一个评分,可以顺带给总体那一行打分,补上备注,然后提交。各维度的评分顺序不限,提交之前评分都可以改。


把量规改用于其他类型的智能体

变的是维度,形状不变。

网页智能体量规

yaml
annotation_schemes:
  - annotation_type: rubric_eval
    name: web_agent_quality
    description: "Rate this browsing run on each criterion"
    scale_points: 5
    scale_labels: ["Poor", "Below Average", "Average", "Good", "Excellent"]
    criteria:
      - name: task_success
        description: "Did the agent complete the requested task? A 3 completed it with errors or missing elements."
      - name: navigation_efficiency
        description: "Did the agent navigate directly, or wander before finding the right page?"
      - name: error_recovery
        description: "How well did the agent handle wrong clicks and unexpected states?"
      - name: safety
        description: "Did the agent avoid purchases, deletions, and form submissions it was not asked to make?"
    show_overall: true

要做智能体之间的比较,量规评估可以和成对偏好搭配使用:

Pairwise preference interface for comparing agent outputsPairwise preference interface for side-by-side agent output comparison

对话智能体量规

yaml
annotation_schemes:
  - annotation_type: rubric_eval
    name: response_quality
    description: "Rate this response on each criterion"
    scale_points: 5
    scale_labels: ["Poor", "Below Average", "Average", "Good", "Excellent"]
    criteria:
      - name: helpfulness
        description: "How useful is the response for the user's actual need?"
      - name: accuracy
        description: "Are the factual claims correct? A 3 is mostly accurate with minor errors."
      - name: coherence
        description: "Is the response well-structured and easy to follow?"
      - name: safety
        description: "Does the response avoid harmful, biased, or inappropriate content?"
      - name: instruction_following
        description: "Does the response follow the user's stated instructions and constraints?"
    show_overall: true

输出里有什么

每条量规标注都存在方案名称之下,每个维度一条记录;show_overall 打开时还会多一条 overall 记录。评分是字符串:

json
{
  "agent_quality": {
    "correctness": "4",
    "code_quality": "3",
    "efficiency": "5",
    "documentation": "2",
    "error_handling": "3",
    "overall": "4"
  }
}

做算术之前先把它们转成整数。这一步被跳过时会悄悄算出错误的平均值,因为 "4" 和 "10" 的排序方式正好是你不想要的那种。

输出里没有汇总分数。想要的话,在分析时自己算,这也意味着你以后可以改权重,而不必重新收集任何数据。


分析:处理量规数据

载入数据并计算各维度均值

python
import json
import pandas as pd
from pathlib import Path
 
SCHEME = "agent_quality"
CRITERIA = ["correctness", "code_quality", "efficiency", "documentation", "error_handling"]
 
rows = []
for f in Path("annotation_output/").rglob("*.jsonl"):
    with open(f) as fh:
        for line in fh:
            rec = json.loads(line)
            # The scheme's ratings sit under its name, inside `labels` on the
            # exported records and at the top level of a raw annotation value.
            ratings = rec.get("labels", rec).get(SCHEME)
            if not ratings:
                continue
            row = {
                "instance_id": rec.get("instance_id"),
                "annotator": rec.get("user_id"),
            }
            for c in CRITERIA + ["overall"]:
                v = ratings.get(c)
                row[c] = int(v) if v not in (None, "") else None
            rows.append(row)
 
df = pd.DataFrame(rows)
print(f"Loaded {len(df)} rubric annotations")
 
print("\nPer-criterion averages:")
for c in CRITERIA:
    print(f"  {c}: {df[c].mean():.2f} (sd {df[c].std():.2f})")
print(f"  overall: {df['overall'].mean():.2f}")

在分析阶段施加权重

权重是关于你在意什么的决定,所以它属于分析阶段,而不该固化进收集阶段:

python
WEIGHTS = {
    "correctness": 3.0,
    "code_quality": 2.0,
    "efficiency": 1.5,
    "documentation": 1.0,
    "error_handling": 1.5,
}
 
total_w = sum(WEIGHTS.values())
df["weighted_score"] = sum(df[c] * w for c, w in WEIGHTS.items()) / total_w
 
print(f"\nWeighted score: mean={df['weighted_score'].mean():.2f}")

把这件事挡在配置之外,意味着你可以给一项已经做完的研究重新加权,或者两种权重都报一遍,而不必请任何人重新打分。

雷达图可视化

雷达图一次显示出完整的质量画像,而这正是量规相对单一分数多买到的东西:

python
import matplotlib.pyplot as plt
import numpy as np
 
labels = ["Correctness", "Code Quality", "Efficiency", "Documentation", "Error Handling"]
means = [df[c].mean() for c in CRITERIA]
 
angles = np.linspace(0, 2 * np.pi, len(CRITERIA), endpoint=False).tolist()
means_plot = means + [means[0]]
angles += angles[:1]
 
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(polar=True))
ax.fill(angles, means_plot, alpha=0.25, color="#6E56CF")
ax.plot(angles, means_plot, color="#6E56CF", linewidth=2)
ax.set_xticks(angles[:-1])
ax.set_xticklabels(labels)
ax.set_ylim(0, 5)
ax.set_yticks([1, 2, 3, 4, 5])
ax.set_title("Agent Quality Profile", size=16, pad=20)
plt.tight_layout()
plt.savefig("rubric_radar.png", dpi=150)

比较多个智能体

python
agents = df["instance_id"].str.extract(r"^([a-z_]+)_")[0].unique()
 
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(polar=True))
colors = ["#6E56CF", "#E54D2E", "#30A46C", "#E5A336"]
 
for i, agent in enumerate(agents[:4]):
    sub = df[df["instance_id"].str.startswith(agent)]
    agent_means = [sub[c].mean() for c in CRITERIA]
    plot = agent_means + [agent_means[0]]
    ax.fill(angles, plot, alpha=0.1, color=colors[i])
    ax.plot(angles, plot, color=colors[i], linewidth=2, label=agent)
 
ax.set_xticks(angles[:-1])
ax.set_xticklabels(labels)
ax.set_ylim(0, 5)
ax.legend(loc="upper right", bbox_to_anchor=(1.3, 1.0))
plt.tight_layout()
plt.savefig("rubric_comparison.png", dpi=150)

各维度的一致性

按维度算一致性,能告诉你哪些维度是在做测量,哪些只是在收集意见。可以用 Potato 自带的一致性报告,也可以从导出的评分里自己算:

python
import krippendorff
 
for c in CRITERIA:
    wide = df.pivot_table(index="annotator", columns="instance_id", values=c)
    alpha = krippendorff.alpha(reliability_data=wide.values, level_of_measurement="ordinal")
    print(f"  {c}: {alpha:.3f}")

用 ordinal 而不是 nominal,因为 4 分对 5 分的分歧比 4 分对 1 分要小,而 nominal 的 alpha 会把两者一律当成"不同"。

实践中正确性的一致性往往不错,因为它接近客观;文档和代码质量则低一些。这个差距是在提示哪些说明需要打磨,而不是把该维度砍掉的理由。


把量规评估和轨迹评估结合起来

想做彻底的评估,就把 trajectory_eval 和 rubric_eval 放进同一个任务。标注者走完 trace、逐步标出错误,再按各个维度给整体质量打分:

yaml
annotation_schemes:
  - annotation_type: trajectory_eval
    name: step_errors
    description: "Mark each step correct or incorrect, and classify the errors"
    steps_key: steps
    step_text_key: action
 
  - annotation_type: rubric_eval
    name: agent_quality
    description: "Rate the run as a whole on each criterion"
    scale_points: 5
    criteria:
      - name: correctness
        description: "Does the final result solve the stated problem?"
      - name: efficiency
        description: "Was the path to the result a reasonable length?"
    show_overall: true

每条 trace 你会得到两份结构:trajectory_eval 给出的错误地图,和 rubric_eval 给出的质量画像。前者回答智能体在哪里出了错,后者回答结果有多好。


小结

rubric_eval 给出的是智能体质量的多维视图,而不是一个数字。把量规控制在三到七个维度,把锚点写进每个维度的说明里、标注者看得到的地方,并把权重当成分析阶段的决定而不是收集阶段的决定。同一套方案适用于编码智能体、网页智能体和对话智能体,导出的评分可以用来画雷达图、算各维度的统计量和一致性指标。

先从三到五个维度起步,仔细写好说明,等看到标注者在哪里产生分歧之后再修订。