0Pricing
AI Prompt Engineering · レッスン

比較評価:A対B

絶対評価を使わず、ペア単位の比較プロンプトで出力を順位付けします。

「比較評価:A対B」はCoddyKit上の無料AI Prompt Engineeringレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Prompt Engineering学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Prompt Engineeringコースには全4レッスンが含まれています。

ペアワイズ(A対B)評価とは

ペアワイズ評価(比較判定とも呼ばれます)では、同じ質問に対する2つの回答を評価モデルに提示し、どちらが優れているかを尋ねます。1つの回答を1~5点で評価する代わりに、Aのほうが優れている、Bのほうが優れている、または同等という相対的な判断を行います。

この方法では絶対評価における一部のバイアスを回避でき、回答ごとの絶対スコアよりも信頼性の高い順位が得られることがよくあります。

基本的なペアワイズ評価モデルのプロンプト

最もシンプルなペアワイズ評価モデルでは、どちらの回答が優れているかと、その理由を尋ねます。重要なのは選択を強制することです。「どちらも良い」といった曖昧な回答で比較を避けられないようにしてください。

import anthropic
import json

client = anthropic.Anthropic(api_key='sk-ant-...')

PAIRWISE_PROMPT = (
    'Given the same question, compare these two responses and decide which is better.\n\n'
    'Question: {question}\n\n'
    'Response A:\n{response_a}\n\n'
    'Response B:\n{response_b}\n\n'
    'Which response is better? You must pick A, B, or TIE (use TIE only if '
    'they are truly equal in all meaningful ways).\n\n'
    'Return JSON: {{"winner": "A" or "B" or "TIE", '
    '"reason": "<one sentence explaining why the winner is better>"}}'
)

def pairwise_judge(question, response_a, response_b):
    prompt = PAIRWISE_PROMPT.format(
        question=question,
        response_a=response_a,
        response_b=response_b
    )
    r = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=150,
        messages=[{'role': 'user', 'content': prompt}]
    )
    return json.loads(r.content[0].text)

result = pairwise_judge(
    'What is machine learning?',
    'Machine learning is a subset of AI.',
    'Machine learning is a method of data analysis that automates model building.'
)
print(result)

順序のランダム化による位置バイアスの低減

位置バイアスによって、評価モデルは最初の選択肢を好みます。これを中和するには、すべての比較を2回実行します。1回目はAを先にし、2回目はBを先にします。両方の順序で結果が一致した場合にのみ勝者として扱ってください。一致しない場合は同点とするか、人間によるレビューに回します。

import anthropic
import json
import random

client = anthropic.Anthropic(api_key='sk-ant-...')

def debiased_pairwise_judge(question, response_a, response_b):
    def single_comparison(first, second, first_label, second_label):
        prompt = (
            f'Question: {question}\n\n'
            f'Response {first_label}:\n{first}\n\n'
            f'Response {second_label}:\n{second}\n\n'
            f'Which is better? Reply with {first_label}, {second_label}, or TIE.'
        )
        r = client.messages.create(
            model='claude-opus-4-5',
            max_tokens=10,
            messages=[{'role': 'user', 'content': prompt}]
        )
        return r.content[0].text.strip()

    # Run A-first
    result_ab = single_comparison(response_a, response_b, 'A', 'B')
    # Run B-first
    result_ba = single_comparison(response_b, response_a, 'B', 'A')

    if result_ab == 'A' and result_ba == 'A':
        return 'A', 'Consistent: A wins in both orderings'
    elif result_ab == 'B' and result_ba == 'B':
        return 'B', 'Consistent: B wins in both orderings'
    else:
        return 'TIE', f'Inconsistent: {result_ab} then {result_ba} — position bias detected'

winner, reason = debiased_pairwise_judge(
    'Explain a hash table.',
    'A hash table maps keys to values.',
    'A hash table is a data structure using a hash function to store key-value pairs for O(1) lookup.'
)
print(f'Winner: {winner} — {reason}')

