CommunityCodierung & Entwicklunggithub.com

LLM-Bewertung & -Test

Muster und Methoden zur Bewertung und zum Testen von LLM-Anwendungen, einschließlich Prompt-Tests, Halluzinationserkennung, Benchmark-Erstellung und Qualitätsmetriken. Verwenden Sie dies beim Testen von LLM-Anwendungen, Validieren der Prompt-Qualität, Implementieren systematischer Bewertungen oder Messen der LLM-Leistung.

Was ist LLM-Bewertung & -Test?

Diese Fähigkeit bietet einen umfassenden Leitfaden zur Bewertung und zum Testen von LLM-Anwendungen. Sie behandelt eine Reihe von Metriken, von traditionellen wie BLEU, ROUGE, BERTScore und Perplexität bis hin zu LLM-als-Richter-Ansätzen (z. B. GPT-4-basierte Bewertung) und aufgabenspezifischen Metriken (exakte Übereinstimmung, F1, Genauigkeit). Der Schwerpunkt liegt auf praktischen, sofort einsetzbaren Python-Codeblöcken, die Muster für die Evaluierung von LLM-Ausgaben implementieren, einschließlich Rubrik-basierter Bewertung, binärer Bestehen/Nichtbestehen-Tests und paarweisen Vergleichen (A/B-Tests) für Prompt-Technik und Modellvergleich. Die Fähigkeit geht auch detailliert auf die Erkennung von Halluzinationen durch Grounding-Checks, Faktenprüfung und Methoden zur Selbstkonsistenzprüfung ein. Sie unterstützt RAG-spezifische Bewertungen, einschließlich Metriken zur Retrieval-Qualität (Präzision, Recall, MRR, NDCG) und verknüpft die Datenerfassung mit der Bewertung der generierten Ausgabe. Sie bietet eine vollständige Pipeline von der Definition von Bewertungsdatensätzen über die Berechnung von Metriken bis hin zur Einrichtung von Gate-Mechanismen und der Integration in CI/CD-Pipelines. Die Fähigkeit ist auf Entwicklungsplattformen wie Claude Code, Cursor und Codex anwendbar.

Funktioniert mit~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 Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht LLM-Bewertung & -Test?

Umfassender Leitfaden zur Bewertung und zum Testen von LLM-Anwendungen, einschließlich Prompt-Tests, Ausgabevalidierung, Halluzinationserkennung, Benchmark-Erstellung, A/B-Tests und Qualitätsmetriken.


Kurzreferenz

Wann Sie diese Fähigkeit verwenden sollten:

  • Testen von LLM-Anwendungsausgaben
  • Validieren der Prompt-Qualität und -Konsistenz
  • Erkennen von Halluzinationen und faktischen Fehlern
  • Erstellen von Bewertungs-Benchmarks
  • A/B-Testen von Prompts oder Modellen
  • Implementieren kontinuierlicher Bewertung (CI/CD)
  • Messen der Retrieval-Qualität (für RAG)
  • Debuggen unerwarteten LLM-Verhaltens

Abgedeckte Metriken:

  • Traditionell: BLEU, ROUGE, BERTScore, Perplexität
  • LLM-als-Richter: GPT-4-Bewertung, Rubrik-basierte Bewertung
  • Aufgabenspezifisch: Exakte Übereinstimmung, F1, Genauigkeit, Recall
  • Qualität: Toxizität, Verzerrung, Kohärenz, Relevanz

Teil 1: Grundlagen der Bewertung

Die Herausforderung der LLM-Bewertung

Warum LLM-Bewertung schwierig ist:

  1. Subjektive Qualität – „Gute“ Ausgabe variiert je nach Anwendungsfall
  2. Keine einzige Grundwahrheit – Mehrere gültige Antworten
  3. Kontextabhängig – Gleiche Ausgabe in verschiedenen Szenarien gut/schlecht
  4. Teuer zu kennzeichnen – Menschliche Bewertung skaliert nicht
  5. Gegnerische Zerbrechlichkeit – Kleine Prompt-Änderungen = große Ausgabeänderungen

