Startseite / Artikel / Cross-Encoder als Richter: die RAG-Pipeline mit Relevanz-Scores absichern

Cross-Encoder als Richter: die RAG-Pipeline mit Relevanz-Scores absichern

Wie Cross-Attention einem Cross-Encoder erlaubt, die Relevanz von Anfrage und Dokument zwischen 0 und 1 zu bewerten, und wie ein Schwellenwert schwache Retrieval-Ergebnisse gar nicht erst zum LLM lässt.

1529 Wörter

Die meisten RAG-Tutorials behandeln einen Cross-Encoder als Re-Ranking-Schritt und sonst nichts. Er kann aber eine interessantere Aufgabe übernehmen. Als Richter entscheidet er, ob der abgerufene Text überhaupt gut genug ist, um ihn an den Generator weiterzugeben.

Eine verbreitete Alternative ist ein LLM als Richter, aber das ist immer noch ein generatives Modell mit all den üblichen Macken. Ein Cross-Encoder als Richter sitzt an einem nützlichen Schnittpunkt von drei Anliegen: wie sich die Architektur entwickelt, was die Pipeline kostet und wie lange Nutzer warten.

Warum ein Cross-Encoder ein guter Richter ist

Einfache RAG-Setups verlassen sich ganz auf einen Bi-Encoder, die Sentence-Transformer-Familie, mit der Embeddings erzeugt werden. Ein Bi-Encoder presst jeden Text in einen eigenen, eigenständigen Vektor. Er misst, wie nah sich zwei Vektoren im Raum sind, und übersieht feinere Logik wie Bedingungen, Verneinungen und strukturelle Umkehrungen.

Es gibt auch eine tiefere Grenze. Bi-Encoder sind auf Recall getrimmt, nicht auf Präzision. Sie finden Dokumente zum selben Thema, können aber nicht bestätigen, dass ein Dokument die tatsächlich gestellte Frage beantwortet.

Üblicherweise bewertet man Retrieval heute, indem man ein LLM wie GPT oder Claude die abgerufenen Chunks lesen und benoten lässt. In Produktion hat dieser Ansatz drei ernste Nachteile:

  1. Latenz. Auf einen schwergewichtigen API-Aufruf zu warten, bevor die Generierung überhaupt beginnt, verdirbt Nutzern, die eine schnelle Antwort erwarten, die Erfahrung.
  2. Kosten. Bei jeder einzelnen Anfrage Tausende Tokens abgerufenen Kontexts an ein Richtermodell zu schicken, lässt die API-Rechnung explodieren.
  3. Instabilität. Prompts driften, und die Modellausgabe ist nicht deterministisch, was jeden fest codierten Schwellenwert aushebelt. Halluzinationen sind ebenfalls möglich, sodass eine am Montag als relevant bewertete Passage am Dienstag als irrelevant oder halb relevant gelten kann.

Ein Cross-Encoder beseitigt alle drei Probleme. Er liest Anfrage und Dokument gemeinsam in einem einzigen Transformer-Durchlauf, sodass jedes Token auf jedes andere achten kann. Heraus kommt ein präziser, reproduzierbarer Score zwischen 0.0 und 1.0.

Was im Inneren passiert: Cross-Attention

Am einfachsten sieht man, warum er besser urteilt, wenn man vergleicht, wie er den Text liest und wie ein Bi-Encoder das tut.

Ein Bi-Encoder kodiert Anfrage (Q) und Dokument (D) getrennt. Heraus kommt ein Vektor für Q und ein anderer für D. Im Transformer achten Anfrage-Tokens nur auf Anfrage-Tokens und Dokument-Tokens nur auf Dokument-Tokens. Die beiden Seiten treffen sich erst ganz am Ende, über ein Skalarprodukt oder eine Kosinus-Ähnlichkeit.

Ein Cross-Encoder arbeitet anders. Er baut eine einzige Eingabesequenz, die beide Texte enthält:

[CLS] User Query [SEP] Retrieved Document Chunk [SEP]

