Calibrazione e bias nei giudici LLM
Bias di posizione e di prolissità e come attenuarli nei prompt dei giudici
Calibrazione e bias nei giudici LLM è una lezione AI Prompt Engineering gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Prompt Engineering, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Prompt Engineering include 4 lezioni in totale.
Perché la calibrazione del giudice è importante
Un giudice LLM che assegna sistematicamente a un tipo di risposta un punteggio superiore a quello meritato produce risultati di valutazione fuorvianti. Potrebbe rilasciare un modello peggiore perché il giudice ne preferisce lo stile prolisso, non la qualità effettiva.
La calibrazione significa che i punteggi del giudice riflettono accuratamente la qualità reale. Un giudice calibrato concorda con i valutatori umani con una frequenza misurabile e non favorisce sistematicamente un attributo non correlato alla qualità.
Approfondimento sul bias di posizione
Il bias di posizione è il bias più forte e più studiato dei giudici LLM. Nei confronti a coppie, i giudici preferiscono la prima opzione nel 60-65% dei casi, indipendentemente dalla qualità. È come una moneta che esce testa nel 60% dei lanci: su larga scala, l'effetto è significativo.
Il bias esiste perché gli LLM vengono addestrati a generare continuazioni: vedere prima «Response A:» li predispone verso A prima ancora che leggano B.
import anthropic
client = anthropic.Anthropic(api_key='sk-ant-...')
def measure_position_bias(question, n_pairs=20):
"""
Measure position bias by comparing IDENTICAL responses.
If both responses are the same, wins should be 50/50.
Any deviation from 50/50 is pure position bias.
"""
response = 'Machine learning is a subset of AI that learns from data.'
first_wins = 0
for _ in range(n_pairs):
prompt = (
f'Which response is better?\nQ: {question}\n'
f'Response A: {response}\n'
f'Response B: {response}\n'
f'Reply with A or B.'
)
r = client.messages.create(
model='claude-opus-4-5',
max_tokens=5,
messages=[{'role': 'user', 'content': prompt}]
)
if 'A' in r.content[0].text:
first_wins += 1
bias = first_wins / n_pairs
print(f'First-position win rate with IDENTICAL responses: {bias:.0%}')
print(f'Expected (no bias): 50%')
print(f'Measured bias: {(bias - 0.5) * 100:+.0f}%')
return biasApprofondimento sul bias della prolissità
Il bias della prolissità porta i giudici a preferire le risposte più lunghe anche quando quelle più brevi sono più accurate e complete. Le ricerche mostrano che, nei confronti a coppie e a parità di qualità del contenuto, i giudici valutano le risposte più lunghe come «migliori» con una frequenza 1,5-2 volte superiore.
Questo bias deriva probabilmente da dati di addestramento in cui anche i valutatori umani confondono la lunghezza con la qualità.
import anthropic
client = anthropic.Anthropic(api_key='sk-ant-...')
def measure_verbosity_bias(question, correct_answer):
"""
Compare a concise correct answer against a verbose one with filler.
A good judge should prefer the concise version or call it a tie.
"""
concise = correct_answer
verbose = (
f'That is a great question! I am happy to help. '
f'{correct_answer} '
f'I hope this comprehensive explanation addresses all your needs. '
f'Please feel free to ask if you need any further clarification!'
)
for label, resp in [('Concise', concise), ('Verbose', verbose)]:
prompt = (
f'Rate this response 1-5 for quality.\n'
f'Q: {question}\nA: {resp}\n'
f'Return only a number 1-5.'
)
r = client.messages.create(
model='claude-opus-4-5',
max_tokens=5,
messages=[{'role': 'user', 'content': prompt}]
)
score = r.content[0].text.strip()
print(f'{label} response score: {score}')
print(f' ({len(resp.split())} words)')Approfondimento sul bias di auto-preferenza
I giudici Claude assegnano in media punteggi più alti alle risposte generate da Claude. I giudici GPT-4 assegnano punteggi più alti alle risposte generate da GPT-4. Questo fenomeno è stato dimostrato in numerosi studi indipendenti.
Il meccanismo è il seguente: ogni modello ha uno stile distintivo, caratterizzato dalla struttura delle frasi, dalle formule di attenuazione e dalle scelte lessicali. Lo stesso modello riconosce e preferisce il proprio stile.
import anthropic
import openai
anthropic_client = anthropic.Anthropic(api_key='sk-ant-...')
openai_client = openai.OpenAI(api_key='sk-...')
def test_self_preference(question):
# Generate one response per model
claude_answer = anthropic_client.messages.create(
model='claude-opus-4-5', max_tokens=100,
messages=[{'role': 'user', 'content': question}]
).content[0].text
gpt_answer = openai_client.chat.completions.create(
model='gpt-4o', max_tokens=100,
messages=[{'role': 'user', 'content': question}]
).choices[0].message.content
judge_prompt = (
f'Which response is better?\nQ: {question}\n'
f'Response A: {claude_answer}\n'
f'Response B: {gpt_answer}\n'
f'Reply: A or B'
)
# Claude judges the comparison
claude_verdict = anthropic_client.messages.create(
model='claude-opus-4-5', max_tokens=5,
messages=[{'role': 'user', 'content': judge_prompt}]
).content[0].text.strip()
print(f'Claude judge verdict: {claude_verdict}')
print('(A=Claude response, B=GPT response)')
print('Self-preference: did Claude prefer its own response?')Mitigazione 1: scambiare l'ordine
La misura di mitigazione più efficace per il bias di posizione consiste nell'eseguire ogni confronto a coppie due volte scambiando l'ordine e nell'utilizzare solo i risultati coerenti. Implementi questa logica come funzione standard attraverso cui passano tutte le valutazioni.
import anthropic
import json
client = anthropic.Anthropic(api_key='sk-ant-...')
def debiased_compare(question, response_a, response_b, label_a='A', label_b='B'):
def single_pass(first, second, first_label, second_label):
prompt = (
f'Q: {question}\n\n'
f'{first_label}: {first}\n\n'
f'{second_label}: {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()
# Pass 1: A first
p1 = single_pass(response_a, response_b, label_a, label_b)
# Pass 2: B first — but labels stay the same (we just swap presentation order)
p2 = single_pass(response_b, response_a, label_b, label_a)
# Normalize p2: if judge said label_b in pass 2, that means they preferred first-shown
# Need to map back: if p2 = label_b, the 'first' won; if p2 = label_a, the 'second' won
if p1 == p2 and p1 != 'TIE':
return p1, 'Consistent result'
else:
return 'TIE', f'Inconsistent: pass1={p1}, pass2={p2}'
winner, reason = debiased_compare(
'What is Docker?',
'Docker is a containerization platform.',
'Docker allows you to package applications into containers for consistent deployment.'
)
print(f'{winner}: {reason}')Mitigazione 2: più giudici diversi
Il bias di auto-preferenza si riduce utilizzando più modelli diversi come giudici e aggregandone i verdetti. Quando Claude, GPT-4 e Gemini concordano, il risultato è molto più affidabile del verdetto di un singolo modello.
import anthropic
import openai
anthropic_client = anthropic.Anthropic(api_key='sk-ant-...')
openai_client = openai.OpenAI(api_key='sk-...')
def multi_model_pairwise(question, response_a, response_b):
results = {}
# Judge 1: Claude
r1 = anthropic_client.messages.create(
model='claude-opus-4-5', max_tokens=5,
messages=[{'role': 'user', 'content':
f'Q: {question}\nA: {response_a}\nB: {response_b}\n'
f'Which is better? Reply A, B, or TIE.'
}]
)
results['Claude'] = r1.content[0].text.strip()
# Judge 2: GPT-4o
r2 = openai_client.chat.completions.create(
model='gpt-4o', max_tokens=5,
messages=[{'role': 'user', 'content':
f'Q: {question}\nA: {response_a}\nB: {response_b}\n'
f'Which is better? Reply A, B, or TIE.'
}]
)
results['GPT4o'] = r2.choices[0].message.content.strip()
print(f'Claude judge: {results["Claude"]}')
print(f'GPT-4o judge: {results["GPT4o"]}')
# Aggregate: majority vote
votes = list(results.values())
if votes.count('A') >= 2: return 'A'
if votes.count('B') >= 2: return 'B'
return 'TIE'
final = multi_model_pairwise(
'Explain recursion.',
'A function that calls itself.',
'Recursion is when a function solves a problem by solving a smaller version of the same problem.'
)
print(f'Final verdict: {final}')Mitigazione 3: istruzioni contro la prolissità
Istruisca direttamente il giudice a penalizzare la prolissità e a premiare la concisione. In questo modo contrasta il bias della prolissità, che altrimenti farebbe apparire migliori le risposte più lunghe.
ANTI_VERBOSITY_JUDGE = (
'Evaluate this response. Apply these corrections for known judge biases:\n\n'
'VERBOSITY CORRECTION: Do NOT rate a response higher simply because it is longer. '
'A concise, accurate 20-word answer is better than a 200-word answer that says the same thing. '
'Actively penalize unnecessary padding, filler phrases like "Great question!", '
'and repetition.\n\n'
'LENGTH PENALTY: If the response contains introductory filler, closing remarks, '
'or restates the question, subtract 1 point from your score.\n\n'
'Question: {question}\n'
'Response: {response}\n\n'
'Score 1-5 (apply verbosity correction above):'
)
# This instruction significantly reduces verbosity inflation in practice
print('Anti-verbosity instructions reduce the length-quality conflation')Calibrazione rispetto ai valutatori umani
Lo standard di riferimento per la calibrazione del giudice consiste nel confrontare i punteggi del giudice LLM con quelli assegnati dai valutatori umani su un set di calibrazione. Misuri il livello di accordo e individui le divergenze sistematiche.
import anthropic
import json
from scipy import stats # pip install scipy
client = anthropic.Anthropic(api_key='sk-ant-...')
def calibrate_judge(calibration_set):
"""
calibration_set: list of dicts with:
{'question': str, 'response': str, 'human_score': float}
"""
llm_scores = []
human_scores = []
for item in calibration_set:
prompt = (
f'Rate this response 1-5.\n'
f'Q: {item["question"]}\nA: {item["response"]}\n'
f'Return only a number.'
)
r = client.messages.create(
model='claude-opus-4-5',
max_tokens=5,
messages=[{'role': 'user', 'content': prompt}]
)
try:
llm_score = float(r.content[0].text.strip())
except ValueError:
llm_score = 3.0 # Default on parse error
llm_scores.append(llm_score)
human_scores.append(item['human_score'])
correlation, p_value = stats.pearsonr(llm_scores, human_scores)
print(f'LLM-Human correlation: {correlation:.3f} (p={p_value:.4f})')
print(f'LLM mean score: {sum(llm_scores)/len(llm_scores):.2f}')
print(f'Human mean score: {sum(human_scores)/len(human_scores):.2f}')
return correlationIndividuare i pattern di bias sistematici
Analizzi i risultati del giudice nelle diverse categorie di risposta per individuare bias sistematici. Il giudice assegna sistematicamente punteggi più alti alle risposte di un determinato modello? Penalizza le risposte su alcuni argomenti?
import json
from collections import defaultdict
def analyze_judge_bias(log_file='pairwise_log.jsonl'):
"""
Analyze pairwise logs to detect systematic bias.
"""
wins_by_label = defaultdict(int)
total_by_label = defaultdict(int)
with open(log_file) as f:
for line in f:
entry = json.loads(line)
winner = entry['winner']
label_a = entry['label_a']
label_b = entry['label_b']
if winner == 'A':
wins_by_label[label_a] += 1
elif winner == 'B':
wins_by_label[label_b] += 1
total_by_label[label_a] += 1
total_by_label[label_b] += 1
print('Win rate by model:')
for label in sorted(total_by_label):
total = total_by_label[label]
wins = wins_by_label[label]
print(f' {label}: {wins}/{total} = {wins/total:.0%}')
# Flag if any model wins >60% — likely systematic bias
for label in total_by_label:
rate = wins_by_label[label] / total_by_label[label]
if rate > 0.65 or rate < 0.35:
print(f'WARNING: {label} win rate {rate:.0%} suggests systematic bias')Checklist per la mitigazione dei bias
Una checklist da applicare prima di distribuire un giudice LLM in produzione:
- Esegua tutti i confronti a coppie due volte scambiando l'ordine
- Includa nella rubrica istruzioni esplicite contro la prolissità
- Utilizzi almeno 2 modelli diversi come giudici per le valutazioni ad alto impatto
- Definisca esplicitamente gli ancoraggi dei livelli di punteggio per evitare l'inflazione
- Calibri il giudice rispetto a valutatori umani su un campione rappresentativo
- Registri e monitori i tassi di vittoria in base all'etichetta del modello per rilevare fenomeni di drift
- Aggiunga un output JSON strutturato per impedire che i punteggi vengano nascosti nel testo libero
Tracce di audit e spiegabilità
Un valutatore calibrato deve anche essere spiegabile. Se il valutatore assegna un punteggio di 4/5 a una risposta, dovrebbe essere possibile ricostruire il motivo: quali criteri sono stati soddisfatti e quali non lo sono stati.
Richiedere al valutatore di restituire JSON con i punteggi per criterio e una breve motivazione crea una traccia di audit naturale. Gli stakeholder possono verificare perché determinate risposte hanno ricevuto quei punteggi e individuare schemi ricorrenti nei punteggi bassi.
Verifica delle conoscenze: mitigazione dell'auto-preferenza
Quale strategia di mitigazione affronta PIÙ direttamente il bias di auto-preferenza nei valutatori LLM?
Riepilogo: calibrazione e bias nei valutatori LLM
I valutatori LLM presentano quattro bias sistematici principali: bias di posizione (preferenza per la prima opzione), bias di prolissità (preferenza per le risposte più lunghe), auto-preferenza (preferenza per il proprio stile) e inflazione dei punteggi (concentrazione sui valori 4-5/5). Mitighi ciascun bias in modo specifico: scambi l'ordine per il bias di posizione, aggiunga istruzioni contro la prolissità per il bias di prolissità, utilizzi valutatori basati su modelli diversi per l'auto-preferenza e impieghi rubriche ancorate per contrastare l'inflazione. Calibri il valutatore confrontandolo con valutatori umani su un insieme di dati etichettato e misuri la correlazione di Pearson. Monitori nel tempo i tassi di vittoria per etichetta, così da individuare i pattern di bias emergenti prima che distorcano la pipeline di valutazione.
Domande Frequenti
La lezione «Calibrazione e bias nei giudici LLM» è gratuita?
Sì — il testo completo di «Calibrazione e bias nei giudici LLM» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Prompt Engineering, passa a CoddyKit PRO. Il corso AI Prompt Engineering include 4 lezioni in totale.
Cosa imparerò in «Calibrazione e bias nei giudici LLM»?
Bias di posizione e di prolissità e come attenuarli nei prompt dei giudici Eserciti AI Prompt Engineering con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Prompt Engineering?
Non è richiesta alcuna esperienza precedente. AI Prompt Engineering su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Calibrazione e bias nei giudici LLM»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Prompt Engineering?
Sì. Ogni lezione AI Prompt Engineering include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Uso degli LLM per valutare gli output degli LLM
- Prompt di valutazione basati su rubriche
- Valutazione comparativa: A contro B
- Calibrazione e bias nei giudici LLM