avaliação-llm

Padrões de avaliação e teste para LLMs, incluindo teste de prompts, detecção de alucinações, criação de benchmarks e métricas de qualidade. Use ao testar aplicações LLM, validar qualidade de prompts, implementar avaliação sistemática ou medir desempenho de LLMs.

O que é avaliação-llm?

Esta skill fornece um guia abrangente para avaliar e testar aplicações de Grandes Modelos de Linguagem (LLMs), abordando desde métricas tradicionais até técnicas avançadas como 'LLM-as-Judge' e detecção de alucinações. É útil para desenvolvedores e engenheiros que precisam validar a qualidade de saídas de LLMs, comparar versões de prompts ou modelos, e implementar avaliações contínuas em pipelines de CI/CD. A skill cobre plataformas como OpenAI e bibliotecas Python (rouge-score, bert-score, scikit-learn), com exemplos de código prontos para uso. Ela explica como estruturar datasets de avaliação, calcular métricas como ROUGE, BERTScore e Perplexidade, e usar o próprio LLM como juiz para pontuação rubrica, avaliação binária e comparação pareada. Além disso, inclui métodos robustos para detecção de alucinações (verificação de fundamentação, checagem factual e consistência interna) e métricas específicas para sistemas RAG, como precisão, recall e MRR. O guia enfatiza a importância de uma estratégia de avaliação em múltiplas camadas (automática, LLM como juiz, revisão humana) para garantir resultados confiáveis.

Funciona com~Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/applied-artificial-intelligence/claude-code-toolkit/tree/main/skills/llm-evaluation

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Avaliação e Teste de LLM

Guia abrangente para avaliar e testar aplicações de Grandes Modelos de Linguagem, incluindo teste de prompts, validação de saída, detecção de alucinações, criação de benchmarks, teste A/B e métricas de qualidade.


Referência Rápida

Quando usar esta skill:

  • Testar saídas de aplicações LLM
  • Validar qualidade e consistência de prompts
  • Detectar alucinações e erros factuais
  • Criar benchmarks de avaliação
  • Testar A/B de prompts ou modelos
  • Implementar avaliação contínua (CI/CD)
  • Medir qualidade de recuperação (para RAG)
  • Depurar comportamento inesperado de LLM

Métricas abordadas:

  • Tradicionais: BLEU, ROUGE, BERTScore, Perplexidade
  • LLM como Juiz: Avaliação GPT-4, pontuação baseada em rubrica
  • Específicas da tarefa: Correspondência exata, F1, precisão, recall
  • Qualidade: Toxicidade, viés, coerência, relevância

Parte 1: Fundamentos da Avaliação

O Desafio da Avaliação de LLM

Por que avaliar LLMs é difícil:

  1. Qualidade subjetiva - Saída "boa" varia conforme o caso de uso
  2. Sem verdade absoluta única - Múltiplas respostas válidas
  3. Dependente do contexto - Mesma saída pode ser boa/ruim em cenários diferentes
  4. Rotulagem cara - Avaliação humana não escala
  5. Fragilidade adversária - Pequenas mudanças no prompt = grandes mudanças na saída

Solução: Avaliação em múltiplas camadas

Camada 1: Métricas Automatizadas (rápido, escalável)
  ↓
Camada 2: LLM como Juiz (flexível, sutil)
  ↓
Camada 3: Revisão Humana (padrão ouro, caro)

Estrutura do Dataset de Avaliação

from dataclasses import dataclass
from typing import List, Optional

@dataclass
class EvalExample:
    """Exemplo de avaliação único."""
    input: str  # Entrada do usuário / prompt
    expected_output: Optional[str]  # Padrão ouro (se existir)
    context: Optional[str]  # Contexto adicional (para RAG)
    metadata: dict  # Categoria, dificuldade, etc.

@dataclass
class EvalResult:
    """Resultado da avaliação para um exemplo."""
    example_id: str
    actual_output: str
    scores: dict  # {'nome_da_metrica': pontuacao}
    passed: bool
    failure_reason: Optional[str]

