flowchart LR
D["Dokumente"] --> Z["in Stücke zerlegen"] --> V["Stücke einbetten"] --> I[("Vektorindex")]
F["Frage"] --> FE["Frage einbetten"] --> S["ähnlichste Stücke suchen"]
I --> S --> P["Prompt mit Material"] --> M["Sprachmodell"] --> A["Antwort mit Belegen"]
Embeddings und RAG
Kernideen
- Ein Embedding übersetzt einen Text in einen Vektor; ähnliche Bedeutung ergibt ähnliche Richtung
- Ähnlichkeit wird meist als Kosinus gemessen: 1 gleiche Richtung, 0 unabhängig, negativ gegensätzlich
- Damit lässt sich nach Bedeutung suchen statt nach gleichen Wörtern: „Paket verspätet” findet „Lieferung kam zu spät”
- RAG (abrufgestützte Generierung): passende Ausschnitte suchen und dem Sprachmodell als Material mitgeben
- Die Qualität hängt am Abruf: Was nicht gefunden wird, kann das Modell nicht verwenden
- Die Suche ist rechenbar und prüfbar; die formulierte Antwort bleibt ein Modellergebnis
Erklärung
Vorwissen: Distanzmasse für Kosinus und euklidische Distanz, Vom Prompt zum Kontext für den Platz von RAG unter den Stufen der Kontextarbeit. Die Vektoren auf dieser Seite sind von Hand gesetzt und vierdimensional, damit die Rechnung im Build läuft und nachvollziehbar bleibt. Echte Embeddings erzeugt ein Modell, das hier nicht verfügbar ist.
Vom Text zum Vektor
Ein Embedding-Modell liest einen Text und gibt eine feste Anzahl Zahlen aus, typischerweise einige Hundert bis wenige Tausend. Die einzelnen Zahlen haben keine benannte Bedeutung. Wichtig ist nur die Lage der Vektoren zueinander: Texte, die in ähnlichen Zusammenhängen vorkommen, landen nahe beieinander.
| Werkzeug | Wo es läuft | Hinweis |
|---|---|---|
| sentence-transformers | lokal in Python | kostenlos, Daten verlassen den Rechner nicht |
| Embedding-APIs von Cloud-Anbietern | beim Anbieter | Kosten je Token, Datenschutz prüfen |
text2vec, ellmer in R |
lokal oder über APIs |
Kosinus-Ähnlichkeit
\[\cos(a, b) = \frac{a \cdot b}{\lVert a \rVert \, \lVert b \rVert}\]
Der Kosinus misst den Winkel, nicht die Länge. Das passt zu Embeddings, weil ein längerer Text nicht bedeutungsvoller ist. Viele Modelle liefern bereits normierte Vektoren; dann ist der Kosinus einfach das Skalarprodukt.
Der Ablauf von RAG
| Schritt | Entscheidung | Typischer Fehler |
|---|---|---|
| Zerlegen | Stückgrösse, Überlappung | zu grosse Stücke verwässern die Ähnlichkeit, zu kleine verlieren den Zusammenhang |
| Einbetten | Modell | für Frage und Dokumente verschiedene Modelle verwendet |
| Suchen | Anzahl Stücke, Mindestähnlichkeit | immer die besten drei nehmen, auch wenn keines passt |
| Prompt | Anweisung, nur aus dem Material zu antworten | ohne diese Anweisung ergänzt das Modell aus eigenem Wissen |
| Prüfen | Stichproben, Quellenangaben | Antwort nicht gegen die gefundenen Stücke gehalten |
Beispiele
Frage und Datenlage
Sechs Kundenrückmeldungen und die Frage „Wann kommt meine Bestellung?” liegen als vierdimensionale Vektoren vor. Welche Rückmeldungen passen am besten?
Rechnung
aehnlichkeit <- apply(E, 1, function(zeile) kosinus(frage, zeile))
ergebnis <- data.frame(text = texte, aehnlichkeit = round(aehnlichkeit, 4))
ergebnis[order(-ergebnis$aehnlichkeit), ] text aehnlichkeit
1 Lieferung kam zu spät 0.9971
2 Paket verspätet zugestellt 0.9961
5 Rechnung doppelt verschickt 0.2672
6 Preis zu hoch 0.1502
4 Produkt hochwertig verarbeitet -0.0120
3 Qualität der Ware sehr gut -0.1370
aehnlichkeit = np.array([kosinus(frage, zeile) for zeile in E])
ergebnis = pd.DataFrame({"text": texte, "aehnlichkeit": aehnlichkeit.round(4)})
print(ergebnis.sort_values("aehnlichkeit", ascending=False).to_string(index=False)) text aehnlichkeit
Lieferung kam zu spät 0.9971
Paket verspätet zugestellt 0.9961
Rechnung doppelt verschickt 0.2672
Preis zu hoch 0.1502
Produkt hochwertig verarbeitet -0.0120
Qualität der Ware sehr gut -0.1370
Output Zeile für Zeile
| Text | Ähnlichkeit | Wie es zu lesen ist |
|---|---|---|
| Lieferung kam zu spät | 0.9971 | fast gleiche Richtung wie die Frage |
| Paket verspätet zugestellt | 0.9961 | kein gemeinsames Wort mit der Frage, trotzdem gefunden |
| Rechnung doppelt verschickt | 0.2672 | teilt nur die negative Stimmung |
| Preis zu hoch | 0.1502 | |
| Produkt hochwertig verarbeitet | -0.0120 | praktisch unabhängig |
| Qualität der Ware sehr gut | -0.1370 | anderes Thema, gegensätzliche Stimmung |
Interpretation
Die beiden Lieferungstexte liegen weit vorn, alle anderen deutlich dahinter. Diese Lücke ist die eigentliche Information: Ein RAG-System sollte nur die beiden ersten Stücke mitgeben, nicht stur die besten drei. Eine Mindestähnlichkeit, hier etwa 0.5, verhindert, dass ein unpassendes Stück als Material im Prompt landet.
Frage und Datenlage
Wie ähnlich sind sich zwei Texte zum selben Thema ohne gemeinsames Wort, und wie ähnlich zwei Texte zu verschiedenen Themen?
Rechnung
round(c(lieferung_paar = kosinus(E[1, ], E[2, ]),
lieferung_qualitaet = kosinus(E[1, ], E[3, ])), 4) lieferung_paar lieferung_qualitaet
0.9864 -0.1962
print("lieferung_paar:", round(kosinus(E[0], E[1]), 4),
" lieferung_qualitaet:", round(kosinus(E[0], E[2]), 4))lieferung_paar: 0.9864 lieferung_qualitaet: -0.1962
Output Zeile für Zeile
| Paar | Kosinus | Wie es zu lesen ist |
|---|---|---|
| „Lieferung kam zu spät” und „Paket verspätet zugestellt” | 0.9864 | kein gemeinsames Wort, gleiche Bedeutung |
| „Lieferung kam zu spät” und „Qualität der Ware sehr gut” | -0.1962 | beide über eine Bestellung, aber verschiedenes Anliegen |
Interpretation
Eine Stichwortsuche nach „Lieferung” hätte „Paket verspätet zugestellt” nicht gefunden. Das ist der Vorteil von Embeddings. Der Nachteil ist das Spiegelbild: Eine exakte Artikelnummer oder ein seltener Fachbegriff wird von einer reinen Bedeutungssuche manchmal schlechter gefunden als von einer Stichwortsuche. In der Praxis kombiniert man deshalb oft beide.
Ein RAG-Prompt
Die gefundenen Stücke werden mit einer klaren Anweisung übergeben. So sieht der Aufruf aus, siehe Sprachmodelle per API; er läuft im Build nicht:
gefunden = ["Lieferung kam zu spät", "Paket verspätet zugestellt"]
material = "\n".join(f"[{i + 1}] {t}" for i, t in enumerate(gefunden))
antwort = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
system="Beantworte Fragen ausschliesslich auf Basis des Materials. "
"Nenne zu jeder Aussage die Nummer der Quelle. "
"Steht die Antwort nicht im Material, sage das.",
messages=[{"role": "user",
"content": f"Material:\n{material}\n\nFrage: Welche Probleme melden Kunden zur Lieferung?"}],
)Verständnisfragen
Ein RAG-System gibt immer die fünf ähnlichsten Stücke mit. Bei einer Frage, zu der kein Dokument passt, erfindet das Modell trotzdem eine Antwort mit Quellenangabe. Was hilft am meisten?
Warum wird für Embeddings meist der Kosinus statt der euklidischen Distanz verwendet?
Dokumente wurden mit Modell A eingebettet, Fragen mit Modell B. Was ist die Folge?