Ich habe BM25 zu meinem RAG-Service hinzugefügt – und die Vektorsuche hat aufgehört, präzise Anfragen zu verpassen

Aktualisiert:
KI zu diesem Artikel befragen
Ich habe BM25 zu meinem RAG-Service hinzugefügt – und die Vektorsuche hat aufgehört, präzise Anfragen zu verpassen

Reiner Vektorsuch verliert exakte Begriffe, Preise und Dokumentennummern. Ich habe das an einem Tag behoben – ohne Änderung der LLM, ohne GPU, ohne neue Abhängigkeiten.

Mein RAG-Service funktionierte. Vektorsuche fand relevante Chunks, LLM generierte Antworten auf Ukrainisch. Aber als ein Kunde fragte "Rechtsberatung 500 UAH" – gab die Vektorsuche Chunks über Rechtsdienstleistungen im Allgemeinen zurück, wobei der genaue Preis ignoriert wurde. Und die Anfrage "Verordnung Nr. 142" fand alles über Verordnungen, außer dem Dokument Nr. 142 selbst.

Das Problem lag weder an der LLM noch am Embedding-Modell. Reine Vektorsuche sucht nach Sinn – aber manchmal braucht man Text. Ich habe BM25 neben der Vektorsuche hinzugefügt, die Ergebnisse über RRF kombiniert – und die Retrieval-Qualität hat sich merklich verbessert. In diesem Artikel – wie genau ich das in der Produktion mit Spring Boot + pgvector gemacht habe, welche Fehler ich gemacht habe und was vor der Implementierung zu beachten ist.

⚡ Kurz gesagt

  • Problem: Vektorsuche "verwässert" exakte Begriffe, Preise, Codes, Dokumentennummern
  • Lösung: Hybrid Search – BM25 (Schlüsselwörter) + Vektor (Semantik) + RRF (Zusammenführung)
  • Stack: Java 21, Spring Boot, PostgreSQL + pgvector, tsvector für BM25
  • Konfiguration: Umschaltung Vektor/Hybrid über Properties, ohne Neukompilierung

📚 Inhaltsverzeichnis des Artikels

🎯 Warum Hybrid Search: Wo Vektorsuche versagt

Ich baue einen kommerziellen RAG-Service – Geschäftskunden laden Unternehmensdokumente (PDF, DOCX, CSV, FAQ) hoch, und ihre Benutzer stellen Fragen in natürlicher Sprache und erhalten Antworten von der LLM, basierend auf dem hochgeladenen Inhalt. Stack: Java 21, Spring Boot + Spring AI, PostgreSQL mit pgvector (IVFFlat-Index), Ollama lokal (nomic-embed-text für Embeddings, mistral-nemo für Chat).

Vor der Hybrid Search funktionierte meine Suche wie folgt: Die Benutzeranfrage wird in einen Vektor umgewandelt (768 Dimensionen über nomic-embed-text), pgvector findet die nächstgelegenen Chunks anhand der Kosinus-Ähnlichkeit. Dies erfasst gut den Sinn – die Anfrage "wie schütze ich Unternehmensdaten" fand Chunks über "Informationssicherheit" und "Schutz personenbezogener Daten", auch wenn die Wörter nicht übereinstimmten.

Aber ich bemerkte drei Arten von Anfragen, bei denen die Vektorsuche stabil daneben lag:

  • Exakte Preise und Zahlen: "500 UAH" – das Embedding-Modell wandelt dies in einen Vektor um, der den allgemeinen "Sinn" des Preises beschreibt, aber der Unterschied zwischen 500 und 550 im Vektorraum ist minimal
  • Codes und Dokumentennummern: "Verordnung Nr. 142" – der Vektor "Verordnung" ist dem Vektor jeder anderen Verordnung ähnlich, die Nummer geht verloren
  • Spezifische Begriffe: "Abschreibung" – die Vektorsuche gab semantisch ähnliche Ergebnisse zurück ("Abnutzung von Anlagevermögen"), aber nicht immer den Chunk mit dem exakten Begriff

Dies ist ein bekanntes Problem der Vektorsuche, das ich detailliert in einem Artikel über Hybrid Search und Reranking beschrieben habe. Die Lösung – Hinzufügen einer Keyword-Suche (BM25), die exakte Wortübereinstimmungen sucht, und Kombinieren der Ergebnisse mit der Vektorsuche.