複数の評価基準によるペアワイズ評価

単に「全体としてどちらが優れているか」と尋ねるのではなく、具体的な評価軸ごとに比較するよう評価モデルに依頼してください。評価軸ごとのペアワイズ比較により、なぜ一方の回答が好まれるのかについて、対応に活かせる情報が得られます。

import anthropic
import json

client = anthropic.Anthropic(api_key='sk-ant-...')

DIMENSIONAL_PAIRWISE = (
    'Compare Response A and Response B on each dimension.\n\n'
    'Question: {question}\n\n'
    'Response A: {response_a}\n\n'
    'Response B: {response_b}\n\n'
    'For each dimension, say A, B, or TIE:\n'
    '1. ACCURACY: Which is more factually correct?\n'
    '2. COMPLETENESS: Which answers the question more fully?\n'
    '3. CLARITY: Which is easier to understand?\n'
    '4. CONCISENESS: Which avoids unnecessary length?\n\n'
    'Return JSON: {{"accuracy":"A/B/TIE", "completeness":"A/B/TIE", '
    '"clarity":"A/B/TIE", "conciseness":"A/B/TIE", "overall":"A/B/TIE"}}'
)

def dimensional_compare(question, a, b):
    r = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=200,
        messages=[{'role': 'user', 'content': DIMENSIONAL_PAIRWISE.format(
            question=question, response_a=a, response_b=b
        )}]
    )
    return json.loads(r.content[0].text)

result = dimensional_compare(
    'How does HTTPS work?',
    'HTTPS encrypts web traffic using SSL/TLS.',
    'HTTPS secures HTTP using TLS. The browser and server perform a handshake, exchange certificates, and establish an encrypted channel for all data transfer.'
)
print(result)

トーナメントの構築:総当たり戦

3つ以上の回答を比較する場合は、総当たり戦を使用します。すべての回答を他のすべての回答と比較する方法です。勝利数が最も多い回答が1位になります。

from itertools import combinations
from collections import defaultdict

def round_robin_tournament(question, responses):
    """
    responses: list of (label, text) tuples
    Returns ranking by win count.
    """
    wins = defaultdict(int)
    ties = defaultdict(int)

    # Every pair compared once
    for (label_a, text_a), (label_b, text_b) in combinations(responses, 2):
        winner, reason = debiased_pairwise_judge(question, text_a, text_b)

        if winner == 'A':
            wins[label_a] += 1
        elif winner == 'B':
            wins[label_b] += 1
        else:  # TIE
            ties[label_a] += 1
            ties[label_b] += 1

        print(f'{label_a} vs {label_b}: {winner}')

    # Rank by wins, then ties
    ranking = sorted(
        responses,
        key=lambda x: (wins[x[0]], ties[x[0]]),
        reverse=True
    )
    print('\nFinal ranking:')
    for i, (label, _) in enumerate(ranking, 1):
        print(f'{i}. {label}: {wins[label]} wins, {ties[label]} ties')
    return ranking

継続的な順位付けのためのEloレーティング

大規模な評価では、総当たり戦の代わりにEloレーティングを使用します。各回答は1000ポイントから始めます。比較のたびに、勝者はポイントを獲得し、敗者はポイントを失います(結果がどれだけ予想外だったかに比例します)。多数の比較を行うと、Eloスコアによって信頼性の高い全体順位が得られます。

import math

def elo_update(rating_a, rating_b, winner, k=32):
    """
    Update Elo ratings after a match.
    winner: 'A' (A won), 'B' (B won), 'TIE' (draw)
    Returns (new_rating_a, new_rating_b)
    """
    expected_a = 1 / (1 + 10 ** ((rating_b - rating_a) / 400))
    expected_b = 1 - expected_a

    if winner == 'A':
        score_a, score_b = 1, 0
    elif winner == 'B':
        score_a, score_b = 0, 1
    else:  # TIE
        score_a, score_b = 0.5, 0.5

    new_a = rating_a + k * (score_a - expected_a)
    new_b = rating_b + k * (score_b - expected_b)
    return new_a, new_b