Lösung: Mehrschichtige Bewertung

Schicht 1: Automatisierte Metriken (schnell, skalierbar)
  ↓
Schicht 2: LLM-als-Richter (flexibel, nuanciert)
  ↓
Schicht 3: Menschliche Überprüfung (Goldstandard, teuer)

Struktur des Bewertungsdatensatzes

from dataclasses import dataclass
from typing import List, Optional

@dataclass
class EvalExample:
    """Einzelnes Bewertungsbeispiel."""
    input: str  # Benutzereingabe / Prompt
    expected_output: Optional[str]  # Goldstandard (falls vorhanden)
    context: Optional[str]  # Zusätzlicher Kontext (für RAG)
    metadata: dict  # Kategorie, Schwierigkeitsgrad usw.

@dataclass
class EvalResult:
    """Bewertungsergebnis für ein Beispiel."""
    example_id: str
    actual_output: str
    scores: dict  # {'metric_name': score}
    passed: bool
    failure_reason: Optional[str]

# Beispiel-Datensatz
eval_dataset = [
    EvalExample(
        input="Was ist die Hauptstadt von Frankreich?",
        expected_output="Paris",
        context=None,
        metadata={'category': 'factual', 'difficulty': 'easy'}
    ),
    EvalExample(
        input="Erkläre Quantenverschränkung",
        expected_output=None,  # Keine einzelne Antwort
        context=None,
        metadata={'category': 'explanation', 'difficulty': 'hard'}
    )
]

Teil 2: Traditionelle Metriken

Metrik 1: Exakte Übereinstimmung (Einfachste)

def exact_match(predicted: str, expected: str, case_sensitive: bool = False) -> float:
    """
    Binäre Metrik: 1,0 bei Übereinstimmung, 0,0 sonst.

    Verwenden für: Klassifikation, kurze Antworten, strukturierte Ausgabe
    Einschränkungen: Zu streng für Generierungsaufgaben
    """
    if not case_sensitive:
        predicted = predicted.lower().strip()
        expected = expected.lower().strip()

    return 1.0 if predicted == expected else 0.0

# Beispiel
score = exact_match("Paris", "paris")  # 1.0
score = exact_match("Die Hauptstadt ist Paris", "Paris")  # 0.0

Metrik 2: ROUGE (Recall-Orientiert)

from rouge_score import rouge_scorer

def compute_rouge(predicted: str, expected: str) -> dict:
    """
    ROUGE-Metriken für Textüberlappung.

    ROUGE-1: Unigramm-Überlappung
    ROUGE-2: Bigramm-Überlappung
    ROUGE-L: Längste gemeinsame Teilsequenz

    Verwenden für: Zusammenfassung, Übersetzung
    Einschränkungen: Erfasst keine Semantik
    """
    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
    }

# Beispiel
scores = compute_rouge(
    predicted="Paris ist die Hauptstadt von Frankreich",
    expected="Die Hauptstadt von Frankreich ist Paris"
)
# {'rouge1': 0.82, 'rouge2': 0.67, 'rougeL': 0.82}

Metrik 3: BERTScore (Semantische Ähnlichkeit)

from bert_score import score as bert_score

def compute_bertscore(predicted: List[str], expected: List[str]) -> dict:
    """
    Semantische Ähnlichkeit mit BERT-Embeddings.

    Besser als ROUGE für:
    - Paraphrasen
    - Semantische Äquivalenz
    - Generierungsqualität

    Gibt zurück: Precision, Recall, F1
    """
    P, R, F1 = bert_score(predicted, expected, lang="en", verbose=False)

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

# Beispiel
scores = compute_bertscore(
    predicted=["Die Hauptstadt von Frankreich ist Paris"],
    expected=["Paris ist die Hauptstadt Frankreichs"]
)
# {'precision': 0.94, 'recall': 0.91, 'f1': 0.92}