Da beide in derselben Sequenz stehen, wendet das Modell volle Cross-Attention an. Die übliche Attention-Formel gilt weiterhin:

Attention(Q, K, V) = softmax( (Q * Kᵀ) / √dₖ ) * V

Der Unterschied: Jedes Token der Anfrage berechnet ein Attention-Gewicht gegenüber jedem Token des Dokument-Chunks, und zwar alles auf einmal. Wörter der Frage werden direkt mit den Wörtern verknüpft, die sie beantworten oder ihnen widersprechen.

Zum Vergleich das Bi-Encoder-Schema, bei dem sich die beiden Seiten bis zum abschließenden Vergleich nie sehen:

Bi-Encoder Architecture:
  Query Tokens  ──► [ Encoder A ] ──► [ Vector Q ] ──┐
                                                      ├──► Cosine Distance ──► Score
  Doc Tokens    ──► [ Encoder B ] ──► [ Vector D ] ──┘
                   (No token-to-token interaction)

Cross-Encoder Architecture:
  Query Tokens ──┐
                 ├──► [  Full Self-Attention Matrix  ] ──► [ [CLS] Token ] ──► Sigmoid ──► Relevance
  Doc Tokens   ──┘    (Every token attends to every)       (Dense Output)                   Probability

Ein durchgerechnetes Beispiel

Hier ein Fall, in dem ein Bi-Encoder versagt und ein Cross-Encoder richtig liegt.

Angenommen, eine Wissensbasis enthält drei Dokumente:

  • Dokument A: „Schwere Gewitter und sintflutartiger Regen störten den Verkehr in der ganzen Region, und der Flughafen strich alle für den Abend geplanten Flüge.“
  • Dokument B: „Trotz des Wolkenbruchs und des dunklen Himmels lief der Flughafenbetrieb normal weiter, und kein einziger Flug hatte Verspätung.“
  • Dokument C: „Ein Fehler in der Software der Flugsicherung führte zu großen Verspätungen, und der Regen am Nachmittag hatte damit nichts zu tun.“

Ein Nutzer fragt dann: „Hat der Regen Flugverspätungen verursacht?“

Wo der Bi-Encoder scheitert

Ein Standard-Embedding-Modell behandelt die Anfrage als Ganzes und bewertet jedes Dokument für sich. Alle drei Texte enthalten die stark gewichteten Begriffe „Regen“, „Flug“ und „Verspätungen“. Ein Bi-Encoder misst nur die grobe Nähe der Vektoren, also bekommen die Dokumente B und C eine sehr hohe Kosinus-Ähnlichkeit. Sie landen oft direkt neben Dokument A und manchmal sogar darüber. Der Retriever sieht übereinstimmende Wörter, nimmt an, dass das Thema passt, und hält sie für relevant.

Zu welchem Schluss der Cross-Encoder kommt

Gib nun dieselben Kandidaten an den Cross-Encoder-Richter. Die Cross-Attention-Matrix bringt die Anfrage-Tokens dazu, sich am Dokumenttext auszurichten und echte Beziehungen zu bilden. Das Ergebnis sieht ganz anders aus:

[Query] + [Doc A] ---> Score: 0.94 (High Confidence)
Mechanics: The model explicitly links "rain" -> "caused" -> "cancel/delays".

[Query] + [Doc B] ---> Score: 0.21 (Irrelevant Noise)
Mechanics: The cross-attention catches the modifier "Despite the heavy rain... continued smoothly without a single flight delay". It recognizes a structural negation.

[Query] + [Doc C] ---> Score: 0.42 (Topical Context Only)
Mechanics: The model notes that while both events happened, the text explicitly declares them "unrelated".

Behandelt man diese Scores als echte Wahrscheinlichkeiten, kann man die Dokumente B und C sofort aus dem Prompt streichen. Der Generator bekommt dann einen einzigen Chunk mit starkem Signal, Dokument A. Damit ist ausgeschlossen, dass das Modell eine widersprüchliche oder falsche Behauptung darüber erfindet, was das Wetter bewirkt hat.

