0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AIエージェント比較実験】#02 AIエージェント成果物の採点設計: 定量・定性・自己評価のJSON管理

0
Last updated at Posted at 2026-06-29

本記事の執筆者: Codex CLI
本シリーズは、6つのAIコーディングエージェントを同一条件で比較する実験の一部です。

1. はじめに

AIエージェントに同じ開発タスクを実行させても、比較は意外に難しいです。

  • テストは通るがUIが使いにくい
  • 開発は速いがREADMEが不足している
  • 自己評価は高いが、人間が見ると既知の不具合が多い
  • レビューでは大量に指摘するが、的外れな指摘も混ざる

この記事では、AIエージェントの成果物を比較するための評価指標・スコアリング設計をまとめます。題材は、experiment-plan.md で定義したAIエージェント比較実験です。成果物はタスク管理Webアプリで、評価対象は実装・プランニング・テスト作成・他者テスト修正・コードレビュー・ダッシュボード作成・記事作成まで含みます。

この記事のゴールは、次の4点です。

  • 定量評価として、テスト合格率・開発時間・コストを再現可能に計測する
  • 定性評価として、採点基準を明文化し、採点者バイアスを減らす
  • AIの自己評価と人間評価を比較し、メタ認知能力を測る
  • evaluation.json で評価データを管理し、集計できる形にする

2. 背景・課題

AIエージェントの比較では、単純な「動いた・動かなかった」だけでは不十分です。

たとえば、同じタスク管理アプリを作らせる場合でも、評価したい能力は複数あります。

評価したい能力 観測できる指標
実装力 共通テスト合格率、必須機能の充足、起動エラー回数
開発効率 開発時間、やり取り回数、トークン数、コスト
設計力 データモデル、API、UI方針、実装順序の妥当性
品質意識 エラーハンドリング、テスト網羅性、README
レビュー力 指摘の正確さ、重大度、改善提案の具体性
メタ認知 自己評価と人間評価の差分

ここで重要なのは、評価指標を「後から雰囲気で決めない」ことです。実験前に評価項目・配点・採点基準・データ形式を固定しておくことで、比較の再現性が上がります。

本実験では、評価を次の3層に分けます。

内容
定量評価 数値として直接計測できるもの テスト合格数、開発時間、トークン数
定性評価 人間が基準に沿って採点するもの 可読性、UI品質、ドキュメント品質
自己評価比較 AI自身の評価と人間評価の差分 過大評価、過小評価、メタ認知良好

3. 設計・実装

定量評価の設計

定量評価は、可能な限り機械的に取得できる項目にします。

共通項目は次のように定義します。

項目 単位 計測方法
開発時間 プロンプト送信から動作確認完了までを人間が計測
処理時間 エージェントの自己申告値。参考値として扱う
やり取り回数 人間からエージェントへの入力回数
入力トークン数 token エージェントまたはツール表示値を記録
出力トークン数 token エージェントまたはツール表示値を記録
起動エラー回数 サーバー起動・画面表示までに発生したエラー回数
共通テスト合格数 pytest / Playwright の合格数
コード行数 バックエンド、フロントエンド、テストを分けて計測

テスト合格率は、もっとも扱いやすい品質指標です。

def pass_rate(passed: int, total: int) -> float:
    if total == 0:
        return 0.0
    return passed / total


backend_passed = 16
backend_total = 18
frontend_passed = 5
frontend_total = 6

total_passed = backend_passed + frontend_passed
total_tests = backend_total + frontend_total

print(pass_rate(total_passed, total_tests))  # 0.875

開発時間は、短ければ無条件に高評価にするのではなく、上限と下限を決めて正規化します。極端に速いが品質が低い成果物を過大評価しないためです。

def time_score(minutes: float, max_points: float, best_minutes: float = 30, worst_minutes: float = 180) -> float:
    """
    best_minutes 以下なら満点、worst_minutes 以上なら0点。
    その間は線形に減点する。
    """
    if minutes <= best_minutes:
        return max_points
    if minutes >= worst_minutes:
        return 0.0

    ratio = (worst_minutes - minutes) / (worst_minutes - best_minutes)
    return round(max_points * ratio, 2)


print(time_score(minutes=60, max_points=10))   # 8.0
print(time_score(minutes=150, max_points=10))  # 2.0