Metrik 4: Perplexität (Modellvertrauen)

import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer

def compute_perplexity(text: str, model_name: str = "gpt2") -> float:
    """
    Perplexität: Wie „überrascht“ ist das Modell von diesem Text?

    Niedriger = Wahrscheinlicher/flüssiger
    Verwenden für: Flüssigkeit, Natürlichkeit
    Einschränkungen: Misst nicht die Korrektheit
    """
    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

# Beispiel
ppl = compute_perplexity("Paris ist die Hauptstadt von Frankreich")  # Niedrig (flüssig)
ppl2 = compute_perplexity("Hauptstadt Frankreich die ist Paris von")  # Hoch (unbeholfen)

Teil 3: LLM-als-Richter-Bewertung

Muster 1: Rubrik-basierte Bewertung

from openai import OpenAI

client = OpenAI()

EVALUATION_PROMPT = """
Sie sind ein Expertenbewerter. Bewerten Sie die Antwort des Assistenten auf einer Skala von 1-5 für jedes Kriterium:

**Kriterien:**
1. **Genauigkeit**: Sind die Informationen sachlich korrekt?
2. **Vollständigkeit**: Beantwortet sie die Frage vollständig?
3. **Klarheit**: Ist sie leicht verständlich?
4. **Prägnanz**: Ist sie angemessen kurz?

**Zu bewertende Antwort:**
{response}

**Erwartete Antwort (Referenz):**
{expected}

Geben Sie Bewertungen im JSON-Format aus:
{{
  "accuracy": <1-5>,
  "completeness": <1-5>,
  "clarity": <1-5>,
  "conciseness": <1-5>,
  "reasoning": "Kurze Erklärung"
}}
"""

def llm_judge_score(response: str, expected: str) -> dict:
    """
    Verwenden Sie GPT-4 als Richter mit Rubrik-Bewertung.

    Vorteile: Flexibel, nuanciert, gut skalierbar
    Nachteile: Kostet Geld, potenzielle Verzerrung, langsamer
    """
    prompt = EVALUATION_PROMPT.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

# Beispiel
scores = llm_judge_score(
    response="Paris ist die Hauptstadt von Frankreich, gelegen im nördlichen Teil des Landes.",
    expected="Paris"
)
# {'accuracy': 5, 'completeness': 5, 'clarity': 5, 'conciseness': 3, 'reasoning': '...'}

Muster 2: Binäre Bestehen/Nichtbestehen-Bewertung

PASS_FAIL_PROMPT = """
Bewerten Sie, ob die Antwort des Assistenten akzeptabel ist.

**Frage:** {question}
**Antwort:** {response}
**Kriterien:** {criteria}

Geben Sie NUR "BESTANDEN" oder "NICHT BESTANDEN" gefolgt von einem ein-sätzigen Grund zurück.
"""

def binary_eval(question: str, response: str, criteria: str) -> tuple[bool, str]:
    """
    Einfache Bestehen/Nichtbestehen-Bewertung.

    Verwenden für: Unit-Tests, Regressionstests, CI/CD
    """
    prompt = PASS_FAIL_PROMPT.format(
        question=question,
        response=response,
        criteria=criteria
    )

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

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

    return passed, reason

# Beispiel
passed, reason = binary_eval(
    question="Was ist die Hauptstadt von Frankreich?",
    response="Die Hauptstadt ist Paris",
    criteria="Die Antwort muss Paris erwähnen"
)
# (True, "Antwort identifiziert Paris korrekt als Hauptstadt")

Muster 3: Paarweiser Vergleich (A/B-Tests)

PAIRWISE_PROMPT = """
Vergleichen Sie zwei Antworten auf dieselbe Frage. Welche ist besser?

**Frage:** {question}

**Antwort A:**
{response_a}

**Antwort B:**
{response_b}

**Kriterien:** {criteria}

Geben Sie NUR zurück: "A", "B" oder "GLEICH", gefolgt von einer ein-sätzigen Erklärung.
"""

