Zum Inhalt springen
LLMlokal

15 min Lesezeit

RAG lokal - Retrieval-Augmented Generation auf eigener Hardware

Eigene Dokumente lokal mit LLM verbinden: Embeddings, Vektor-Stores, Chunking-Strategien. Praxis-Setup mit Ollama, nomic-embed-text und Qdrant lokal.

Ein lokales LLM ist mächtig, kennt aber nur Inhalte aus seinem Trainingsdatensatz. Sobald du es mit deinen eigenen Handbüchern, Mailverläufen, internen Wikis oder Branchenstudien arbeiten lassen willst, brauchst du Retrieval-Augmented Generation. RAG verbindet Modellwissen mit deiner Dokumentenbasis, ohne das Modell neu zu trainieren. Lokal läuft das Setup komplett offline, ohne API-Kosten und ohne dass deine Inhalte das Haus verlassen. In dieser Lektion baust du eine vollständige RAG-Pipeline mit Ollama, nomic-embed-text und Qdrant, lernst Chunking-Strategien, Re-Ranking und die typischen Fallstricke kennen, die in jedem zweiten Produktions-Setup zuschlagen.

Wie RAG funktioniert - in 4 Schritten

RAG zerlegt das Problem in vier klar getrennte Phasen. Zunächst werden alle relevanten Quelldokumente in kleine, sinnvolle Abschnitte zerlegt, die sogenannten Chunks. Aus jedem Chunk wird ein numerischer Vektor berechnet, der die semantische Bedeutung des Textes repräsentiert. Diese Vektoren landen zusammen mit dem Originaltext in einer Vektor-Datenbank, dem Vektor-Store. Soweit der Indexierungsteil, der einmalig pro Dokument läuft.

Wenn ein Nutzer nun eine Frage stellt, beginnt der zweite Teil. Die Frage wird mit demselben Embedding-Modell ebenfalls in einen Vektor verwandelt. Der Vektor-Store sucht dann die Chunks, deren Vektoren dem Frage-Vektor mathematisch am ähnlichsten sind. Das ist meist Cosine Similarity, manchmal auch Dot Product oder euklidischer Abstand. Diese Top-K-Treffer werden dem LLM als zusätzlicher Kontext mit der eigentlichen Frage übergeben.

Das LLM bekommt also nicht nur die Frage, sondern auch eine Auswahl an passenden Textstücken aus deiner Wissensbasis. Es nutzt diesen Kontext, um die Antwort zu formulieren, statt nur auf sein Trainingswissen zurückzugreifen. Damit kann ein 3B-Modell plötzlich Fragen zu deinen aktuellen Produktdaten beantworten, obwohl es diese Daten nie gesehen hat. Dieser Trick ist der Kern jeder modernen Wissensassistenz.

Wichtig ist, dass das LLM ausschließlich aus dem mitgelieferten Kontext schöpft. Das wird über den System-Prompt gesteuert, der dem Modell sagt, dass es nur auf Basis der gelieferten Snippets antworten soll. Damit verhinderst du Halluzinationen, also frei erfundene Antworten, weil das Modell explizit angewiesen wird, fehlende Information zuzugeben. Eine gute RAG-Pipeline liefert nicht nur die Antwort, sondern auch die Quellen, aus denen sie stammt.

Indexierung (einmalig pro Dokument):

  Dokumente -> Chunking -> Embedding-Modell -> Vektor-Store
     PDFs       300-800       768-Dim Vektor      Qdrant
     Markdown   Tokens                            Chroma
     HTML

Abfrage (jede Frage):

  Frage -> Embedding -> Vektor-Suche -> Top-K Chunks -> LLM -> Antwort
                                       (relevant)      mit
                                                       Kontext

Embedding-Modell wählen

Das Embedding-Modell ist die Brücke zwischen Text und Mathematik. Es liest einen Textabschnitt und gibt einen Vektor mit fester Länge zurück, üblich sind 384 bis 1024 Dimensionen. Zwei Texte mit ähnlicher Bedeutung sollen Vektoren erzeugen, die im Vektorraum nah beieinander liegen. Genau diese Eigenschaft macht später die semantische Suche möglich, also das Finden von Passagen, die nicht dieselben Wörter wie die Frage enthalten, aber dieselbe Bedeutung tragen.

