RAG, wie es wirklich gut funktioniert
Sven 1. Einführung: Was sind Embeddings?
Stell dir vor, du hast eine Datenbank mit Hunderten von Kochrezepten und möchtest einem Nutzer auf die Frage „Was kann ich mit übrig gebliebenem Huhn kochen?" die passendsten Rezepte zeigen. Klassische Volltextsuche scheitert hier schnell: Sie findet nur exakte Wortübereinstimmungen. Was aber, wenn das Rezept „Hähnchensalat mit Avocado" heißt und das Wort „übrig geblieben" gar nicht enthält?
Hier kommen Embeddings ins Spiel. Ein Embedding-Modell (z. B.
OpenAIs text-embedding-3-small oder Coheres
embed-multilingual-v3.0) wandelt einen Text in einen
Zahlenvektor um – typischerweise mit 384 bis 3072 Dimensionen. Texte mit
ähnlicher Bedeutung landen im Vektorraum nah beieinander, unabhängig
davon, ob dieselben Wörter vorkommen.
Die zentrale Frage bei der Vektorsuche ist nun: Wie messen wir, wie „nah" sich zwei Vektoren sind? Die zwei wichtigsten Maße dafür sind die Kosinus-Ähnlichkeit und die Kosinus-Distanz.
2. Kosinus-Ähnlichkeit vs. Kosinus-Distanz
2.1 Kosinus-Ähnlichkeit (Cosine Similarity)
Die Kosinus-Ähnlichkeit misst den Winkel zwischen zwei Vektoren A und B:
similarity(A, B) = (A · B) / (||A|| × ||B||) Der Wert liegt zwischen -1 (genau entgegengesetzt) und +1 (identische Richtung). In der Praxis liegen Embedding-Werte bei modernen Modellen meistens zwischen 0 und 1, weil die Vektoren normalisiert und positiv orientiert sind.
2.2 Kosinus-Distanz (Cosine Distance)
Die Kosinus-Distanz ist einfach das Komplement:
distance(A, B) = 1 - similarity(A, B) Der Wert liegt zwischen 0 (identisch) und 2 (maximal verschieden). Viele Vektordatenbanken wie Pinecone, pgvector oder Qdrant verwenden intern die Kosinus-Distanz, weil sie als Verlustfunktion praktischer ist – kleiner = besser.
Eselsbrücke: Kosinus-Ähnlichkeit = „Wie ähnlich?" (höher = besser). Kosinus-Distanz = „Wie weit entfernt?" (niedriger = besser). Beide enthalten exakt dieselbe Information, nur umgekehrt.
3. Praxisbeispiel: Rezeptsuche
Nehmen wir an, wir haben folgende vier Rezepte in unserer Datenbank und eine Suchanfrage. Wir berechnen für jedes Rezept das Embedding und vergleichen es mit dem Embedding der Suchanfrage.
3.1 Unsere Rezeptdatenbank
| # | Rezeptname | Beschreibung |
|---|---|---|
| R1 | Spaghetti Carbonara | Klassische italienische Pasta mit Ei, Pecorino, Guanciale und schwarzem Pfeffer. Schnell zubereitet in 20 Minuten. |
| R2 | Thai-Kokos-Suppe (Tom Kha Gai) | Cremige Suppe mit Kokosmilch, Galgant, Zitronengras, Hähnchenbrust und Limettensaft. Leicht schärfbar mit Chili. |
| R3 | Griechischer Bauernsalat | Frischer Salat mit Tomaten, Gurke, Oliven, Zwiebeln und Feta. Dressing aus Olivenöl und Oregano. |
| R4 | Käsespätzle | Schwäbisches Traditionsgericht mit hausgemachten Spätzle, Bergkäse und Röstzwiebeln. Deftiges Wohlfühlessen. |
3.2 Suchanfrage und Ergebnisse
Suchanfrage: „Schnelles Pasta-Gericht mit Käse"
Hier die simulierten Scores eines typischen Embedding-Modells:
| Rezept | Kosinus-Ähnlichkeit | Kosinus-Distanz | Rang |
|---|---|---|---|
| Spaghetti Carbonara | 0,89 | 0,11 | #1 |
| Käsespätzle | 0,82 | 0,18 | #2 |
| Thai-Kokos-Suppe | 0,41 | 0,59 | #3 |
| Griech. Bauernsalat | 0,28 | 0,72 | #4 |
Die Spaghetti Carbonara gewinnt: Es ist ein schnelles Pasta-Gericht und enthält Käse (Pecorino). Die Käsespätzle landen auf Platz 2 – Käse passt, aber es ist keine Pasta. Die Sortierung ist bei beiden Maßen identisch, nur die Zahlen sind invertiert.
4. Was steckt im Embedding? Titel vs. Beschreibung vs. Volltext
Die spannendere Frage ist nicht welches Distanzmaß man verwendet, sondern welchen Text man in das Embedding packt. Das hat enormen Einfluss auf die Qualität der Suchergebnisse.
4.1 Experiment: Drei Embedding-Strategien
Wir vergleichen drei Strategien für dasselbe Rezept, die Thai-Kokos-Suppe:
Strategie A – Nur Titel:
embed("Thai-Kokos-Suppe (Tom Kha Gai)") Strategie B – Titel + kurze Beschreibung:
embed("Thai-Kokos-Suppe (Tom Kha Gai). Cremige Suppe mit Kokosmilch, Hähnchenbrust und Limette.") Strategie C – Titel + Beschreibung + volle Zutatenliste + Anleitung:
embed("Thai-Kokos-Suppe (Tom Kha Gai). Cremige Suppe mit Kokosmilch, Galgant,
Zitronengras, Hähnchenbrust und Limettensaft. Zutaten: 400ml Kokosmilch, 300g
Hähnchenbrust, 2 Stängel Zitronengras, 3cm Galgant, 200g Champignons, 2 EL
Fischsauce, 1 Limette, Thai-Chilis nach Geschmack. Anleitung: Kokosmilch
erhitzen, Galgant und Zitronengras ...") 4.2 Ergebnisse bei verschiedenen Suchanfragen
Suchanfrage 1: „Scharfe asiatische Suppe"
| Strategie | Ähnlichkeit | Gefunden? |
|---|---|---|
| A: Nur Titel | 0,74 | Ja |
| B: Titel + Beschreibung | 0,81 | Ja |
| C: Volltext | 0,78 | Ja |
Der Titel allein ist hier schon recht aussagekräftig. Die Beschreibung erhöht den Score leicht. Der Volltext „verwässert" den Vektor etwas, weil Zubereitungsdetails hinzukommen, die wenig mit „scharfe asiatische Suppe" zu tun haben.
Suchanfrage 2: „Rezept mit Kokosmilch und Champignons"
| Strategie | Ähnlichkeit | Gefunden? |
|---|---|---|
| A: Nur Titel | 0,38 | Nein |
| B: Titel + Beschreibung | 0,62 | Vielleicht |
| C: Volltext | 0,87 | Ja |
Hier kehrt sich das Bild um: „Champignons" steht nur in der Zutatenliste. Nur der Volltext-Embedding findet das Rezept zuverlässig. Der reine Titel-Embedding versagt völlig, weil weder „Kokosmilch" noch „Champignons" im Titel vorkommen.
Suchanfrage 3: „Schnelles Abendessen unter 30 Minuten"
| Strategie | Ähnlichkeit | Gefunden? |
|---|---|---|
| A: Nur Titel | 0,22 | Nein |
| B: Titel + Beschreibung | 0,58 | Ja |
| C: Volltext | 0,52 | Grenzwertig |
Die Spaghetti Carbonara hat in der Beschreibung „Schnell zubereitet in 20 Minuten". Der Titel allein enthält diese Info nicht. Strategie B trifft den Sweet Spot, während der Volltext wieder durch zu viele Details verwässert wird.
5. Wie die Länge des Embedding-Inputs die Qualität beeinflusst
Embedding-Modelle haben ein maximales Token-Limit (z. B. 512 oder 8192 Tokens). Aber „mehr Text = besseres Embedding" gilt nicht pauschal. Es gibt einen Punkt, ab dem zusätzlicher Text das Embedding verschlechtert.
5.1 Das Signal-Rausch-Verhältnis
Ein Embedding ist eine Zusammenfassung in Vektorform. Je mehr Text hineingestopft wird, desto mehr muss das Modell komprimieren. Dabei können wichtige Merkmale „untergehen".
Beispiel: Käsespätzle
| Input-Länge | Was im Embedding landet | Effekt auf Suche |
|---|---|---|
| 3 Wörter | „Käsespätzle" – Konzentriertes Signal | Findet Suchanfragen gut, die direkt Käsespätzle meinen |
| 2 Sätze | „Käsespätzle mit Bergkäse und Röstzwiebeln. Schwäbisches Traditionsessen." | Findet auch „deftige schwäbische Gerichte" oder „Mit Bergkäse überbacken" |
| 500 Wörter | Vollrezept inkl. „Wasser zum Kochen bringen", „Salz hinzufügen" ... | Das Embedding „riecht" jetzt auch nach anderen Kochrezepten – weniger spezifisch |
5.2 Die Sweet-Spot-Regel
Für die meisten Anwendungen gilt: Titel + 1–2 Sätze Beschreibung liefern die besten Ergebnisse. Das ist in der Regel der Sweet Spot zwischen zu wenig und zu viel Information.
Faustregel für Rezepte: Embed den Rezeptnamen + eine kurze Beschreibung (Küche, Hauptzutaten, Zubereitungszeit, Besonderheiten). Speichere den Volltext separat als Metadaten, die nach dem Retrieval angezeigt werden.
6. Praxisfall: Warum eine Suche nach „Zander" scheitert
Ein häufiges Missverständnis bei der Vektorsuche wird an einem realen Beispiel deutlich. Nehmen wir an, unsere Datenbank enthält folgendes Rezept:
Gebratener Zander auf Couscous-Salat
Zartes Zanderfilet, knusprig gebraten, auf lauwarmem Couscous-Salat mit Zitrone, frischer Minze und Cherry-Tomaten. Dazu ein leichtes Joghurt-Dressing.
Ein Nutzer sucht nun einfach nach: „Zander"
Die Vektorsuche liefert eine Kosinus-Distanz von 0,82 – das Rezept wird nicht gefunden, obwohl „Zander" wörtlich im Titel steht. Warum?
6.1 Embedding-Modelle erfassen Bedeutung, keine Schlüsselwörter
Das Wort „Zander" allein ist ein isoliertes, kurzes Konzept. Das Embedding-Modell weiß nicht, ob damit der Fisch gemeint ist, ein Nachname, eine Marke oder etwas anderes. Es erzeugt einen Vektor mit sehr wenig semantischem Inhalt.
Das Rezept-Embedding hingegen codiert ein reichhaltiges Bedeutungsfeld: Kochtechnik (gebraten), Beilage (Couscous-Salat), Aromen (Zitrone, Minze), Art des Gerichts (leichtes Fischgericht). Diese beiden Vektoren zeigen in völlig unterschiedliche Richtungen im hochdimensionalen Raum – selbst wenn das Wort „Zander" in beiden vorkommt.
6.2 Kurze Queries produzieren schlechte Vektor-Matches
Einzel-Wort-Suchanfragen sind das Worst-Case-Szenario für Vektorsuche. Sie enthalten fast kein semantisches Signal. Vergleiche:
| Suchanfrage | Distanz | Gefunden? | Warum? |
|---|---|---|---|
| „Zander" | 0,82 | Nein | Zu wenig Signal |
| „Zander Rezept" | 0,54 | Grenzwertig | Etwas mehr Kontext |
| „Leichtes Fischgericht mit Couscous" | 0,15 | Ja | Semantisch präzise |
| „Gebratener Fisch mit frischen Kräutern" | 0,21 | Ja | Gute semantische Übereinstimmung |
Die Tabelle zeigt das Kernproblem: Je mehr semantischer Kontext in der Suchanfrage steckt (was für ein Gericht? welche Art? welche Beilage?), desto besser findet die Vektorsuche das passende Rezept – sogar ohne dass das Wort „Zander" in der Anfrage vorkommt.
6.3 Die Lösung: Textsuche als Fallback
Dieses Verhalten ist kein Bug, sondern eine architektonische Eigenschaft der Vektorsuche. In der Praxis löst man das Problem durch einen hybriden Suchansatz:
- Schritt 1 – Vektorsuche: Suche semantisch nach den besten Matches. Ideal für natürlichsprachliche Anfragen wie „Leichtes Sommergericht mit Fisch".
- Schritt 2 – Textsuche als Fallback: Wenn die Vektorsuche keine oder zu wenige Ergebnisse liefert (z. B. Distanz > 0,6), falle auf klassische Textsuche zurück. Ein einfaches CONTAINS oder LIKE findet „Zander" zuverlässig im Titel.
- Schritt 3 – Ergebnisse zusammenführen: Kombiniere beide Ergebnismengen und dedupliziere. So bekommt der Nutzer sowohl semantisch passende als auch keyword-basierte Treffer.
Merke: Vektorsuche ist hervorragend für die Frage „Was meine ich?" (Semantik), aber schlecht für „Welches Wort steht im Text?" (Keywords). Eine robuste Rezeptsuche braucht beides.
7. Praktische Empfehlungen
7.1 Embedding-Strategie für Rezepte
| Strategie | Wann sinnvoll | Beispiel-Suchanfragen |
|---|---|---|
| Nur Titel | Wenn Nutzer nach konkreten Rezeptnamen suchen | „Spaghetti Carbonara", „Tom Kha Gai" |
| Titel + Beschreibung | Bester Allrounder – deckt thematische Suchen ab | „Schnelles Pasta-Gericht", „Leichter Sommersalat" |
| Titel + Zutaten | Wenn Nutzer primär nach Zutaten suchen | „Rezept mit Aubergine und Feta" |
| Volltext | Nur bei Nischen-Details oder sehr langen Texten mit Chunk-Strategie | „Rezept mit Galgant" (seltene Zutat) |
7.2 Hybride Ansätze: Das Beste aus beiden Welten
In der Praxis kombiniert man oft mehrere Ansätze:
- Mehrere Embeddings pro Rezept: Erstelle separate Vektoren für Titel, Beschreibung und Zutatenliste. Suche in allen dreien und kombiniere die Scores.
- Chunking: Teile lange Rezepte in überlappende Abschnitte auf (z. B. 200 Tokens mit 50 Token Überlappung). Jeder Chunk bekommt ein eigenes Embedding.
- Metadaten-Filter + Vektorsuche: Filtere zuerst nach Kategorien (Küche, Zubereitungszeit, Schwierigkeitsgrad), dann suche per Vektor in der gefilterten Menge.
- Hybrid Search (BM25 + Vektor): Kombiniere klassische Keyword-Suche (BM25) mit Vektorsuche. Viele Datenbanken wie Qdrant oder Weaviate unterstützen dies nativ.
7.3 Combined Search: Die Scoring-Formel
Wenn Vektor- und Textsuche kombiniert werden, braucht man eine Formel, um die Ergebnisse beider Sucharten in einen einheitlichen Score zu übersetzen. Die folgende Formel hat sich in der Praxis bewährt:
finalScore = vectorWeight × vectorRelevance + (1 - vectorWeight) × textRelevance vectorRelevance wird aus der Kosinus-Distanz berechnet:
vectorRelevance = 1 - (cosineDistance / 2) Diese Normalisierung wandelt die Kosinus-Distanz (Wertebereich 0–2, wie z. B. in Cosmos DB) in eine 0–1-Relevanzskala um: Ein Wert von 0 (identisch) wird zu 1,0 und ein Wert von 2 (maximal verschieden) wird zu 0,0.
textRelevance bewertet die Qualität der Keyword-Treffer über alle Felder hinweg mit unterschiedlichen Gewichtungen:
| Feld | Punkte pro Treffer | Begründung |
|---|---|---|
| Titel | +2,0 | Höchste Gewichtung – ein Treffer im Titel ist am aussagekräftigsten |
| Beschreibung | +1,0 | Mittel – beschreibender Kontext ist wertvoll |
| Volltext / Zutaten | +0,5 | Niedrig – viele generische Wörter, aber fängt seltene Zutaten auf |
Die Punkte werden pro Suchbegriff vergeben und anschließend auf einen Wertebereich von 0–1 normalisiert.
vectorWeight steuert die Balance zwischen beiden Suchmethoden und ist konfigurierbar (Standard: 0,7):
| vectorWeight | Effekt | Ideal für |
|---|---|---|
| 0,9 | Fast reine Vektorsuche, Textsuche nur minimaler Einfluss | Nutzer, die in ganzen Sätzen suchen |
| 0,7 (Standard) | Vektorsuche dominiert, aber Keyword-Treffer können Rang ändern | Guter Allrounder für gemischte Suchanfragen |
| 0,5 | Gleichgewicht – beide Methoden gleich stark | Datenbanken mit vielen ähnlichen Rezepten |
| 0,3 | Textsuche dominiert, Vektor nur für Feinranking | Nutzer, die nach einzelnen Zutaten suchen |
7.4 Scoring in Aktion: Das Zander-Beispiel
Betrachten wir die Suchanfrage „Zander" mit dem Rezept „Gebratener Zander auf Couscous-Salat" und einem vectorWeight von 0,7:
| Komponente | Berechnung | Wert |
|---|---|---|
| Kosinus-Distanz | – | 0,82 |
| vectorRelevance | 1 - (0,82 / 2) | 0,59 |
| Text-Treffer: Titel („Zander") | 1 Term × 2,0 Punkte | 2,0 Punkte |
| textRelevance (normalisiert) | 2,0 / max | ≈ 0,90 |
| finalScore | 0,7 × 0,59 + 0,3 × 0,90 | 0,68 |
Ohne die Textsuche hätte das Rezept nur einen Score von 0,59 – vermutlich zu niedrig für die Top-Ergebnisse. Durch den starken Titel-Treffer (+2 Punkte) hebt die Textkomponente den Score auf 0,68, sodass das Rezept trotz des schwachen Vektor-Matches sichtbar wird.
Vergleiche das mit einer semantischen Anfrage:
Suchanfrage: „Leichtes Fischgericht mit Couscous"
| Komponente | Berechnung | Wert |
|---|---|---|
| vectorRelevance | 1 - (0,15 / 2) | 0,925 |
| textRelevance | Kein exakter Wort-Treffer für „Fischgericht" | ≈ 0,30 |
| finalScore | 0,7 × 0,925 + 0,3 × 0,30 | 0,74 |
Hier dominiert die Vektorsuche: Die semantische Nähe ist hoch, obwohl keines der Suchwörter exakt im Titel steht. Beide Suchanfragen führen zum selben Rezept – aber über unterschiedliche Wege.
Wichtig für die Implementierung: Rezepte, die sowohl von der Vektor- als auch der Textsuche gefunden werden, erhalten Beiträge aus beiden Scores. Deduplizierung nach Rezept-ID ist essenziell, damit der beste Score pro Rezept verwendet wird.
8. Zusammenfassung
| Frage | Antwort | Rezept-Beispiel |
|---|---|---|
| Kosinus-Ähnlichkeit oder -Distanz? | Spielt keine Rolle – selbe Info, nur invertiert | Carbonara: Similarity 0,89 = Distance 0,11 |
| Nur Titel embedden? | Gut für Namenssuche, schlecht für Zutatenfragen | „Rezept mit Champignons" findet Tom Kha Gai nicht |
| Volltext embedden? | Kann Signal verwässern, aber findet seltene Details | Findet „Galgant", aber Scores insgesamt diffuser |
| Einzelne Keywords? | Vektorsuche versagt – Textsuche als Fallback nötig | „Zander" hat Distanz 0,82 trotz wörtlichem Treffer |
| Vektor + Text kombinieren? | finalScore aus gewichteter Kombination (Standard: 70/30) | „Zander": Vektor schwach (0,59), aber Titel-Treffer rettet Score auf 0,68 |
| Bester Kompromiss? | Titel + Beschreibung als Embedding-Input | Trifft den Sweet Spot für die meisten Suchanfragen |
Die wichtigste Erkenntnis: Die Wahl zwischen Kosinus-Ähnlichkeit und Kosinus-Distanz ist irrelevant für die Qualität der Suchergebnisse. Was wirklich zählt, ist welchen Text man dem Embedding-Modell gibt – und wie man Vektor- und Textsuche in einem gewichteten Score kombiniert. Ein kluges Embedding-Design zusammen mit einer hybriden Scoring-Formel macht den Unterschied zwischen einer mittelmäßigen und einer hervorragenden Rezeptsuche.