def pairwise_comparison(
    question: str,
    response_a: str,
    response_b: str,
    criteria: str = "Gesamtqualität, Genauigkeit und Hilfsbereitschaft"
) -> tuple[str, str]:
    """
    A/B-Test von zwei Antworten.

    Verwenden für: Prompt-Technik, Modellvergleich
    """
    prompt = PAIRWISE_PROMPT.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" oder "GLEICH"
    reason = result.split(":", 1)[1].strip() if ":" in result else result

    return winner, reason

# Beispiel
winner, reason = pairwise_comparison(
    question="Erklären Sie Quantencomputing",
    response_a="Quantencomputer verwenden Qubits anstelle von Bits...",
    response_b="Quantencomputing ist komplex. Es verwendet Quantenmechanik."
)
# ("A", "Antwort A bietet mehr Details und Erklärungen")

Teil 4: Halluzinationserkennung

Methode 1: Grundlagenprüfung

def check_grounding(response: str, context: str) -> dict:
    """
    Überprüfen, ob die Antwort im bereitgestellten Kontext verankert ist.

    Kritisch für RAG-Systeme.
    """
    GROUNDING_PROMPT = """
    Kontext: {context}

    Antwort: {response}

    Wird die Antwort vollständig durch den Kontext gestützt? Antworten Sie mit:
    - "VERANKERT": Alle Behauptungen gestützt
    - "TEILWEISE_VERANKERT": Einige Behauptungen nicht gestützt
    - "NICHT_VERANKERT": Enthält nicht gestützte Behauptungen

    Listen Sie alle nicht gestützten Behauptungen auf.
    """

    prompt = GROUNDING_PROMPT.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 != "VERANKERT"
    }

Methode 2: Faktentreue-Prüfung (Externe Verifikation)

def check_factuality(claim: str, use_search: bool = True) -> dict:
    """
    Überprüfen Sie faktische Behauptungen mit externen Quellen.

    Optionen:
    1. Websuche + Verifikation
    2. Wissensdatenbank-Suche
    3. Querverweis mit vertrauenswürdiger Quelle
    """
    if use_search:
        # Verwenden Sie die Websuche zur Verifikation
        from tavily import TavilyClient
        tavily = TavilyClient(api_key="your-key")

        # Suchen Sie nach Beweisen
        results = tavily.search(claim, max_results=3)

        # Bitten Sie das LLM, basierend auf den Suchergebnissen zu überprüfen
        VERIFY_PROMPT = """
        Behauptung: {claim}

        Suchergebnisse:
        {results}

        Wird die Behauptung durch diese Quellen gestützt? Antworten Sie: WAHR, FALSCH oder UNGEWISS.
        Erklärung:
        """

        prompt = VERIFY_PROMPT.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("WAHR")

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

Methode 3: Selbstkonsistenz-Prüfung

def self_consistency_check(question: str, num_samples: int = 5) -> dict:
    """
    Generieren Sie mehrere Antworten, prüfen Sie auf Konsistenz.

    Wenn das Modell zuversichtlich ist, sollten die Antworten konsistent sein.
    Inkonsistenz deutet auf Halluzinationsrisiko hin.
    """
    responses = []

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

    # Berechnen Sie die paarweise Ähnlichkeit
    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)

    # Durchschnittliche paarweise Ähnlichkeit
    avg_similarity = similarities.sum() / (len(responses) * (len(responses) - 1))

    return {
        'responses': responses,
        'avg_similarity': avg_similarity,
        'is_consistent': avg_similarity > 0.7,  # Schwellenwert
        'confidence': 'hoch' if avg_similarity > 0.85 else 'mittel' if avg_similarity > 0.7 else 'niedrig'
    }

Teil 5: RAG-spezifische Bewertung

Metriken zur Retrieval-Qualität