Für lokales RAG sind vier Modelle besonders interessant. Wer auf einem deutschen Wissensbestand arbeitet, sollte zwingend ein multilinguales Modell nehmen, weil englisch-trainierte Modelle deutsche Synonyme oft nicht erkennen. Das ist ein klassischer Anfängerfehler, der zu schlechter Trefferqualität führt, ohne dass die Ursache offensichtlich ist. Englisch-fokussierte Modelle wie all-MiniLM-L6-v2 sind extrem schnell und klein, aber für deutschsprachige Wissensbasen die zweite Wahl.

ModellDimensionGrößeStärke
nomic-embed-text v1.5768137 MBMultilingual, Long-Context
all-MiniLM-L6-v238480 MBSchnell, leicht, Englisch-fokussiert
BGE-M31024600 MBMultilingual stark, Mehrsprachig
jina-embeddings-v31024600 MBTop-Multilingual, lange Kontexte

Nomic-embed-text v1.5 ist der pragmatische Allrounder. Mit 137 MB Größe lädt das Modell schnell, läuft mühelos auf CPU, unterstützt deutsche Texte gut und kann Eingaben bis 8192 Tokens am Stück verarbeiten. Das ist wichtig, weil dadurch auch lange Chunks ohne Vorverarbeitung embedded werden können. Für die meisten Use Cases ist das die richtige Wahl, vor allem in Kombination mit Ollama, das nomic-embed-text als erste Wahl listet.

BGE-M3 von der Beijing Academy of AI ist die nächste Stufe. Mit 1024 Dimensionen und sehr guter Multilingualität liefert es spürbar bessere Treffer auf gemischten oder sehr fachlichen Korpora. Die Kosten sind ein größerer Index, langsamere Embedding-Berechnung und höherer RAM-Bedarf bei großen Sammlungen. Wer ernsthafte Wissensbasen mit über 100.000 Dokumenten betreibt und Trefferqualität braucht, ist hier richtig. Für kleine Setups ist BGE-M3 oft Overkill.

Jina-embeddings-v3 ist ein sehr aktueller Konkurrent mit Top-Werten in deutschen MTEB-Benchmarks und nativer Long-Context-Unterstützung. Wer mit langen Dokumenten arbeitet, etwa juristischen Texten oder technischen Spezifikationen, profitiert deutlich. Die Lizenzbedingungen sind in der Open-Variante sauber, kommerzielle Nutzung in größerem Stil erfordert aber einen Blick ins Kleingedruckte.

Welches Modell du auch wählst, eines ist Pflicht: Indexierung und Abfrage müssen mit demselben Modell laufen. Ein gemischter Index aus zwei Embedding-Modellen ist wertlos, weil die Vektoren in unterschiedlichen Räumen leben und Ähnlichkeitsmessungen unbrauchbare Werte produzieren. Wenn du das Modell wechseln willst, musst du den gesamten Index neu aufbauen. Plane dafür Wartungsfenster ein, vor allem bei großen Beständen mit Millionen Dokumenten.

Vektor-Store wählen

Der Vektor-Store ist die Datenbank, die deine Embeddings speichert und eine schnelle Nachbarsuche im Vektorraum bereitstellt. Anders als klassische SQL-Datenbanken ist hier die Kernfrage nicht der Schlüssel-Lookup, sondern die approximative nächste-Nachbarn-Suche, kurz ANN. Algorithmen wie HNSW oder IVF erlauben es, in Millionen Vektoren in Millisekunden die ähnlichsten zu finden, mit minimaler Genauigkeitseinbuße gegenüber einer exakten Suche.

