RAG: dem Model Ihre Daten geben
Das Wissen eines LLM frierte bei seinem Trainings-Cutoff ein; Ihre Dokumente ändern sich wöchentlich. Retrieval-Augmented Generation (RAG) taut es auf: Zur Abfragezeit die relevanten Scheiben Ihrer Daten holen, in den Prompt einfügen und das Model aus Belegen mit Zitaten antworten lassen. Das Model macht Reasoning; der Index macht Gedächtnis.
Die Pipeline, ein durchgerechnetes Beispiel
Nehmen Sie ein 12.000-Token-Wartungshandbuch und führen Sie es von Ende zu Ende durch:
- Chunken. Zuerst an Überschriften splitten (Abschnitte überleben intakt), dann jeden Abschnitt über ~400 Tokens neu splitten, mit ~60 Tokens Überlappung (10–15%). Sinnvolle Chunk-Größen liegen bei 200–800 Tokens. Ergebnis: grob 35 Chunks.
- Embedden. Jeder Chunk wird ein Vektor — bge-m3 stößt 1.024 Floats pro Chunk aus. Auf der CPU dauert das für das ganze Handbuch Sekunden.
- Indexieren. Vektoren + Text + Metadaten (Quelldatei, Überschriftenpfad) in einem Vektorspeicher ablegen.
- Abrufen. Die Frage embedden, die Top k = 5 Chunks nach Cosinus-Ähnlichkeit holen (4–10 ist der vernünftige Bereich).
- Generieren. Den Prompt zusammenbauen: Anweisungen, die 5 Chunks mit ID-Tags, die Frage.
Chunken an semantischen Grenzen schlägt feste Fenster. Eine Tabelle, die von ihrer Kopfzeile getrennt wurde, oder eine nummerierte Prozedur, die bei Schritt 4 abgeschnitten wurde, ist unabrufbarer Müll, egal wie gut das Embedding-Modell ist.
Zwei Extras, die sich sofort bezahlt machen: den Überschriftenpfad (manual › §4 › Bolts) als Metadaten speichern, damit Sie präzise filtern und zitieren können, und die Abfrage exakt so embedden, wie der Nutzer sie tippte — „Bereinigungs“-Vorverarbeitung macht Retrieval meist schlechter, nicht besser.
Embeddings: die multilinguale Anforderung
Das Embedding-Modell entscheidet, was „ähnlich“ heißt. Für koreanische und gemischt koreanisch/englische Korpora ist die Wahl eng — rein englische Embedder scheitern still bei koreanischen Abfragen und liefern plausible, aber falsche Chunks:
| Model | Dimensionen | Notizen |
|---|---|---|
| bge-m3 | 1024 | multilingual, starkes Koreanisch — die sichere lokale Standardwahl |
| nomic-embed-text-v1.5 | 768 | solides Englisch, langer Kontext |
| all-MiniLM-L6-v2 | 384 | schnell, englisch-zentriert, schwach auf Koreanisch |
Embedding-Modelle sind winzig (0,1–0,6B Parameter); ein CPU-Kern kaut Tausende von Chunks durch — CPU-Inferenz. Betten Sie den Korpus einmal ein, Abfragen zur Fragetime. Halten Sie das Embedding-Modell pro Index eingefroren: Der Modellwechsel mitten im Index erzeugt Vektoren aus verschiedenen Räumen, und Ähnlichkeits-Scores werden zu Rauschen.
Vektorspeicher
| Speicher | Wann |
|---|---|
| sqlite-vec | Einzelner Nutzer, bis ~1M Vektoren, null Ops — es ist eine Datei |
| ChromaDB | lokales Prototyping mit Metadatenfiltern |
| Qdrant | Produktion: Filterung, hybride Suche, Skalierung |
Alle drei laufen lokal. Starten Sie mit sqlite-vec; wechseln Sie, wenn Sie wirklich an seine Wand stoßen, nicht vorher.
Den Prompt zusammenbauen
Answer only from the excerpts below. Cite every claim as [n].
If the excerpts do not contain the answer, reply "not in the docs".
[1] (manual §4.2) Torque the M8 bolt to 12 Nm after lubrication...
[2] (manual §7.1) Do not re-use stretch bolts...
Question: what torque spec applies to the M8 bolt?
Zitate lassen Nutzer verifizieren, statt zu vertrauen. Der Ausgang „nicht in den Docs“ zählt: Ohne ihn erfindet das Model eine Antwort, statt am Format zu scheitern.
Hybride Suche und Reranking
Reine Vektorsuche verpasst exakte Strings — Fehlercodes, Funktionsnamen, Teilenummern —, die Stichwortsuche nagelt. Hybrid = BM25 + Vektoren, verschmolzen mit Reciprocal Rank Fusion; es ist nahezu gratis und zuverlässig besser als jedes allein.
Für Qualität fügen Sie einen Reranker hinzu: 20–50 Kandidaten abrufen, mit einem Cross-Encoder (die bge-reranker-Familie — dieselbe multilinguale Geschichte wie bge-m3) auf die Top 5 reranken. Der Reranker liest Chunk und Abfrage zusammen, was ein Ein-Vektor-Embedder nicht kann. Billiges Rezept, das die meisten Bedürfnisse abdeckt: hybrid 25 abrufen, auf 5 reranken.
Diagnose: Retrieval-Fehler oder Generierungs-Fehler?
Bevor Sie irgendetwas tunen, beantworten Sie eine Frage: Wurde der richtige Chunk abgerufen?
- Drucken Sie die Top-k-Chunks einer scheiternden Abfrage, mit Scores.
- Ist der antworttragende Chunk nicht in der Liste, ist es ein Retrieval-Problem — Chunken oder Embeddings reparieren oder k erhöhen.
- Ist er in der Liste, aber die Antwort ist falsch, ist es ein Generierungs-Problem — größeres Model, besserer Prompt, reranken, um Distraktoren herauszudrängen.
Dieser eine Test spart Stunden damit, die falsche Hälfte der Pipeline zu tunen.
Fehlermodi
| Symptom | Ursache | Fix |
|---|---|---|
| Richtige Docs abgerufen, falsche Antwort | Distraktoren im Top-k | reranken; k verkleinern |
| Antwort existiert in den Docs, taucht aber nie auf | Top-k-Aushungerung oder schlechte Grenzen | k erhöhen; nach Überschriften chunken |
| Selbstsichere, aber veraltete Antworten | Index älter als die Docs | bei Änderung neu indexieren |
| Koreanische Abfragen verpassen englische Docs | einsprachige Embeddings | bge-m3 |
| Zitate zeigen auf den falschen Chunk | Chunk verlor seine Quell-Metadaten | Quelle + Überschrift pro Chunk speichern |
Messen, nicht viben
Halten Sie ein Golden Set aus 20–50 echten Fragen mit bekannten Antworten. Bewerten Sie zwei Zahlen getrennt: Retrieval-Trefferrate (ist der richtige Chunk im Top-k?) und Antwortkorrektheit. Verfolgen Sie sie nach jeder Änderung an Chunk-Größe, Embedder oder k. RAG-Arbeit ohne Golden Set ist Astrologie.
Wann stattdessen fine-tunen
Fine-Tuning ändert Verhalten — Format, Stil, Terminologie. Es ist ein schlechter Weg, Fakten hinzuzufügen: Das Model kann nicht zitieren, worauf es getunt wurde, und frische Dokumente bedeuten einen frischen Trainingslauf. Faustregel: Fakten → RAG, Verhalten → LoRA, beides erst, wenn Sie Prompting ausgereizt haben: Fine-Tuning.
Lokale Stacks, kleinste zuerst
Open WebUI liefert Dokument-Upload und hybrides Retrieval eingebaut mit — starten Sie dort, und Sie schreiben womöglich nie Pipeline-Code. Der ehrliche Kern von RAG sind ~50 Zeilen; schreiben Sie ihn einmal, damit Sie wissen, was die Frameworks tun:
chunks = chunk_by_header(read("manual.md"), max_tokens=400, overlap=60)
index = [(embed(c), c) for c in chunks] # einmal erledigt
def answer(question):
hits = topk_by_cosine(embed(question), index, k=5)
ctx = "\n\n".join("[{}] {}".format(i, c) for i, (v, c) in enumerate(hits))
prompt = TEMPLATE.replace("{ctx}", ctx).replace("{q}", question)
return chat(prompt) # ein kleines lokales Model genügt
Greifen Sie zu LlamaIndex oder LangChain, wenn Sie Berechtigungen, geplante Aktualisierung oder Evaluations-Rahmen brauchen — nicht für die Schleife oben. Für koreanische Dokumente speziell sind mehrsprachige Embeddings (bge-m3) nicht optional, und überschriftenbasiertes Chunken sollte die koreanische Dokumentstruktur (장/절-Überschriften) genauso respektieren wie ##-Überschriften in Markdown.
Verwandt: Fine-Tuning · Prompting · Strukturierte Ausgabe · CPU-Inferenz