Search-Ranking-Stack: BM25, Embeddings und Reranking

Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.

Search muss sowohl exakte als auch semantische Intentionen erfüllen. Eine Query nach „wireless headphones“ sollte diese Wörter matchen, aber die endgültige Reihenfolge kann auch von Produktqualität, Nutzerpräferenzen und Verfügbarkeit abhängen. Keine einzelne Ranking-Methode verarbeitet all diese Signale zuverlässig.

In diesem Beitrag bauen wir den Stack Schritt für Schritt auf: BM25-Retrieval, dichte Embeddings, Reciprocal Rank Fusion, Cross-Encoder-Reranking und schließlich LLM-Listwise-Ranking. Ein begleitendes Demo-Repository enthält ausführbaren Code für die einzelnen Stufen auf einem gesampelten Ausschnitt der Amazon-ESCI-Produkt-Suchdaten.

Eine kurze Anleitung zur Auswahl der Stufen findest du unter BM25 vs Embeddings vs Rerankers.


Stufen anhand des Failure Modes auswählen

Der Production-Stack ist ein Funnel, aber der passende Funnel hängt von Query und Business-Surface ab.

AnwendungsfallEmpfohlener Ausgangs-StackValidieren
ProduktsucheBM25 + Dense Retrieval + RRF + Cross-EncoderAttribute Recall, Substitutions, Latency, Business Constraints
DokumentationssucheHybrid Retrieval + Cross-EncoderExakte Identifier, semantische Fragen, Versionsfilter
Support-DeflectionHybrid Retrieval + Citation ChecksRetrieval Recall, Grounding, Abstention
Marketplace oder ListingsLexical Filters + Dense Retrieval + Business-RerankerVerfügbarkeit, Aktualität, Richtlinien, Seller-Diversität
Kleines internes CorpusBM25-Baseline, danach ein RerankerOb ein Vocabulary Mismatch einen Dense Index rechtfertigt
Rechtliche oder medizinische Suche mit hohen RisikenRecall-fokussiertes Retrieval plus ExpertenprüfungCoverage, Provenance, kalibrierte Abstention

Starte mit BM25 als Baseline. Ergänze Dense Retrieval, wenn ein Vocabulary Mismatch den Recall beeinträchtigt. Füge einen Cross-Encoder hinzu, wenn die erste Seite die richtigen Candidates enthält, aber in der falschen Reihenfolge anzeigt. Setze ein LLM erst dann ein, wenn du die Latency tragen kannst und die Ranking-Entscheidungen evaluieren kannst.

Wie wir hierher gekommen sind

Der Stack lässt sich am einfachsten als drei Ebenen verstehen. Lexical Retrieval findet exakte Begriffe, Dense Retrieval schließt Vocabulary Gaps, und Reranker vergleichen die stärksten Candidates im Detail.

BM25 und Lexical Retrieval

Über Jahrzehnte war BM25 der Standard. Es handelt sich um ein probabilistisches Model, das Dokumente anhand der Häufigkeit von Query-Terms im Dokument bewertet, normalisiert nach Dokumentlänge und Inverse Document Frequency (IDF).

BM25 ist stark, wenn wörtliche Begriffe die Intention tragen: Error Codes, Product SKUs, Namen und API-Identifier. Seine wichtigste Einschränkung ist der Vocabulary Mismatch. Eine Query nach „cheap laptop“ kann ein Dokument über einen „budget notebook computer“ verfehlen, wenn der indexierte Text keine Verbindung zwischen den Ausdrücken herstellt.

Trotzdem ist BM25 eine solide Baseline. Die BEIR-Leaderboard berichtet für ihren BM25 multifield-Run über einen durchschnittlichen nDCG@10-Wert von 0.429 über 18 Datasets. Pyserinis Reproduktion verwendet einen Lucene-Multifield-Index mit contents=1.0 und title=1.0, durchsucht mit --bm25. Das ist nicht die einfache, per Whitespace tokenisierte rank_bm25-Implementierung dieses Demos. BM25 schlägt außerdem einige Neural Models bei Argumentative-Retrieval-Tasks wie Touche-2020.

Dense Retrieval und Embeddings

BERT-basierte Encoder machten Dense Retrieval praktikabel. Sie projizieren Queries und Dokumente in einen gemeinsamen Vektorraum und ranken anschließend Candidates mit einer Similarity Function wie Cosine Similarity oder Dot Product.

Die Bi-Encoder- (oder „Two-Tower“-) Architektur verarbeitet Query und Dokument unabhängig voneinander durch separate Encoder-Türme und erzeugt Embeddings mit fester Länge. Dokumentvektoren können vorab berechnet, offline indexiert und anschließend per Approximate Nearest Neighbor (ANN)-Algorithmen schnell abgerufen werden. Dadurch liegen „cheap laptop“ und „budget notebook“ im Vektorraum nahe beieinander.

Einige Bi-Encoder verwenden eine Siamese Architecture, wie bei Sentence-BERT, bei der beide Seiten Weights teilen. Andere verwenden separate Query- und Document-Towers. Pooling, Vektorgröße, Similarity Function und Training Objective sind Model-Entscheidungen und keine Eigenschaften jedes Dense Retrievers.

Diese Models werden mit Contrastive Learning trainiert, üblicherweise mit dem InfoNCE-Loss. Für einen Batch aus (query, positive_document)-Paaren maximiert das Objective sim(query, positive_doc) und minimiert sim(query, negative_docs). Negatives stammen von den Positives anderer Queries im selben Batch (In-Batch Negatives). Ein Temperature-Parameter τ\tau steuert, wie deutlich das Model die beiden trennen muss.

Die Trainingsdaten sind oft wichtiger als die Embedding-Dimension. Retrieval Models lernen aus Query-Positive-Paaren und sorgfältig ausgewählten Hard Negatives: plausiblen, aber nicht relevanten Dokumenten. Der spätere Trainingsabschnitt zeigt, wie SimANS sowohl triviale Negatives als auch wahrscheinliche False Negatives vermeidet.

Der Preis ist der Representation Bottleneck. Bi-Encoder komprimieren sämtliche semantischen Nuancen in einen einzelnen Vektor mit fester Größe und übersehen daher häufig feingranulare Interaktionen zwischen bestimmten Query-Terms und bestimmten Dokumentinhalten.

Cross-Encoder und LLMs

Cross-Encoder (Nogueira & Cho, 2019) führen Query und Dokument gemeinsam als konkatenierte Sequence in einen Transformer ein ([CLS] Query [SEP] Document), sodass jeder Query-Token über vollständige Self-Attention jedem Dokument-Token Aufmerksamkeit schenken kann. Diese tiefe Interaction erfasst Nuancen, die eine unabhängige Encoding-Strategie verfehlt.