Qdrant ist die solide Standardwahl für lokales RAG. In Rust geschrieben, vollständig offline lauffähig, mit klarer REST- und gRPC-API, sehr guter Filter-Unterstützung und einer brauchbaren Web-UI. Du kannst Metadaten an jeden Vektor hängen und beim Suchen filtern, etwa nach Dokument-Typ, Sprache oder Erstellungsdatum. Qdrant skaliert von einer Docker-Instanz mit ein paar tausend Dokumenten bis zu Cluster-Setups mit Milliarden Vektoren, ohne dass du den Code ändern musst.

Chroma ist die Python-native Alternative, ideal für schnelle Prototypen und kleinere Wissensbasen. Eine Datei, ein Pip-Install, ein paar Zeilen Code, fertig ist der Store. Für Produktions-Setups mit wachsendem Datenvolumen wird Chroma irgendwann eng, vor allem bei vielen parallelen Schreibzugriffen. Bis 100.000 Chunks ist es bequem und schnell. Wer ohnehin mit LangChain oder LlamaIndex arbeitet, findet hier den geringsten Reibungsverlust.

Pgvector ist die richtige Antwort, wenn ohnehin schon ein PostgreSQL läuft. Du brauchst keinen zweiten Service, hast Backups, ACID-Transaktionen und Joins gleich mit. Für Setups, in denen Embeddings nur ein Aspekt eines größeren Datenmodells sind, ist pgvector oft die saubere Wahl. Performance liegt auf hohem Niveau, vor allem mit der HNSW-Indexierung, die seit Version 0.5 verfügbar ist. Schwächer ist pgvector bei sehr großen Vektor-Mengen jenseits einiger Millionen, dort schlagen spezialisierte Stores klar besser ab.

Weaviate und Milvus sind ausgewachsene Vektor-Datenbanken mit vielen Features wie Modulen für hybride Suche, Multi-Tenant-Setups und ausgefeilten Replication-Strategien. Für lokale Einzelnutzer-Setups sind beide Overkill. Sie beeindrucken in größeren Architekturen mit dezidierten Such-Teams, wo Features wie Multi-Vector pro Dokument oder Schemavalidierung Mehrwert liefern. Wer alleine oder im kleinen Team an einer Wissensbasis arbeitet, fährt mit Qdrant oder Chroma einfacher.

Faustregel für die Wahl: unter 100.000 Chunks reicht Chroma, ab da ist Qdrant die bessere Investition, ab einer Million Chunks wird die Wahl ein eigenes Architektur-Thema. Pgvector ist die richtige Antwort, wenn die Datenbank-Existenzfrage bereits zugunsten von PostgreSQL entschieden wurde. Wer im Zweifel ist, beginnt mit Qdrant via Docker. Der Migrationsaufwand zu größeren Setups ist später überschaubar, weil die API einfach ist und Backups als Snapshots problemlos transportiert werden.

Praxis-Setup mit Ollama plus Qdrant

Das hier ist der Pfad, der in den meisten Fällen funktioniert. Ollama als Inference-Layer für sowohl das Sprachmodell als auch das Embedding-Modell, Qdrant als Vektor-Store. Beide laufen unabhängig voneinander, sprechen über HTTP miteinander und können neu gestartet werden, ohne dass der jeweils andere Service davon etwas merkt. Diese Entkopplung ist wertvoll, weil du Sprachmodelle wechseln kannst, ohne den Index neu aufzubauen.

Schritt eins ist die Installation des Embedding-Modells über Ollama. Wenn Ollama bereits läuft, genügt ein einziger Pull-Befehl, um das Modell lokal verfügbar zu machen. Danach steht eine HTTP-Schnittstelle bereit, über die du Embeddings für beliebige Texte abrufen kannst. Das Modell verbraucht etwa 137 MB auf Disk und läuft im RAM problemlos parallel zu einem 3B- oder 7B-Sprachmodell, ohne dass du Last spürst.

# Ollama-Embeddings
ollama pull nomic-embed-text

# Qdrant via Docker
docker run -d -p 6333:6333 -v $(pwd)/qdrant_data:/qdrant/storage qdrant/qdrant

