In diesem Artikel möchten wir einige praktische Hinweise geben, wie man ein lokales RAG-System Schritt für Schritt aufsetzt. Von der sorgfältigen Aufbereitung der eigenen Daten über nützliche Tipps für die technische Implementierung bis hin zu Erfahrungswerten für die Feinkonfiguration: Mit den folgenden Punkten kann die Qualität der Ergebnisse spürbar verbessert und typische Stolperfallen vermieden werden. Für einen Allgemeinen Überblick über RAG haben wir einen eigenen Blogpost
Datenaufbereitung
Die Qualität der Antworten steht und fällt mit der Qualität der eingespielten Daten. Viele RAG-Frameworks können zwar theoretisch mit ganz unterschiedlichen Dateitypen umgehen — von PDFs über Office-Dokumente bis zu Webseiten — dennoch lohnt es sich, die Inhalte vorab in eine möglichst schlanke und gut strukturierte Form zu bringen.
Kompaktes Format mit semantischem Markup
Ein einfaches, aber semantisch reiches Format wie Markdown ist oft ideal: Es ist leicht zu parsen, hält Überschriften, Listen oder Codeblöcke sauber auseinander und vermeidet unnötigen Ballast. Gleichzeitig bleibt der Inhalt für Menschen leicht lesbar.
Ballast entfernen, Fokus erhöhen
Gerade bei großen Mengen an Texten kann es sinnvoll sein, irrelevante Elemente wie eingebettete Bilder, dekorative Formatierungen oder externe Links zu entfernen. Das spart Speicherplatz in der Vektordatenbank und sorgt dafür, dass die Embeddings nur wirklich relevante Informationen abdecken. Kompaktere Dokumente führen meist auch zu besseren Suchergebnissen.
Metadaten integrieren
Es ist durchaus eine Überlegung wert, ob bestimmte Metadaten direkt mit ins Dokument aufgenommen werden sollen. Dazu können z. B. ein erweiterter Titel, Autorenhinweise, Versionsstände oder Hinweise zur Hierarchie innerhalb der Dokumente gehören. Solche Zusatzinformationen können später helfen, Suchergebnisse zu filtern oder sinnvoll zu priorisieren.
Updaten der RAG-Anwendung
Hier zeigt sich der Vorteil von RAG. Es genügt einfach die Vektordatenbank zu aktualisieren und neues Wissen ist dann sofort verfügbar! Es muss weder ein neues Modell trainiert und ausgerollt werden, noch muss die eigentlich Anwendung aktualisiert werden.
Die Updates der Vektordatenbank können je nach Bedarf manuell, oder auch mit einem Hintergrund-Job ausgeführt werden. Für die meisten Fälle ist es wohl nicht notwendig sofort bei jedem neuen Datensatz zu updaten, aber z.B. einmal pro Woche Daten aus den relevanten Quellen zu ziehen, zu transformieren und in die Vektordatenbank einzuspeisen macht für viele Systeme Sinn.
Implementierung
Für die eigentliche Umsetzung eines lokalen RAG-Systems braucht es ein paar zentrale Bausteine: ein Sprachmodell (LLM), ein Framework für das Retrieval, ein Embedding-Modell, welches den Inhalt der Dokumente in Vektoren überführt, eine Vektordatenbank in der die Dokumente abgelegt und abgerufen werden können. Zum Abschluss fehlt natürlich noch eine Logik, die diese Komponenten miteinander verbindet. Ein bewährter Technologie-Stack sieht zum Beispiel so aus:
Sprachmodell (LLM)
Für viele Anwendungsfälle reichen Open-Source-LLMs wie Llama 3, Mistral oder Gemma. Diese lassen sich lokal auf eigener Hardware betreiben — zum Beispiel über den Ollama-Server oder mit vLLM als performanter Inferenz-Engine. Je nach Hardware-Budget kann die Modellgröße angepasst werden.
LLamaIndex (Framework)
Ein sehr beliebtes Open-Source-Framework ist LLamaIndex. Es übernimmt die Brücke zwischen den eigenen Daten (in der Vektordatenbank) und dem LLM. LLamaIndex kümmert sich um das Einbetten, das Abrufen relevanter Chunks und das dynamische Erstellen von Prompts.
Embedding-Modell
Um Texte durchsuchbar zu machen, wird ein Embedding-Modell benötigt. Hier kann z. B. InstructorXL oder ein E5-Modell verwenden — wichtig ist, dass es zu den Daten passt (Sprache, Länge, Semantik). Viele Anbieter bieten vortrainierte Modelle an, die lokal laufen. Einen Überblick über die Performance einer Vielzahl von Text-Embedding-Modellen kann man auf Hugging Face finden.
Wichtig: Bei einem Wechsel des Embedding-Models ist darauf zu achten, dass die konfigurierte Größe der Embeddings in der Vektor-Datenbank auch (noch) zu den Embeddings passt, die das Model produziert!
Die Qualität der RAG-Piepline steht und fällt zu einem großen Teil mit der Qualität der Embeddings!
Daher ist es hier auf jeden Fall sinnvoll verschiedene Modelle auszuprobieren und – sofern möglich – deren Konfiguration anzupassen. Zum Beispiel ermöglichen einige Modelle verschiedene Größen für die Embeddings (Matryoshka Embeddings), um einen besseren Kompromiss aus Qualität und Geschwindigkeit erzielen zu können.
Vektordatenbank (z. B. Milvus)
Die erstellten Embeddings werden in einer Vektordatenbank wie Milvus gespeichert. Milvus ist skalierbar, gut dokumentiert und auch für größere Datenmengen performant. Alternativ können kleinere Projekte mit Chroma gestartet werden, wenn weniger Infrastruktur gewollt ist.
Daten einbetten
Im ersten Schritt werden die aufbereiteten Dokumente in LlamaIndex eingespeist. Das Framework erstellt daraus Vektor-Embeddings und speichert sie in der Vektordatenbank.
Query Engine & Retrieval-Step
Bei einer Nutzeranfrage wandelt LlamaIndex die Frage in einen Vektor um, sucht per Similarity Search nach den relevantesten Chunks und baut daraus den erweiterten Prompt für das LLM. Über Prompt Templates
kann gesteuert werden, wie die gefundenen Inhalte in den finalen Prompt integriert werden.
Prompt Templates
Gut gestaltete Prompts sind entscheidend für die Qualität der Antworten. Mit Prompt Templates kann festlegt werden, wie die abgerufenen Inhalte formatiert werden — z. B. ob sie als Zitate, Bulletpoints oder Fließtext ins LLM eingespeist werden.
Die Standardtemplates von LlamaIndex können unter Umständen auch völlig ausreichend sein, dennoch sollte man Prompt Templates
als wichtige Stellschraube im Hinterkopf behalten.
Konfiguration
In diesem Abschnitt geht es um einige der Stellschrauben, die man in LlamaIndex anpassen kannst, um die Qualität der Antworten des Sprachmodells spürbar zu verbessern.
Chunking von Dokumenten
Die Wahl der richtigen Chunk-Größe hat einen großen Einfluss auf die Antwortqualität. Sind die Chunks zu groß, füllt sich der Kontext des Prompts sehr schnell mit vielen Informationen, die für die eigentliche Frage oft irrelevant sind. Sind sie dagegen zu klein, fehlen möglicherweise wichtige Zusammenhänge, um die Anfrage vollständig und korrekt zu beantworten.
Leider gibt es hier keine universell „richtige“ Lösung — es hilft nur, verschiedene Einstellungen auszuprobieren, um herauszufinden, was für die eigenen Daten am besten funktioniert. In den meisten Fällen sollten Chunks mehrere hundert Wörter umfassen. Ein sogenannter Chunk-Overlap (bei dem ein Teil des vorherigen Textes in den nächsten Chunk übernommen wird) hat sich fast immer als hilfreich erwiesen, um Zusammenhänge zu erhalten.
Der Standardwert in LlamaIndex liegt bei einer chunk_size
von 1024 Tokens und einem chunk_overlap
von 20. Abhängig von den Inhalten können andere Werte oder ein größerer Overlap zu besseren Ergebnissen führen. Für uns haben sich Chunks mit 1024 Tokens und einem Overlap von 64 als praktikabel erwiesen.
Das Framework bietet auch verschiedene Node Parser
, mit denen sich bestimmte, strukturierte Daten noch gezielter verarbeiten lassen.
Retrieval fine-tuning
Das Chunking optimiert die Daten, die in die Vektordatenbank eingespeist werden. Doch auch beim Abrufen der Daten lassen sich einige Parameter anpassen, um die Qualität der Ergebnisse weiter zu verbessern.
Der wichtigste Parameter ist dabei similarity_top_k
. Er bestimmt, wie viele relevante Ergebnisse bei einer Anfrage zurückgegeben werden sollen. Für kleinere Wissensbasen reichen meist 5–10 Ergebnisse aus, bei größeren Datenmengen können mehr Ergebnisse sinnvoll sein. Wie viele Chunks tatsächlich nötig sind, hängt auch davon ab, wie breit Informationen verteilt sind und ob sie in den Daten mehrfach vorkommen.
Eine weitere hilfreiche Option ist es, die Treffer zusätzlich nach ihren Similarity-Scores zu filtern. Gerade bei sehr spezifischen Anfragen gibt es manchmal nur ein oder zwei wirklich relevante Ergebnisse und mehr Chunks verwässern nur den Kontext der Anfrage. Mit LlamaIndex lässt sich das wie folgt umsetzen:
from llama_index.core.postprocessor import SimilarityPostprocessor
query_engine = index.as_query_engine(
similarity_top_k=top_k,
node_postprocessors=[
SimilarityPostprocessor(similarity_cutoff=cutoff_score)
],
...
)
Darüber hinaus gibt es noch weitere Strategien wie Metadaten- oder Keyword-Filter, Chunk-Reranking oder -Reordering. Auf diese soll hier jedoch nicht näher eingegangen werden.
Response Modes auswählen
Response Modes steuern, wie LlamaIndex dem LLM die abgerufenen Kontextdaten präsentiert — und mit welcher Strategie daraus eine Antwort erstellt oder synthetisiert wird.
Einen vollständigen Überblick über die verfügbaren Modi findet sich in der Dokumentation. Eine detailliertere Erklärung mit Anwendungsbeispielen bietet unter anderem dieser Blogpost.
Wir geben hier einen kurzen Überblick über die drei nützlichsten Modi:
compact
(der Standard)refine
tree_summary
compact
minimiert die Anzahl der LLM-Anfragen. Wenn alle relevanten Chunks in den Kontext des Modells passen, wird nur eine Anfrage gestellt und deren Antwort zurückgegeben. Falls der Kontext zu groß ist, wird er in mehrere Teile aufgeteilt: Das Modell verarbeitet dann nacheinander mehrere Prompts, deren Teilantworten anschließend zu einer finalen Antwort zusammengeführt werden.
refine
arbeitet iterativ: Jeder Chunk wird einzeln an das LLM geschickt. Die Antwort dient dabei jeweils als Ausgangspunkt für die nächste Anfrage. Holt man z. B. 10 Chunks aus der Vektordatenbank, resultiert dies in 10 aufeinanderfolgenden Anfragen. Dieses Vorgehen eignet sich besonders gut für komplexeres Reasoning, verbraucht aber deutlich mehr Rechenzeit — und es kann passieren, dass über die Antwortkette hinweg wichtige Details verloren gehen.
tree_summary
fasst mehrere Chunks in Gruppen zusammen, die nacheinander in mehreren LLM-Calls verarbeitet werden. Dabei werden Zwischenergebnisse jeweils weiter verdichtet, bis keine Chunks mehr übrig sind. Dieses Verfahren benötigt meist mehr Laufzeit als compact
, führt aber gerade bei umfangreicheren oder stark verteilten Informationen häufig zu besseren, konsistenteren Antworten.
Wir haben mit tree_summary oft die besten Ergebnisse erzielt, insbesondere bei komplexeren Anfragen, die mehr als nur einen Fakt zurückgeben müssen.
Wie so oft gilt jedoch auch hier: Welche Strategie am besten funktioniert, hängt stark von den eigenen Daten und Fragestellungen ab. Ausprobieren lohnt sich!
Modus | Prinzip | Vorteile | Nachteile | Geeignet für |
---|---|---|---|---|
compact | Alle Chunks in möglichst wenigen LLM-Calls verarbeiten | Schnell, ressourcenschonend | Weniger robust bei großen Kontexten | Kürzere, präzise Fragen mit wenigen relevanten Chunks |
refine | Chunks einzeln verarbeiten, Antworten iterativ verfeinern | Besseres Reasoning bei komplexen Fragen | Mehr Laufzeit, Risiko von Informationsverlust | Fragen mit logischen Ableitungen, komplexe Ketten |
tree_summary | Chunks gruppiert verarbeiten und Zwischenergebnisse zusammenfassen | Konsistente, umfassende Zusammenfassungen | Höherer Ressourcenverbrauch | Längere Texte, breit gestreute Infos, komplexe Zusammenhänge |
Übersichtstabelle für Response-Modes
Respones-Modes lassen sich mit LlamaIndex auch ganz einfach setzen:
query_engine = index.as_query_engine(response_mode=mode)
Promp Templates anpassen
LlamaIndex stellt Standard-Templates für die verschiedenen Response Modes bereit. Oft lohnt es sich jedoch, diese Vorlagen manuell anzupassen, um noch bessere Ergebnisse zu erzielen. Zum Beispiel durch klarere Anweisungen, spezielle Formulierungen oder einen definierten Antwortstil.
Einen guten Einstieg in dieses Thema bietet die LlamaIndex-Dokumentation zum Thema Prompts
Selbst definierte Templates lassen sich anschließend ganz einfach als Parameter an die Query-Engine von LlamaIndex übergeben.
query_engine = index.as_query_engine(similarity_top_k=top_k,
llm= Settings.llm,
text_qa_template=qa_template,
refine_template=refine_template,
summarize_template=summarize_template,
response_mode=mode)
Fazit
Der Aufbau einer eigenen RAG-Anwendung erfordert zwar einige technische Komponenten und sorgfältige Planung – doch der Aufwand lohnt sich. Mit einem durchdachten Setup lassen sich große Sprachmodelle effizient, flexibel und datenschutzkonform mit dem eigenen Wissen verbinden. Besonders im lokalen Betrieb entsteht so eine mächtige Lösung, die sowohl Kosten spart als auch volle Kontrolle über die eigenen Daten bietet. Ob interne Wissensdatenbank, Rechercheassistenz oder Support-Tool: Mit RAG entstehen Anwendungen, die nicht nur Antworten geben, sondern echten Mehrwert schaffen. Wir hoffen, dass Ihnen dieser technische Leitfaden den Einstieg erleichtert und Lust macht, selbst zu experimentieren – denn eines ist sicher: Die Möglichkeiten sind (fast) so groß wie die Modelle selbst.