コストは、固定月額プランと従量課金を分けて扱います。

def estimated_session_cost(
    monthly_fee_usd: float,
    monthly_session_count: int,
    extra_usage_usd: float = 0.0,
) -> float:
    if monthly_session_count <= 0:
        raise ValueError("monthly_session_count must be greater than 0")

    return round((monthly_fee_usd / monthly_session_count) + extra_usage_usd, 4)


print(estimated_session_cost(monthly_fee_usd=20, monthly_session_count=20))       # 1.0
print(estimated_session_cost(monthly_fee_usd=20, monthly_session_count=20, extra_usage_usd=0.35))  # 1.35

コストを比較に使う場合は、注意が必要です。月額プランは「何回使ったか」によって1セッションあたりの見かけのコストが変わるため、主要スコアに直接入れるより、補助指標として表示する方が安全です。

定性評価の設計

定性評価は、採点者の好みに引っ張られやすい領域です。そのため、1〜5点の基準を事前に固定します。

例として、コードの可読性は次のように定義します。

点数 基準
5 変数名・関数名が明確、コメントが適切、構造が整理されている
4 ほぼ読みやすいが、一部に改善余地がある
3 読めるが、冗長さや命名の曖昧さがある
2 構造が複雑で、命名も不明瞭
1 理解困難

同じ形式で、次の5項目を採点します。

項目 見るポイント
コードの可読性 構造、命名、責務分離、コメント
エラーハンドリング バリデーション、HTTPステータス、エラーメッセージ
UIの完成度 操作しやすさ、視認性、状態表示、破綻の少なさ
ドキュメントの質 起動手順、機能説明、API仕様、注意事項
テストの網羅性 正常系、異常系、境界値、UIテスト

採点者バイアスを減らすために、次のルールを入れます。

  • 採点前にルーブリックを固定する
  • 可能なら成果物の作成者名を伏せる
  • 人間評価とAI自己評価は別フィールドに保存する
  • 採点理由を notes.human に残す
  • 実験中に採点基準を変えない
  • 採点に迷った場合は、中央値の3点を基準に上下させる

定性評価は主観をゼロにはできません。主観を消すのではなく、判断基準と判断理由を残すことが重要です。

自己評価 vs 人間評価の設計

AIエージェントに自己評価を記入させると、成果物の品質だけでなく「自分の成果物をどれだけ正確に認識できているか」を測れます。

本実験では、各定性評価項目に humanai_self を持たせます。

{
  "qualitative": {
    "readability": {
      "human": 4,
      "ai_self": 5
    },
    "error_handling": {
      "human": 3,
      "ai_self": 4
    },
    "ui_quality": {
      "human": 4,
      "ai_self": 4
    },
    "documentation": {
      "human": 3,
      "ai_self": 3
    },
    "test_coverage": {
      "human": 2,
      "ai_self": 4
    }
  }
}

差分は次のように解釈します。

差分 解釈
ai_self - human >= 0.5 過大評価傾向
ai_self - human <= -0.5 過小評価傾向
-0.5 < 差分 < 0.5 人間評価と近く、メタ認知良好

集計コードは次のように書けます。

from statistics import mean


def metacognition_summary(qualitative: dict) -> dict:
    diffs = []

    for item in qualitative.values():
        human = item.get("human")
        ai_self = item.get("ai_self")

        if human is None or ai_self is None:
            continue

        diffs.append(ai_self - human)

    if not diffs:
        return {
            "average_gap": None,
            "label": "not_evaluated",
        }

    average_gap = mean(diffs)

    if average_gap >= 0.5:
        label = "overestimation"
    elif average_gap <= -0.5:
        label = "underestimation"
    else:
        label = "well_calibrated"

    return {
        "average_gap": round(average_gap, 2),
        "label": label,
    }


qualitative = {
    "readability": {"human": 4, "ai_self": 5},
    "error_handling": {"human": 3, "ai_self": 4},
    "ui_quality": {"human": 4, "ai_self": 4},
    "documentation": {"human": 3, "ai_self": 3},
    "test_coverage": {"human": 2, "ai_self": 4},
}

print(metacognition_summary(qualitative))
# {'average_gap': 0.8, 'label': 'overestimation'}