Schritt zwei ist der Start von Qdrant. Der Docker-Befehl bindet den lokalen Port 6333 für die REST-API und mountet ein lokales Verzeichnis als Storage. Damit überlebt der Index Neustarts und Container-Updates. Im Browser kannst du danach unter localhost:6333/dashboard die Web-UI öffnen, Collections inspizieren, einzelne Punkte ansehen und Suchanfragen interaktiv testen. Das hilft enorm beim Debugging, vor allem in den ersten Wochen.

Schritt drei ist die Anlage einer Collection. Eine Collection ist Qdrants Begriff für eine Tabelle. Du legst dabei fest, welche Vektor-Dimension verwendet wird und welche Distanzfunktion zur Ähnlichkeitsmessung dient. Bei nomic-embed-text sind das 768 Dimensionen und Cosine Similarity. Diese Werte hängen direkt vom gewählten Embedding-Modell ab. Falsche Dimension fällt sofort auf, falsche Distanzfunktion erst durch schlechte Treffer.

Schritt vier ist das Indexieren der Dokumente. Du gehst alle Dateien durch, zerlegst sie in Chunks, embeddest jeden Chunk und schreibst den Vektor zusammen mit dem Originaltext und Metadaten in die Collection. Bei der ersten Befüllung läuft das oft mehrere Minuten oder Stunden, je nach Korpusgröße. Bei späteren Aktualisierungen aktualisierst du nur die geänderten Dokumente, was sehr viel schneller geht. Für inkrementelle Updates speicherst du am besten Hashes der Quelldateien als Metadaten.

Schritt fünf ist die Abfrage. Eine Nutzerfrage wird embedded, die Collection wird durchsucht, die Top-K Treffer werden mit ihrem Originaltext zurückgeliefert, dieser Text wird zusammen mit der Frage an das Sprachmodell geschickt. Das Sprachmodell antwortet, idealerweise unter Verweis auf die Quellen. Diese Schleife dauert bei einem 3B-Modell und 4 Chunks Kontext typischerweise unter zwei Sekunden auf einer modernen GPU oder unter zehn Sekunden auf reiner CPU.

Schritt sechs, optional aber empfohlen, ist das Logging der Anfragen. Du speicherst jede Frage zusammen mit den gefundenen Chunks, der Antwort und einem Feedback-Feld in einer kleinen SQLite-Datei. Damit kannst du später analysieren, welche Fragen schlecht beantwortet wurden, welche Chunks häufig getroffen werden und wo die Wissensbasis Lücken hat. Ohne dieses Logging optimierst du im Blindflug.

Python-Code für eine vollständige RAG-Pipeline

Der folgende Python-Code zeigt eine minimal lauffähige RAG-Pipeline. Sie nutzt den Ollama-Client für Embeddings und Inferenz und den Qdrant-Client für die Vektor-Suche. Die Abhängigkeiten installierst du mit pip install ollama qdrant-client. Beachte, dass beide Services laufen müssen, bevor du den Code ausführst, sonst bekommst du Connection-Errors. Der Code ist als didaktische Vorlage gedacht und enthält keine Production-Hardening wie Retry-Logik oder Batching, die du später ergänzen wirst.

Der erste Block initialisiert die Clients. Ollama hört standardmäßig auf Port 11434, Qdrant auf 6333. Beide Adressen sind hier hartcodiert, in einem ernsthaften Setup würdest du sie aus Umgebungsvariablen lesen. Die embed-Funktion ist die Schaltzentrale: jedes Mal, wenn ein Stück Text in den Vektorraum gehoben werden soll, ruft sie nomic-embed-text über Ollama auf und gibt die 768 Float-Werte zurück.

Die recreate_collection-Funktion löscht eine eventuell vorhandene Collection und legt sie neu an. Praktisch für den Einstieg, in Produktion willst du das natürlich nicht, sondern create_collection mit Existenz-Prüfung. Die Vektor-Konfiguration legt fest, dass die Vektoren 768 Dimensionen haben und mit Cosine Similarity verglichen werden. Beide Werte müssen exakt zur Embedding-Konfiguration passen, sonst weigert sich Qdrant beim ersten Upsert.