LLM-Reranking verwendet ein per Prompt gesteuertes Model, um mehrere Candidates gleichzeitig zu vergleichen. RankGPT zeigte starke Zero-Shot-Listwise-Ergebnisse mit GPT-4 auf den evaluierten Benchmarks. Output-Stabilität, Kosten und Domain-Fit müssen jedoch weiterhin separat getestet werden.

Diese Scores können für beliebige Queries nicht vorab berechnet werden, daher wird Reranking hinter dem Retrieval platziert. Diese Kostenasymmetrie motiviert den Multi-Stage-Funnel.


Der Multi-Stage-Funnel

Ein teurer Cross-Encoder oder ein LLM über Millionen von Dokumenten ist nicht praktikabel, daher verwenden moderne Search-Stacks einen Funnel. Jede Stufe verkleinert die Candidate Pool, während die Model-Komplexität zunimmt.

Ein günstiger Retriever bietet außerdem keine ausreichende finale Precision. Deshalb verwendet der Funnel jedes Model nur dort, wo seine Kosten vertretbar sind.

Multi-Stage-Ranking-FunnelMulti-Stage-Ranking-Funnel

StufeInput-SkalaPrimäres ObjectiveTypische MethodenExit-Messung
RetrievalCorpus oder IndexCandidate RecallBM25, Bi-EncoderRecall am Candidate Cutoff
Pre-RankingGroße Candidate SetGünstiges FilteringLeichtgewichtige Models, RulesErhaltener Recall pro Millisekunde
Full RankingShortlistTop-Rank-QualitätCross-Encoder, LLMsNDCG/MRR, Latency, Kosten
BlendingFinale Ranked Lists oder SlotsConstraints und MixRules, Multi-Objective RankingPolicy, Diversität, Business Guardrails

Retrieval setzt die Obergrenze, und Reranking optimiert innerhalb dieser Grenze. Wenn ein relevantes Dokument das Retrieval nicht überlebt, kann kein nachgelagertes Model es wiederherstellen.


Das Demo: eine Pipeline mit fünf Stufen

Um das zu veranschaulichen, habe ich ein search-ranking-stack-Demo erstellt, das eine Pipeline mit fünf Stufen auf dem Amazon-ESCI-Produktsuch-Benchmark ausführt. Jede Stufe wird unabhängig gemessen, sodass du siehst, woher die tatsächlichen Verbesserungen kommen.

Demo-Pipeline-ArchitekturDemo-Pipeline-Architektur

Die Pipeline:

  1. BM25 Sparse Retrieval — lexikalische Baseline (rank_bm25)
  2. Dense-Bi-Encoder-Retrieval — semantische Candidate Generation (all-MiniLM-L6-v2)
  3. Hybrid-RRF-Fusion — rank-basierte Fusion von Sparse- und Dense-Ergebnissen
  4. Cross-Encoder-Reranking — paarweise Relevance Scores (ms-marco-MiniLM-L-12-v2)
  5. LLM-Listwise-Reranking — Prompt-basierter Vergleich der finalen Shortlist (Ollama, API oder lokales Model)

Die Schritte 1–3 bilden die Retrieval-Stufe des Funnels (Recall maximieren); die Schritte 4–5 bilden die Full-Ranking-Stufe (Precision maximieren). Das Demo überspringt Pre-Ranking und Blending. Bei etwa 8.500 Dokumenten kannst du dir leisten, alle Hybrid-Ergebnisse direkt an das Reranking zu senden.

Schnellstart

git clone https://github.com/slavadubrov/search-ranking-stack.git
cd search-ranking-stack
uv sync

# Download and sample ESCI dataset (~2.5GB download, ~5MB sample)
uv run download-data

# Run the full pipeline (without LLM reranking)
uv run run-all

# Run with LLM reranking via Ollama
uv run run-all --llm-mode ollama

Dataset und Sampling: Amazon ESCI

Das Demo verwendet das Amazon Shopping Queries Dataset (ESCI) aus dem KDD Cup 2022 — einen realen Produktsuch-Benchmark mit vierstufigen Relevance Labels:

LabelGainBedeutungBeispiel (Query: „wireless headphones“)
Exact (E)3Erfüllt alle Query-AnforderungenSony WH-1000XM5 Wireless Headphones
Substitute (S)2Funktionale AlternativeWired headphones with Bluetooth adapter
Complement (C)1Verwandter, nützlicher ArtikelHeadphone carrying case
Irrelevant (I)0Keine bedeutsame BeziehungUSB charging cable

Graded Relevance ist wichtig, weil du dadurch NDCG (Normalized Discounted Cumulative Gain) verwenden kannst. Diese Metrik unterscheidet ein „perfektes“ Ranking von einem „gerade noch ausreichenden“. Binäre Metriken bewerten beide gleich.

Ich habe das small_version-Sample des Demos verwendet: etwa 500 Queries, 8.500 Produkte und 12.000 Judgments. Der Downloader liest den einzelnen train-Split von tasksource/esci, filtert die englische us-Locale und small_version == 1 und verwendet anschließend Seed 42, um bis zu 500 eindeutige Query-IDs ohne Zurücklegen zu sampeln. Das Corpus enthält die eindeutigen Produkte aus den ausgewählten Judgment-Zeilen. Dies ist weder ein Held-out-Split noch ein unabhängiges Corpus. Das Sample ist klein genug, um auf einem Laptop ausgeführt zu werden, aber zu klein und zu stark domänenspezifisch, um ein Production Ranking zu begründen. Verwende es, um die Stufen zu reproduzieren und Failure Modes zu untersuchen; verwende Held-out-Queries, die die Produktion repräsentieren, für Deployment-Entscheidungen.


Die Aufgabe der Retrieval-Schicht besteht darin, den Recall zu maximieren: möglichst viele relevante Dokumente in die Candidate Set zu bringen.

BM25: die lexikalische Baseline

BM25 bewertet Dokumente anhand der Term-Überschneidung mit der Query, einschließlich Term-Frequency-Saturation und Normalisierung der Dokumentlänge:

BM25(q,d)=tqIDF(t)tf(t,d)(k1+1)tf(t,d)+k1(1b+bd/avgdl)\text{BM25}(q, d) = \sum_{t \in q} \text{IDF}(t) \cdot \frac{tf(t,d) \cdot (k_1 + 1)}{tf(t,d) + k_1 \cdot (1 - b + b \cdot |d|/\text{avgdl})}

