RAG
Retrieval-Augmented Generation: Statt aus dem Gedächtnis zu antworten, schlägt das Modell erst in passenden Dokumenten nach. Die wichtigste Technik, um Sprachmodelle mit eigenem Wissen zu verbinden.
RAG steht für Retrieval-Augmented Generation, also etwa „durch Nachschlagen ergänztes Erzeugen“. Vor der Antwort werden zur Frage passende Textstücke aus einer Dokumentensammlung gesucht und dem Modell ins Kontextfenster gelegt. Das Modell antwortet dann auf Grundlage dieser Texte.
So kann es Fragen zu Firmenhandbüchern, Verträgen, Code oder aktuellen Daten beantworten, die es nie im Training gesehen hat – mit Quellenangabe und deutlich weniger Halluzinationen. Das Modell selbst wird dabei nicht verändert.
Das Problem
Ein Sprachmodell weiß nur, was in seinen Trainingsdaten stand. Drei Arten von Wissen fehlen ihm deshalb grundsätzlich:
Internes
Firmenhandbücher, Kundenakten, eigener Quellcode – nie öffentlich, also nie im Training.
Aktuelles
Alles nach dem Wissensstichtag: neue Preise, neue Versionen, Nachrichten.
Seltenes
Details, die im Training kaum vorkamen und deshalb nur verschwommen gespeichert sind.
Fragt man trotzdem, bekommt man im schlechtesten Fall eine plausible Erfindung. Die Lösung von RAG ist so einfach wie wirkungsvoll – sie macht aus einer Gedächtnisprüfung eine Prüfung mit offenem Buch.
Zwei Phasen
Ein RAG-System hat zwei getrennte Abläufe: einen, der die Dokumente vorbereitet, und einen, der bei jeder Frage läuft.
① Vorbereiten (einmalig, bei Änderungen)
② Fragen (bei jeder Anfrage)
Mini-RAG zum Ausprobieren
Ein erfundenes Firmenhandbuch, in acht Abschnitte zerlegt. Wähl eine Frage und vergleiche die Antwort mit und ohne Nachschlagen:
Worauf es ankommt
Das Prinzip ist einfach, gute Ergebnisse sind es nicht. Die meisten RAG-Probleme sind Suchprobleme: Wird der richtige Abschnitt nicht gefunden, kann auch das beste Modell nicht richtig antworten.
Wie groß sind die Abschnitte?
Zu klein
„… beträgt 30 Tage.“ – Was beträgt 30 Tage? Der Zusammenhang fehlt.
Passend
Ein Absatz oder Abschnitt mit eigener Überschrift – vollständig, aber fokussiert.
Zu groß
Das ganze Kapitel: Das Embedding wird ein unscharfer Mittelwert, und es kostet viel Platz im Kontext.
Weitere Stellschrauben
Hybride Suche
Embedding-Suche findet Bedeutung, versagt aber bei Artikelnummern oder Fachbegriffen. Kombiniert mit klassischer Stichwortsuche wird es deutlich besser.
Nachsortieren
Erst grob 50 Kandidaten holen, dann mit einem genaueren Modell („Reranker“) die besten 5 auswählen.
Berechtigungen
Wer etwas nicht lesen darf, darf es auch nicht über RAG erfahren. Die Rechte müssen schon bei der Suche gefiltert werden.
RAG, Feinabstimmung oder langer Kontext?
Es gibt drei Wege, einem Modell eigenes Wissen mitzugeben. Sie schließen sich nicht aus:
| RAG | Feinabstimmung | Alles in den Kontext | |
|---|---|---|---|
| Prinzip | passende Stücke nachschlagen | Modell weitertrainieren | komplette Dokumente mitschicken |
| Aktualität | sofort, Index neu bauen genügt | erst nach neuem Training | sofort |
| Datenmenge | praktisch unbegrenzt | groß | bis zur Größe des Kontextfensters |
| Quellenangabe | ja | nein | ja |
| Gut für | Wissen, Fakten, Dokumentation | Stil, Format, Verhalten | wenige Dokumente, genaue Analyse |
Faustregel: Wenn ein Modell etwas wissen soll, ist RAG meist der richtige Weg. Wenn es sich anders verhalten soll, eher Feinabstimmung. Und wenn die Dokumente klein genug sind, ist alles in den Kontext zu legen oft am einfachsten.
Für Entwickler übersetzt
| Begriff | Entspricht ungefähr |
|---|---|
| RAG | ein Cache-Lookup vor dem Methodenaufruf, dessen Treffer als Parameter mitgegeben werden |
| Chunk | ein Datensatz in der Suchtabelle – Text plus Metadaten (Quelle, Seite, Berechtigung) |
| Vektordatenbank | ein Index für Ähnlichkeit, z. B. pgvector in PostgreSQL oder ein eigener Dienst |
| Top-k | ORDER BY aehnlichkeit DESC LIMIT k |
| Prompt-Vorlage | ein Template mit Platzhaltern für Frage und Fundstellen |
// RAG im Kern – Bibliotheken wie Spring AI oder LangChain4j kapseln genau das
String antworte(String frage, Benutzer user) {
float[] q = embedder.embed(frage);
List<Chunk> treffer = vektorDb.suche(q, /* topK */ 5)
.stream()
.filter(c -> user.darfLesen(c.quelle())) // Rechte nicht vergessen!
.filter(c -> c.score() > 0.5)
.toList();
String kontext = treffer.stream()
.map(c -> "[%s] %s".formatted(c.quelle(), c.text()))
.collect(Collectors.joining("\n\n"));
String prompt = """
Beantworte die Frage nur anhand der folgenden Auszüge.
Nenne die Quelle in eckigen Klammern. Steht die Antwort
nicht darin, sag das ehrlich.
%s
Frage: %s
""".formatted(kontext, frage);
return llm.complete(prompt);
}