Dokki: eine RAG-Pipeline in drei Planes
PDF/eBook rein, geerdete Antworten mit Quellen raus — aufgeteilt in Data, Control und Query Plane auf dem bestehenden MJTHD-Stack.
Dokki ist eine RAG-Pipeline: Dokumente — PDFs, eBooks, interne Notizen — gehen rein, und raus kommen LLM-Antworten, die gegen genau diese Quellen geerdet sind und sie zitieren. Kein Halluzinieren über Wissen, das nie im Korpus stand.
Das Interessante ist nicht das LLM, sondern alles drumherum: Wie kommt ein Dokument verlässlich in einen durchsuchbaren, versionierten Zustand — und wie verhindert man, dass ein kaputter Import die Produktion vergiftet? Dokki beantwortet das mit drei NestJS-Microservices, geschnitten nach Verantwortung.
Drei Services, drei Planes
| Service | Plane | Zustand | Verantwortung |
|---|---|---|---|
| Ingest | Data Plane | zustandslos, skalierbar | Dokument → Blob → Hash → parsen → chunken → embedden → Vektoren |
| Knowledge | Control Plane | Single Source of Truth | Registry, Dedup, Versionen, Snapshots, Quality Gate, Promotion/Rollback |
| RAG / Inference | Query Plane | zustandslos | Frage → embedden → Top-k aus aktivem Snapshot → Prompt → Antwort mit Quellen |
Die Trennung ist die zentrale Entwurfsentscheidung. Der Ingest produziert nur Chunks und Zahlen — er entscheidet nie, ob ein Ergebnis „gut" ist. Diese Entscheidung gehört allein dem Knowledge Service. Und der RAG-Service ist reine Lese-Seite: er fragt immer nur den aktiven Stand ab.
Wie es zusammenhängt
Entkoppelt wird über NATS JetStream: Der Ingest-Worker hört auf
ingest.requested, meldet ingest.completed zurück, und eine erfolgreiche
Promotion sendet snapshot.activated, woraufhin der RAG-Service seinen Cache
invalidiert. Jede Collection (ein abgegrenzter Wissenskorpus) bekommt einen
eigenen Subject-Namespace mit eigenem Token.
Der Stack
Dokki erfindet keine Infrastruktur, sondern setzt auf das, was im MJTHD-Cluster ohnehin läuft:
- CNPG / pgvector — Vektoren und Metadaten, HNSW-Index auf
halfvec - NATS JetStream — Event-Bus, 3-Replica, Per-Collection-Token
- MinIO — Original-Blobs, content-adressiert über ihren Hash
- Ollama (
bge-m3) — Embeddings, als gemeinsames Deployment für Ingest und RAG - Claude — das Antwort-LLM, mit nativer Citations API für die Quellen
- k3s + ArgoCD — GitOps-Deployment wie der Rest des Stacks
Anthropic hat keine Embeddings-API — deshalb übernimmt Ollama das Text-zu-Vektor, und Claude ausschließlich die Antwortgenerierung. Zwei verschiedene Jobs, zwei verschiedene Modelle.
Die eine Idee, die alles trägt
Re-Ingest erzeugt nie ein Update, sondern immer eine neue immutable Version. Produktiv wird ein Stand erst durch aktive Promotion.
Daraus folgt fast die gesamte restliche Architektur: Versionen, Snapshots, Rollback als reiner Pointer-Flip, und ein Quality Gate, das schlechte Stände gar nicht erst live lässt. Die nächsten Posts dieser Serie nehmen genau diese Teile auseinander — angefangen bei der Frage, warum ein schlechter Ingest Dokki nicht kaputt machen kann.