# Exemplo de dataset
eval_dataset = [
    EvalExample(
        input="Qual é a capital da França?",
        expected_output="Paris",
        context=None,
        metadata={'categoria': 'factual', 'dificuldade': 'facil'}
    ),
    EvalExample(
        input="Explique o emaranhamento quântico",
        expected_output=None,  # Sem resposta única
        context=None,
        metadata={'categoria': 'explicacao', 'dificuldade': 'dificil'}
    )
]

Parte 2: Métricas Tradicionais

Métrica 1: Correspondência Exata (Mais Simples)

def exact_match(predicted: str, expected: str, case_sensitive: bool = False) -> float:
    """
    Métrica binária: 1.0 se corresponde, 0.0 caso contrário.

    Uso para: Classificação, respostas curtas, saída estruturada
    Limitações: Muito rigorosa para tarefas de geração
    """
    if not case_sensitive:
        predicted = predicted.lower().strip()
        expected = expected.lower().strip()

    return 1.0 if predicted == expected else 0.0

# Exemplo
score = exact_match("Paris", "paris")  # 1.0
score = exact_match("A capital é Paris", "Paris")  # 0.0

Métrica 2: ROUGE (Orientado a Recall)

from rouge_score import rouge_scorer

def compute_rouge(predicted: str, expected: str) -> dict:
    """
    Métricas ROUGE para sobreposição de texto.

    ROUGE-1: Sobreposição de unigramas
    ROUGE-2: Sobreposição de bigramas
    ROUGE-L: Subsequência comum mais longa

    Uso para: Sumarização, tradução
    Limitações: Não captura semântica
    """
    scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'], use_stemmer=True)
    scores = scorer.score(expected, predicted)

    return {
        'rouge1': scores['rouge1'].fmeasure,
        'rouge2': scores['rouge2'].fmeasure,
        'rougeL': scores['rougeL'].fmeasure
    }

# Exemplo
scores = compute_rouge(
    predicted="Paris é a capital da França",
    expected="A capital da França é Paris"
)
# {'rouge1': 0.82, 'rouge2': 0.67, 'rougeL': 0.82}

Métrica 3: BERTScore (Similaridade Semântica)

from bert_score import score as bert_score

def compute_bertscore(predicted: List[str], expected: List[str]) -> dict:
    """
    Similaridade semântica usando embeddings BERT.

    Melhor que ROUGE para:
    - Paráfrases
    - Equivalência semântica
    - Qualidade de geração

    Retorna: Precisão, Recall, F1
    """
    P, R, F1 = bert_score(predicted, expected, lang="pt", verbose=False)

    return {
        'precision': P.mean().item(),
        'recall': R.mean().item(),
        'f1': F1.mean().item()
    }

# Exemplo
scores = compute_bertscore(
    predicted=["A capital da França é Paris"],
    expected=["Paris é a cidade capital da França"]
)
# {'precision': 0.94, 'recall': 0.91, 'f1': 0.92}

Métrica 4: Perplexidade (Confiança do Modelo)

import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer

def compute_perplexity(text: str, model_name: str = "gpt2") -> float:
    """
    Perplexidade: Quão "surpreso" o modelo está com este texto?

    Mais baixo = Mais provável/fluente
    Uso para: Fluência, naturalidade
    Limitações: Não mede correção
    """
    model = GPT2LMHeadModel.from_pretrained(model_name)
    tokenizer = GPT2Tokenizer.from_pretrained(model_name)

    inputs = tokenizer(text, return_tensors="pt")

    with torch.no_grad():
        outputs = model(**inputs, labels=inputs["input_ids"])
        loss = outputs.loss

    perplexity = torch.exp(loss).item()
    return perplexity

# Exemplo
ppl = compute_perplexity("Paris é a capital da França")  # Baixo (fluente)
ppl2 = compute_perplexity("Capital França a é de Paris")  # Alto (estranho)

Parte 3: Avaliação LLM como Juiz

Padrão 1: Pontuação Baseada em Rubrica

from openai import OpenAI

client = OpenAI()