from ollama import Client
from qdrant_client import QdrantClient
from qdrant_client.http import models

ollama = Client(host="http://localhost:11434")
qdrant = QdrantClient(host="localhost", port=6333)

def embed(text: str) -> list[float]:
    return ollama.embeddings(model="nomic-embed-text", prompt=text)["embedding"]

# Collection anlegen
qdrant.recreate_collection(
    collection_name="docs",
    vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE),
)

# Dokument hinzufügen
def index(doc_id: str, text: str):
    vector = embed(text)
    qdrant.upsert(
        collection_name="docs",
        points=[models.PointStruct(id=doc_id, vector=vector, payload={"text": text})],
    )

# Suche und Antwort
def ask(query: str) -> str:
    qvec = embed(query)
    hits = qdrant.search(collection_name="docs", query_vector=qvec, limit=4)
    context = "\n---\n".join([h.payload["text"] for h in hits])
    response = ollama.chat(model="llama3.2:3b", messages=[
        {"role": "system", "content": "Antworte ausschließlich auf Basis des gegebenen Kontexts."},
        {"role": "user", "content": f"Kontext:\n{context}\n\nFrage: {query}"},
    ])
    return response["message"]["content"]

Die index-Funktion zeigt das Indexierungsmuster: Vektor berechnen, Punkt mit ID und Payload anlegen, in die Collection einfügen. Die Payload ist ein freies Dictionary, hier nur mit dem Originaltext. In der Praxis legst du dort auch Quelle, Pfad, Erstellungsdatum, Autor, Tags und einen Hash ab. Diese Metadaten ermöglichen später Filter und Quellenangaben in der Antwort.

Die ask-Funktion ist die Suche und Generierung in einem. Erst wird die Frage embedded, dann sucht Qdrant die Top-4-Treffer, deren Originaltext wird zu einem Block zusammengefügt, getrennt durch Trennlinien. Dieser Block geht zusammen mit der Frage an das Sprachmodell. Der System-Prompt ist absichtlich streng, damit das Modell nicht halluziniert. In Produktion würdest du diesen Prompt stark erweitern, etwa mit Anweisungen zu Quellenangaben, zum Verhalten bei fehlenden Antworten und zum Tonfall.

In dieser Form skaliert der Code bis etwa 50.000 Chunks problemlos. Darüber hinaus brauchst du Batching beim Indexieren, sonst dauert die Befüllung Stunden. Für Batching nutzt du embed-Batch-Endpunkte und upsert mit mehreren Punkten pro Aufruf. Außerdem solltest du in Produktion ein Embedding-Cache vorhalten, damit identische Texte nicht doppelt embedded werden. Das spart bei Re-Indexierungen massiv Zeit.

Chunking-Strategien

Chunking ist die unscheinbarste, aber wichtigste Stellschraube in jedem RAG-System. Ein schlechtes Chunking macht jeden hochwertigen Embedding-Aufwand zunichte. Die zentrale Frage lautet, wie du lange Quelldokumente in kleinere Stücke zerlegst, ohne Sinn-Einheiten zu zerreißen. Es gibt verschiedene Strategien, die je nach Dokumenttyp unterschiedlich gut funktionieren. Die Wahl ist nicht zwingend technisch, sondern oft inhaltlich getrieben.

Token-basiertes Chunking ist der einfachste Ansatz. Du zählst Tokens und schneidest alle 500 Tokens ab, eventuell mit 50 Tokens Überlappung. Vorteil: schnell, deterministisch, einfach zu implementieren. Nachteil: Schnitte fallen oft mitten in Sätze oder Absätze, was zu verwirrenden Chunks führt. Für rein technische Dokumentationen mit klarer Struktur funktioniert das oft, für Fließtext deutlich seltener. Die Überlappung kompensiert teilweise das Sinn-Reißen, weil benachbarte Chunks gemeinsame Phrasen tragen.

