0Pricing
AI Prompt Engineering · Lección

Evaluación comparativa: A frente a B

Prompts de comparación por pares para clasificar salidas sin una puntuación absoluta.

Evaluación comparativa: A frente a B es una lección gratuita de AI Prompt Engineering en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Prompt Engineering, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Prompt Engineering incluye 4 lecciones en total.

¿Qué es la evaluación por pares (A frente a B)?

La evaluación por pares (también llamada evaluación comparativa) presenta al juez dos respuestas a la misma pregunta y le pregunta cuál es mejor. En lugar de puntuar una sola respuesta del 1 al 5, el juez realiza una valoración relativa: A es mejor, B es mejor o están empatadas.

Este enfoque evita algunos sesgos de la puntuación absoluta y a menudo produce clasificaciones más fiables que las puntuaciones absolutas asignadas a cada respuesta.

Prompt básico de juez por pares

El juez por pares más sencillo pregunta qué respuesta es mejor y por qué. La clave es obligarlo a elegir: no permita que el juez evite la comparación con un «ambas son buenas» impreciso.

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)

Aleatorizar el orden para reducir el sesgo de posición

El sesgo de posición hace que los jueces prefieran la primera opción. Para neutralizarlo, ejecute cada comparación dos veces: una con A en primer lugar y otra con B en primer lugar. Considere que hay un ganador solo si ambos órdenes coinciden. Si no coinciden, declare un empate o derive el caso a una revisión humana.

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}')

Evaluación por pares con varios criterios

Pida al juez que compare dimensiones específicas en lugar de limitarse a preguntar «¿cuál es mejor en general?». La comparación por pares específica para cada dimensión proporciona información útil sobre por qué se prefiere una respuesta a otra.

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)

Crear un torneo: todos contra todos

Cuando compare más de dos respuestas, utilice un torneo de todos contra todos: cada respuesta se compara con todas las demás. La respuesta con más victorias ocupa el primer puesto.

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

Puntuación Elo para una clasificación continua

Para evaluaciones a gran escala, utilice puntuaciones Elo en lugar de un torneo de todos contra todos. Cada respuesta comienza con 1000 puntos. Después de cada comparación, la ganadora obtiene puntos y la perdedora los pierde, en proporción a lo sorprendente que haya sido el resultado. Tras muchas comparaciones, las puntuaciones Elo producen una clasificación global fiable.

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}')

Cuándo usar la puntuación por pares o la absoluta

Use la evaluación por pares cuando:

  • Quiera clasificar varios modelos o variantes de prompts
  • Sea difícil definir umbrales de puntuación absoluta
  • Compare resultados que son ambos «buenos», pero de maneras diferentes

Use la puntuación absoluta (basada en rúbricas) cuando:

  • Necesite un umbral de aprobado o suspenso («¿es esta respuesta suficientemente buena?»)
  • Solo tenga una respuesta que evaluar por consulta
  • Necesite puntuaciones por criterio para depurar

Evaluación por pares mediante muestreo a escala

Ejecutar todos los pares para N respuestas requiere N*(N-1)/2 comparaciones, es decir, O(N^2). Para valores grandes de N, utilice un muestreo aleatorio: compare cada respuesta con K oponentes elegidos al azar en lugar de con todas las demás. Esto aproxima la clasificación real con un coste mucho menor.

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

Interpretar los desacuerdos

Cuando dos órdenes producen resultados distintos (A gana en el orden A-B y B gana en el orden B-A), esto indica una comparación realmente dudosa, no solo un sesgo de posición. Estos casos límite merecen un análisis más profundo.

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

Registrar los resultados por pares

Registre todos los resultados de las comparaciones por pares para crear un historial consultable de qué respuestas vencieron a cuáles y en qué condiciones. Estos datos son valiosos para las pruebas de regresión y para comprender las mejoras del modelo a lo largo del tiempo.

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

Evaluación por pares frente a puntuación absoluta: ventajas y desventajas

La evaluación por pares y la puntuación absoluta basada en rúbricas son enfoques complementarios, no contrapuestos. Úselos conjuntamente: la puntuación absoluta para establecer un umbral mínimo de calidad y la comparación por pares para clasificar los candidatos que superen ese umbral.

La principal desventaja: la evaluación por pares requiere O(N^2) comparaciones para N respuestas, mientras que la puntuación absoluta requiere O(N). A gran escala, resultan necesarios la evaluación por pares mediante muestreo o las puntuaciones Elo.

Comprobación de conocimientos: eliminar el sesgo en la evaluación por pares

¿Cuál es la forma más eficaz de reducir el sesgo de posición en la evaluación de LLM por pares?

Repaso: evaluación comparativa A frente a B

La evaluación por pares pregunta cuál de dos respuestas es mejor, en lugar de puntuar cada una en una escala absoluta. Para controlar el sesgo de posición, ejecute cada comparación dos veces, intercambiando el orden, y acepte únicamente los resultados coherentes. Para N respuestas, use un torneo de todos contra todos (todos los pares) cuando N sea pequeño, o torneos mediante muestreo cuando sea grande. Las puntuaciones Elo producen clasificaciones continuas a partir de muchas comparaciones por pares. Use la evaluación por pares por dimensión (exactitud, completitud y claridad por separado) para obtener información diagnóstica útil. Registre todos los resultados para las pruebas de regresión y el análisis de tendencias entre versiones del modelo.

Preguntas frecuentes

¿La lección «Evaluación comparativa: A frente a B» es gratis?

Sí — el texto completo de «Evaluación comparativa: A frente a B» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Prompt Engineering, actualiza a CoddyKit PRO. El curso de AI Prompt Engineering incluye 4 lecciones en total.

¿Qué aprenderé en «Evaluación comparativa: A frente a B»?

Prompts de comparación por pares para clasificar salidas sin una puntuación absoluta. Practicas AI Prompt Engineering con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar AI Prompt Engineering?

No se requiere experiencia previa. AI Prompt Engineering en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Evaluación comparativa: A frente a B»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de AI Prompt Engineering?

Sí. Cada lección de AI Prompt Engineering incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Uso de un LLM para evaluar salidas de otro LLM
  2. Prompts de puntuación basados en rúbricas
  3. Evaluación comparativa: A frente a B
  4. Calibración y sesgos en jueces LLM
← Volver a AI Prompt Engineering