Startseite / Artikel

Fehler bei leisen RAG-Fehlern beheben: Retrieval, Chunking, Filter und Evaluierungen

RAG-Antworten können richtig klingen und dennoch falsch sein. Ein für Python-Entwickler gedachtes Handbuch zur Erfassung von Retrieval, Chunking, Metadatenfiltern, Evaluierungen, hybriden Suchverfahren und Grundlagen.

Verlag: REACTAPP.TOP8 Min. Lesezeit1774 Wörter

Quellen: RAG Is Failing Quietly: A Debugging Playbook for Python Teams

Die Fehler, die niemand sieht

Die lästigsten RAG-Fehler sind nicht die lautstarken. Lautstarke Fehler sind einfach zu erkennen: Der Bot gibt zu, nichts gefunden zu haben, die Vektordatenbank ist down, eine Embedding-Aufgabe ist abgestürzt oder die Antwort kommt ohne Quellenangaben.

Leise Fehler sind teurer. Dokumente werden abgerufen, die Antwort liest sich flüssig, die Quellenangabe scheint glaubwürdig und das Dashboard meldet eine erfolgreiche Abfrage. Dann liest ein Fachexperte die Antwort und sagt, sie sei falsch. In der Regel ist das der Moment, in dem ein Team erkennt, dass es die falsche Ebene beim Debuggen bearbeitet hat.

RAG ist keine einzelne Funktion. Es handelt sich um eine Abfolge von Entscheidungen, die sich überschneiden. Jemand entscheidet darüber, wie eine Frage gelesen und umformuliert wird, wo Dokumente geteilt werden, welcher Suchmechanismus verwendet wird, welche Metadaten die Kandidaten eingrenzen, wie die Shortlist neu rangiert wird, was letztendlich in die Anfrage aufgenommen wird, wie sich das Modell verhält, wenn die Beweise unzureichend sind, und ob die schriftliche Antwort tatsächlich aus dem Gelieferten abgeleitet werden kann. Wenn man nur auf die endgültige Anfrage schaut, kommt man erst, nachdem der Schaden bereits entstanden ist.

Der zu debuggende Prozess

Ein realistisches System sieht eher so aus wie das Folgende als wie das ordentliche Diagramm auf einer Präsentationsdiashow:

flowchart LR
    A[User question] --> B[Query rewrite]
    B --> C[Retriever]
    C --> D[Reranker]
    D --> E[Evidence pack]
    E --> F[Answer generator]
    F --> G[Verifier]
    G --> H[Final answer]
    C --> I[Trace log]
    D --> I
    E --> I
    F --> I
    G --> I

Jede Komponente kann versagen. Der Generator wird dafür verantwortlich gemacht, weil er die am meisten sichtbare Stufe ist, doch viele Fehler entstehen bereits früher in der Kette. Der Abrufmechanismus könnte den falschen Zeitraum ausgewählt haben. Der Neurankierer könnte einen unbedeutenden, ähnlichen Textabschnitt priorisiert haben. Das Beweismittelpaket könnte den einzigen nützlichen Absatz weggelassen haben, weil das Tokenbudget zu knapp war. Gutes Debuggen bedeutet, jede Stufe sichtbar zu machen.

Fehler 1: Ähnlicher Text ist kein nützliches Beweismittel

Die Vektorsuche erkennt semantische Ähnlichkeiten, die jedoch nicht immer mit der Aufgabenstellung zusammenhängen. Nehmen wir einen Nutzer, der fragt, ob Unternehmenskunden nach der Policy-Änderung im zweiten Quartal einen Rückzahlung erhalten können. Der Abrufmechanismus könnte einfach einen Abschnitt zur Rückzahlungspolitik zurückgeben, weil darin Begriffe wie „Unternehmen“, „Rückzahlung“ und „Policy“ vorkommen. Doch dieser Abschnitt könnte veraltet, unvollständig oder nicht mit dem zweiten Quartal verbunden sein.