def evaluate_retrieval(query: str, retrieved_docs: List[dict], relevant_doc_ids: List[str]) -> dict:
    """
    Bewerten Sie die Retrieval-Qualität mit IR-Metriken.

    Precision: Wie viel % der abgerufenen Dokumente sind relevant?
    Recall: Wie viel % der relevanten Dokumente wurden abgerufen?
    MRR: Mittlerer Kehrwert des Rangs
    NDCG: Normalisierter diskontierter kumulativer Gewinn
    """
    retrieved_ids = [doc['id'] for doc in retrieved_docs]

    correct = len(set(retrieved_ids) & set(relevant_doc_ids))
    precision = correct / len(retrieved_ids) if retrieved_ids else 0
    recall = correct / len(relevant_doc_ids) if relevant_doc_ids else 0

    # MRR: Reziproker Rang des ersten relevanten Dokuments
    for rank, doc_id in enumerate(retrieved_ids, 1):
        if doc_id in relevant_doc_ids:
            mrr = 1.0 / rank
            break
    else:
        mrr = 0.0

    # Vereinfachter NDCG
    dcg = 0
    idcg = 0
    for rank, doc_id in enumerate(retrieved_ids, 1):
        relevance = 1 if doc_id in relevant_doc_ids else 0
        dcg += (2**relevance - 1) / (rank + 1)
        idcg += 1 / (rank + 1)  # Ideal: Alle relevant
    ndcg = dcg / idcg if idcg else 0

    return {
        'precision': precision,
        'recall': recall,
        'f1': 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0,
        'mrr': mrr,
        'ndcg': ndcg
    }

Metrik 6: Antwortqualität (Vollständig)

def evaluate_response_quality(query: str, response: str, context: str) -> dict:
    """
    Umfassende Bewertung der Antwort unter Verwendung mehrerer Metriken.

    Kombiniert:
    - Grundlagenprüfung (Grounding)
    - Faktentreue (Factuality)
    - Antwortrelevanz (Relevance)
    - Informationsdichte (Density)
    """
    EVAL_PROMPT = """
    Bewerten Sie diese Antwort basierend auf:

    **Frage:** {query}
    **Kontext:** {context}
    **Antwort:** {response}

    Bewerten Sie jede Dimension auf einer Skala von 1-5:
    1. **Grounding:** Nutzt die Antwort den Kontext, ohne etwas zu erfinden?
    2. **Relevance:** Beantwortet sie die Frage direkt?
    3. **Factuality:** Ist jede Behauptung überprüfbar wahr?
    4. **Completeness:** Deckt sie alle Aspekte der Frage ab?
    5. **Conciseness:** Ist sie ohne unnötige Details?

    Geben Sie JSON zurück:
    {{'scores': {{'grounding': int, 'relevance': int, 'factuality': int, 'completeness': int, 'conciseness': int}}, 'overall': float, 'feedback': 'String'}}
    """

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

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

Teil 6: Benchmark-Erstellung

Benchmark erstellen

from typing import Callable

class EvalBenchmark:
    """
    Rahmenwerk zum Erstellen und Ausführen von Bewertungen.
    """
    
    def __init__(self, name: str):
        self.name = name
        self.examples = []
        self.metrics = []
        self.results = []
    
    def add_example(self, example: EvalExample):
        self.examples.append(example)
    
    def add_metric(self, metric_fn: Callable, name: str):
        """
        Füge eine Metrikfunktion hinzu.
        Metrikfunktion: (predicted, expected) -> float
        """
        self.metrics.append({'name': name, 'fn': metric_fn})
    
    def run(self, model_fn: Callable) -> List[EvalResult]:
        """
        Führe den Benchmark mit einer Modellfunktion aus.
        model_fn: (input, context) -> output
        """
        results = []
        
        for i, example in enumerate(self.examples):
            actual = model_fn(example.input, example.context)
            
            scores = {}
            for metric in self.metrics:
                scores[metric['name']] = metric['fn'](actual, example.expected_output)
            
            passed = all(s >= 0.5 for s in scores.values())  # Konfigurierbar
            
            results.append(EvalResult(
                example_id=f"{i}",
                actual_output=actual,
                scores=scores,
                passed=passed
            ))
        
        self.results = results
        return results
    
    def summary(self) -> dict:
        """Berechne die Zusammenfassungsstatistiken."""
        if not self.results:
            return {'error': 'No results'}
        
        n_passed = sum(1 for r in self.results if r.passed)
        
        all_scores = {}
        for r in self.results:
            for metric, score in r.scores.items():
                if metric not in all_scores:
                    all_scores[metric] = []
                all_scores[metric].append(score)
        
        avg_scores = {
            metric: sum(scores) / len(scores)
            for metric, scores in all_scores.items()
        }
        
        return {
            'total': len(self.results),
            'passed': n_passed,
            'failed': len(self.results) - n_passed,
            'pass_rate': n_passed / len(self.results),
            'average_scores': avg_scores
        }

