Automatisierte RAG-Evaluierungs-Pipeline: Regressionen erkennen bevor Ihre Nutzer es tun
Kompletter Guide zum Aufbau einer automatisierten RAG-Evaluierungs-Pipeline: Golden Datasets, RAGAS-Metriken, Regressionserkennung, Alerting und CI/CD-Integration mit GitHub Actions.
TL;DR
83% der RAG-Teams entdecken Regressionen durch Nutzerbeschwerden. Dieser Guide zeigt Ihnen, wie Sie eine automatisierte Evaluierungs-Pipeline aufbauen, die Probleme vor der Produktion erkennt: verwaltete Golden Datasets, automatisierte RAGAS-Metriken (Faithfulness, Relevancy, Context Recall), Alarmschwellenwerte und GitHub-Actions-Integration. Mit einer gut konfigurierten Pipeline erkennen Sie 95% der Regressionen, bevor sie die Nutzer erreichen.
Warum eine automatisierte Evaluierungs-Pipeline
Das Problem mit manuellen Evaluierungen
| Ansatz | Abdeckung | Haeufigkeit | Kosten | Regressionserkennung |
|---|---|---|---|---|
| Manuelle Tests | 5-10% der Faelle | Ad-hoc | Hoch (Zeit) | Spaet |
| Nutzer-Feedback | Auswahlbias | Kontinuierlich | Kostenlos | Sehr spaet |
| Automatisierte Evaluierung | 100% des Golden Dataset | Bei jeder Aenderung | Niedrig | Sofort |
Ursachen von Regressionen in RAG
┌────────────────────────────────────────────────────┐
│ RAG-REGRESSIONSURSACHEN │
├────────────────────────────────────────────────────┤
│ │
│ Dokumentenaenderungen │
│ ├─ Schlecht formatierte neue Dokumente │
│ ├─ Geloeschte relevante Dokumente │
│ └─ Updates die das Chunking beschaedigen │
│ │
│ Konfigurationsaenderungen │
│ ├─ Neues Embedding-Modell │
│ ├─ Chunk-Groessenaenderung │
│ ├─ Prompt-Modifikation │
│ └─ Reranker-Update │
│ │
│ LLM-Modellaenderungen │
│ ├─ Neue Modellversion │
│ ├─ Provider-Wechsel │
│ └─ Temperatur/Top-p Aenderung │
│ │
│ Stille Degradation │
│ ├─ Daten-Drift │
│ ├─ Fragmentierter Vektor-Index │
│ └─ Embedding-Provider API-Aenderung │
└────────────────────────────────────────────────────┘
Architektur der Evaluierungs-Pipeline
Ueberblick
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Golden │ │ RAG │ │ Automatische │
│ Dataset │────▶│ Pipeline │────▶│ Evaluierung │
│ │ │ │ │ │
│ - Fragen │ │ - Retrieval │ │ - RAGAS │
│ - Antworten │ │ - Generation │ │ - DeepEval │
│ - Kontexte │ │ │ │ - Custom │
└─────────────┘ └──────────────┘ └──────┬───────┘
│
┌────────┴────────┐
│ │
┌─────┴─────┐ ┌──────┴──────┐
│ Schwellen │ │ Dashboard │
│ & Alarme │ │ Langfuse │
└─────┬─────┘ └─────────────┘
│
┌─────┴─────┐
│ CI/CD │
│ Gate │
└───────────┘
Schritt 1: Golden Dataset aufbauen
Struktur des Golden Dataset
DEVELOPERpythonfrom dataclasses import dataclass, field from typing import List, Optional import json @dataclass class GoldenExample: """Ein Beispiel aus dem Golden Dataset.""" id: str question: str expected_answer: str expected_contexts: List[str] category: str # "factual", "comparison", "multi-hop", "opinion" difficulty: str # "easy", "medium", "hard" tags: List[str] = field(default_factory=list) metadata: dict = field(default_factory=dict) @dataclass class GoldenDataset: """Vollstaendiges Evaluierungs-Dataset.""" version: str created_at: str examples: List[GoldenExample] def to_json(self, path: str): with open(path, "w") as f: json.dump(self.__dict__, f, indent=2, default=str) @classmethod def from_json(cls, path: str): with open(path) as f: data = json.load(f) examples = [GoldenExample(**ex) for ex in data["examples"]] return cls( version=data["version"], created_at=data["created_at"], examples=examples )
Golden Dataset automatisch generieren
DEVELOPERpythonimport anthropic client = anthropic.Anthropic() def generate_golden_examples( documents: list, num_questions: int = 50, categories: list = None ) -> list: """Generiert Testbeispiele aus Dokumenten.""" if categories is None: categories = ["factual", "comparison", "multi-hop"] examples = [] for doc in documents: response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=2000, messages=[{ "role": "user", "content": f"""Generiere aus dem folgenden Dokument {num_questions // len(documents)} Frage/Antwort-Paare zum Testen eines RAG-Systems. Fuer jedes Paar: - question: natuerlich, wie ein echter Nutzer fragen wuerde - expected_answer: die korrekte und vollstaendige Antwort - context: die exakte Passage aus dem Dokument mit der Antwort - category: {', '.join(categories)} - difficulty: easy, medium, oder hard Gib JSON zurueck: [{{"question": "...", "expected_answer": "...", "context": "...", "category": "...", "difficulty": "..."}}] Dokument: {doc['text'][:5000]}""" }] ) batch = json.loads(response.content[0].text) for item in batch: examples.append(GoldenExample( id=f"gen_{len(examples)}", question=item["question"], expected_answer=item["expected_answer"], expected_contexts=[item["context"]], category=item["category"], difficulty=item["difficulty"], tags=[doc.get("source", "unknown")] )) return examples
Empfohlene Golden-Dataset-Groesse
| Korpusgroesse | Empfohlene Beispiele | Verteilung |
|---|---|---|
| < 100 Docs | 30-50 | 50% faktisch, 30% Vergleich, 20% Multi-Hop |
| 100-1000 Docs | 50-100 | 40% faktisch, 30% Vergleich, 20% Multi-Hop, 10% Randfaelle |
| 1000-10000 Docs | 100-200 | Wie oben + 10% adversarial |
| > 10000 Docs | 200-500 | Stratifiziert nach Dokumentkategorie |
Schritt 2: Metriken implementieren
Mit RAGAS
DEVELOPERpythonfrom ragas import evaluate from ragas.metrics import ( faithfulness, answer_relevancy, context_precision, context_recall, answer_correctness, ) from datasets import Dataset def evaluate_rag_with_ragas( golden_dataset: GoldenDataset, rag_pipeline, metrics=None ): """Evaluiert die RAG-Pipeline mit RAGAS.""" if metrics is None: metrics = [ faithfulness, answer_relevancy, context_precision, context_recall, answer_correctness, ] questions = [] answers = [] contexts = [] ground_truths = [] for example in golden_dataset.examples: result = rag_pipeline.query(example.question) questions.append(example.question) answers.append(result["answer"]) contexts.append(result["retrieved_contexts"]) ground_truths.append(example.expected_answer) eval_dataset = Dataset.from_dict({ "question": questions, "answer": answers, "contexts": contexts, "ground_truth": ground_truths, }) results = evaluate(eval_dataset, metrics=metrics) return results results = evaluate_rag_with_ragas(golden_dataset, my_rag_pipeline) print(f"Faithfulness: {results['faithfulness']:.3f}") print(f"Answer Relevancy: {results['answer_relevancy']:.3f}") print(f"Context Recall: {results['context_recall']:.3f}")
Mit DeepEval
DEVELOPERpythonfrom deepeval import evaluate from deepeval.metrics import ( FaithfulnessMetric, AnswerRelevancyMetric, ContextualRecallMetric, ContextualPrecisionMetric, HallucinationMetric, ) from deepeval.test_case import LLMTestCase def evaluate_with_deepeval( golden_dataset: GoldenDataset, rag_pipeline ): """Evaluiert die RAG-Pipeline mit DeepEval.""" test_cases = [] for example in golden_dataset.examples: result = rag_pipeline.query(example.question) test_case = LLMTestCase( input=example.question, actual_output=result["answer"], expected_output=example.expected_answer, retrieval_context=result["retrieved_contexts"], context=example.expected_contexts, ) test_cases.append(test_case) metrics = [ FaithfulnessMetric(threshold=0.8), AnswerRelevancyMetric(threshold=0.7), ContextualRecallMetric(threshold=0.7), ContextualPrecisionMetric(threshold=0.7), HallucinationMetric(threshold=0.2), ] results = evaluate(test_cases, metrics) return results
Custom-Metriken mit pytest
DEVELOPERpython# tests/test_rag_quality.py import pytest import json GOLDEN_DATASET = GoldenDataset.from_json("tests/golden_dataset.json") RAG_PIPELINE = init_rag_pipeline() THRESHOLDS = { "faithfulness": 0.85, "answer_relevancy": 0.80, "context_recall": 0.75, "context_precision": 0.70, "answer_correctness": 0.75, "latency_p95_ms": 3000, "hallucination_rate": 0.05, } class TestRAGQuality: """Automatisierte RAG-Qualitaetstests.""" @pytest.fixture(autouse=True) def setup(self): self.results = [] for example in GOLDEN_DATASET.examples: result = RAG_PIPELINE.query(example.question) self.results.append({ "example": example, "result": result }) def test_faithfulness_above_threshold(self): """Antworten sind quellentreu.""" scores = [ compute_faithfulness(r["result"]["answer"], r["result"]["retrieved_contexts"]) for r in self.results ] avg_score = sum(scores) / len(scores) assert avg_score >= THRESHOLDS["faithfulness"], \ f"Faithfulness {avg_score:.3f} < {THRESHOLDS['faithfulness']}" def test_context_recall_above_threshold(self): """Die richtigen Dokumente werden abgerufen.""" scores = [] for r in self.results: retrieved = r["result"]["retrieved_contexts"] expected = r["example"].expected_contexts recall = compute_context_recall(retrieved, expected) scores.append(recall) avg_score = sum(scores) / len(scores) assert avg_score >= THRESHOLDS["context_recall"], \ f"Context Recall {avg_score:.3f} < {THRESHOLDS['context_recall']}" def test_no_hallucination_spike(self): """Nicht mehr als 5% Halluzinationen.""" hallucination_count = sum( 1 for r in self.results if is_hallucination( r["result"]["answer"], r["result"]["retrieved_contexts"] ) ) rate = hallucination_count / len(self.results) assert rate <= THRESHOLDS["hallucination_rate"], \ f"Halluzinationsrate {rate:.3f} > {THRESHOLDS['hallucination_rate']}" def test_latency_within_bounds(self): """P95-Latenz unter 3 Sekunden.""" latencies = [r["result"]["latency_ms"] for r in self.results] latencies.sort() p95 = latencies[int(len(latencies) * 0.95)] assert p95 <= THRESHOLDS["latency_p95_ms"], \ f"P95-Latenz {p95}ms > {THRESHOLDS['latency_p95_ms']}ms"
Schritt 3: CI/CD-Integration mit GitHub Actions
GitHub Actions Workflow
DEVELOPERyaml# .github/workflows/rag-evaluation.yml name: RAG Evaluation Pipeline on: push: paths: - 'backend/rag/**' - 'backend/prompts/**' - 'backend/config/**' pull_request: branches: [main, develop] schedule: - cron: '0 6 * * 1' # Jeden Montag um 6 Uhr jobs: evaluate: runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install dependencies run: | pip install ragas deepeval pytest langfuse pip install -r backend/requirements.txt - name: Run RAG evaluation env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} QDRANT_URL: ${{ secrets.QDRANT_URL }} LANGFUSE_SECRET_KEY: ${{ secrets.LANGFUSE_SECRET_KEY }} run: | pytest tests/test_rag_quality.py \ --tb=long \ --json-report \ --json-report-file=eval_results.json \ -v - name: Check thresholds run: | python scripts/check_eval_thresholds.py \ --results eval_results.json \ --baseline tests/baseline_scores.json - name: Alert on regression if: failure() run: | python scripts/send_regression_alert.py \ --results eval_results.json \ --channel slack \ --webhook ${{ secrets.SLACK_WEBHOOK }}
Schritt 4: Dashboard und Monitoring
Vergleich der Evaluierungs-Tools
| Tool | Metriken | CI/CD | Open-Source | Dashboard | Preis |
|---|---|---|---|---|---|
| RAGAS | Faithfulness, Relevancy, Recall, Precision | Via pytest | Ja | Nein (custom) | Kostenlos |
| DeepEval | 14+ Metriken, Halluzination, Toxizitaet | Nativ | Ja | Ja (Confident AI) | Freemium |
| Langfuse | Custom + LLM-as-Judge | Via SDK | Ja | Ja (ausgezeichnet) | Freemium |
| Braintrust | Custom, RAGAS, A/B-Vergleich | Nativ | Nein | Ja | Kostenlos / $249/Mo (Pro) |
| Arize Phoenix | Traces, Embeddings, Drifts | Via SDK | Ja | Ja | Freemium |
| TruLens | Feedback-Funktionen, Groundedness | Via SDK | Ja | Ja | Kostenlos |
Langfuse-Integration
DEVELOPERpythonfrom langfuse import Langfuse langfuse = Langfuse( secret_key="sk-lf-...", public_key="pk-lf-...", host="https://cloud.langfuse.com" ) def log_evaluation_to_langfuse( results: dict, run_id: str, metadata: dict = None ): """Protokolliert Evaluierungsergebnisse in Langfuse.""" for example_result in results["examples"]: trace = langfuse.trace( name="rag-evaluation", metadata={ "run_id": run_id, "category": example_result["category"], **(metadata or {}) } ) trace.span( name="retrieval", input=example_result["question"], output=example_result["retrieved_contexts"], ) trace.score(name="faithfulness", value=example_result["faithfulness"]) trace.score(name="answer_relevancy", value=example_result["relevancy"]) trace.score(name="context_recall", value=example_result["context_recall"]) langfuse.flush()
Dashboard-Metriken zum Tracken
| Metrik | Kritischer Schwellenwert | Alarm-Schwellenwert | Haeufigkeit |
|---|---|---|---|
| Faithfulness | < 0,80 | < 0,85 | Pro Commit |
| Answer Relevancy | < 0,75 | < 0,80 | Pro Commit |
| Context Recall | < 0,70 | < 0,75 | Pro Commit |
| Context Precision | < 0,65 | < 0,70 | Pro Commit |
| Halluzinationsrate | > 0,10 | > 0,05 | Pro Commit |
| Latenz P95 | > 5000ms | > 3000ms | Pro Commit |
| Kosten pro Anfrage | > $0,10 | > $0,05 | Woechentlich |
| Drift-Score | > 0,15 | > 0,10 | Taeglich |
Schritt 5: Fortgeschrittene Regressionserkennung
Statistische Erkennung
DEVELOPERpythonimport numpy as np from scipy import stats def detect_regression( current_scores: list, baseline_scores: list, alpha: float = 0.05, min_delta: float = 0.03 ) -> dict: """Erkennt statistisch signifikante Regressionen.""" current = np.array(current_scores) baseline = np.array(baseline_scores) # Welch t-Test (fuer ungleiche Stichprobengroessen) t_stat, p_value = stats.ttest_ind( baseline, current, equal_var=False, alternative='greater' ) delta = np.mean(baseline) - np.mean(current) is_regression = ( p_value < alpha and delta > min_delta ) return { "is_regression": is_regression, "p_value": p_value, "delta": delta, "current_mean": np.mean(current), "baseline_mean": np.mean(baseline), "confidence": 1 - p_value, "effect_size": delta / np.std(baseline) }
Intelligentes Alerting
DEVELOPERpythondef evaluate_and_alert(rag_pipeline, golden_dataset, config): """Evaluiert RAG und sendet Alarme bei Bedarf.""" results = evaluate_rag_with_ragas(golden_dataset, rag_pipeline) baseline = load_baseline() alerts = [] for metric, value in results.items(): threshold = config["thresholds"].get(metric) previous = baseline.get(metric) if threshold and value < threshold: alerts.append({ "level": "critical", "metric": metric, "value": value, "threshold": threshold, "message": ( f"{metric} fiel auf {value:.3f}, " f"unter kritischem Schwellenwert {threshold:.3f}" ) }) elif previous and value < previous - 0.03: alerts.append({ "level": "warning", "metric": metric, "value": value, "previous": previous, "message": ( f"{metric} sank von {previous:.3f} auf " f"{value:.3f} (Delta: {value - previous:+.3f})" ) }) if alerts: send_alerts(alerts, config) if not any(a["level"] == "critical" for a in alerts): update_baseline(results) return results, alerts
Best Practices
Golden-Dataset-Verwaltung
- Versionieren Sie das Golden Dataset in Git (wie Code)
- Aktualisieren wenn sich Dokumente wesentlich aendern
- Stratifizieren nach Kategorie, Schwierigkeit und Quelle
- Menschlich validieren Sie regelmaessig eine Stichprobe
- Niemals fuer das Golden Dataset optimieren (Overfitting)
Robuste Pipeline
| Praxis | Warum | Wie |
|---|---|---|
| Deterministische Tests | Reproduzierbare Ergebnisse | Fester Seed, Temperatur 0 |
| Versionierte Baseline | Entwicklungen vergleichen | JSON in Git |
| Abgestufte Alarme | Keine Alarm-Muedigkeit | Critical vs Warning |
| Archivierte Ergebnisse | Trendanalyse | Langfuse oder DB |
| Timeout pro Test | Blockierungen vermeiden | pytest-timeout |
Evaluierungshaeufigkeit
| Ereignis | Evaluierung | Umfang |
|---|---|---|
| Push auf develop | Schnell (20 Beispiele) | Smoke Test |
| PR nach main | Vollstaendig (gesamtes Golden Dataset) | Gate |
| Woechentlich | Vollstaendig + Drift | Monitoring |
| Modellaenderung | Vollstaendig + A/B-Test | Validierung |
| Dokumentenaenderung | Nur Retrieval | Fokus |
FAQ
Wie viele Beispiele im Golden Dataset?
Minimum 30 fuer statistisch signifikante Ergebnisse. Ideal sind 100-200 Beispiele, stratifiziert nach Fragenkategorie. Ueber 500 steigen die Evaluierungskosten ohne proportionalen Vertrauensgewinn. Beginnen Sie mit 50 und erhoehem Sie schrittweise. Siehe unseren Guide zu RAG-Evaluierungsmetriken fuer Details zu den Metriken.
RAGAS oder DeepEval, welches waehlen?
RAGAS ist reifer, besser dokumentiert und die akademische Referenz. DeepEval bietet mehr Metriken (14+), native CI/CD-Integration und ein Dashboard. Fuer ein einfaches Projekt reicht RAGAS. Fuer eine Produktions-Pipeline mit Monitoring ist DeepEval + Langfuse vollstaendiger. Beide sind Open-Source und mit pytest kompatibel.
Wie geht man mit Fragen um, die keine gute Antwort haben?
Nehmen Sie in Ihr Golden Dataset Fragen auf, die das RAG nicht beantworten sollte (ausserhalb des Umfangs, fehlende Information). Fuegen Sie eine "Refusal Accuracy"-Metrik hinzu: Das RAG sollte die Antwort verweigern oder "Ich weiss nicht" sagen, anstatt zu halluzinieren. Dies ist oft die schwierigste Metrik zum Aufrechterhalten.
Ist die Evaluierungs-Pipeline teuer?
Fuer 100 Beispiele mit RAGAS (das standardmaessig GPT-4o-mini fuer die Evaluierung verwendet): von wenigen Cent bis zu einigen Dollar pro Durchlauf, je nach gewaehltem Judge-Modell. Mit DeepEval und einem lokalen Modell ist es nahezu kostenlos. Die Hauptkosten sind die Laufzeit (~5-15 Minuten fuer 100 Beispiele). Das ist vernachlaessigbar im Vergleich zu den Kosten einer Regression in der Produktion. Siehe unseren Guide zur RAG-Kostenoptimierung.
Wie testet man Prompt-Aenderungen?
Verwenden Sie A/B-Evaluierung: Fuehren Sie das Golden Dataset mit dem alten und neuen Prompt aus und vergleichen Sie die Metriken. Der neue Prompt muss statistisch besser sein (nicht nur marginal), um die Aenderung zu rechtfertigen. Integrieren Sie diesen Vergleich in Ihre PR-Review. Siehe unseren Guide zum RAG Prompt Engineering.
Eine automatisierte RAG-Evaluierungs-Pipeline ist Ihr Sicherheitsnetz gegen stille Regressionen. Ein paar Stunden Investition in den Aufbau sparen Ihnen Tage des Debuggings und schuetzen das Vertrauen Ihrer Nutzer. Testen Sie Ailog, um von integriertem Qualitaetsmonitoring auf Ihrem RAG-Chatbot zu profitieren.
Tags
Verwandte Artikel
RAG-Systeme testen: Die 5-Schritte-Methodik die Google nutzt (Und Sie auch sollten)
Vollstaendige Methodik zum Testen von RAG-Systemen in 5 Schritten: Golden Dataset, Retrieval-Unit-Tests, Generierungstests, End-to-End-RAGAS-Bewertung, A/B-Tests in der Produktion.
Bewertung eines RAG-Systems: Metriken und Methoden
Umfassender Leitfaden zur Messung der Leistung Ihres RAG: faithfulness, relevancy, recall und automatisierte Evaluations-Frameworks.
RAG-Latenz unter 500ms: Der ultimative Guide für blitzschnelle Antworten
Umfassender technischer Leitfaden zur Optimierung Ihrer RAG-Pipeline-Latenz unter 500ms. Zeitaufschluesselung, Optimierungstechniken, Caching, Streaming und Benchmarks.