Ein nützlicher Ausdruck zeigt die abgerufene Dokument-ID, ihre Version, das Inkrafttrittsdatum, die Ähnlichkeitsbewertung, die Neubewertungsscore, die angewendeten Metadatenfilter sowie an, ob die endgültige Antwort darauf verweist. Wenn die Protokolle nur eine Zahl wie 0,83 angeben, lässt sich der Fehler nicht diagnostizieren.

Fehler 2: Das Chunking hat die Bedeutung zerstört

Das Chunking ist kein Speicherdetail – es bestimmt vielmehr die Qualität der Antwort. Eine Rückerstattungspolitik könnte in Kategorien wie Berechtigung, Ausnahmen, ein Unternehmensanhang, Inkrafttrittsdaten und regionale Unterschiede unterteilt sein. Wenn das Chunking-Tool das Inkrafttrittsdatum von den Ausnahmen trennt, kann die Antwort zwar den Abschnitt zur Berechtigung zitieren, dabei aber die Bedingung übersehen, die das Ergebnis verändert.

Bessere Blöcke enthalten eine stabile Dokument-ID, einen Abschnittsüberschrift, eine Version, ein Datum, ausreichend umliegenden Text, um die Bedeutung zu bewahren, sowie einen Verweis auf das Original. All das spricht jedoch nicht für riesige Blöcke, die nur Unordnung verursachen. Ziel sind Blöcke, die den Punkt bewahren, an dem eine Entscheidung getroffen wird.

Fehler 3: Fehlende Metadatenfilter

Viele RAG-Systeme beginnen als reine semantische Suchsysteme. Das funktioniert, solange das gleiche Thema in verschiedenen Versionen, Produkten, Nutzern, Regionen oder Berechtigungsgruppen vorkommt. Eine Frage zur aktuellen Rückerstattungspolitik des Unternehmens sollte nicht dazu führen, dass das Suchsystem jedes Rückerstattungsdokument durchsucht, das das Unternehmen je erstellt hat. Zunächst sollten Filter angewandt werden.

from dataclasses import dataclass
from datetime import date

@dataclass(frozen=True)
class SearchFilters:
    product: str | None
    customer_tier: str | None
    region: str | None
    as_of: date
    permission_group: str

def build_filters(user_context: dict) -> SearchFilters:
    return SearchFilters(
        product=user_context.get("product"),
        customer_tier=user_context.get("tier"),
        region=user_context.get("region"),
        as_of=date.today(),
        permission_group=user_context["permission_group"],
    )

Die Reihenfolge ist wichtig. Wenden Sie Berechtigungs- und Frischheitsfilter an, bevor Sie das Modell bitten, zu reasoningen. Es ist keine gute Strategie, privaten oder veralteten Kontext abzurufen und zu hoffen, dass der Prompt das Problem löst.

Fehler 4: ein Evaluationsset mit problematischen Szenarien

Der schnellste Weg, ein RAG-System zu verbessern, besteht darin, ein kleines, gezielt ausgewähltes Evaluationsset zu verwenden. Vermeiden Sie tausende Beispiele – beginnen Sie mit etwa dreißig Beispielen, die tatsächliche Fehler des Systems widerspiegeln:

  • Fragen, deren Antwort nicht im Korpus enthalten ist
  • Fragen, die zwei Quellen erfordern
  • Fragen, bei denen eine alte und eine neue Richtlinie miteinander kollidieren
  • Fragen mit ähnlicher Formulierung zu unterschiedlichen Produkten
  • Fragen, die von einer Tabelle abhängen
  • Fragen, bei denen die richtige Antwort darin besteht, an einen Menschen weiterzuleiten

Ein einfaches Retrieval-Tool könnte so aussehen:

from dataclasses import dataclass

@dataclass(frozen=True)
class RagCase:
    question: str
    required_doc_ids: set[str]
    forbidden_doc_ids: set[str]