# Verwendung
benchmark = EvalBenchmark("FAQ Quality")
benchmark.add_metric(exact_match, "exact_match")
benchmark.add_metric(lambda p, e: compute_rouge(p, e)['rougeL'], "rouge_l")

results = benchmark.run(lambda input, ctx: client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[{"role": "user", "content": input}]
).choices[0].message.content)

print(benchmark.summary())
# {'total': 10, 'passed': 7, 'failed': 3, 'pass_rate': 0.7, 'average_scores': {...}}

Automatisierte Bewertungspipeline

def eval_pipeline(dataset_path: str, config: dict) -> dict:
    """
    Vollständige Bewertungspipeline mit:
    1. Datensatz laden
    2. Bewertungsmetriken konfigurieren
    3. Benchmark ausführen
    4. Ergebnisse erfassen
    5. Bericht erstellen
    """
    import json
    from datetime import datetime
    
    # Datensatz laden
    with open(dataset_path) as f:
        data = json.load(f)
    
    examples = [EvalExample(**item) for item in data]
    
    # Benchmark erstellen
    benchmark = EvalBenchmark(config.get('name', 'Pipeline Eval'))
    
    for example in examples:
        benchmark.add_example(example)
    
    # Metriken konfigurieren
    for metric_config in config.get('metrics', []):
        name = metric_config['name']
        if name == 'exact_match':
            benchmark.add_metric(exact_match, name)
        elif name == 'rouge':
            benchmark.add_metric(
                lambda p, e: compute_rouge(p, e)['rougeL'],
                'rouge_l'
            )
        elif name == 'bertscore':
            benchmark.add_metric(
                lambda p, e: compute_bertscore([p], [e])['f1'],
                'bertscore_f1'
            )
    
    # Modellfunktion
    model = config['model']
    model_fn = lambda input, ctx: query_model(model, input, ctx)
    
    # Benchmark ausführen
    results = benchmark.run(model_fn)
    summary = benchmark.summary()
    
    # Ergebnisse erfassen
    report = {
        'timestamp': datetime.now().isoformat(),
        'config': config,
        'summary': summary,
        'detailed_results': [
            {
                'example_id': r.example_id,
                'scores': r.scores,
                'passed': r.passed,
                'failure_reason': r.failure_reason
            }
            for r in results
        ]
    }
    
    return report

Teil 7: Bewährte Praktiken & Tipps

Bewertungsstrategie

def build_eval_strategy(use_case: str, budget: str) -> dict:
    """
    Wählen Sie die richtige Bewertungsmethode basierend auf dem Anwendungsfall.
    """
    strategies = {
        'rag_qa': {
            'automated': ['exact_match', 'rouge', 'bertscore'],
            'llm_judge': ['grounding', 'relevance'],
            'human': ['factuality', 'completeness']
        },
        'summarization': {
            'automated': ['rouge', 'bertscore', 'perplexity'],
            'llm_judge': ['coherence', 'conciseness'],
            'human': ['quality', 'informativeness']
        },
        'code_generation': {
            'automated': ['exact_match', 'bleu'],
            'llm_judge': ['correctness', 'style'],
            'human': ['efficiency', 'readability']
        },
        'creative_writing': {
            'automated': ['perplexity'],
            'llm_judge': ['creativity', 'engagement'],
            'human': ['quality', 'originality']
        }
    }
    
    base = strategies.get(use_case, strategies['rag_qa'])
    
    if budget == 'low':
        return {'layer': 1, 'metrics': base['automated'][:2]}
    elif budget == 'medium':
        return {'layer': 2, 'metrics': base['automated'] + base['llm_judge']}
    else:
        return {'layer': 3, 'metrics': base['automated'] + base['llm_judge'] + base['human']}