Satzbasiertes oder absatzbasiertes Chunking nutzt strukturelle Merkmale des Texts. Du schneidest an Satzenden oder Absatzgrenzen, sammelst so viele Sätze wie möglich, bis ein Token-Limit erreicht ist. Damit bleiben Sinn-Einheiten intakt. Bibliotheken wie spaCy oder NLTK liefern hierfür robuste Sentence-Splitter, die auch mit Sonderzeichen und Abkürzungen klarkommen. Diese Strategie ist bei Fließtext deutlich besser als rein token-basiertes Chunking und sollte der Default für jeden ernsthaften Einsatz sein.

Sliding-Window-Chunking erzeugt überlappende Chunks, etwa 500 Tokens Länge mit 250 Tokens Überlappung. Das verdoppelt die Anzahl der Chunks, erhöht aber die Wahrscheinlichkeit, dass relevante Phrasen gut wiedergefunden werden. Sinnvoll bei Texten mit dichter, verteilter Information, etwa wissenschaftlichen Papieren oder rechtlichen Dokumenten. Nachteil sind höherer Storage-Bedarf und mehr Treffer-Redundanz, was bei zu hoher Überlappung das LLM mit Wiederholungen überflutet.

Semantic Chunking nutzt das Embedding-Modell selbst, um Bruchstellen zu finden. Du embeddest jeden Satz, vergleichst aufeinanderfolgende Vektoren und schneidest dort, wo die Ähnlichkeit am stärksten abfällt. Das erzeugt thematisch homogene Chunks, ist aber rechenaufwendig in der Indexierungsphase. Bei großen Korpora wird das schnell teuer. Für kleinere, hochwertige Wissensbasen wie technische Whitepaper kann sich der Aufwand klar lohnen.

Markdown-aware Chunking respektiert die Dokumentstruktur. Überschriften definieren natürliche Trennpunkte, Code-Blöcke bleiben zusammenhängend, Tabellen werden nicht mitten durchschnitten. Bibliotheken wie LangChain bringen hierfür Splitter mit, die HTML- oder Markdown-Strukturen erkennen. Das ist die richtige Wahl für Software-Dokumentation, Entwickler-Wikis und alle Inhalte, die strukturiert geschrieben sind. Code-Snippets innerhalb von Chunks bleiben so syntaktisch ganz und sind später durch das LLM korrekt zitierbar.

Häufige Fallen beim Chunking: zu kleine Chunks ohne Kontext, sodass einzelne Pronomen ohne Bezugswort dastehen. Zu große Chunks, die Relevanz verwässern, weil ein Chunk zu vielen Themen gleichzeitig nahesteht. Fehlende Überlappung, sodass Sätze an Chunk-Grenzen verloren gehen. Tabellen, die mitten durchgeschnitten werden und so völlig unverständlich werden. Code-Blöcke, deren Indentation in der Tokenisierung verlorengeht. Diese Fehler treten in fast jedem ersten RAG-Prototyp auf und werden oft erst erkannt, wenn ein Nutzer mit der Antwortqualität unzufrieden ist.

Re-Ranking - der unterschätzte Hebel

Re-Ranking ist eine zweite Stufe nach der initialen Vektor-Suche. Du holst aus dem Vektor-Store nicht nur die Top-4-Treffer, sondern die Top-30 oder Top-50, schickst diese an einen Cross-Encoder, der jede Frage-Treffer-Kombination separat scort, und nimmst dann nur die Top-4 dieses neuen Rankings. Der Cross-Encoder ist deutlich genauer als die ursprüngliche Vektor-Suche, weil er Frage und Treffer gemeinsam bewertet, nicht über getrennte Vektoren.

BGE-Reranker von der Beijing Academy of AI ist hier die populärste Wahl. Die base-Variante mit 278 MB läuft auf CPU akzeptabel, die large-Variante mit 1,1 GB liefert noch bessere Werte, braucht aber idealerweise eine GPU. Die Modelle sind multilingual und liefern auch auf deutschen Texten saubere Ergebnisse. Wichtig ist, dass Re-Ranking nur Sinn ergibt, wenn die initiale Vektor-Suche genug Kandidaten liefert. Wer nur Top-4 holt und dann re-ranked, verschenkt das Potenzial.