Dabei ist IDF(t)\text{IDF}(t) die Inverse Document Frequency des Terms tt, tf(t,d)tf(t,d) die Term Frequency im Dokument dd, d|d| die Dokumentlänge und avgdl\text{avgdl} die durchschnittliche Dokumentlänge im gesamten Corpus. Zwei Parameter sind entscheidend: k1k_1 (typischerweise 1,2–2,0) steuert die TF-Saturation — also, wie schnell wiederholte Terms keinen zusätzlichen Wert mehr liefern — und bb (typischerweise 0,75) steuert die Normalisierung der Dokumentlänge.

Die Implementierung ist kurz. Einfaches Whitespace-Tokenizing mit rank_bm25:

# src/search_ranking_stack/stages/s01_bm25.py

from rank_bm25 import BM25Okapi

def run_bm25(data: ESCIData, top_k: int = 100):
    doc_ids = list(data.corpus.keys())
    tokenized_corpus = [text.lower().split() for text in data.corpus.values()]

    bm25 = BM25Okapi(tokenized_corpus)

    results = {}
    for query_id, query_text in data.queries.items():
        scores = bm25.get_scores(query_text.lower().split())
        top_indices = np.argsort(scores)[::-1][:top_k]
        results[query_id] = {doc_ids[idx]: float(scores[idx]) for idx in top_indices}

    return results

BM25 erreicht einen Recall@100 von 0,741 — 74 % der relevanten Produkte erscheinen irgendwo in den Top 100. Für eine rein lexikalische Methode ist das nicht schlecht, aber 26 % der relevanten Artikel bleiben für alle nachgelagerten Stufen unsichtbar.

Dense-Bi-Encoder-Retrieval

Der Bi-Encoder projiziert Queries und Dokumente unabhängig voneinander in einen gemeinsamen Embedding-Raum:

# src/search_ranking_stack/stages/s02_dense.py

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")

# Encode corpus once, cache to disk
corpus_embeddings = model.encode(
    doc_texts,
    batch_size=128,
    normalize_embeddings=True,  # Cosine sim = dot product
    convert_to_numpy=True,
)

# At query time: encode query, compute dot product
query_embeddings = model.encode(query_texts, normalize_embeddings=True)
similarity_matrix = np.dot(query_embeddings, corpus_embeddings.T)

Bei normalisierten Embeddings reduziert sich Cosine Similarity auf ein Dot Product. Das Demo berechnet die vollständige Query-by-Corpus-Matrix, weil 8.500 Dokumente problemlos in den Speicher passen; ein Production Corpus würde normalerweise einen Approximate-Nearest-Neighbor-Index verwenden. In diesem Sample erhöht all-MiniLM-L6-v2 den Recall@100 von 0,741 auf 0,825.

Wie Bi-Encoder gute Repräsentationen lernen

Das Training von Bi-Encodern erfolgt üblicherweise in zwei Phasen. Zuerst wird das Model auf Natural Language Inference (NLI) und Semantic Textual Similarity (STS) Datasets vortrainiert. Diese vermitteln ein allgemeines semantisches Verständnis. In dieser Phase lernt das Model, dass „a cat sits on a mat“ und „a feline rests on a rug“ ähnliche Embeddings haben sollten. Anschließend wird es auf Retrieval-spezifischen Daten wie MS MARCO Fine-Tuned. Dabei lernt es, dass eine Search Query und ihr relevantes Passage näher beieinander liegen sollten als die Query und irrelevante Passagen.

Die zweite Phase hängt vom Hard-Negative-Mining ab. Zufällige Negatives — etwa ein Dokument über Kochen, das mit einer Query über Kopfhörer gepaart wird — sind trivial zu unterscheiden, sodass das Model daraus wenig lernt. Stattdessen verwendest du das aktuelle Model selbst, um Dokumente zu finden, die es hoch rankt, die aber tatsächlich nicht relevant sind.

Der SimANS-Ansatz (Simple Ambiguous Negatives Sampling) formalisiert dies. Rank alle Dokumente mit dem aktuellen Bi-Encoder, schließe anschließend einfache Negatives aus, die für das Model zu weit unten ranken, sowie potenzielle False Negatives, die so hoch ranken, dass sie tatsächlich relevant, aber nicht gelabelt sein könnten. Was in der Mitte übrig bleibt, trägt das stärkste Trainingssignal.

# What a training triplet looks like after hard negative mining
training_triplet = {
    "query": "wireless noise canceling headphones",
    "positive": "Sony WH-1000XM5 Wireless Noise Cancelling Headphones",
    "negative": "Sony headphone replacement ear pads",  # Hard negative: same brand, related product, but wrong intent
}
# The bi-encoder must learn that "ear pads" is NOT what the user wants,
# even though it shares many tokens with the positive document.

Die Contrastive-Loss-Funktion (InfoNCE) verbindet diese Aspekte. Für jede Query qq mit positivem Dokument d+d^+ und einer Menge negativer Dokumente {d1,,dn}\{d^-_1, \ldots, d^-_n\} gilt:

L=logesim(q,d+)/τesim(q,d+)/τ+i=1nesim(q,di)/τ\mathcal{L} = -\log \frac{e^{\text{sim}(q, d^+) / \tau}}{e^{\text{sim}(q, d^+) / \tau} + \sum_{i=1}^{n} e^{\text{sim}(q, d^-_i) / \tau}}

Dabei ist sim(q,d)\text{sim}(q, d) die Cosine Similarity zwischen Query- und Dokument-Embeddings und τ\tau der Temperature-Parameter (typischerweise 0,05–0,1). Niedrigere Werte machen den Loss empfindlicher gegenüber Hard Negatives. Im Grunde handelt es sich um eine Softmax Cross-Entropy: Die Similarity des positiven Paars wird relativ zu allen Negatives erhöht. Wenn τ\tau klein ist, erzeugen bereits geringe Similarity-Unterschiede große Gradienten, wodurch das Model zu feingranulareren Unterscheidungen gezwungen wird.

Bi-Encoder-Training-PipelineBi-Encoder-Training-Pipeline

Bi-Encoder-Embeddings im großen Maßstab bereitstellen

Der architektonische Vorteil eines Bi-Encoders ist die Offline/Online-Trennung. Dokument-Embeddings werden beim Indexieren berechnet und in einem Vector Index gespeichert. Zur Query-Zeit encodiert das System die Query und durchsucht die gespeicherten Vektoren. Die Latency hängt von Encoder, Hardware, Index, Filtern und Recall-Ziel ab. Profiliere daher beide Schritte getrennt.