Häufige Fehler

  1. Überanpassung an Metriken: Optimierung auf ROUGE vs. tatsächliche Qualität
  2. Zu wenige Beispiele: < 50 Beispiele = unzuverlässige Ergebnisse
  3. Verzerrter Datensatz: Nur einfache Fälle testen
  4. Ignorieren von Rändern: Ausreißer und Fehlermodi übersehen
  5. Fehlen einer Baseline: Kein Vergleich mit früherer Leistung

CI/CD-Integration

# .github/workflows/eval.yml
name: LLM Evaluation

on:
  pull_request:
    paths:
      - 'prompts/**'
      - 'evaluation/**'

jobs:
  evaluate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run LLM Evaluation
        run: |
          python evaluation/run_benchmark.py
      - name: Check Quality Gates
        run: |
          python evaluation/check_gates.py --min-pass-rate 0.8

Qualitäts-Gates

def check_quality_gates(results: dict, thresholds: dict) -> bool:
    """
    Erzwinge Qualitäts-Schwellenwerte.

    Beispiel-Schwellenwerte:
    {
        'pass_rate': 0.8,      # 80% Bestehen
        'rouge_l': 0.5,        # ROUGE-L >= 0.5
        'bertscore_f1': 0.85,  # BERTScore F1 >= 0.85
        'sample_size': 50      # Mindestens 50 Beispiele
    }
    """
    for metric, threshold in thresholds.items():
        if metric == 'pass_rate':
            if results['summary']['pass_rate'] < threshold:
                return False
        elif metric == 'sample_size':
            if results['summary']['total'] < threshold:
                return False
        else:
            avg = results['summary']['average_scores'].get(metric)
            if avg is None or avg < threshold:
                return False
    return True

# Beispiel
thresholds = {
    'pass_rate': 0.8,
    'rouge_l': 0.5,
    'sample_size': 50
}

all_gates_passed = check_quality_gates(report, thresholds)
if not all_gates_passed:
    raise SystemExit("Qualitäts-Gates nicht bestanden. Pipeline gestoppt.")

Zusammenfassung

Auswahl-Leitfaden

AnwendungsfallPrimäre MetrikSekundäre MetrikMethode
Frage-AntwortExakte ÜbereinstimmungBERTScoreAutomatisiert
ZusammenfassungROUGELLM-als-RichterGemischt
ÜbersetzungBLEUBERTScoreAutomatisiert
Code-GenerierungExakte ÜbereinstimmungLLM-als-RichterGemischt
Kreatives SchreibenPerplexitätMenschliche BewertungManuell
RAG-SystemRetrieval-RelevanzAntwort-GroundingGemischt +
(Precision, Recall)LLM-als-Richter

Schlüsselkonzepte

  1. Schichten Sie Ihre Bewertung: Automatisiert → LLM-Richter → Menschlich
  2. Kombinieren Sie mehrere Metriken: Keine einzelne Metrik ist perfekt
  3. Erfassen Sie alles: Jede Anfrage, jede Ausgabe, jede Bewertung
  4. Testen Sie iterativ: Bewerten → Verbessern → Erneut bewerten
  5. Verwenden Sie Qualitäts-Gates: Verhindern Sie Regressionen in CI/CD

Für weitere Informationen siehe LLM Evaluation Guide und RAG Evaluation Best Practices.

Individual skills in this repo

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

Verwandte Skills