Der Aufwand lohnt sich vor allem dann, wenn deine Wissensbasis Mehrdeutigkeiten oder eng verwandte Themen enthält. Beispiele sind Produktkataloge mit ähnlichen Varianten, juristische Texte mit fein verzahnten Paragrafen oder Support-Datenbanken mit ähnlich klingenden Tickets. Bei kleinen, klar abgegrenzten Wissensbasen mit wenigen Themen bringt Re-Ranking oft kaum messbaren Gewinn. Hier reicht die reine Vektor-Suche.

Die Latenz steigt durch Re-Ranking spürbar. Top-30 Cross-Encoder-Bewertungen dauern auf einer modernen Consumer-GPU etwa 50 bis 150 Millisekunden, auf CPU eher 500 Millisekunden bis zwei Sekunden. Das ist im interaktiven Chat noch akzeptabel, kann aber im API-Modus problematisch werden. Wenn Latenz kritisch ist, kannst du Re-Ranking nur bei kritischen Anfragen aktivieren und für triviale Anfragen weglassen, etwa erkannt durch Frage-Kategorisierung.

Eine Alternative zum klassischen Cross-Encoder ist hybride Suche. Du kombinierst die Ergebnisse einer reinen Keyword-Suche etwa via BM25 mit der Vektor-Suche und mischst die Rankings über Reciprocal Rank Fusion. Das ist deutlich schneller als Cross-Encoder-Reranking und liefert auf vielen Korpora vergleichbare Verbesserungen. Qdrant unterstützt hybride Suche nativ seit Version 1.10, sodass du mit wenig Code eine zweite Such-Stufe ergänzen kannst, ohne ein zusätzliches Modell zu laden.

Häufige RAG-Fehler

Embedding-Modell wechseln, ohne den Index neu aufzubauen. Das ist der Klassiker. Du installierst ein neueres Modell, embeddest neue Dokumente, nutzt aber den alten Index für Abfragen. Die Vektoren leben in unterschiedlichen Räumen, die Suche liefert Müll. Die Lösung ist einfach: bei Modellwechsel immer kompletter Re-Index, ohne Ausnahme. Versionsnummer als Metadatum mitspeichern hilft, solche Fehler früh zu bemerken.

Englisches Embedding-Modell für deutsche Inhalte nutzen. all-MiniLM-L6-v2 ist schnell und beliebt, aber englisch trainiert. Auf deutschen Korpora sinkt die Trefferqualität spürbar, weil Synonyme und idiomatische Wendungen nicht erkannt werden. Multilinguale Modelle wie nomic-embed-text, BGE-M3 oder jina-embeddings-v3 sind für deutsche Inhalte deutlich besser. Bei mehrsprachigen Korpora gilt das ohnehin als Pflicht.

Chunks ohne Kontext. Wenn du in einem Chunk nur drei Sätze hast und der erste Satz auf etwas im vorigen Absatz verweist, fehlt dem LLM der Bezug. Antworten werden lückenhaft oder falsch. Lösung: bei jedem Chunk den übergeordneten Titel und gegebenenfalls den Abschnittstitel als Präfix mitspeichern. Auch Dateipfad und Quelltyp helfen dem Modell, den Kontext einzuordnen.

Zu viele Chunks ans LLM geben. Mehr ist nicht besser. Wer 20 Chunks an ein 8k-Kontextfenster gibt, drückt die wichtigen Signale ans Ende und verwässert die Antwort. Top 3 bis 5 Chunks sind in den meisten Fällen das Optimum. Wenn die Antwortqualität schlecht ist, hilft eher Re-Ranking als mehr Kontext. Mehr Kontext erhöht außerdem die Latenz und den Speicherverbrauch.

Keine Quellenangaben. Eine Antwort ohne Quelle ist im professionellen Einsatz schwer prüfbar. Speichere immer Dokument-ID, Pfad und Abschnitts-Index als Payload, weise das LLM an, am Ende der Antwort die genutzten Quellen zu listen. Das macht die Antwort nachvollziehbar und ermöglicht es Nutzern, im Originaldokument nachzuschlagen. Ohne Quellen ist RAG nur eine bessere Halluzination.