Im Demo ist die Mathematik überschaubar: 8.500 Dokumente ×\times 384 Dimensionen ×\times 4 Byte pro Float ergeben ungefähr 13 MB Embeddings. Im Production-Maßstab werden die Zahlen deutlich größer: 1 Milliarde Dokumente mit 768-dimensionalen Embeddings benötigen ungefähr 3 TiB Speicher. Hier kommen Quantization (Komprimierung von 32-Bit-Floats auf 8-Bit-Integer), Product Quantization (Zerlegung von Vektoren in Subspaces) und SSD-basierte Indizes wie DiskANN zum Einsatz. Der Abschnitt zum Indexieren von Dense Vectors behandelt die Index-Algorithmen.

Bi-Encoder-Serving-PipelineBi-Encoder-Serving-Pipeline

Warum Hybrid Retrieval testen?

Die beiden Methoden machen häufig unterschiedliche Fehler. BM25 eignet sich gut für Eigennamen, Product SKUs und Error Codes. Dense Retrieval kann einen Vocabulary Mismatch wie „cheap laptop“ gegenüber „budget notebook computer“ überwinden. Ob Fusion hilft, hängt davon ab, wie häufig diese komplementären Fälle im Ziel-Query-Set auftreten.

Ein übliches Folgeexperiment ist Hybrid Search: Beide Retrieval-Methoden ausführen und anschließend ihre Ranked Lists fusionieren.

Reciprocal Rank Fusion (RRF)

BM25 und Dense Retrieval erzeugen Scores mit unterschiedlicher Bedeutung und Skalierung. Eine lineare Kombination erfordert daher eine Calibration und Validierung, sobald sich Retriever oder Corpus ändern.

Hybrid Search mit RRFHybrid Search mit RRF

Reciprocal Rank Fusion (Cormack et al., 2009) verwirft die Raw Scores vollständig und verwendet nur die Rank-Position:

RRF(d)=rRankings1k+rank(d,r)\text{RRF}(d) = \sum_{r \in \text{Rankings}} \frac{1}{k + \text{rank}(d, r)}

Hier ist kk eine Smoothing Constant; 60 ist ein üblicher Startwert. RRF belohnt Artikel, die in den Input-Listen weit oben ranken, ohne ihre Raw Scores miteinander zu vergleichen. Dadurch entfällt die Calibration der Score-Skalen, aber Retrieval-Cutoffs, Weights und kk müssen weiterhin evaluiert werden.

Die Implementierung:

# src/search_ranking_stack/stages/s03_hybrid_rrf.py

def reciprocal_rank_fusion(ranked_lists, k=60, top_k=100):
    fused_results = {}

    for query_id in all_query_ids:
        rrf_scores = defaultdict(float)

        for results in ranked_lists:
            sorted_docs = sorted(results[query_id].items(),
                                 key=lambda x: x[1], reverse=True)

            for rank, (doc_id, _score) in enumerate(sorted_docs, start=1):
                rrf_scores[doc_id] += 1.0 / (k + rank)

        sorted_rrf = sorted(rrf_scores.items(),
                            key=lambda x: x[1], reverse=True)[:top_k]
        fused_results[query_id] = dict(sorted_rrf)

    return fused_results

Hybrid RRF erreicht einen Recall@100 von 0,842 und einen NDCG@10 von 0,628 — und übertrifft damit BM25 (0,585) und Dense (0,611) jeweils allein. Ein Dokument muss nur in einer Methode gut ranken, um die Fusion zu überleben.


Cross-Encoder-Reranking

!!! byte „Byte sagt“

Ich habe einen Cross-Encoder hinzugefügt, weil die Top-Ergebnisse falsch aussahen. Die Reihenfolge wurde besser. Der Recall@100 veränderte sich nicht, da ein Reranker nur das sortieren kann, was das Retrieval bereits geliefert hat.

Mit 100 Hybrid-Candidates pro Query kannst du dir ein teureres Model leisten. Der Cross-Encoder verarbeitet Query und Dokument gemeinsam durch einen einzelnen Transformer, mit vollständiger Cross-Attention zwischen allen Tokens.

Bi-Encoder vs. Cross-EncoderBi-Encoder vs. Cross-Encoder

Token-Level-Interaction

Der Unterschied liegt in der Attention Matrix. Bei einem Bi-Encoder ist die Attention blockdiagonal: Query-Tokens attendieren nur auf andere Query-Tokens, und Dokument-Tokens nur auf andere Dokument-Tokens. Die beiden Repräsentationen treffen sich auf Token-Ebene nie — sie überschneiden sich erst am Ende über ein Dot Product. Ein Cross-Encoder berechnet die vollständige Attention Matrix, in der jeder Query-Token auf jeden Dokument-Token und umgekehrt attendiert. Diese Cross-Attention ermöglicht tiefe Token-Level-Interaction.

Cross-Encoder-Attention-ArchitekturCross-Encoder-Attention-Architektur

Beim Bi-Encoder wird die Query „apple“ encodiert, bevor ein Dokument gesehen wurde. Ein Cross-Encoder sieht Query und Candidate gemeinsam und kann daher ihre Token-Level-Beziehung nutzen. Das kann unter anderem in folgenden Fällen helfen:

  • Negation: „headphones that are not wireless.“ Ein gepooltes Embedding kann die Negation zu schwach gewichten, während Joint Encoding eine direkte Query-Document-Interaction ermöglicht. Dies ist eine zu überprüfende Hypothese und keine Garantie.
  • Qualifikation: „laptop under $500.“ Joint Encoding kann die Constraint mit einem Preis im Produkttext verknüpfen, allerdings sind strukturierte Price Filters sicherer, wenn das Feld verfügbar ist.

Der Input des Cross-Encoders wird als [CLS] query tokens [SEP] document tokens [SEP] formatiert. [CLS] ist ein Classification Token, dessen finaler Hidden State durch einen Linear Head geführt wird, um einen einzelnen Relevance Score zu erzeugen. Segment Embeddings unterscheiden Query-Tokens von Dokument-Tokens, und [SEP] markiert die Grenze zwischen den Segmenten.

Wie Cross-Encoder trainiert werden

Cross-Encoder können mit (query, document, relevance_label)-Beispielen und Pointwise-, Pairwise- oder Listwise-Objectives lernen. Das folgende Pointwise-Beispiel verwendet ein einzelnes Relevance Label und ist nicht das einzige mögliche Trainingsdesign.

# Cross-encoder training data format
training_example = {
    "query": "wireless headphones",
    "document": "Sony WH-1000XM5 Wireless Headphones",
    "label": 1.0,  # Relevant
}
# Forward pass: [CLS] hidden state → Linear layer → sigmoid → score
# Loss: binary cross-entropy between predicted score and label