📌 Was ist BM25 und warum funktioniert der Algorithmus von 1994 immer noch

BM25 (Best Matching 25) ist ein Ranking-Algorithmus für die Textsuche, der von Robertson und Walker im Jahr 1994 formalisiert wurde. Seit 30 Jahren ist er nicht tot – und hier ist der Grund.

BM25 bewertet die Relevanz eines Dokuments anhand von drei Faktoren:

  • TF (Term Frequency) – wie oft ein Wort in einem bestimmten Chunk vorkommt. Je öfter – desto relevanter
  • IDF (Inverse Document Frequency) – wie selten ein Wort in der gesamten Sammlung ist. Das Wort "Dokument" kommt überall vor – es ist weniger wertvoll. Das Wort "Abschreibung" ist selten – es ist wichtiger
  • Dokumentenlänge – eine Normalisierung, damit kurze und lange Chunks gleich behandelt werden

Für meinen Anwendungsfall ist BM25 entscheidend, da Geschäftsdokumente exakte Begriffe, Preise, Nummern enthalten – Dinge, die die Vektorsuche "verwässert". BM25 findet einen Chunk mit "500 UAH" in Millisekunden, ohne neuronale Netze, ohne GPU.

Einschränkung von BM25: Es versteht keine Synonyme. "Auto" ≠ "Wagen". Wenn der Benutzer "Abonnement kündigen" schreibt und im Dokument "Tarif ablehnen" steht, findet BM25 nichts. Genau deshalb ist eine Kombination nötig – Vektorsuche erfasst den Sinn, BM25 erfasst die exakten Wörter.

Tabelle: Wann was funktioniert

AnfragetypVektorsucheBM25Hybrid
"Rechtsberatung 500 UAH"⚠️ findet Juristisches, ignoriert Preis✅ exakte Übereinstimmung✅✅
"wie schütze ich Unternehmensdaten"✅ Semantik❌ keine exakten Übereinstimmungen
"Verordnung Nr. 142 zur Entlassung"⚠️ findet Verordnungen allgemein✅ "Verordnung Nr. 142"✅✅
"Rückerstattung"✅ Semantik✅ exakte Übereinstimmung✅✅ beide Signale

Ein detaillierter Vergleich von BM25 vs. Dense Vector Search mit Benchmarks – in meinem Artikel über Hybrid Search, Abschnitt 1.

📌 Datenbankvorbereitung: Migration, tsvector, GIN-Index

Bevor ich Java-Code schrieb, habe ich PostgreSQL vorbereitet. In meiner Tabelle vector_store gab es bereits Vektoren (Embeddings) für die Kosinus-Ähnlichkeitssuche. Für BM25 ist eine zusätzliche Struktur erforderlich – tsvector. Dies ist ein integrierter PostgreSQL-Typ, bei dem der Text in Tokens (Lexeme) mit Positionen zerlegt wird. Ohne ihn funktioniert die Volltextsuche über den Operator @@ nicht.

Analogie: Ein Vektor (Embedding) ist das "Verständnis des Sinns" eines Textes. Und tsvector – ist ein alphabetischer Index in einem Buch. Für verschiedene Suchtypen sind unterschiedliche Datenstrukturen erforderlich.

Migration – zwei Befehle

-- 1. Hinzufügen einer Spalte für die Volltextsuche
ALTER TABLE vector_store ADD COLUMN content_tsv tsvector;

-- 2. Erstellen eines GIN-Indexes für schnelle Stichwortsuche
CREATE INDEX idx_vector_store_content_tsv ON vector_store USING GIN (content_tsv);

Der erste Befehl fügt die Spalte content_tsv hinzu. Nach der Migration wird sie für alle vorhandenen Chunks NULL sein – das ist in Ordnung, wir füllen sie später.

Der zweite Befehl erstellt einen GIN (Generalized Inverted Index) – einen Index-Typ, der für die Volltextsuche optimiert ist. Ohne ihn würden BM25-Abfragen mit dem Operator @@ alle Zeilen scannen. Mit GIN – ist die Suche schnell. Es ist wie ein IVFFlat-Index für Vektoren, nur GIN – für Text.

Füllen von tsvector für vorhandene Chunks

UPDATE vector_store
SET content_tsv = to_tsvector('simple', content)
WHERE content_tsv IS NULL;

⚠️ Fallstrick: Auswahl der Textsuchkonfiguration