# Simulate ratings for 4 model variants
ratings = {'ModelA': 1000, 'ModelB': 1000, 'ModelC': 1000, 'ModelD': 1000}

# After running many pairwise comparisons:
ratings['ModelA'], ratings['ModelB'] = elo_update(ratings['ModelA'], ratings['ModelB'], 'A')
ratings['ModelC'], ratings['ModelD'] = elo_update(ratings['ModelC'], ratings['ModelD'], 'TIE')

ranking = sorted(ratings.items(), key=lambda x: x[1], reverse=True)
for model, score in ranking:
    print(f'{model}: {score:.0f}')

ペアワイズ評価と絶対スコアリングの使い分け

ペアワイズ評価を使う場面:

  • 複数のモデルやプロンプトのバリエーションに順位を付けたい場合
  • 絶対スコアのしきい値を定義するのが難しい場合
  • どちらも「良い」ものの、優れている点が異なる出力を比較する場合

絶対評価(ルーブリックによる採点)を使う場面:

  • 合格・不合格のしきい値(「この回答で十分か」)が必要な場合
  • クエリごとに評価対象の回答が1つしかない場合
  • デバッグのために評価基準ごとのスコアが必要な場合

大規模運用のためのサンプリングベースのペアワイズ評価

N個の回答についてすべてのペアを実行すると、N*(N-1)/2回、つまりO(N^2)回の比較が必要です。Nが大きい場合は、すべての回答と比較する代わりに、各回答をK個のランダムな対戦相手と比較します。これにより、はるかに低いコストで真の順位を近似できます。

import random
from collections import defaultdict

def sampled_tournament(question, responses, k_opponents=5):
    """
    Compare each response against k random opponents.
    More efficient than full round-robin for large N.
    """
    wins = defaultdict(int)
    n = len(responses)

    for i, (label, text) in enumerate(responses):
        # Sample k random opponents (not self)
        opponent_indices = random.sample(
            [j for j in range(n) if j != i],
            min(k_opponents, n - 1)
        )
        for j in opponent_indices:
            opp_label, opp_text = responses[j]
            winner, _ = debiased_pairwise_judge(question, text, opp_text)
            if winner == 'A':
                wins[label] += 1
            elif winner == 'B':
                wins[opp_label] += 1

    ranking = sorted(responses, key=lambda x: wins[x[0]], reverse=True)
    for i, (label, _) in enumerate(ranking, 1):
        print(f'{i}. {label}: {wins[label]} wins')
    return ranking

不一致の解釈

2つの順序で結果が異なる場合(A-Bの順序ではAが勝ち、B-Aの順序ではBが勝つ)、単なる位置バイアスではなく、本当に判断が分かれる比較であることを示しています。このような境界的なケースは、より詳しく分析する価値があります。

import anthropic
import json

client = anthropic.Anthropic(api_key='sk-ant-...')

def deep_analyze_tie(question, response_a, response_b):
    """When A vs B is a genuine tie, ask the judge to explain strengths of each."""
    analysis_prompt = (
        f'These two responses to the same question are closely matched in quality.\n\n'
        f'Question: {question}\n\n'
        f'Response A: {response_a}\n\n'
        f'Response B: {response_b}\n\n'
        f'Analyze both:\n'
        f'1. What does A do better than B?\n'
        f'2. What does B do better than A?\n'
        f'3. For what audience or context would you prefer A?\n'
        f'4. For what audience or context would you prefer B?'
    )
    r = client.messages.create(
        model='claude-opus-4-5',
        max_tokens=400,
        messages=[{'role': 'user', 'content': analysis_prompt}]
    )
    return r.content[0].text