Ein üblicher Classifier bildet die finale [CLS]-Repräsentation auf einen Score ab. Binäre Labels können Binary Cross-Entropy verwenden; Graded Relevance kann mit Regression, ordinalen, paarweisen oder Listwise Losses trainiert werden. Wähle anhand von Ranking-Metriken auf Held-out-Daten, statt anzunehmen, dass ein Objective universell besser ist.

Hard-Negative-Mining ist für Cross-Encoder noch wichtiger als für Bi-Encoder. Cross-Encoder sind teuer zu trainieren — jedes Trainingsbeispiel benötigt einen vollständigen Forward Pass durch die konkatenierte Sequence — deshalb kannst du keine Compute-Kapazität für trivial einfache Negatives verschwenden. Das praktische Vorgehen: Verwende einen Bi-Encoder, um für jede Trainings-Query die Top-K-Candidates abzurufen, und wähle Hard Negatives aus bestimmten Rank-Bereichen aus (z. B. Ranks 10–100). So erhält der Cross-Encoder Beispiele, bei denen die Unterscheidung zwischen relevant und irrelevant tatsächlich tiefe Token-Level-Interaction erfordert.

# src/search_ranking_stack/stages/s04_cross_encoder.py

from sentence_transformers import CrossEncoder

model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-12-v2")

def run_cross_encoder(data, hybrid_results, top_k_rerank=50):
    reranked_results = {}

    for query_id, query_text in data.queries.items():
        candidates = list(hybrid_results[query_id].items())[:top_k_rerank]

        # Form (query, document) pairs for joint encoding
        pairs = []
        doc_ids = []
        for doc_id, _ in candidates:
            doc_text = data.corpus.get(doc_id, "")[:2048]
            pairs.append([query_text, doc_text])
            doc_ids.append(doc_id)

        # Score all pairs with full cross-attention
        scores = model.predict(pairs, batch_size=64)

        # Rerank by cross-encoder score
        scored_docs = sorted(zip(doc_ids, scores),
                             key=lambda x: x[1], reverse=True)
        reranked = {
            doc_id: float(score) for doc_id, score in scored_docs
        }

        # Keep original hybrid candidates at ranks 51--100 for Recall@100.
        for doc_id, score in list(hybrid_results[query_id].items())[top_k_rerank:100]:
            if doc_id not in reranked:
                reranked[doc_id] = float(score) * 0.01

        reranked_results[query_id] = reranked

    return reranked_results

Im aufgezeichneten Demo-Run rerankt ms-marco-MiniLM-L-12-v2 50 Candidates pro Query und erhöht NDCG@10 von 0,628 auf 0,645. Miss die Latency auf der Deployment-Hardware; Model, Sequence Length, Batch Size und Runtime beeinflussen das Ergebnis.

Der Trade-off zwischen Geschwindigkeit und Qualität

Warum Cross-Encoder nicht für alles verwenden? Weil du die Scores nicht vorab berechnen kannst. Dokument-Embeddings eines Bi-Encoders sind Query-unabhängig, daher berechnest du sie einmal und speicherst sie. Der Output eines Cross-Encoders hängt dagegen gleichzeitig von Query und Dokument ab. Der Relevance Score für „wireless headphones“ zusammen mit einem Sony-Produkt entsteht aus der vollständigen Cross-Attention zwischen genau diesen Tokens. Du kannst ihn weder cachen noch für eine andere Query wiederverwenden.

Ein Bi-Encoder benötigt eine Query-Encoding-Operation plus Vector Search über vorab berechnete Dokument-Embeddings. Ein Cross-Encoder bewertet jedes Query-Document-Paar in der Shortlist, wobei die Kosten sowohl mit der Candidate-Anzahl als auch mit der Sequence Length steigen. Batching hilft, aber 100.000 Candidates zu bewerten ist weiterhin der falsche Operating Point; führe zuerst Retrieval aus und benchmarkt die größte Shortlist, die deine Qualitäts- und Latency-Ziele erfüllt.

Im Demo bleibt der Recall@100 während der gesamten Cross-Encoder-Stufe konstant bei 0,842. Reranking kann Ergebnisse neu ordnen, aber keine Dokumente hinzufügen. Retrieval setzt die Obergrenze.


LLM-Listwise-Reranking

!!! byte „Byte sagt“

Ich habe dem ersten LLM-Ranking vertraut, ohne das Parsing zu prüfen. Das Model hatte mehrere Dokument-IDs entfernt, und meine Padding-Logik füllte die Lücken stillschweigend auf. Ich bewertete meine eigene Fallback-Lösung.

Die finale Demo-Stufe verwendet ein LLM für Listwise-Reranking. Statt jedes Dokument unabhängig zu bewerten, sieht das Model die Top 10 und gibt eine Reihenfolge zurück. Inspiriert von RankGPT macht der Prompt den relativen Vergleich explizit, führt aber auch Context Limits, Position Bias, Parsing-Fehler und Run-to-Run-Varianz ein.

LLM-Reranking-AnsätzeLLM-Reranking-Ansätze

Der Listwise-Prompt

Das Prompt-Template fordert das LLM auf, die ESCI-Relevance-Hierarchy zu berücksichtigen:

# src/search_ranking_stack/stages/s05_llm_rerank.py

def _create_listwise_prompt(query, documents, max_words=200):
    n = len(documents)

    doc_texts = []
    for i, (doc_id, doc_text) in enumerate(documents, start=1):
        words = doc_text.split()[:max_words]
        doc_texts.append(f"[{i}] {' '.join(words)}")

    return (
        f"I will provide you with {n} product listings, each indicated by "
        f"a numerical identifier [1] to [{n}]. Rank the products based on "
        f'their relevance to the search query: "{query}"\n\n'
        "Consider:\n"
        "- Exact matches should rank highest\n"
        "- Substitutes should rank above complements\n"
        "- Irrelevant products should rank lowest\n\n"
        f"{chr(10).join(doc_texts)}\n\n"
        "Output ONLY a comma-separated list of identifiers: [3], [1], [2], ...\n"
        "Do not explain your reasoning."
    )

Drei Ausführungsmodi

Das Demo unterstützt drei Backends für LLM-Reranking:

ModusModelAusführung
ollamallama3.2:3b (konfigurierbar)Lokal über Ollama API
apiclaude-haiku-4-5-20251001Anthropic API
localQwen/Qwen2.5-1.5B-InstructHuggingFace Transformers

Parsing und Fallback