Wenn PostgreSQL Text in tsvector konvertiert, muss es die Sprache kennen – um Stoppwörter ("und", "oder", "in") zu entfernen und Wörter auf ihre Grundform zu reduzieren (Stemming: "Arbeiter" → "Arbeit").

Für die ukrainische Sprache hat PostgreSQL kein integriertes Wörterbuch. Ich hatte zwei Optionen:

  • simple – zerlegt den Text einfach in Wörter, konvertiert in Kleinbuchstaben. Ohne Stemming, ohne Stoppwörter. Zuverlässig – es wird keine Situation geben, in der PostgreSQL ein ukrainisches Wort falsch stemmt
  • russian – die nächstgelegene integrierte Option. Stemming funktioniert teilweise für Ukrainisch (Sprachen sind ähnlich), kann aber einige Wörter falsch stemmen

Ich habe simple gewählt – weniger "intelligent", aber zuverlässig. BM25 mit der simple-Konfiguration findet immer noch exakte Übereinstimmungen von Schlüsselwörtern, und für das "Verständnis des Sinns" habe ich Vektorsuche.

📌 Implementierung von HybridSearchService: Zwei Suchen + RRF

Mein HybridSearchService führt im search()-Methode drei Dinge aus:

  1. Vektorsuche — derselbe vectorStore.similaritySearch() über pgvector, Kosinus-Ähnlichkeit
  2. BM25-Suche — SQL-Abfrage mit dem Operator @@ auf der Spalte content_tsv
  3. RRF-Zusammenführung — Zusammenführung beider Ergebnislisten nach der Formel 1/(k + rank)

BM25-Suche: SQL-Abfrage

Für BM25 verwende ich plainto_tsquery() — es zerlegt die Benutzeranfrage automatisch in Wörter und sucht sie mit AND. Die Ergebnisse werden mit ts_rank() bewertet — eine integrierte Funktion von PostgreSQL, die einen BM25-ähnlichen Score berechnet.

SELECT id, content, metadata FROM vector_store
WHERE content_tsv @@ plainto_tsquery(CAST(:tsconfig AS regconfig), :question)
ORDER BY ts_rank(content_tsv, plainto_tsquery(CAST(:tsconfig AS regconfig), :question)) DESC
LIMIT :topK

⚠️ Stolperstein: CAST zu regconfig

Mein erster Versuch ohne CAST ergab einen BadSqlGrammarException. PostgreSQL kann den String-Parameter ? (der als String über JdbcClient kommt) nicht automatisch in den Typ regconfig umwandeln. Ein expliziter Cast ist erforderlich: CAST(:tsconfig AS regconfig). Der gleiche Fehler trat auch beim Füllen von content_tsv während der Indizierung auf — musste an zwei Stellen behoben werden.

RRF (Reciprocal Rank Fusion): Wie die Zusammenführung funktioniert

