Skip to content
チュートリアル16分で読めます

PotatoによるAIエージェントのMT-Benchスタイル・ルーブリック評価

Potatoの rubric_eval を使い、カスタム基準・設定可能な評価尺度・次元ごとの重みを備えた多基準ルーブリック評価を組み立て、AIエージェントを体系的に評価する方法を解説します。

Potato Team

ルーブリック評価とは

ルーブリック評価は、構造化された採点の手法です。アノテーターは、定められた尺度を使って、独立した複数の基準ごとに出力を採点します。MT-Benchを使ったことがあれば見覚えがあるはずです。「この応答はどれくらい良いか」と一度に尋ねる代わりに、「有用性の観点ではどうか。正確性では。一貫性では。安全性では」と尋ねます。基準ごとに評価が付き、それらが合わさって品質のプロファイルになります。

エージェント評価では、この方法が単一スコアの取りこぼすニュアンスを拾います。エージェントは、正しいけれど非効率(5ステップで済むところに30ステップかけて正解に到達する)こともあれば、安全だが役に立たない(タスクを完了させるアクションを拒否する)、速いが雑、丁寧だが冗長、ということもあります。数値がひとつだけだと、これらはすべて平らに潰れます。ルーブリックはそれを残し、どれくらい悪いかだけでなくどこを直すべきかを教えてくれます。

ルーブリック評価のインターフェースは、体系的な評価のために多基準のグリッドを表示します。

複数の基準を並べたMT-Benchスタイルのルーブリック評価グリッドRubric evaluation grid showing multiple criteria with anchored rating scales


rubric_eval スキーマ

Potatoの rubric_eval アノテーションスキーマでは、次のものを定義できます。

  • カスタム基準:評価の次元をいくつでも定義できます。それぞれに名前と説明を付けます
  • 評価尺度:1〜5、1〜7、1〜10、または任意のカスタム尺度
  • 尺度点の説明:各基準について、各評価レベルが何を意味するかの詳細な説明(アンカー付き尺度)
  • 総合品質(任意):アノテーターの全体的な印象を受け止めるサマリー行
  • 次元の重み:加重集計スコアを計算するための重み(任意)

インターフェースはグリッドです。左端に基準が並び、上部に評価ボタンが並び、各尺度点の説明はツールチップで表示されます。アノテーターは基準を任意の順序で評価でき、送信前であれば評価を変更できます。スキーマの完全なリファレンスはルーブリック評価のドキュメントを参照してください。


エージェントの種類別の基準例

コーディングエージェント(Claude Code、Aider、SWE-Agent)

基準測っているもの
正確性(Correctness)そのコードは提示された問題を解いているか
コード品質(Code Quality)コードは読みやすく、整っていて、その言語らしい書き方になっているか
効率性(Efficiency)エージェントの手数は妥当か
ドキュメント(Documentation)変更内容がコメントやコミットメッセージで説明されているか
エラー処理(Error Handling)エッジケースやエラーを適切に扱えているか

Webブラウジングエージェント(WebArena、VisualWebArena)

基準測っているもの
タスク成功(Task Success)エージェントは依頼されたタスクを完了したか
ナビゲーション効率(Navigation Efficiency)直線的に進んだか、それとも迷走したか
エラー回復(Error Recovery)誤クリックや行き止まりからどれだけうまく立ち直ったか
安全性(Safety)確認なしにフォーム送信・購入・取り消せない操作をしていないか

対話エージェント(ChatGPT、Claude、独自実装)

基準測っているもの
有用性(Helpfulness)ユーザーの実際のニーズに対してどれだけ役に立つか
正確性(Accuracy)事実の主張は正しいか
一貫性(Coherence)応答の構成が良く、追いやすいか
安全性(Safety)有害・偏った・不適切な内容を避けているか
指示追従(Instruction Following)ユーザーの具体的な指示や制約に従っているか

セットアップ手順

ステップ1:評価基準を決める

まず、対象のエージェントにとって重要な品質の次元を書き出します。良いルーブリックの基準数は3〜7個です。3個を下回るとルーブリックにする意味が薄れ、7個を超えるとアノテーターが疲れて、その分データの質が落ちます。

このチュートリアルでは、コーディングエージェント向けに基準5個のルーブリックを組みます。

ステップ2:尺度点の説明を書く

アンカー付き尺度はアノテーター間一致度を大きく改善します。「正確性で5点中3点」が何を指すのかをアノテーターの推測に委ねず、各レベルを明文化します。

コーディングエージェント用ルーブリックの尺度説明は次のとおりです。

正確性:

  • 1:問題にまったく対応していない、または新たなバグを持ち込んでいる
  • 2:部分的には対応しているが、機能上の重大な誤りがある
  • 3:主要な問題は解決したが、エッジケースで失敗する、または軽微なバグがある
  • 4:問題を正しく解決しており、残る問題はごく些細なものだけ
  • 5:完全に正しく、すべてのエッジケースを扱えている