LLM-Outputs folgen nicht garantiert dem angeforderten Schema. Deshalb sind Parsing und ein Fallback-Pfad wichtig:

def _parse_ranking(output: str, n: int) -> list[int] | None:
    """Parse LLM output to extract ranking order."""
    matches = re.findall(r"\[(\d+)\]", output)

    if not matches:
        return None

    positions = [int(m) - 1 for m in matches]

    # Pad with remaining positions if LLM returned partial output
    if len(positions) < n:
        seen = set(positions)
        for i in range(n):
            if i not in seen:
                positions.append(i)

    return positions[:n]

Wenn das Parsing vollständig fehlschlägt, fällt das Demo auf die Cross-Encoder-Reihenfolge zurück. Ein Production Parser sollte außerdem Identifier außerhalb des gültigen Bereichs und doppelte Identifier ablehnen, ausgelassene Candidates in ihrer vorherigen Reihenfolge anhängen, den Fehler loggen und die Fallback-Rate mit einem Launch Threshold vergleichen.


Ergebnisse dieses ESCI-Samples

Diese aufgezeichneten Ergebnisse vor dem LLM verwenden das small_version des Demos: die englische us-Locale nach dem small_version == 1-Filter, mit bis zu 500 Query-IDs, die mithilfe von Seed 42 ohne Zurücklegen gesampelt wurden, sowie einem Corpus, das aus den bewerteten Produkten dieser Queries aufgebaut wurde. Sie stellen weder eine Held-out-Evaluation noch ein unabhängiges Corpus dar. Die MRR-Werte verwenden die binäre Regel des Demo-Evaluators, relevance > 0: Ein Complement-, Substitute- oder Exact-Ergebnis gilt als relevant, ein Irrelevant-Ergebnis nicht. recip_rank evaluiert jede zurückgegebene Candidate List mit bis zu 100 Candidates; es handelt sich nicht um MRR@10.

StufeNDCG@10MRRRecall@100NDCG Delta
BM250,5850,8120,741
Dense Bi-Encoder0,6110,8080,825+0,026
Hybrid (RRF)0,6280,8340,842+0,017
+ Cross-Encoder0,6450,8600,842+0,017

Zentrale Beobachtungen

Hybrid Search schlägt beide Einzelmethoden. Der RRF-NDCG-Wert (0,628) liegt über BM25 (0,585) und Dense (0,611). Die beiden Methoden können bei unterschiedlichen Queries scheitern; ihre Kombination gewinnt daher Dokumente zurück, die jeweils einzeln verfehlt würden.

Der Recall wird beim Retrieval festgelegt. Recall@100 bleibt während der Cross-Encoder-Stufe bei 0,842. Reranker ordnen neu, sie fügen keine Dokumente hinzu. Wenn du höheren Recall möchtest, musst du die Retrieval-Schicht verbessern. Die oben aufgeführten MRR-Werte verwenden dieselbe demoweite Regel: Complement oder höher gilt als relevant.

Das LLM-Ergebnis wird nicht berichtet. Der Parser akzeptiert doppelte und außerhalb des gültigen Bereichs liegende Identifier; anschließend kann sein Padding-Pfad die Candidate List stillschweigend verändern. Der vorherige LLM-Vergleich ist daher illustrativ und kein auditierbares Ergebnis. Führe ihn mit strikter Identifier-Validierung, geloggtem Fallback-Verhalten, wiederholten Runs, Latency- und Kostenmessungen sowie einem Held-out-Domain-Set erneut aus, bevor du dem Reranker einen Gain zuschreibst.

Das begleitende Repository ist für Setup und Code nützlich, berichtet in seiner README jedoch weiterhin den ungültigen + LLM Reranker-NDCG@10-Wert von 0,717. Betrachte die Pre-LLM-Tabelle dieses Artikels und den obigen LLM-Hinweis als maßgebliche Dokumentation, bis das Repository über eine auditierbare LLM-Evaluation verfügt.

Dense Retrieval schlägt BM25 in diesem Sample. Untersuche Query-Slices, bevor du die Ursache bestimmst. Vocabulary Mismatch ist ein plausibler Faktor, aber auch Sample-Konstruktion, Tokenizer, Trainingsdomäne des Models und Corpus-Felder beeinflussen den Vergleich.


Evaluation: Was wirklich zählt

Das Demo verwendet drei Metriken, die das Ranking aus unterschiedlichen Perspektiven betrachten:

NDCG@10 (primäre Metrik)

Normalized Discounted Cumulative Gain misst die Qualität des Top-10-Rankings anhand von Graded Relevance. Die Metrik belohnt es, hochrelevante Dokumente weit oben zu platzieren, mit einem logarithmischen Discount:

DCG@k=i=1k2reli1log2(i+1)NDCG@k=DCG@kIDCG@k\text{DCG@k} = \sum_{i=1}^{k} \frac{2^{rel_i} - 1}{\log_2(i + 1)} \qquad \text{NDCG@k} = \frac{\text{DCG@k}}{\text{IDCG@k}}

Von den drei Metriken nutzt nur NDCG die vierstufige Graded Relevance von ESCI vollständig. Ein System, das einen Exact Match auf Position 1 platziert, erzielt einen höheren Score als eines, das dort ein Substitute platziert. Deshalb ist NDCG hier die primäre Metrik.

MRR (erstes Complement-oder-höher-Ergebnis)

Mean Reciprocal Rank verwendet die Position des ersten Ergebnisses, das als relevant gilt. In diesem Demo übergibt der Evaluator ESCIs Graded Qrels an pytrec_eval und verwendet recip_rank über jede zurückgegebene Liste mit bis zu 100 Candidates. relevance > 0 ist daher der binäre Threshold: Complement (1), Substitute (2) und Exact (3) gelten als relevant. Irrelevant (0) nicht. Ein relevantes Ergebnis auf Position 1 ergibt einen Reciprocal Rank von 1,0; auf Position 3 beträgt er 0,333. Dies ist MRR über die zurückgegebenen Candidate Lists, nicht MRR@10.

Recall@100 (Retrieval-Coverage)

Recall misst, welcher Anteil der bewerteten relevanten Dokumente in den Top 100 erscheint. Für die evaluierten Judgments ist Recall eine Candidate-Ceiling-Metrik: Ein Reranker kann kein Dokument hinzufügen, das Retrieval ausgelassen hat. Unvollständige Judgments können die scheinbare Obergrenze jedoch unsicher machen.


Dense Vectors jenseits des Demos indexieren

