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:
- Subjektive Qualität – „Gute“ Ausgabe variiert je nach Anwendungsfall
- Keine einzige Grundwahrheit – Mehrere gültige Antworten
- Kontextabhängig – Gleiche Ausgabe in verschiedenen Szenarien gut/schlecht
- Teuer zu kennzeichnen – Menschliche Bewertung skaliert nicht
- 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
- Überanpassung an Metriken: Optimierung auf ROUGE vs. tatsächliche Qualität
- Zu wenige Beispiele: < 50 Beispiele = unzuverlässige Ergebnisse
- Verzerrter Datensatz: Nur einfache Fälle testen
- Ignorieren von Rändern: Ausreißer und Fehlermodi übersehen
- 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
| Anwendungsfall | Primäre Metrik | Sekundäre Metrik | Methode |
|---|---|---|---|
| Frage-Antwort | Exakte Übereinstimmung | BERTScore | Automatisiert |
| Zusammenfassung | ROUGE | LLM-als-Richter | Gemischt |
| Übersetzung | BLEU | BERTScore | Automatisiert |
| Code-Generierung | Exakte Übereinstimmung | LLM-als-Richter | Gemischt |
| Kreatives Schreiben | Perplexität | Menschliche Bewertung | Manuell |
| RAG-System | Retrieval-Relevanz | Antwort-Grounding | Gemischt + |
| (Precision, Recall) | LLM-als-Richter |
Schlüsselkonzepte
- Schichten Sie Ihre Bewertung: Automatisiert → LLM-Richter → Menschlich
- Kombinieren Sie mehrere Metriken: Keine einzelne Metrik ist perfekt
- Erfassen Sie alles: Jede Anfrage, jede Ausgabe, jede Bewertung
- Testen Sie iterativ: Bewerten → Verbessern → Erneut bewerten
- Verwenden Sie Qualitäts-Gates: Verhindern Sie Regressionen in CI/CD
Für weitere Informationen siehe LLM Evaluation Guide und RAG Evaluation Best Practices.