def evaluate_retrieval(cases: list[RagCase], retrieve) -> dict:
    total = len(cases)
    hit = 0
    leaked_forbidden = 0

    for case in cases:
        results = retrieve(case.question)
        retrieved_ids = {item["doc_id"] for item in results}

        if case.required_doc_ids & retrieved_ids:
            hit += 1

        if case.forbidden_doc_ids & retrieved_ids:
            leaked_forbidden += 1

    return {
        "cases": total,
        "required_hit_rate": hit / total,
        "forbidden_leak_rate": leaked_forbidden / total,
    }

Es ist absichtlich einfach gehalten. Es garantiert keine hohe Qualität der Antworten, erkennt aber viele Abruffehler, bevor das Modell überhaupt Kontext erhält.

Fehler 5: Beurteilung von Antworten ohne Beweise

Eine RAG-Antwort sollte anhand der tatsächlich bereitgestellten Beweise bewertet werden, nicht am gesamten Korpus. Der Unterschied ist aus drei Gründen wichtig. Wenn die richtige Antwort in der Wissensdatenbank vorhanden ist, aber nicht abgerufen wurde, kann man nicht erwarten, dass das Generierungsmodell sie kennt – das ist ein Abruffehler. Wenn die richtigen Beweise abgerufen wurden, die Antwort sie jedoch ignoriert, handelt es sich um einen Generierungsfehler. Wenn die Antwort auf eine Quelle verweist, die die Behauptung nicht stützt, ist es ein Verankerungsfehler. Für alle drei Fälle sind Kennzeichnungen erforderlich.

@dataclass(frozen=True)
class AnswerEval:
    question: str
    answer: str
    evidence_doc_ids: set[str]
    expected_claims: set[str]

def simple_claim_check(eval_case: AnswerEval) -> dict:
    answer_lower = eval_case.answer.lower()
    missing = [
        claim
        for claim in eval_case.expected_claims
        if claim.lower() not in answer_lower
    ]

    return {
        "passed": len(missing) == 0,
        "missing_claims": missing,
        "evidence_count": len(eval_case.evidence_doc_ids),
    }

Produktionsumgebungen können bei subtileren Behauptungen eine menschliche Überprüfung oder einen LLM-Prüfer hinzufügen, doch bereits eine grobe Überprüfung der Behauptungen ist besser als ein vager Zustimmungsklick.

Bessere Nachverfolgbarkeit

Jede Antwort sollte eine strukturierte Nachverfolgbarkeit hinterlassen:

import time
import uuid
from dataclasses import dataclass, field

@dataclass
class RagTrace:
    run_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    started_at: float = field(default_factory=time.time)
    query: str = ""
    rewritten_query: str | None = None
    filters: dict = field(default_factory=dict)
    retrieved: list[dict] = field(default_factory=list)
    evidence_doc_ids: list[str] = field(default_factory=list)
    prompt_tokens: int = 0
    completion_tokens: int = 0
    verifier_result: str | None = None
    latency_ms: int | None = None

def finish_trace(trace: RagTrace) -> RagTrace:
    trace.latency_ms = int((time.time() - trace.started_at) * 1000)
    return trace

Dadurch erhalten praktische Fragen Antworten. Wurde die richtige Sammlung durchsucht? Haben die Filter die richtigen Dokumente entfernt? Hat der Suchmechanismus die erwartete Quelle zutage gefördert? Enthielt das Beweismittelmaterial zu viel Kontext? Ist dem Überprüfer fehlende Zitate aufgefallen? Entstand die Verzögerung durch die Suche, die Erstellung oder die Überprüfung? Ohne Antworten auf diese Fragen gibt es noch kein RAG-System – nur einen Prompt mit einem angehängten Suchfeld.

Hybride Suche ist oft die langweilige Lösung

Teams wechseln oft viel zu schnell von einer einfachen Vektorsuche zu einer agierenden RAG-Lösung. Eine hybride Suchmethode sollte zunächst ausprobiert werden. Die semantische Suche kommt dann zum Tragen, wenn Benutzer und Dokument unterschiedliche Wörter verwenden. Die Schlüsselwortsuche ist dann vorteilhaft, wenn Genauigkeit bei wörtlichen Zeichenfolgen entscheidend ist – beispielsweise bei Identifikatoren, Fehlerrcodes, Feldnamen oder Formulierungen in rechtlichen Texten. Reale Systeme benötigen häufig beides.