PROMPT_AVALIACAO = """
Você é um avaliador especialista. Atribua uma pontuação de 1 a 5 para a resposta do assistente em cada critério:

**Critérios:**
1. **Precisão**: A informação é factualmente correta?
2. **Completude**: Ela responde totalmente à pergunta?
3. **Clareza**: É fácil de entender?
4. **Concisão**: É adequadamente breve?

**Resposta a avaliar:**
{response}

**Resposta esperada (referência):**
{expected}

Forneça as pontuações em formato JSON:
{{
  "precisao": <1-5>,
  "completude": <1-5>,
  "clareza": <1-5>,
  "concisao": <1-5>,
  "justificativa": "Explicação breve"
}}
"""

def llm_judge_score(response: str, expected: str) -> dict:
    """
    Use GPT-4 como juiz com pontuação por rubrica.

    Prós: Flexível, sutil, escala bem
    Contras: Custa $, viés potencial, mais lento
    """
    prompt = PROMPT_AVALIACAO.format(response=response, expected=expected)

    completion = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"}
    )

    import json
    scores = json.loads(completion.choices[0].message.content)
    return scores

# Exemplo
scores = llm_judge_score(
    response="Paris é a capital da França, localizada na parte centro-norte do país.",
    expected="Paris"
)
# {'precisao': 5, 'completude': 5, 'clareza': 5, 'concisao': 3, 'justificativa': '...'}

Padrão 2: Avaliação Binária Aprovado/Reprovado

PROMPT_APROVACAO = """
Avalie se a resposta do assistente é aceitável.

**Pergunta:** {question}
**Resposta:** {response}
**Critério:** {criteria}

Retorne APENAS "APROVADO" ou "REPROVADO" seguido de uma justificativa de uma frase.
"""

def binary_eval(question: str, response: str, criteria: str) -> tuple[bool, str]:
    """
    Avaliação simples de aprovado/reprovado.

    Uso para: Testes unitários, testes de regressão, CI/CD
    """
    prompt = PROMPT_APROVACAO.format(
        question=question,
        response=response,
        criteria=criteria
    )

    completion = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.0  # Determinístico
    )

    result = completion.choices[0].message.content
    passed = result.startswith("APROVADO")
    reason = result.split(":", 1)[1].strip() if ":" in result else result

    return passed, reason

# Exemplo
passed, reason = binary_eval(
    question="Qual é a capital da França?",
    response="A capital é Paris",
    criteria="A resposta deve mencionar Paris"
)
# (True, "A resposta identifica corretamente Paris como a capital")

Padrão 3: Comparação Pareada (Teste A/B)

PROMPT_PAR = """
Compare duas respostas para a mesma pergunta. Qual é melhor?

**Pergunta:** {question}

**Resposta A:**
{response_a}

**Resposta B:**
{response_b}

**Critério:** {criteria}

Retorne APENAS: "A", "B" ou "EMPATE", seguido de uma explicação de uma frase.
"""

def pairwise_comparison(
    question: str,
    response_a: str,
    response_b: str,
    criteria: str = "Qualidade geral, precisão e utilidade"
) -> tuple[str, str]:
    """
    Teste A/B de duas respostas.

    Uso para: Engenharia de prompts, comparação de modelos
    """
    prompt = PROMPT_PAR.format(
        question=question,
        response_a=response_a,
        response_b=response_b,
        criteria=criteria
    )

    completion = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.0
    )

    result = completion.choices[0].message.content
    winner = result.split()[0]  # "A", "B", ou "EMPATE"
    reason = result.split(":", 1)[1].strip() if ":" in result else result

    return winner, reason

# Exemplo
winner, reason = pairwise_comparison(
    question="Explique computação quântica",
    response_a="Computadores quânticos usam qubits em vez de bits...",
    response_b="Computação quântica é complexa. Ela usa mecânica quântica."
)
# ("A", "A resposta A fornece mais detalhes e explicação")

Parte 4: Detecção de Alucinações

Método 1: Verificação de Fundamentação