analysis = deep_analyze_tie(
    'Explain async/await in Python.',
    'Async/await allows non-blocking code execution.',
    'Async/await lets you write asynchronous code that looks synchronous, '
    'using the asyncio event loop to handle I/O without blocking.'
)
print(analysis[:300])

ペアワイズ結果の記録

すべてのペアワイズ比較結果を記録し、どの回答がどの回答に勝ったのか、またどのような条件だったのかを検索できる記録を構築してください。このデータは、回帰テストや、時間の経過に伴うモデルの改善状況の把握に役立ちます。

import json
import datetime

def logged_pairwise(question, response_a, response_b,
                    label_a='A', label_b='B', log_file='pairwise_log.jsonl'):
    winner, reason = debiased_pairwise_judge(question, response_a, response_b)

    entry = {
        'timestamp': datetime.datetime.utcnow().isoformat(),
        'question': question[:100],  # Truncate for storage
        'label_a': label_a,
        'label_b': label_b,
        'winner': winner,
        'reason': reason,
        'response_a_length': len(response_a.split()),
        'response_b_length': len(response_b.split()),
    }

    with open(log_file, 'a') as f:
        f.write(json.dumps(entry) + '\n')

    print(f'{label_a} vs {label_b}: {winner} — {reason}')
    return winner

logged_pairwise(
    'What is SQL?',
    'SQL is a database query language.',
    'SQL (Structured Query Language) is used to query and manage relational databases.',
    label_a='ModelV1',
    label_b='ModelV2'
)

ペアワイズ評価と絶対スコアリング:トレードオフ

ペアワイズ評価と絶対評価のルーブリックによる採点は、競合する方法ではなく、相互に補完する方法です。両方を組み合わせて使用してください。絶対評価で最低限の品質基準を設定し、その基準を満たした候補の順位付けにペアワイズ比較を使います。

重要なトレードオフは、N個の回答に対してペアワイズ評価ではO(N^2)回の比較が必要なのに対し、絶対評価ではO(N)回で済むことです。大規模運用では、サンプリングベースのペアワイズ評価やEloレーティングが必要になります。

知識チェック:ペアワイズ評価のバイアス低減

ペアワイズLLM評価で位置バイアスを減らす最も効果的な方法は何ですか?

振り返り:比較判定 A対B

ペアワイズ評価では、2つの回答を絶対的な尺度で個別に評価するのではなく、どちらが優れているかを尋ねます。位置バイアスを抑えるには、すべての比較を順序を入れ替えて2回実行し、一貫した結果だけを採用します。N個の回答には、Nが小さい場合は総当たり戦(すべてのペア)を、大きい場合はサンプリングしたトーナメントを使用します。Eloレーティングを使うと、多数のペアワイズ比較から継続的な順位を作成できます。対応に活かせる診断情報を得るには、評価軸ごとのペアワイズ判定(正確性、完全性、明瞭性を個別に評価)を使用してください。モデルのバージョン間での回帰テストや傾向分析のため、すべての結果を記録します。

よくある質問

「比較評価:A対B」レッスンは無料ですか?

はい。「比較評価:A対B」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Prompt Engineeringコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Prompt Engineeringコースには全4レッスンが含まれています。

「比較評価:A対B」で何を学びますか?

絶対評価を使わず、ペア単位の比較プロンプトで出力を順位付けします。 ブラウザで直接実行するハンズオンコードでAI Prompt Engineeringを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Prompt Engineeringを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Prompt Engineeringは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「比較評価:A対B」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Prompt Engineeringレッスンでコードを書いて実行できますか?

はい。すべてのAI Prompt Engineeringレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. LLMによるLLM出力の評価
  2. ルーブリックベースの採点プロンプト
  3. 比較評価:A対B
  4. LLM評価者のキャリブレーションとバイアス
← AI Prompt Engineeringに戻る