def hybrid_rank(vector_results: list[dict], keyword_results: list[dict]) -> list[dict]:
    scores: dict[str, float] = {}
    items: dict[str, dict] = {}

    for rank, item in enumerate(vector_results, start=1):
        doc_id = item["doc_id"]
        scores[doc_id] = scores.get(doc_id, 0.0) + 1.0 / (rank + 10)
        items[doc_id] = item

    for rank, item in enumerate(keyword_results, start=1):
        doc_id = item["doc_id"]
        scores[doc_id] = scores.get(doc_id, 0.0) + 1.0 / (rank + 10)
        items[doc_id] = item

    return sorted(
        items.values(),
        key=lambda item: scores[item["doc_id"]],
        reverse=True,
    )

Diese Art der Kombination mittels gegenseitiger Rangbewertung ist zwar nicht ausgefeilt, doch nützlich. Sie belohnt Dokumente, die in beiden Ergebnislisten auftauchen, gibt den Suchalgorithmen gleichzeitig aber auch Raum, etwas Wichtiges hervorzuheben.

Wann sich agenteles Suchen lohnt

Agenteles RAG eignet sich für Probleme, die eine Untersuchung erfordern. Geeignete Anwendungsfälle sind unter anderem Situationen, in denen die erste Durchsuchung oft nicht ausreichend Beweise liefert, Fragen, die mehrere Dokumente umfassen, Workflows, die zwischen SQL, Vektorabfragen und API-Aufrufen wählen müssen, Modelle, die prüfen müssen, ob sie ausreichend Beweise haben, sowie Abläufe, die von erneuten Versuchen mit einer anderen Abfrage profitieren.

Ungeeignete Anwendungsfälle sind einfache FAQ-Antworten, Eingabevervollständigung mit geringer Latenz, stabile Dokumentensuche sowie alle Fälle, in denen jeder zusätzliche Modellaufruf zu teuer ist. Das Problem muss eine Verwendung agenter Suchmethoden rechtfertigen. Wenn ein Metadatenfilter es lösen kann, sollte dieser verwendet werden.

Checkliste für die Produktion

Bevor Sie das Modell beschuldigen, prüfen Sie zunächst Folgendes:

  1. Sind die Dokumente versioniert?
  2. Werden Berechtigungen vor dem Abrufen überprüft?
  3. Werden veraltete Dokumente standardmäßig ausgeschlossen?
  4. Basiert die Aufteilung auf Inhalt statt nur auf Zeichenzahlen?
  5. Werden exakte Begriffe durch die Schlüsselwortsuche erfasst?
  6. Werden Tabellen und Bilder separat behandelt?
  7. Beinhalten die Bewertungen auch Fälle ohne Antwort?
  8. Werden die IDs der abgerufenen Dokumente protokolliert?
  9. Werden die zitierten Quellen überprüft?
  10. Wird die Qualität der Antwort getrennt von der Qualität des Abrufs gemessen?

Die meisten RAG-Fehler sind nicht rätselhaft – sie werden einfach nicht beobachtet.

Wo die Beweiskette reißt

RAG sorgt dafür, dass LLM-Systeme ein solides Fundament haben, doch diese Grundlage entsteht nicht automatisch. Das Modell kann nur die Beweismittel verwenden, die es erhält. Der Retriever kann nur das finden, was korrekt indiziert wurde. Der Index kann nur die Metadaten bereitstellen, die erhalten geblieben sind. Ein Evaluator kann nur die Fehler erkennen, die jemand extra benannt hat. Diese Arbeit ist weniger spektakulär als eine Demo, doch genau hier entsteht das Vertrauen.

Wenn also ein RAG-System leise versagt, liegt das Problem nicht im Prompt. Die bessere Frage lautet: An welcher Stelle ist die Beweiskette gerissen?