RAG-Systeme in der Praxis — Vom Prototyp zur Produktion
Retrieval-Augmented Generation (RAG) hat sich schnell zum Standard-Pattern entwickelt, um Large Language Models mit domänenspezifischem Wissen zu verankern. Das Konzept ist einleuchtend: Anstatt ein Modell auf eigenen Daten zu fine-tunen, werden zur Abfragezeit relevante Dokumente abgerufen und in den Prompt-Kontext eingebettet. Aber die Kluft zwischen einem funktionierenden Prototyp und einem produktionsreifen RAG-System ist enorm.
Dieser Beitrag behandelt die praktischen Entscheidungen und Fallstricke, die beim Bau von RAG-Systemen auftreten, die zuverlässig, skalierbar und kosteneffizient funktionieren müssen.
Warum RAG?
LLMs haben einen Wissens-Cutoff und keinen Zugriff auf private Daten. Fine-Tuning ist teuer, langsam und erfordert erneutes Training bei jeder Datenänderung. RAG löst das, indem es die Wissensquelle vom Modell entkoppelt:
- Indexieren: Dokumente in Vektor-Embeddings umwandeln
- Abrufen: Relevante Chunks basierend auf der Nutzeranfrage finden
- Generieren: Antwort unter Verwendung des abgerufenen Kontexts erzeugen
So erhält man aktuelle, quellenbasierte Antworten, ohne das Modell neu zu trainieren.
Chunking: Das Fundament, das man nicht ignorieren darf
Die folgenreichste Entscheidung in einer RAG-Pipeline ist, wie man Dokumente in Chunks aufteilt. Macht man das falsch, rettet auch keine noch so ausgefeilte Retrieval-Strategie das Ergebnis.
Gängige Chunking-Strategien:
- Feste Größe (z.B. 512 Tokens mit 50 Token Überlappung): Einfach und vorhersehbar, zerteilt aber oft semantische Einheiten
- Rekursives Text-Splitting: Trennt nach Absätzen, dann Sätzen, dann Zeichen — respektiert natürliche Grenzen besser
- Semantisches Chunking: Nutzt Embedding-Ähnlichkeit zur Erkennung von Themengrenzen — höchste Qualität, höchste Kosten
- Dokumentstruktur-basiertes Splitting: Nutzt Überschriften, Abschnitte und Formatierungshinweise aus dem Quelldokument
Hier eine praktische Chunking-Implementierung mit LangChain:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=100,
separators=["\n\n", "\n", ". ", " ", ""],
length_function=len,
)
chunks = splitter.split_documents(documents)
Meine Empfehlung: Mit rekursivem Text-Splitting bei 500–800 Tokens und 10–15 % Überlappung starten. Retrieval-Qualität messen, dann anpassen. Kleinere Chunks verbessern in der Regel die Präzision, kosten aber Kontextvollständigkeit.
Wahl des Embedding-Modells
Das Embedding-Modell wandelt Text in dichte Vektoren um, die semantische Bedeutung erfassen. Die Wahl ist wichtiger, als die meisten denken.
| Modell | Dimensionen | Stärken |
|---|---|---|
OpenAI text-embedding-3-small | 1536 | Gute Balance aus Qualität und Kosten |
OpenAI text-embedding-3-large | 3072 | Höchste Qualität von OpenAI |
Cohere embed-v3 | 1024 | Exzellente mehrsprachige Unterstützung |
BAAI/bge-large-en-v1.5 | 1024 | Beste Open-Source-Option |
nomic-embed-text | 768 | Schnell, gut für lokales Deployment |
Wichtige Überlegungen:
- Dimensionalität: Höhere Dimensionen erfassen mehr Nuancen, erhöhen aber Speicherkosten und Retrieval-Latenz
- Mehrsprachigkeit: Bei mehrsprachigen Inhalten müssen mehrsprachige Modelle explizit getestet werden
- Kosten bei Skalierung: Bei Millionen von Dokumenten summieren sich Embedding-Kosten schnell — Open-Source-Modelle auf eigener Infrastruktur können 10x günstiger sein
Vektordatenbanken: Wo die Embeddings leben
Eine Vektordatenbank speichert Embeddings und führt effiziente Ähnlichkeitssuchen durch. Die wichtigsten Optionen:
Pinecone: Vollständig gemanagt, exzellente Developer Experience, Pay-per-Query-Preismodell. Gut zum Einstieg, aber Vendor-Lock-in und Kosten können bei Skalierung überraschen.
Weaviate: Open Source, unterstützt hybride Suche (Vektor + Keyword), eingebaute Module für gängige Embedding-Modelle. Guter Mittelweg.
pgvector: PostgreSQL-Erweiterung für Vektor-Ähnlichkeitssuche. Wer bereits PostgreSQL nutzt (z.B. über Supabase), braucht keine neue Infrastruktur.
Qdrant: Open Source, hohe Performance, umfangreiche Filter-Möglichkeiten. Meine Wahl für selbst gehostete Produktions-Deployments.
Hier ein minimales Beispiel mit pgvector und Supabase:
import openai
from supabase import create_client
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
# Embedding generieren
response = openai.embeddings.create(
model="text-embedding-3-small",
input="Wie implementiere ich Rate Limiting?"
)
query_embedding = response.data[0].embedding
# Ähnliche Dokumente suchen
result = supabase.rpc(
"match_documents",
{
"query_embedding": query_embedding,
"match_threshold": 0.78,
"match_count": 5
}
).execute()
for doc in result.data:
print(f"Score: {doc['similarity']:.3f} — {doc['content'][:100]}")
Re-Ranking: Der Qualitäts-Multiplikator
Die initiale Vektorsuche liefert oft Ergebnisse, die semantisch verwandt, aber nicht präzise relevant sind. Re-Ranking wendet ein anspruchsvolleres Modell auf die Top-k-Ergebnisse an, um die Reihenfolge zu verbessern.
from cohere import Client
co = Client(api_key=COHERE_API_KEY)
# Initiale Retrieval-Ergebnisse re-ranken
reranked = co.rerank(
model="rerank-english-v3.0",
query=user_query,
documents=[doc["content"] for doc in initial_results],
top_n=3
)
final_docs = [initial_results[r.index] for r in reranked.results]
Re-Ranking verbessert die Antwortqualität nach meiner Erfahrung typischerweise um 15–25 %, zum Preis eines zusätzlichen API-Calls mit 100–200 ms Latenz. Für Produktionssysteme, bei denen Antwortqualität zählt, lohnt es sich fast immer.
Häufige Fallstricke
1. Chunks zu gross. Bei Chunks von 2000+ Tokens ruft man weniger ab (Context-Window-Limits), und jeder Chunk enthält verdünnte Information. Das LLM hat Schwierigkeiten, die relevante Nadel im Heuhaufen zu finden.
2. Kein Metadata-Filtering. Ohne Metadaten (Dokumenttyp, Datum, Autor, Kategorie) kann man Retrieval nicht eingrenzen. Eine Frage zu „Q4 2025 Umsatz" sollte keine Dokumente von 2023 abrufen.
3. Retrieval-Qualitätsmetriken ignorieren. Man muss messen, wie gut das Retrieval unabhängig von der Generierung funktioniert. Wichtige Metriken: Recall@k, MRR (Mean Reciprocal Rank), Precision@k.
4. Kein Evaluierungsdatensatz. 50–100 Frage-Antwort-Paare mit Quellenreferenzen erstellen. Jede Pipeline-Änderung dagegen testen. Ohne das optimiert man blind.
5. Latenz und Kosten vernachlässigen. In Produktion tracken: Embedding-Latenz, Retrieval-Latenz, Re-Ranking-Latenz, gesendete Tokens ans LLM und Kosten pro Anfrage.
Fazit
Einen RAG-Prototyp baut man an einem Nachmittag. Ein produktionsreifes RAG-System erfordert Wochen sorgfältiger Iteration an Chunking, Retrieval-Qualität und operativen Belangen. Die zentrale Erkenntnis: RAG ist im Kern ein Information-Retrieval-Problem mit einer generativen Schicht obendrauf — und der Grossteil der Arbeit sollte in die Optimierung des Retrievals fliessen.
Einfach anfangen, alles messen und der Versuchung widerstehen, Komplexität hinzuzufügen, bevor man versteht, wo die Pipeline tatsächlich versagt.