コード品質:

  • 1:読めない。スタイルに一貫性がなく、構造もない
  • 2:ある程度は読めるが、スタイルや設計に大きな問題がある
  • 3:許容できる品質で、その言語の基本的な慣習には従っている
  • 4:命名と構成が良く、整理された読みやすいコード
  • 5:その言語らしく、十分に文書化され、保守しやすい優れたコード

効率性:

  • 1:極端に遠回りな経路をたどり、無駄なステップが多い
  • 2:非効率が目立つ。作業の重複や不要な探索がある
  • 3:多少の無駄はあるが、おおむね妥当な進め方
  • 4:効率的で、不要なステップはわずか
  • 5:解決までの経路が最適、またはほぼ最適

ドキュメント:

  • 1:変更の説明もコメントもない
  • 2:説明が最小限で、重要な点が抜けている
  • 3:何を変更したかの説明としては十分
  • 4:何を、なぜ変更したかがきちんと説明されている
  • 5:背景・根拠・注意点まで含めた丁寧な説明

エラー処理:

  • 1:エラー処理がなく、想定外の入力でクラッシュする
  • 2:エラー処理が最小限で、多くの失敗経路が手当てされていない
  • 3:よくあるケースには基本的なエラー処理がある
  • 4:情報量のあるエラーメッセージを伴う良好なエラー処理
  • 5:網羅的なエラー処理があり、破綻せずに機能を落とせる

ステップ3:YAMLで rubric_eval を設定する

config.yaml の全体は次のようになります。

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

ステップ4:アノテーションサーバーを起動する

bash
potato start config.yaml -p 8000

ステップ5:アノテーターの作業の流れ

アノテーターがタスクを開くと、次のものが表示されます。

  1. 上部にタスクの説明(「空のQuerySetに対して .values() を呼んだときに django/db/models/query.py で発生する TypeError を修正する」)
  2. 中央にエージェントのトレース。ステップごとの推論とコードの変更が表示されます
  3. トレースの下にルーブリックのグリッド

ルーブリックのグリッドは、すべての基準を行として表示します。各行には次のものがあります。

  • 左側に基準の名前と説明
  • 行に沿って並ぶ評価ボタン(1〜5)
  • 評価ボタンにカーソルを合わせると、そのレベルの尺度説明が表示される

アノテーターは次のように進めます。

  1. エージェントのトレースを読み、進め方と出力を把握する
  2. 該当する評価ボタンをクリックして、基準ごとに評価する
  3. (任意)総合品質の評価を付ける
  4. (任意)補足のメモを書く
  5. 「Submit」をクリックするか Ctrl+Enter を押して送信する

基準は任意の順序で評価でき、送信前なら変更できます。インターフェースは未評価の基準をハイライトして、記入漏れを防ぎます。


他のエージェント向けにルーブリックを作り替える

Webエージェント用ルーブリック

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 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

ルーブリックデータのエクスポート

送信されたルーブリックは、構造化されたJSONオブジェクトになります。

json
{
  "trace_id": "trace_042",
  "annotator": "annotator_03",
  "timestamp": "2026-03-20T10:15:32Z",
  "rubric": {
    "criteria_ratings": {
      "correctness": 4,
      "code_quality": 3,
      "efficiency": 5,
      "documentation": 2,
      "error_handling": 3
    },
    "overall": 4,
    "notes": "Agent found and fixed the bug efficiently but did not add any comments explaining the change. Error handling for the edge case is minimal.",
    "weighted_score": 3.56
  }
}

weighted_score は、設定した重みを使って自動的に計算されます。

text
weighted_score = sum(rating * weight for each criterion) / sum(weights)
             = (4*3.0 + 3*2.0 + 5*1.5 + 2*1.0 + 3*1.5) / (3.0 + 2.0 + 1.5 + 1.0 + 1.5)
             = (12 + 6 + 7.5 + 2 + 4.5) / 9.0
             = 32.0 / 9.0
             = 3.56

分析:ルーブリックデータを扱う

読み込みと基準ごとの平均の算出

python
import json
import pandas as pd
import numpy as np
from pathlib import Path
 
# Load rubric annotations
rubrics = []
for f in Path("output/").glob("*.jsonl"):
    with open(f) as fh:
        for line in fh:
            rubrics.append(json.loads(line))
 
print(f"Loaded {len(rubrics)} rubric annotations")
 
# Extract criteria ratings into a DataFrame
ratings_list = []
for r in rubrics:
    row = {"trace_id": r["trace_id"], "annotator": r["annotator"]}
    row.update(r["rubric"]["criteria_ratings"])
    row["overall"] = r["rubric"].get("overall")
    row["weighted_score"] = r["rubric"].get("weighted_score")
    ratings_list.append(row)
 
df = pd.DataFrame(ratings_list)
 
# Per-criterion averages
criteria = ["correctness", "code_quality", "efficiency", "documentation", "error_handling"]
print("\nPer-criterion averages:")
for c in criteria:
    print(f"  {c}: {df[c].mean():.2f} (std: {df[c].std():.2f})")
print(f"\n  overall: {df['overall'].mean():.2f}")
print(f"  weighted_score: {df['weighted_score'].mean():.2f}")