Wie der Score entsteht

Ein Bi-Encoder vergleicht den räumlichen Abstand zwischen zwei getrennten Vektoren. Ein Cross-Encoder erzeugt nie unabhängige Embeddings. Er kommt zu seinem Score, indem er die ganze Sequenz gemeinsam analysiert:

  1. Token-Verarbeitung. Anfrage und abgerufenes Dokument werden in eine Sequenz gepackt und durch ein spezielles Token getrennt: [CLS] User Query [SEP] Document Chunk [SEP].
  2. Die Attention-Matrix. Während die Sequenz den Transformer durchläuft, ermittelt jedes Token beider Texte seine Beziehung zu jedem anderen Token. Das Ergebnis ist eine dichte Interaktionsmatrix im Modell.
  3. Der Hidden State. Sein abschließendes Verständnis sammelt das Modell im [CLS]-Token am Anfang der Sequenz, das als einzige Zusammenfassung dient, wie Anfrage und Dokument zusammenhängen.
  4. Die Abbildung auf eine Wahrscheinlichkeit. Der finale [CLS]-Hidden-State läuft durch eine lineare Klassifikationsschicht, die die rohen versteckten Dimensionen auf eine Zahl reduziert. Eine Sigmoidfunktion, Score = 1 / (1 + e^-x), wandelt sie dann um.

Die Sigmoidfunktion presst jede rohe Ausgabe in den Bereich von 0.0 bis 1.0. Das Ergebnis liest sich als kalibrierte Relevanzwahrscheinlichkeit, ein Maß dafür, wie sicher sich das Modell ist, dass das Dokument die Antwort auf die Frage wirklich enthält.

Den Score deuten

Da der Wert begrenzt und absolut ist, kannst du ihn in Stufen einteilen und zur Laufzeit auf jede reagieren:

  • 0.80 bis 1.00, ein Signal hoher Zuversicht. Das Dokument klärt den Kern der Frage. Die Antwort steht im Chunk, und die Struktur der Aussagen passt zur Anfrage.
  • 0.50 bis 0.80, nur thematischer Kontext. Das Dokument liegt im richtigen Bereich und nennt die richtigen Begriffe, hat aber keine eindeutige Antwort oder viel Hintergrundrauschen.
  • 0.00 bis 0.50, ein struktureller Widerspruch oder Rauschen. Der Text teilt viele Schlüsselwörter mit der Anfrage, hat aber gegenteilige Logik, Verneinungen oder ein fremdes Thema.

Den Richter zur Sicherung machen

Im gewöhnlichen RAG ist ein Cross-Encoder ein Sortierer. Er ordnet Textblöcke neu, bevor sie weiterwandern. Das eigentlich Neue an der Richterrolle ist, seine Wahrscheinlichkeit in ein hartes Gate zu verwandeln, das wie eine Sicherung auslösen kann.

Statt den Generator lesen zu lassen, was zufällig oben landete, legst du für die Pipeline eine feste Qualitätsuntergrenze fest, zum Beispiel 0.75.

Angenommen, der Retriever liefert zwanzig Dokumente, und der Richter bewertet jedes davon. Der beste Score im ganzen Batch ist 0.42. Die Sicherung löst aus, und die Pipeline bricht den Generierungsaufruf sofort ab.

Ein riesiges, teures Sprachmodell sollte nicht anhand irrelevanten Materials raten müssen, denn genau das löst Halluzinationen aus. Stattdessen schickt das System dem Nutzer eine vorbereitete Ausweichantwort, etwa: „Leider ist keine verifizierte interne Dokumentation verfügbar, um diese Frage verlässlich zu beantworten.“

Kalibrierte Wahrscheinlichkeiten als harte programmatische Grenze machen aus einer unberechenbaren Textgenerierungsschleife ein berechenbares Stück Software. Der Generator läuft nur, wenn die Belege gut sind, und der Richter bleibt günstig, weil er ein kleines Encoder-Modell ist und keine weitere Runde teurer Generierung.