Dense Embeddings werden im großen Maßstab erst dann nützlich, wenn ein Approximate-Nearest-Neighbor- (ANN-)Index vorhanden ist. Das Demo verwendet Brute-Force-Cosine-Similarity, was bei etwa 8.500 Dokumenten in Ordnung ist; Production-Systeme benötigen spezialisierte Indizes.

HNSW (Hierarchical Navigable Small World)

HNSW baut einen Graphen mit mehreren Ebenen auf: Dünn besetzte obere Ebenen navigieren großflächig, dichtere untere Ebenen verfeinern die Nachbarschaft. M steuert die Graph Connectivity, während efSearch den Query-Aufwand gegen Recall abwägt. Sinnvolle Werte hängen von Dimension, Distanzverteilung, Filtern, Implementierung und Ziel-Recall ab.

Updates und Deletions sind operative Aspekte, da Graph-Indizes möglicherweise im Hintergrund repariert werden müssen. Das Verhalten variiert je nach Datenbank. Ein Qdrant-Issue berichtet beispielsweise von verschlechterter Filtered-Search-Qualität in einer Multi-Tenant-HNSW-Konfiguration. Der Bericht maß eine Mean Precision@100 von 0,597 ± 0,0541 mit einem library_id-Filter, während Exact Search in der tieferen Analyse 100 % Recall erreichte. Eine Änderung von payload_m erzwang einen Rebuild, der die Filtered-Search-Qualität wiederherstellte. Der Bericht behandelt Tenant-Filter, HNSW-Konfiguration und einen erzwungenen HNSW-Index-Rebuild, nicht einen Workload mit vielen Deletions. Reproduziere das für dein Ziel relevante Churn-Muster und berücksichtige Compaction- oder Rebuild-Verhalten in der Evaluation.

IVF (Inverted File)

IVF-Indizes partitionieren den Vektorraum in Cluster und durchsuchen anschließend die nprobe Cluster, die der Query am nächsten liegen. Besonders in Kombination mit Compression können sie einen guten Trade-off zwischen Memory, Build-Zeit und Recall bieten. Update-Semantik und Performance hängen jedoch von der Implementierung ab, nicht allein von der Index-Familie.

Für extreme Skalierung komprimiert IVF_RaBitQ (Gao & Long, SIGMOD 2024) Floating-Point-Vektoren in Single-Bit-Repräsentationen. In hochdimensionalen Räumen trägt das Vorzeichen einer Koordinate (+/−) genügend Winkelinformation für die Similarity-Berechnung.

DimensionHNSW-GraphIVF-Cluster
Query-SteuerungefSearchnprobe
Build-SteuerungConnectivity und Construction BeamCluster-Anzahl und Trainingssample
Memory-ProfilGraph-Kanten plus VektorenCentroids, Listen und gespeicherte Vektoren
Update-VerhaltenDatenbankspezifische Reparatur/CleanupDatenbankspezifische Listenpflege
Evaluieren mitRecall-Latency-Memory-Churn-KurveRecall-Latency-Memory-Churn-Kurve

In einer Uber-Case-Study zur Delivery Search reduzierte die Senkung eines Search-Parameters auf Shard-Ebene von 1.200 auf 200 die berichtete Latency um 34 % und die CPU-Auslastung um 17 %, bei kaum messbarem Recall-Verlust. Die übertragbare Lehre ist, die Recall-Cost-Kurve mit produktionsnahem Traffic zu tunen, nicht den Wert 200 zu kopieren.


Optionale Erweiterungen nach der Core-Pipeline

Sobald Retrieval und Reranking separat gemessen werden, lassen sich mehrere Erweiterungen leichter evaluieren, ohne die Core-Pipeline zu verschleiern.

Query Understanding

Query Expansion und Rewriting können Vocabulary Mismatch vor dem Retrieval adressieren. Query2doc erzeugte Pseudo-Dokumente und berichtete BM25-Gains in seinen MS-MARCO-Experimenten. Expansion kann jedoch auch die falsche Intention einführen. Vergleiche daher Recall und Precision auf Slices mit ambigen, navigationalen und exakten Identifier-Queries.

Praktische Patterns sind Abbreviation Expansion, Entity Enrichment, Sub-Query-Decomposition für Multi-Hop Reasoning und RAG-Fusion — das Erzeugen mehrerer Query-Varianten und die Kombination der Ergebnisse über RRF.

LLM-unterstütztes Relevance Labeling

LLMs können Relevance Labels vorab erstellen, wenn menschliche Judgments knapp sind. TALEC und Pinterests Arbeit zum Relevance Labeling stellen zwei evaluierte Designs vor. Ein LLM-Label ist weiterhin Model-Output: Kalibriere es gegen verblindete menschliche Judgments, untersuche Disagreement-Slices und behalte ein menschliches Gold-Set für Regression Tests.

Nützliche Controls sind:

  • ein Rubric mit konkreten Relevance-Grenzen und Beispielen;
  • verblindete menschliche Calibration und regelmäßige Rechecks;
  • Randomisierung der Reihenfolge und wiederholte Judgments für instabile Fälle;
  • Model Panels, wenn ihre zusätzlichen Kosten die Übereinstimmung verbessern; und
  • explizite Checks auf Position Bias und Central-Tendency Bias.

Knowledge Distillation

Wenn ein LLM Teacher einen Mehrwert liefert, aber Serving Constraints nicht erfüllen kann, ist Distillation eine Option:

  1. Verwende ein leistungsfähiges LLM (den Teacher), um Tausende von Trainings-Queries zu reranken
  2. Trainiere einen kleinen, schnellen Cross-Encoder (den Student, etwa 100–200 Millionen Parameter), der die Ranking Distribution des LLM nachahmt
  3. Vergleiche den Student mit Teacher und Baseline anhand von Qualität, Calibration und Serving-Kosten

InRanker destilliert MonoT5-3B in Models mit 60 Millionen und 220 Millionen Parametern — eine Reduktion der Größe um den Faktor 50 bei konkurrenzfähiger Performance. Der Ansatz Rank-Without-GPT erzeugt offene Listwise-Reranker mit 7B, die durch QLoRA-Fine-Tuning 97 % der GPT-4-Wirksamkeit erreichen.

Veröffentlichte Compression-Ergebnisse sind Ausgangspunkte und keine erwartbaren Production Ratios. Distillation kann die Biases des Teachers übernehmen und bei seltenen Query-Slices Qualität verlieren. Behalte daher die ursprünglichen Relevance Judgments in der Evaluation.


Personalization und Position Bias

Generische Relevance bringt dich nur bis zu einem gewissen Punkt. Eine Search nach „apple“ sollte einem Tech-Enthusiasten iPhones liefern und jemandem, der Cooking Content angesehen hat, Rezepte mit Äpfeln.