def check_grounding(response: str, context: str) -> dict:
    """
    Verifique se a resposta está fundamentada no contexto fornecido.

    Crítico para sistemas RAG.
    """
    PROMPT_FUNDAMENTACAO = """
    Contexto: {context}

    Resposta: {response}

    A resposta é totalmente suportada pelo contexto? Responda com:
    - "FUNDAMENTADA": Todas as afirmações são suportadas
    - "PARCIALMENTE_FUNDAMENTADA": Algumas afirmações não suportadas
    - "NAO_FUNDAMENTADA": Contém afirmações não suportadas

    Liste quaisquer afirmações não suportadas.
    """

    prompt = PROMPT_FUNDAMENTACAO.format(context=context, response=response)

    completion = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}]
    )

    result = completion.choices[0].message.content
    status = result.split("\n")[0]
    unsupported = [line for line in result.split("\n")[1:] if line.strip()]

    return {
        'grounding_status': status,
        'unsupported_claims': unsupported,
        'is_hallucination': status != "FUNDAMENTADA"
    }

Método 2: Verificação de Factualidade (Verificação Externa)

def check_factuality(claim: str, use_search: bool = True) -> dict:
    """
    Verifique afirmações factuais usando fontes externas.

    Opções:
    1. Pesquisa na web + verificação
    2. Consulta a base de conhecimento
    3. Referência cruzada com fonte confiável
    """
    if use_search:
        # Use pesquisa na web para verificar
        from tavily import TavilyClient
        tavily = TavilyClient(api_key="sua-chave")

        # Pesquise por evidências
        results = tavily.search(claim, max_results=3)

        # Peça ao LLM para verificar com base nos resultados da pesquisa
        PROMPT_VERIFICACAO = """
        Afirmação: {claim}

        Resultados da pesquisa:
        {results}

        A afirmação é suportada por essas fontes? Responda: VERDADEIRO, FALSO ou INCERTO.
        Explicação:
        """

        prompt = PROMPT_VERIFICACAO.format(
            claim=claim,
            results="\n\n".join([r['content'] for r in results])
        )

        completion = client.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}]
        )

        result = completion.choices[0].message.content
        is_factual = result.startswith("VERDADEIRO")

        return {
            'claim': claim,
            'factual': is_factual,
            'evidence': results,
            'explanation': result
        }

Método 3: Verificação de Autoconsistência

def self_consistency_check(question: str, num_samples: int = 5) -> dict:
    """
    Gere múltiplas respostas, verifique consistência.

    Se o modelo estiver confiante, as respostas devem ser consistentes.
    Inconsistência sugere risco de alucinação.
    """
    responses = []

    for _ in range(num_samples):
        completion = client.chat.completions.create(
            model="gpt-4",
            messages=[{"role": "user", "content": question}],
            temperature=0.7  # Alguma aleatoriedade
        )
        responses.append(completion.choices[0].message.content)

    # Calcular similaridade pareada
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity

    vectorizer = TfidfVectorizer()
    vectors = vectorizer.fit_transform(responses)
    similarities = cosine_similarity(vectors)

    # Similaridade pareada média
    avg_similarity = similarities.sum() / (len(responses) * (len(responses) - 1))

    return {
        'responses': responses,
        'avg_similarity': avg_similarity,
        'is_consistent': avg_similarity > 0.7,  # Limiar
        'confidence': 'alta' if avg_similarity > 0.85 else 'media' if avg_similarity > 0.7 else 'baixa'
    }

Parte 5: Avaliação Específica para RAG

Métricas de Qualidade de Recuperação

def evaluate_retrieval(query: str, retrieved_docs: List[dict], relevant_doc_ids: List[str]) -> dict:
    """
    Avalie a qualidade da recuperação usando métricas de RI.

    Precisão: Qual % dos documentos recuperados são relevantes?
    Recall: Qual % dos documentos relevantes foram recuperados?
    MRR: Mean Reciprocal Rank
    NDCG: Ganho Cumulativo Descontado Normalizado
    """
    retrieved_ids = [doc['id'] 

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

Habilidades Relacionadas