Index nicht aktuell halten. Eine Wissensbasis lebt. Dokumente werden geändert, neue kommen dazu, alte werden gelöscht. Wenn dein Index das nicht mitmacht, antwortet das LLM mit veralteten Inhalten. Lösung: ein Cron-Job, der täglich oder stündlich geänderte Dateien identifiziert, alte Vektoren löscht und neue einspielt. Der Hash der Quelldatei als Payload macht das Erkennen von Änderungen trivial.

Nächster Schritt

Mit diesem Setup hast du eine produktionsreife lokale RAG-Pipeline. Bevor du sie auf sensible Inhalte loslässt, solltest du dich mit den rechtlichen Aspekten beschäftigen. Ein lokaler Vektor-Store ist DSGVO-freundlicher als jede Cloud-Lösung, dennoch gelten bestimmte Pflichten, etwa zur Auskunft, Löschung und Datensparsamkeit. Diese Themen behandelt der Abschnitt Sicherheit und DSGVO ausführlich.

Hardware ist die zweite Stellschraube. Embedding-Modelle laufen problemlos auf CPU, Sprachmodelle profitieren stark von einer GPU. Welche Hardware-Konfiguration für welche Modellgröße realistisch ist, mit Mindestanforderungen, RAM-Bedarf und VRAM-Empfehlungen, findest du im Hardware-Guide. Vor allem die Frage, ob ein 7B-Modell mit RAG noch auf einer Consumer-Karte läuft oder doch eine RTX 4090 nötig ist, wird dort beantwortet.

Häufige Fragen

Was ist RAG?

Retrieval-Augmented Generation: Statt das LLM allein antworten zu lassen, holst du erst relevante Snippets aus deiner eigenen Dokumentensammlung und gibst sie dem Modell als Kontext mit. Das verbindet aktuelles Wissen mit der Sprachfähigkeit des LLM.

Brauche ich für RAG ein zweites Modell?

Ja, ein Embedding-Modell. Empfehlenswert ist nomic-embed-text oder all-MiniLM-L6 v2 - beide laufen lokal über Ollama oder direkt via Sentence-Transformers.

Welcher Vektor-Store für lokales RAG?

Qdrant (komplett offline lauffähig, Rust-Performance), Chroma (Python-nativ, einfach), oder pgvector wenn du PostgreSQL eh schon nutzt. Für unter 100k Dokumente reicht ChromaDB.

Wie groß sollten die Chunks sein?

Typischerweise 300 bis 800 Tokens pro Chunk mit 50-100 Tokens Überlappung. Zu kleine Chunks zerreißen Sinneinheiten, zu große verwässern die Relevanz.

Wie viele Chunks soll ich dem LLM mitgeben?

Top 3 bis 5 Chunks bei 8k-Kontextfenstern, bis zu 10 Chunks bei 32k-Kontextfenstern. Mehr ist selten besser - relevante Chunks oben statt viele Chunks irgendwo.

Funktioniert RAG mit Llama 3.2 3B?

Ja, sogar gut. Kleine Modelle profitieren überproportional von gutem Kontext, weil ihr eigenes Weltwissen begrenzt ist. RAG plus 3B schlägt oft 70B ohne RAG.

Wie aktualisiere ich den Vektor-Store?

Periodisches Re-Embedding aller geänderten Dokumente plus inkrementelles Indexing neuer Dateien. Faustregel: täglicher Cron-Job für aktive Wissensbasen, sofortiges Update bei kritischen Inhalten.

Sind RAG-Daten DSGVO-relevant?

Ja - dein Vektor-Store enthält Inhalte deiner Dokumente. Solange er lokal läuft, fällt kein Drittstaaten-Transfer an. Cloud-Embeddings (OpenAI, Cohere) erfordern eine Auftragsverarbeitungsvereinbarung.