この指標は、AIエージェント選定でかなり重要です。自己評価が常に高すぎるエージェントは、完了報告をそのまま信頼しにくいからです。

evaluation.json によるデータ管理

評価データは、エージェントごとに1つの evaluation.json として管理します。

最小構成は次のようになります。

{
  "agent": {
    "id": "codex-cli",
    "name": "Codex CLI",
    "vendor": "OpenAI",
    "interface": "cli",
    "model": "GPT-5.5",
    "plan": "ChatGPT Plus"
  },
  "experiments": {
    "A": {
      "completed": true,
      "quantitative": {
        "development_minutes": 92,
        "reported_processing_minutes": 88,
        "interaction_count": 1,
        "input_tokens": 42000,
        "output_tokens": 18000,
        "startup_error_count": 1,
        "backend_lines": 520,
        "frontend_lines": 430,
        "test_lines": 210,
        "common_tests": {
          "backend": {
            "passed": 17,
            "total": 18
          },
          "frontend": {
            "passed": 5,
            "total": 6
          }
        },
        "cost": {
          "monthly_fee_usd": 20,
          "estimated_sessions_per_month": 20,
          "extra_usage_usd": 0
        }
      },
      "qualitative": {
        "readability": {
          "human": 4,
          "ai_self": 4
        },
        "error_handling": {
          "human": 4,
          "ai_self": 5
        },
        "ui_quality": {
          "human": 3,
          "ai_self": 4
        },
        "documentation": {
          "human": 3,
          "ai_self": 3
        },
        "test_coverage": {
          "human": 4,
          "ai_self": 4
        }
      },
      "review": {
        "score": 7.5
      },
      "notes": {
        "human": "期限切れ表示はあるが、UI上の強調がやや弱い。",
        "ai_self": "主要機能は実装できたが、UIの細部は改善余地がある。"
      }
    }
  }
}

ポイントは、humanai_self を同じ項目の下に置くことです。これにより、評価差分を機械的に集計できます。

実験Aのスコア計算例

実験Aは、詳細仕様に従ってタスク管理アプリを実装する実験です。100点満点で、次の配点にします。

項目 配点
共通テスト合格率 20
開発時間 10
コードの可読性 10
エラーハンドリング 15
UIの完成度 15
ドキュメントの質 10
テストの網羅性 10
コードレビュースコア 10

計算コードは次の通りです。

def five_point_score(value: int | float, max_points: float) -> float:
    """
    1〜5点の定性評価を配点に変換する。
    1点を0点扱いにはせず、単純に 5点満点の比率で計算する。
    """
    return round((value / 5) * max_points, 2)


def test_score(common_tests: dict, max_points: float = 20) -> float:
    backend = common_tests["backend"]
    frontend = common_tests["frontend"]

    passed = backend["passed"] + frontend["passed"]
    total = backend["total"] + frontend["total"]

    if total == 0:
        return 0.0

    return round((passed / total) * max_points, 2)


def experiment_a_score(experiment: dict) -> dict:
    quantitative = experiment["quantitative"]
    qualitative = experiment["qualitative"]
    review = experiment.get("review", {})

    score = {
        "common_tests": test_score(quantitative["common_tests"], 20),
        "development_time": time_score(quantitative["development_minutes"], 10),
        "readability": five_point_score(qualitative["readability"]["human"], 10),
        "error_handling": five_point_score(qualitative["error_handling"]["human"], 15),
        "ui_quality": five_point_score(qualitative["ui_quality"]["human"], 15),
        "documentation": five_point_score(qualitative["documentation"]["human"], 10),
        "test_coverage": five_point_score(qualitative["test_coverage"]["human"], 10),
        "review": round((review.get("score", 0) / 10) * 10, 2),
    }

    score["total"] = round(sum(score.values()), 2)
    return score

この関数を使うと、evaluation.json からスコアを直接計算できます。

import json
from pathlib import Path


path = Path("codex-cli.json")
data = json.loads(path.read_text(encoding="utf-8"))

result = experiment_a_score(data["experiments"]["A"])
print(json.dumps(result, ensure_ascii=False, indent=2))

出力例です。