レーダーチャートによる可視化

ルーブリックデータの可視化には、レーダーチャート(スパイダープロット)が素直に向いています。品質のプロファイル全体をひと目で見せられます。

python
import matplotlib.pyplot as plt
import numpy as np
 
criteria = ["correctness", "code_quality", "efficiency", "documentation", "error_handling"]
labels = ["Correctness", "Code Quality", "Efficiency", "Documentation", "Error Handling"]
 
# Compute mean ratings
means = [df[c].mean() for c in criteria]
 
# Create radar chart
angles = np.linspace(0, 2 * np.pi, len(criteria), endpoint=False).tolist()
means_plot = means + [means[0]]  # close the polygon
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_yticklabels(["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)
print("Saved rubric_radar.png")

複数のエージェントを比較する

データセットに複数のエージェントのトレースが含まれている場合は、レーダーチャートを重ねて描けます。

python
agents = df["trace_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]):
    agent_df = df[df["trace_id"].str.startswith(agent)]
    agent_means = [agent_df[c].mean() for c in criteria]
    agent_plot = agent_means + [agent_means[0]]
    ax.fill(angles, agent_plot, alpha=0.1, color=colors[i])
    ax.plot(angles, agent_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))
ax.set_title("Agent Quality Comparison", size=16, pad=20)
plt.tight_layout()
plt.savefig("rubric_comparison.png", dpi=150)
print("Saved rubric_comparison.png")

基準ごとのアノテーター間一致度

ルーブリック評価では、基準ごとの一致度を簡単に測れます。どの次元が主観的で、どの次元が比較的客観的かが分かります。

python
from itertools import combinations
 
def krippendorff_alpha_simple(ratings_by_annotator, value_domain):
    """Simplified Krippendorff's alpha for ordinal data."""
    # Group ratings by item
    items = {}
    for ann, ann_ratings in ratings_by_annotator.items():
        for trace_id, rating in ann_ratings.items():
            if trace_id not in items:
                items[trace_id] = []
            items[trace_id].append(rating)
 
    # Only use items with 2+ ratings
    items = {k: v for k, v in items.items() if len(v) >= 2}
    if not items:
        return float("nan")
 
    # Observed disagreement
    Do = 0
    n_pairs = 0
    for ratings in items.values():
        for a, b in combinations(ratings, 2):
            Do += (a - b) ** 2
            n_pairs += 1
    Do /= n_pairs
 
    # Expected disagreement
    all_ratings = [r for ratings in items.values() for r in ratings]
    De = 0
    n_total = 0
    for a, b in combinations(all_ratings, 2):
        De += (a - b) ** 2
        n_total += 1
    De /= n_total
 
    if De == 0:
        return 1.0
    return 1 - Do / De
 
# Compute alpha per criterion
print("Inter-annotator agreement (Krippendorff's alpha):")
for criterion in criteria:
    ratings_by_ann = {}
    for _, row in df.iterrows():
        ann = row["annotator"]
        if ann not in ratings_by_ann:
            ratings_by_ann[ann] = {}
        ratings_by_ann[ann][row["trace_id"]] = row[criterion]
 
    alpha = krippendorff_alpha_simple(
        ratings_by_ann,
        value_domain=list(range(1, 6))
    )
    print(f"  {criterion}: {alpha:.3f}")

実際にやってみると、正確性は比較的客観的なので一致度が高く出て、ドキュメントとコード品質は主観が入るぶん低く出るのが普通です。これは、尺度説明のどこに一番手を入れるべきかを示すシグナルです。


ルーブリック評価とトラジェクトリ評価を組み合わせる

最も踏み込んだ評価をしたい場合は、ひとつのアノテーションタスクの中で rubric_eval と trajectory_eval を併用します。アノテーターはまずトレースをステップごとにたどってエラーと重大度を記録し(trajectory_eval)、続いて各基準にわたって全体の品質を評価します(rubric_eval)。

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

結果として、トレース1件につき2つのデータ構造が手に入ります。trajectory_eval から得られる詳細なエラーマップと、rubric_eval から得られる品質プロファイルです。前者は「エージェントはどこで間違えたか」に、後者は「結果として全体的にどれだけ良かったか」に答えます。


まとめ

rubric_eval によるルーブリック評価は、エージェントの品質を単一の数値ではなく多次元で見せます。カスタム基準とアンカー付きの尺度説明を用意すれば、どの次元を改善すべきかが分かる診断的な情報が得られ、同じ基準でエージェント同士を公平に比較でき、アンカーが一致度を押し上げるぶん測定も安定します。同じスキーマはコーディングエージェントにもWebエージェントにも対話エージェントにも、それ以外にも使えて、得られたデータはレーダーチャート、基準ごとの統計、一致度指標のいずれにも回せます。

対象のエージェントに合わせて3〜5個の基準から始め、尺度説明を詳しく書き、アノテーターからのフィードバックを受けてルーブリックを直していってください。良いルーブリックとは、各レベルが何を意味するかについてアノテーターが迷わないルーブリックです。