Eine übliche Retrieval-Architektur für Personalization verwendet ein Two-Tower-Embedding-Model: Der Query-Tower encodiert Query und User Context, während der Item-Tower Items und Metadaten encodiert. Die Offline/Online-Trennung unterstützt Approximate-Nearest-Neighbor-Retrieval. Die Latency hängt weiterhin von Encoder, Index, Filtern und Serving-System ab.

Airbnbs Listing Embeddings, Pinterests OmniSearchSage und Ubers Two-Tower-Systeme zeigen unterschiedliche Production Designs. Ihre Skalierung und berichteten Uplifts gehören zu diesen Systemen; das übertragbare Pattern ist ein Offline-Item-Tower plus ein Online-Query-/User-Tower.

Click-Daten enthalten Position- und Exposure-Bias. PAL ist ein Debiasing-Ansatz: Nutze die Position während des Trainings und halte sie beim Serving konstant. Das ist keine universelle Lösung; für ein anderes Produkt können randomisierte Interventionen, Inverse-Propensity-Methoden und Counterfactual Evaluation geeigneter sein.


Domain Adaptation mit synthetischen Queries

Ein häufiger Fehler bei Search-Strategien ist die Annahme, dass ein Model, das auf allgemeinen Webdaten wie MS MARCO trainiert wurde, in einer spezialisierten Domäne gut funktioniert. Das ist das Out-of-Domain-(OOD)-Problem.

LLMs können den Bottleneck bei gelabelten Daten durch Generative Pseudo-Labeling (GPL, InPars) reduzieren, aber nicht eliminieren:

  1. Nimm dein domänenspezifisches Dokument-Corpus
  2. Bitte ein LLM per Prompt: „Generate a search query that this document would answer“
  3. Verwende die synthetischen (Query, Document)-Paare für Fine-Tuning von Retriever und Reranker

Synthetische Paare können helfen, wenn echte Queries knapp sind, spiegeln aber Generator und Prompt wider. Entferne Duplikate, filtere unplausible Queries und validiere auf echtem Held-out-Traffic.

Eine Experimentabfolge

Füge Komplexität nur dann hinzu, wenn die vorherige Stufe einen gemessenen Failure Mode offenlegt:

Praktischer ReifegradpfadPraktischer Reifegradpfad

Schritt 1 (Baseline): Implementiere BM25 oder das aktuelle Lexical System und erstelle ein bewertetes Query-Set. Zeichne Recall, NDCG, Latency und Failure Slices auf.

Schritt 2 (Candidate Recall): Teste Dense Retrieval und Fusion nur dann, wenn die Baseline relevante Dokumente verfehlt. Tune den Candidate Cutoff anhand von Recall und Kosten.

Schritt 3 (Ranking Precision): Füge einen Cross-Encoder hinzu, wenn die richtigen Candidates vorhanden sind, aber in der falschen Reihenfolge erscheinen. Wähle die Shortlist-Größe anhand einer Quality-Latency-Kurve.

Schritt 4 (Domain-Fit): Führe Fine-Tuning oder Distillation erst durch, wenn generische Models stabile domänenspezifische Fehler zeigen. Halte echte Held-out-Judgments von synthetischen Trainingsdaten getrennt.

Schritt 5 (optionale teure Schicht): Teste Listwise- oder Reasoning-basiertes Reranking nur dann, wenn seine inkrementelle Qualität in wiederholten Runs bestehen bleibt und Latency, Kosten, Privacy sowie Fallback-Komplexität rechtfertigt.


Research-Richtungen separat evaluieren

Reasoning-Reranker und Search-Using Agents sind vielversprechend, beantworten aber andere Fragen als das fünfstufige Produktsuch-Demo.

Reasoning-basierte Reranker

Rank1 trainiert Reranker mit Reasoning Traces und berichtet starke Ergebnisse auf dem BRIGHT-Benchmark. Diese Evidenz ist für Reasoning-lastiges Retrieval relevant, aber keine direkte Vorhersage für die ESCI-Produktsuche.

Für rechtliche oder wissenschaftliche Search solltest du Reasoning-Reranker mit starken Cross-Encoder- und Listwise-Baselines anhand von Experten-Judgments, Citations, Latency und Failure Consistency vergleichen.

Search-o1 untersucht ein Model, das während Multi-Hop Question Answering zusätzliche Searches ausführt. Das ist ein Orchestration-Problem — Query Generation, Stopping, Evidence Use und Answer Evaluation — und keine weitere Reranking-Stufe. Evaluiere es anhand der Correctness der End-Task und der Citation Support, nicht nur anhand von Retrieval-Metriken.


Zentrale Erkenntnisse

  1. Betrachte den Stack als Abfolge von Experimenten. Etabliere eine lexikalische Baseline und ein bewertetes Query-Set, bevor du Dense Retrieval, Fusion oder Reranking hinzufügst.

  2. Miss Candidate Recall getrennt von Ranking Precision. In diesem ESCI-Sample erreicht Recall@100 nach der Fusion 0,842 und bleibt über beide Reranking-Stufen hinweg konstant.

  3. Verwende Hybrid Retrieval bei komplementären Fehlern. RRF verbesserte im Demo sowohl Recall@100 als auch NDCG@10, aber ein anderes Corpus rechtfertigt möglicherweise keine zwei Indizes.

  4. Füge einen Cross-Encoder hinzu, wenn die Shortlist stimmt, aber die Reihenfolge falsch ist. Wähle die Candidate-Anzahl anhand einer gemessenen Quality-Latency-Kurve.

  5. Behandle LLM-Reranking als optionales finales Experiment. Der aktuelle LLM-Vergleich des Demos ist illustrativ, weil sein Parser keine vollständige Permutation der Candidate-IDs validiert. Striktes Parsing, geloggte Fallbacks, wiederholte Runs und Kostenmessungen gehören in die Evaluation, bevor du einen Gain meldest.

  6. Halte die Evidenztypen getrennt. Ein Paper-Ergebnis, eine Vendor-Case-Study, dieses Laptop-Demo und ein Production-A/B-Test beantworten unterschiedliche Fragen.

Der vollständige Pipeline-Code befindet sich im begleitenden Repository. Klone es, um die Pre-LLM-Stufen zu reproduzieren oder andere Models und Parameter zu testen; behandle die veraltete LLM-Zeile nicht als Ergebnis.

Referenzen

Papers

Datasets und Benchmarks

Im Demo verwendete Models

Tools und Plattformen

Industry-Referenzen

Demo-Projekt