RRF wurde von Cormack, Clarke und Buettcher im Jahr 2009 (SIGIR '09) vorgeschlagen und ist seitdem zum Standard für die hybride Suche geworden. Die Formel:

score(d) = Σ 1 / (k + rank)

wobei rank die Position des Dokuments in jedem einzelnen Ranking ist und k eine Glättungskonstante ist (ich verwende den Standardwert 60).

Was k tut: Es steuert, wie stark sich der "erste Platz" vom "fünften Platz" unterscheidet.

  • k=60 (Standard): 1. Platz = 1/61 = 0.0164, 5. Platz = 1/65 = 0.0154. Der Unterschied ist gering — alle Ergebnisse sind "fast gleich"
  • k=1 (klein): 1. Platz = 1/2 = 0.5, 5. Platz = 1/6 = 0.167. Der Unterschied ist dreifach — die Top-Ergebnisse dominieren
  • k=200 (groß): Es gibt fast keinen Unterschied — wichtig ist nur, ob der Chunk in den Ergebnissen enthalten ist

Warum genau 60? Dieser Wert stammt aus der ursprünglichen wissenschaftlichen Arbeit. Er wird von Elasticsearch, Qdrant, Weaviate verwendet.

Beispiel mit realen Daten

Anfrage: "Anordnung Nr. 142 zur Entlassung"

Vektorsuche gibt zurück (nach Kosinus-Ähnlichkeit — nach der Bedeutung von "Entlassung"):

  1. Chunk über das Entlassungsverfahren (Rang 1)
  2. Chunk über den Arbeitsvertrag (Rang 2)
  3. Chunk "Anordnung Nr. 142" (Rang 5)

BM25 gibt zurück (nach exakter Übereinstimmung der Wörter "Anordnung Nr. 142"):

  1. Chunk "Anordnung Nr. 142" (Rang 1)
  2. Chunk "Anordnung Nr. 155" (Rang 2)

RRF-Score für den Chunk "Anordnung Nr. 142":

Vektor Rang=5: 1/(60+5) = 0.0154
BM25 Rang=1:   1/(60+1) = 0.0164
Gesamt:                     0.0318 ← höchster von allen

Der Chunk "Anordnung Nr. 142" gewinnt — er ist in beiden Rankings hoch. Ohne Hybrid-Suche wäre er auf Position 5 und hätte möglicherweise nicht in den Kontext der LLM gelangen können.

Füllen von tsvector bei der Indizierung neuer Dokumente

Neue Dokumente durchlaufen den PgVectorIndexingService. Nach vectorStore.add(documents) (das Embeddings speichert) habe ich ein UPDATE hinzugefügt, das content_tsv füllt:

private void updateTsVector(Long docId) {
    jdbcClient.sql(
        "UPDATE vector_store SET content_tsv = to_tsvector(CAST(:tsconfig AS regconfig), content) " +
        "WHERE metadata->>'doc_id' = :docId AND content_tsv IS NULL"
    )
    .param("tsconfig", tsConfig)  // @Value("${app.search.tsconfig:simple}")
    .param("docId", String.valueOf(docId))
    .update();
}

Warum ohne Trigger: Ich habe die Option mit einem PostgreSQL-Trigger in Betracht gezogen, aber ein Trigger ist SQL, er kennt Spring @Value nicht. Wenn der Kunde die Sprache ändert (z. B. von simple zu german für einen deutschen Kunden) — müsste der Trigger über eine Migration neu erstellt werden. Mit Java-Code wird alles über application.properties gesteuert.

📌 Konfiguration: Vektor vs. Hybrid über Properties

Ich habe den alten PgVectorSearchService nicht gelöscht. Stattdessen habe ich eine Umschaltung über @ConditionalOnProperty vorgenommen:

# application.properties
app.search.mode=hybrid       # oder "vector" für reine Vektorsuche
app.search.tsconfig=simple   # Sprache für tsvector: simple, russian, german, english...
app.search.rrf-k=60          # Glättungskonstante RRF
@ConditionalOnProperty(name = "app.search.mode", havingValue = "vector", matchIfMissing = true)
public class PgVectorSearchService implements SearchService { ... }

@ConditionalOnProperty(name = "app.search.mode", havingValue = "hybrid")
public class HybridSearchService implements SearchService { ... }

Warum zwei Modi

Mein Dienst bedient verschiedene Geschäftskunden, und für nicht alle ist die Hybrid-Suche optimal:

  • Hybrid-Suche belastet die Datenbank stärker — zwei Abfragen statt einer (Vektor + BM25), plus der zusätzliche GIN-Index verbraucht RAM. Für einige Kunden mit einer kleinen Dokumentenbasis und einfachen Anfragen ist dies überflüssig
  • Fallback — wenn der BM25-Teil ausfällt oder tsvector für einige Chunks nicht gefüllt ist, kann man über eine einzige Eigenschaft sofort zur reinen Vektorsuche zurückkehren
  • A/B-Tests — man kann die Qualität der Antworten zwischen den Modi für dieselben Anfragen vergleichen

Standardmäßig ist matchIfMissing = true für PgVectorSearchService gesetzt — wenn die Eigenschaft nicht angegeben ist, funktioniert es wie zuvor. Nichts geht kaputt.

Konfiguration pro Kunde

Für ukrainische Kunden:

app.search.mode=hybrid
app.search.tsconfig=simple

Für deutsche Kunden:

app.search.mode=hybrid
app.search.tsconfig=german

Für Kunden mit einer kleinen Basis, wo Hybrid überflüssig ist:

app.search.mode=vector

Die Sprache für tsvector und tsquery muss übereinstimmen — sonst funktioniert die Suche nicht korrekt. PostgreSQL unterstützt standardmäßig: simple, english, german, french, spanish, russian, italian, dutch, turkish und andere.

⚠️ Stolpersteine, auf die ich gestoßen bin

1. BadSqlGrammarException bei plainto_tsquery

Problem: Der erste Start der BM25-Suche ergab eine bad SQL grammar. PostgreSQL konnte den String-Parameter ? nicht in den Typ regconfig umwandeln.

Lösung: Expliziter Cast CAST(:tsconfig AS regconfig) an zwei Stellen — im WHERE- und im ORDER BY-Teil der SQL-Abfrage.

2. Gleicher Fehler bei der Indizierung neuer Dokumente

Problem: Ich habe HybridSearchService behoben, aber beim Laden eines neuen Dokuments — die gleiche BadSqlGrammarException in PgVectorIndexingService.updateTsVector().

Lösung: Auch dort CAST hinzufügen. Lektion — wenn to_tsvector() mit einem Parameter über JdbcClient verwendet wird, ist CAST *immer* erforderlich.

3. @RequiredArgsConstructor funktioniert nicht mit @Value

Problem: Lombok @RequiredArgsConstructor generiert einen Konstruktor nur für final Felder. Ein Feld mit @Value ist nicht final und gelangt daher nicht in den Konstruktor.

Lösung: Ersetzte @RequiredArgsConstructor durch einen expliziten Konstruktor in Klassen, in denen sowohl final Abhängigkeiten als auch @Value-Konfiguration vorhanden sind.

4. content_tsv = NULL für bestehende Dokumente

Problem: Nach der Migration war die neue Spalte content_tsv für alle bestehenden Chunks NULL. BM25 gab 0 Ergebnisse zurück.

Lösung: Einmaliger UPDATE: UPDATE vector_store SET content_tsv = to_tsvector('simple', content) WHERE content_tsv IS NULL;

5. Ähnlichkeitsschwelle für ukrainischen Text

Die Standard-Ähnlichkeitsschwelle für die Vektorsuche ist für ukrainischen Text mit nomic-embed-text zu hoch. Ich habe sie auf 0.1 gesenkt — sonst gab die Vektorsuche wenige Ergebnisse zurück. Mehr über die Auswahl des Embedding-Modells und der Schwelle — im Artikel über Embedding-Modelle.

📊 Ergebnisse: vector=5, bm25=2, merged=5

Logs von meinem Service nach der Implementierung von Hybrid Search:

Stream query: 'Скільки часу займає повернення коштів?', sessionId=3
HybridSearchService: Hybrid search results: 5 chunks (vector=5, bm25=1, merged=5)

Stream query: 'Скільки часу займає розробка лендінгу?', sessionId=null
HybridSearchService: Hybrid search results: 5 chunks (vector=5, bm25=2, merged=5)

Stream query: 'напиши про Локальний деплой Як це працює?', sessionId=3
HybridSearchService: Hybrid search results: 5 chunks (vector=5, bm25=0, merged=5)

Was wir sehen:

  • "Rückerstattung von Geldern" — BM25 fand 1 Chunk mit exakter Wortübereinstimmung, Vector fand 5 nach Bedeutung. Der Chunk, der in beiden Rankings auftauchte, erhielt den höchsten RRF-Score und landete auf dem ersten Platz
  • "Landingpage-Entwicklung" — BM25 fand 2 Chunks. Hybrid lieferte 5 Ergebnisse — Chunks aus beiden Rankings, sortiert nach RRF-Score
  • "Lokales Deployment" — BM25 fand 0. Das ist normal: Die Anfrage "Schreib über lokales Deployment, wie es funktioniert" ist semantisch, ohne exakte Begriffe aus den Dokumenten. Die Vektorsuche hat es selbst geschafft

Die wichtigste Schlussfolgerung: Hybrid Search verschlechtert die Ergebnisse nicht, wenn BM25 nichts findet — es gibt einfach die Vector-Ergebnisse zurück. Aber wenn BM25 *etwas findet* — steigt die Qualität des endgültigen Rankings, weil RRF die Chunks verstärkt, die in beiden Suchen vorkommen.

Über das Chunking von Dokumenten — wie ich PDF-, DOCX- und CSV-Dateien in Chunks für die Indizierung aufteile, einschließlich semantischem FAQ-Chunking — lesen Sie im Artikel über Chunking Strategies. Und über die Auswahl von Ollama-Modellen, die lokal auf 8 GB RAM laufen — im Artikel über Ollama auf 8 GB.

❓ Häufig gestellte Fragen (FAQ)

Wird Hybrid Search benötigt, wenn die Dokumentenbasis klein ist (< 100 Dokumente)?

Nicht unbedingt. Bei einer kleinen Basis ist die Vektorsuche normalerweise ausreichend — es gibt weniger "Rauschen" und relevante Chunks gelangen in die Top-Ergebnisse. Hybrid ist gerechtfertigt, wenn Dokumente exakte Begriffe, Codes oder Preise enthalten, die die Vektorsuche "verwässert". Deshalb habe ich die Umschaltung über app.search.mode vorgenommen — für kleine Kunden lasse ich vector.

Warum tsvector und nicht Elasticsearch für BM25?

Ich habe bereits PostgreSQL — es speichert sowohl Dokumente als auch Vektoren (pgvector). Das Hinzufügen von Elasticsearch als separatem Dienst bedeutet DevOps-Overhead, Überwachung, Datensynchronisation. Der eingebaute tsvector mit GIN-Index löst die Aufgabe der BM25-Suche ohne zusätzliche Infrastruktur. Für einen Umfang von 10.000+ Dokumenten mit hohem QPS lohnt es sich, Elasticsearch oder Qdrant mit nativem Hybrid in Betracht zu ziehen.

Wie wirkt sich Hybrid Search auf die Latenz aus?

Minimal. BM25 und Vektorsuche werden sequenziell (noch nicht parallel) ausgeführt, aber BM25 über den GIN-Index dauert Millisekunden. Die RRF-Zusammenführung dauert Mikrosekunden (Berechnung der Ränge). Die Hauptzeit wird für das Embedding der Anfrage über nomic-embed-text und die Vektorsuche über pgvector benötigt. Hybrid fügt dem gesamten Suchvorgang etwa 5–15 ms hinzu.

Kann man die Konfiguration "russian" anstelle von "simple" für Ukrainisch verwenden?

Man kann — Stemming funktioniert teilweise (die Sprachen sind ähnlich). Aber es besteht die Gefahr eines falschen Stemmings für einige ukrainische Wörter. simple ist zuverlässiger: es tokenisiert einfach ohne Stemming. Für das "Verständnis" von Wörtern habe ich die Vektorsuche — BM25 wird nur für exakte Übereinstimmungen benötigt.

Was tun, wenn BM25 immer 0 Ergebnisse liefert?

Überprüfen Sie drei Dinge: (1) Ist die Spalte content_tsv gefüllt — führen Sie SELECT count(*) FROM vector_store WHERE content_tsv IS NOT NULL aus; (2) Stimmt tsconfig in to_tsvector() und plainto_tsquery() überein; (3) Gibt es exakte Übereinstimmungen der Suchbegriffe im Text der Chunks. Wenn die Anfragen überwiegend semantisch sind — liefert BM25 0, und das ist ein normales Verhalten.

✅ Schlussfolgerungen

  • 🔹 Vektorsuche allein reicht nicht aus: sie "verwässert" exakte Begriffe, Preise, Dokumentennummern. BM25 schließt diese Lücken
  • 🔹 Hybrid Search (BM25 + Vektor + RRF): zwei parallele Suchen, Zusammenführung über die Formel 1/(k + rank). Ein Chunk, der in beiden Rankings hoch platziert ist, gewinnt
  • 🔹 pgvector + tsvector: kein Elasticsearch erforderlich — PostgreSQL mit GIN-Index ist ausreichend für BM25 neben der Vektorsuche
  • 🔹 Konfiguration pro Kunde: app.search.mode=hybrid/vector, app.search.tsconfig=simple/german/english — alles über Eigenschaften, ohne Neukompilierung
  • 🔹 Fallstricke: CAST(:tsconfig AS regconfig) ist für JdbcClient obligatorisch, content_tsv muss für bestehende Chunks gefüllt werden, Ähnlichkeitsschwelle für ukrainischen Text — 0.1
  • 🔹 Hybrid verschlechtert nicht: wenn BM25 nichts findet — Ergebnisse = Vektorsuche. Wenn es etwas findet — die Qualität steigt

Mein Hauptgedanke: Hybrid Search ist der einfachste und effektivste Schritt zur Verbesserung der Qualität eines RAG-Systems nach der grundlegenden Vektorsuche. Wenn Ihre Dokumente Begriffe, Preise, Codes enthalten — ist der Effekt sofort spürbar. Und wenn nicht — funktioniert Hybrid einfach wie eine Vektorsuche, ohne etwas kaputt zu machen.

📖 Quellen

📚 Verwandte Artikel