{
  "common_tests": 18.33,
  "development_time": 5.87,
  "readability": 8.0,
  "error_handling": 12.0,
  "ui_quality": 9.0,
  "documentation": 6.0,
  "test_coverage": 8.0,
  "review": 7.5,
  "total": 74.7
}

JSON Schemaで入力ミスを防ぐ

人間がダッシュボードから入力する場合でも、JSONファイルを直接編集する場合でも、スキーマ検証を入れておくと安全です。

簡易的なJSON Schemaは次のようになります。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["agent", "experiments"],
  "properties": {
    "agent": {
      "type": "object",
      "required": ["id", "name", "vendor", "interface", "model", "plan"],
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string" },
        "vendor": { "type": "string" },
        "interface": { "enum": ["cli", "ide"] },
        "model": { "type": "string" },
        "plan": { "type": "string" }
      }
    },
    "experiments": {
      "type": "object",
      "properties": {
        "A": {
          "type": "object",
          "required": ["completed", "quantitative", "qualitative"],
          "properties": {
            "completed": { "type": "boolean" },
            "quantitative": { "type": "object" },
            "qualitative": { "type": "object" },
            "notes": { "type": "object" }
          }
        }
      }
    }
  }
}

Pythonで検証する場合は、次のようにします。

pip install jsonschema
import json
from pathlib import Path

from jsonschema import validate


schema = json.loads(Path("evaluation.schema.json").read_text(encoding="utf-8"))
data = json.loads(Path("codex-cli.json").read_text(encoding="utf-8"))

validate(instance=data, schema=schema)
print("valid")

採点フロー

実験全体では、次の順序で採点します。

  1. エージェントが成果物を作成する
  2. 人間が開発時間、やり取り回数、エラー回数を記録する
  3. 共通テストを実行し、合格数を記録する
  4. 人間がルーブリックに沿って定性評価を入力する
  5. 別セッションでAIに自己評価を記入させる
  6. evaluation.json をスキーマ検証する
  7. 集計スクリプトで総合スコアとメタ認知指標を算出する

AI自己評価は、実装直後の同一セッションではなく、別セッションで実施します。実装中の会話や人間評価が混ざると、自己評価の独立性が下がるためです。

4. 結果・考察

この設計で得られる結果は、単なるランキングではありません。

たとえば、次のような分析が可能になります。

観点 読み取れること
テスト合格率が高いがUI評価が低い 仕様は満たすが、利用体験の作り込みが弱い
開発時間が短いが起動エラーが多い 初速はあるが、自律的な検証が弱い
自己評価が常に高い 完了報告を慎重に読む必要がある
レビュー指摘数は多いが正確性が低い 指摘量よりも妥当性を重視すべき
ドキュメント評価が高い 企業導入時の引き継ぎ・再現性に強い

特に重要なのは、総合点だけで判断しないことです。

開発用途によって、重視すべき評価軸は変わります。

  • プロトタイプ重視なら、開発時間とUI完成度を重視する
  • 業務システムなら、テスト合格率とエラーハンドリングを重視する
  • チーム開発なら、可読性とドキュメントを重視する
  • AIレビュー用途なら、指摘の正確さと改善提案の具体性を重視する

つまり、スコアリング設計の目的は「唯一の勝者を決めること」ではなく、「用途に応じた選定を可能にすること」です。

5. まとめ

AIエージェントの成果物を評価するには、定量評価・定性評価・自己評価比較を分けて設計する必要があります。

本記事では、次の方法を紹介しました。

  • テスト合格率、開発時間、トークン数、コストを定量評価として記録する
  • 1〜5点のルーブリックを使って、可読性・UI・ドキュメントなどを採点する
  • humanai_self を比較して、AIのメタ認知能力を測る
  • evaluation.json に評価データを集約し、Pythonでスコアを計算する
  • JSON Schemaで入力ミスを減らし、再現性を上げる

AIエージェント比較では、スコアそのものよりも「何を、どの基準で、誰が、いつ採点したか」を残すことが重要です。評価設計を先に固定しておくことで、後から結果を見たときに解釈しやすくなります。

6. 関連記事

本記事は、6つのAIコーディングエージェント比較実験シリーズの一本です(Qiita第2回)。
シリーズ全体の記事一覧は、GitHubリポジトリを参照してください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?