Die meisten RAG-Demos scheitern in der Produktion. Sie funktionieren bei den 10 Fragen des Demo-Decks und brechen zusammen, sobald Benutzer etwas Neues fragen. Dieser Leitfaden beschreibt die Architektur und Muster, die standhalten: Chunking unter Beachtung semantischer Grenzen, hybrides Retrieval für unscharfe und wörtliche Abfragen, eine Prompt-Struktur zur Vermeidung von Halluzinationen und Kostendisziplin, die die Rechnung auch bei wachsender Skalierung stabil hält.
Die fünf wichtigen Entscheidungen
- Chunking-Strategie — beeinflusst die Retrieval-Qualität stärker als das Modell
- Embedding-Modell — bestimmt die Recall-Obergrenze
- Retrieval-Strategie — Vektorsuche allein reicht nicht aus
- Prompt-Struktur — entscheidet, ob das Modell halluziniert
- Generierungsmodell + Caching — bestimmt die Stückkosten
1. Chunking — langweilig ist besser als clever
Überdenken Sie das nicht. Rekursiver Zeichen-Splitter mit 1000–1500 Zeichen pro Chunk und 100–200 Zeichen Überlappung. Er versucht, zuerst an Absatzgrenzen, dann an Satzgrenzen und schließlich an Wortgrenzen zu trennen. Über verschiedene Domänen hinweg stabil:
# Recursive character splitter with overlap — the boring choice that wins
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1200,
chunk_overlap=200,
separators=["\n\n", "\n", ". ", " ", ""],
)
chunks = splitter.split_text(document)
# 1200 chars ≈ 300-400 tokens — fits 5+ chunks in a Sonnet context window
# with room for system prompt + question + answerDinge, die intelligenter wirken, aber selten helfen: satzbewusste Modelle, Markdown-bewusste Splitter und Sliding Windows mit Embeddings. Sie sind nicht schlecht — bei enormem Aufwand nur geringfügig besser. Investieren Sie diesen Aufwand stattdessen in Retrieval.
2. Embedding-Modell — text-embedding-3-large ist der Standard
Embeddings werden nicht von Kunavo bereitgestellt — rufen Sie für diesen Schritt direkt einen Embedding-Anbieter auf. /v1/embeddings ist ein implementiertes Wire-Format, hinter dem kein Modell aktiviert ist, daher schlägt eine Anfrage daran fehl; GET /v1/models ist immer maßgeblich dafür, was aufgerufen werden kann. In der Praxis kostet dies eine RAG-Pipeline nichts außer einem zweiten Schlüssel: Der Embedding-Aufruf und der Generierungsaufruf sind ohnehin getrennte Anfragen. Erstellen Sie daher Embeddings mit OpenAI, Voyage oder Cohere und generieren Sie mit Kunavo.
text-embedding-3-large trifft für die meisten Produktionsanwendungsfälle den besten Kompromiss aus Mehrsprachigkeit und Genauigkeit, zum von OpenAI selbst veröffentlichten Preis — prüfen Sie ihn auf deren Preisseite statt hier, da wir ihn nicht weiterverkaufen und dafür keinen Betrag nennen sollten. Erstellen Sie Embeddings neu, wenn Sie die Chunking-Strategie oder das Modell selbst ändern, nicht wenn Inhalte aktualisiert werden (fügen Sie einfach neue Chunks hinzu).
Ausnahmen, bei denen kleinere Embeddings funktionieren: reiner englischer Kurztext-Retrieval (FAQs, Produktkarten). Dafür ist text-embedding-3-small 4x günstiger bei vernachlässigbarem Qualitätsverlust. Für mehrsprachige oder technische Inhalte bleiben Sie bei large.
3. Retrieval — reine Vektorsuche verliert gegen Hybrid
Vektorähnlichkeit eignet sich hervorragend für unscharfes semantisches Matching („wie kündige ich“ → „Richtlinie zur Abonnementkündigung“). Bei wörtlichen Begriffen („SKU-A92837“ oder „Bestellung Nr. 4729“) ist sie schlecht. Die Lösung ist Hybrid-Retrieval: parallele BM25-Schlagwortsuche, anschließend Reciprocal Rank Fusion:
# Hybrid retrieval: vector similarity + BM25 keyword scoring
# Pure vector misses literal keywords (product SKUs, IDs, dates)
def retrieve(question: str, k: int = 5) -> list[dict]:
q_embed = embed([question])[0]
vector_hits = vector_db.similarity_search(q_embed, k=k * 2)
keyword_hits = bm25_search(question, k=k * 2)
# Reciprocal rank fusion — simple, robust
scores: dict[str, float] = {}
for rank, hit in enumerate(vector_hits):
scores[hit["id"]] = scores.get(hit["id"], 0) + 1 / (rank + 60)
for rank, hit in enumerate(keyword_hits):
scores[hit["id"]] = scores.get(hit["id"], 0) + 1 / (rank + 60)
top_ids = sorted(scores, key=scores.get, reverse=True)[:k]
return [chunk_by_id[i] for i in top_ids]Dies ist der größte einzelne Qualitätssprung nach der Auswahl eines brauchbaren Embedding-Modells. Verfolgen Sie recall@5 anhand einer zurückgehaltenen Evaluation mit 100 Fragen — wenn der Wert nach dem Hinzufügen von BM25 von 70 % auf 90 % steigt, haben Sie ein Drittel der „Ich weiß es nicht“-Fehler beseitigt.
4. Prompt-Struktur — drei Muster, die Halluzinationen verhindern
# Final RAG prompt structure — three sections, citations enforced
SYSTEM_PROMPT = """You answer based exclusively on the supplied Context.
- Cite the [doc_id] for each factual claim.
- If the context doesn't answer the question, say "I don't have that
information" — do not extrapolate or use general knowledge.
- Be concise. No throat-clearing."""
def answer(question: str) -> dict:
chunks = retrieve(question, k=5)
context = "\n\n---\n\n".join(
f"[doc:{c['id']}] {c['text']}" for c in chunks
)
resp = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{"role": "system", "content": [{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"},
}]},
{"role": "user", "content": f"# Context\n{context}\n\n# Question\n{question}"},
],
max_tokens=600,
)
return {
"text": resp.choices[0].message.content,
"sources": [c["id"] for c in chunks],
}Drei unverzichtbare Elemente im System-Prompt:
- Quellen zitieren — lassen Sie das Modell
[doc:42]an jede Behauptung anhängen. Wenn die zitierte ID nicht echt ist, haben Sie eine Halluzination erkannt - Explizit ablehnen — „Wenn der Kontext keine Antwort liefert, sagen Sie: Ich habe diese Information nicht.“ Ohne diese Vorgabe ergänzt das Modell aus Weltwissen
- Prägnante Ausgabe — kurze Antworten korrelieren mit präzisen Antworten. Maximal 600 Tokens sind ein guter Produktionsstandard
5. Generierungsmodell + Caching — die Kostenschicht
Claude Sonnet 4.6 ist der Standard. Claude Haiku 4.5 ist die günstige Ausweichoption, wenn die Kosten im Vordergrund stehen. Umgeben Sie den System-Prompt immer mit cache_control — der System-Prompt ist bei jedem Aufruf gleich, daher reduziert Caching diesen Anteil auf 10 % des Input-Preises.
Realistische Kosten pro Abfrage im Produktionsmaßstab (5K Kontext, 500 Output, Caching aktiviert):
| Modell | Kosten pro Abfrage | Bei 10.000 Abfragen/Tag | Qualität gegenüber Sonnet |
|---|---|---|---|
claude-sonnet-4-6 | ~$0,007 | ~70 $/Tag | Basiswert |
claude-haiku-4-5 | ~$0,001 | ~10 $/Tag | ~85 %, 4× günstiger |
Bei 10.000 Abfragen/Tag → 70 $/Tag bei Sonnet, 10 $/Tag bei Haiku. Wählen Sie Sonnet für Anwendungsfälle, bei denen viel auf dem Spiel steht (kundenorientierte, rechtliche, medizinische), und Haiku für interne Verarbeitung oder Massenverarbeitung.
Was in der Produktion schiefgeht (und wie man es erkennt)
- Verteilungsdrift: Der Trainingskorpus entspricht nicht mehr den echten Benutzerfragen. Stichprobenartig 100 Abfragen/Woche nehmen und recall@5 manuell prüfen
- Veraltete Embeddings: Quelldokumente wurden aktualisiert, die Embeddings aber nicht. Indexgröße im Vergleich zur Quellgröße wöchentlich verfolgen
- Gefälschte Zitate: Das Modell erfindet Dokument-IDs, die echt aussehen. Jede
[doc:N]vor der Darstellung gegen die tatsächliche Liste der abgerufenen IDs validieren - Latenzklippen: Die Vektordatenbank bricht bei mehr als einer Million Vektoren ein. HNSW-Indizierung verwenden, bei Multi-Tenant-Systemen nach Tenant partitionieren
Produktions-Roadmap für 4 Wochen
- Woche 1: mit 100 Dokumenten und 10 Testfragen prototypisieren, manuelle Evaluation
- Woche 2: auf den vollständigen Korpus skalieren, hybrides Retrieval erstellen, Evaluation mit 100 Fragen schreiben
- Woche 3: Chunking + System-Prompt anhand der Evaluation iterativ optimieren, für interne Benutzer bereitstellen
- Woche 4: Monitoring, Kostenbudget, öffentliche Veröffentlichung mit UI für Quellenangaben
Häufig gestellte Fragen
Welche Chunk-Größe sollte ein produktives RAG-System verwenden?
1.000–1.500 Zeichen pro Chunk mit einer Überlappung von 100–200 Zeichen, aufgeteilt mit einem rekursiven Zeichen-Splitter, der zuerst an Absatzgrenzen, dann an Satzgrenzen und schließlich an Wortgrenzen trennt. Satzbewusste Modelle, Markdown-bewusste Splitter und Sliding Windows sind bei deutlich höherem Aufwand geringfügig besser — diesen Aufwand investiert man besser in den Retrieval-Schritt.
Welches Embedding-Modell sollte eine RAG-Pipeline verwenden?
text-embedding-3-large ist der Standard für mehrsprachige und technische Inhalte. Für reinen englischen Kurztext-Retrieval wie FAQs und Produktkarten ist text-embedding-3-small etwa 4× günstiger bei vernachlässigbarem Qualitätsverlust. Beachten Sie, dass Embeddings nicht von Kunavo bereitgestellt werden: /v1/embeddings ist ein implementiertes Wire-Format, hinter dem kein Modell aktiviert ist. Daher ruft der Embedding-Schritt OpenAI, Voyage oder Cohere direkt auf, während der Generierungsschritt Kunavo aufruft. Das kostet eine Pipeline nichts außer einem zweiten Schlüssel, da die beiden Schritte ohnehin getrennte Anfragen sind.
Reicht die Vektorsuche allein für RAG-Retrieval aus?
Nein. Vektorähnlichkeit funktioniert gut beim unscharfen semantischen Matching, versagt aber bei wörtlichen Begriffen wie einer SKU oder einer Bestellnummer. Eine parallele BM25-Schlagwortsuche mit anschließender Zusammenführung mittels Reciprocal Rank Fusion erhöht recall@5 typischerweise von etwa 70 % auf etwa 90 % — der größte einzelne Qualitätssprung nach der Auswahl eines brauchbaren Embedding-Modells.
Wie verhindert man Halluzinationen in einem RAG-System?
Drei Muster im System-Prompt: Fordern Sie eine [doc:N]-Zitatangabe für jede faktische Behauptung, weisen Sie das Modell an zu sagen, dass es die Information nicht hat, wenn der Kontext keine Antwort liefert, und halten Sie Antworten kurz — maximal 600 Output-Tokens sind ein guter Produktionsstandard. Validieren Sie anschließend jede zitierte ID vor der Darstellung gegen die abgerufene Menge; eine ID, die nicht echt ist, ist eine erkannte Halluzination.
Wie viel kostet eine produktive RAG-Abfrage?
Bei 5K Kontext und 500 Output-Tokens mit aktiviertem Prompt-Caching: etwa 0,007 $ pro Abfrage bei Claude Sonnet 4.6 und etwa 0,001 $ bei Claude Haiku 4.5 (4× günstiger, ungefähr 85 % der Qualität). Bei 10.000 Abfragen pro Tag sind das etwa 70 $/Tag bei Sonnet und etwa 10 $/Tag bei Haiku.