Calibração e vieses em avaliadores LLM
Viés de posição, viés de verbosidade e como reduzi-los nos prompts do avaliador.
Calibração e vieses em avaliadores LLM é uma aula grátis de AI Prompt Engineering no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Prompt Engineering, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Prompt Engineering inclui 4 aulas no total.
Por que a calibração do avaliador é importante
Um avaliador LLM que atribui sistematicamente a um tipo de resposta uma pontuação maior do que merece produz resultados de avaliação enganosos. Poderá colocar um modelo pior em produção porque o avaliador preferiu seu estilo prolixo — não sua qualidade real.
Calibração significa que as pontuações do avaliador refletem com precisão a qualidade verdadeira. Um avaliador calibrado concorda com avaliadores humanos a uma taxa mensurável e não favorece sistematicamente nenhum atributo não relacionado à qualidade.
Viés de posição: análise aprofundada
O viés de posição é o viés mais forte e mais estudado dos avaliadores LLM. Em comparações aos pares, os avaliadores preferem a primeira opção em 60–65% das vezes, independentemente da qualidade. Isso equivale a uma moeda que dá cara em 60% das vezes — uma diferença significativa em escala.
O viés existe porque os LLMs são treinados para gerar continuações — ver «Resposta A:» primeiro os induz a favorecer A antes de lerem 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 biasViés de verbosidade: análise aprofundada
O viés de verbosidade faz com que os avaliadores prefiram respostas mais longas, mesmo quando as mais curtas são mais precisas e completas. Pesquisas mostram que os avaliadores LLM classificam respostas mais longas como «melhores» 1,5 a 2 vezes mais frequentemente em comparações aos pares, mantendo constante a qualidade do conteúdo.
Esse viés provavelmente resulta de dados de treinamento nos quais os avaliadores humanos também confundem extensão com qualidade.
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)')Viés de autopreferência: análise aprofundada
Os avaliadores Claude atribuem, em média, pontuações mais altas a respostas geradas por Claude. Os avaliadores GPT-4 atribuem pontuações mais altas a respostas geradas por GPT-4. Isso foi demonstrado em vários estudos independentes.
O mecanismo é o seguinte: cada modelo tem um estilo característico — estrutura das frases, padrões de ressalvas e escolhas de vocabulário. O mesmo modelo reconhece e prefere o próprio estilo.
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?')Mitigação 1: inverter a ordem
A mitigação isolada mais eficaz para o viés de posição é executar cada comparação aos pares duas vezes, invertendo a ordem, e usar apenas resultados consistentes. Implemente isso como uma função padrão pela qual passem todas as avaliações.
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}')Mitigação 2: vários avaliadores diversificados
O viés de autopreferência é reduzido quando se usam vários modelos diferentes como avaliadores e se agregam seus veredictos. Quando Claude, GPT-4 e Gemini concordam, o resultado é muito mais confiável do que o veredicto de qualquer modelo isolado.
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}')Mitigação 3: instruções contra a verbosidade
Instrua diretamente o avaliador a penalizar a verbosidade e recompensar a concisão. Isso neutraliza o viés de verbosidade que, de outra forma, faria as respostas mais longas parecerem melhores.
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')Calibrando com avaliadores humanos
O padrão-ouro para a calibração do avaliador é comparar as pontuações do seu avaliador LLM com as pontuações de avaliadores humanos em um conjunto de calibração. Meça a concordância e identifique divergências sistemáticas.
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 correlationDetectando padrões sistemáticos de viés
Analise os resultados do seu avaliador em diferentes categorias de respostas para detectar vieses sistemáticos. O avaliador atribui consistentemente pontuações mais altas às respostas de um modelo? Ele penaliza respostas sobre determinados tópicos?
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')Lista de verificação para redução de viés
Uma lista de verificação a aplicar antes de colocar um avaliador LLM em produção:
- Execute todas as comparações aos pares duas vezes, invertendo a ordem
- Inclua instruções explícitas contra a verbosidade na rubrica
- Use pelo menos 2 modelos diferentes como avaliadores em avaliações de alto impacto
- Ancore explicitamente os níveis de pontuação para evitar a inflação
- Calibre com avaliadores humanos usando uma amostra representativa
- Registre e monitore as taxas de vitória por rótulo do modelo para detectar desvios
- Adicione uma saída JSON estruturada para impedir que as pontuações fiquem ocultas em texto livre
Trilhas de auditoria e explicabilidade
Um avaliador calibrado também deve ser explicável. Se o avaliador atribuir uma pontuação de 4/5 a uma resposta, deve ser possível rastrear o motivo — quais critérios foram atendidos e quais ficaram aquém do esperado.
Exigir que o avaliador retorne JSON com pontuações por critério e uma justificativa breve cria uma trilha de auditoria natural. As partes interessadas podem analisar por que respostas específicas receberam determinada pontuação, e é possível identificar padrões recorrentes nas pontuações baixas.
Verificação de conhecimentos: mitigação da autopreferência
Qual estratégia de mitigação aborda MAIS diretamente o viés de autopreferência em avaliadores LLM?
Recapitulação: calibração e vieses em avaliadores LLM
Os avaliadores LLM apresentam quatro vieses sistemáticos principais: viés de posição (preferência pela primeira opção), viés de verbosidade (preferência por respostas mais longas), autopreferência (preferência pelo próprio estilo) e inflação das pontuações (concentração em 4–5/5). Mitigue cada um de forma específica: altere a ordem para combater o viés de posição, adicione instruções contra a verbosidade para combater o viés de verbosidade, use avaliadores de modelos diversos para combater a autopreferência e use rubricas ancoradas para combater a inflação. Calibre o avaliador comparando-o com avaliadores humanos em um conjunto rotulado e meça a correlação de Pearson. Monitore as taxas de vitória por rótulo ao longo do tempo para detectar padrões de viés emergentes antes que distorçam seu fluxo de avaliação.
Perguntas Frequentes
A aula “Calibração e vieses em avaliadores LLM” é grátis?
Sim — o texto completo de “Calibração e vieses em avaliadores LLM” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Prompt Engineering, atualize para CoddyKit PRO. O curso de AI Prompt Engineering inclui 4 aulas no total.
O que vou aprender em “Calibração e vieses em avaliadores LLM”?
Viés de posição, viés de verbosidade e como reduzi-los nos prompts do avaliador. Você pratica AI Prompt Engineering com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Prompt Engineering?
Nenhuma experiência prévia é necessária. AI Prompt Engineering no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Calibração e vieses em avaliadores LLM”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Prompt Engineering?
Sim. Cada aula de AI Prompt Engineering inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Usando LLM para avaliar saídas de LLM
- Prompts de pontuação baseados em critérios
- Avaliação comparativa: A versus B
- Calibração e vieses em avaliadores LLM