Dokumenten-RAG, das nichts speichert
Ein durchgängiges RAG-System für PDFs und Textdateien: eigenes Hugging-Face-Token mitbringen, gestreamte Antworten auf Basis der eigenen Datei erhalten – ohne Datenspeicherung.
Überblick
- Rolle
- Alleinentwickler
- Zeitraum
- März 2026 – heute
- Technologien
- FastAPI · LangChain · Hugging Face · pypdf · React · Vite · Cloudflare · Sentry
0
gespeicherte Uploads
Verarbeitung im Arbeitsspeicher, pro Anfrage
5 Min.
Vektorspeicher-Cache pro Dokument
Schlüssel aus Datei-Hash, Modell und Chunking
1–10
abgerufene Chunks, pro Anfrage wählbar
Auf dieser Seite
Problem
Die meisten „Chat mit deinem PDF“-Tools legen Ihr Dokument in einer Vektordatenbank ab, die Sie nicht kontrollieren. Ich wollte Fragen an Dokumente, bei denen man sein eigenes Hugging-Face-Token mitbringt, ein PDF oder eine Textdatei hochlädt und Antworten erhält, die nur auf dieser Datei beruhen – ohne dass auf dem Server etwas gespeichert wird.
Rahmenbedingungen
- Keine Speicherung. Uploads werden im Arbeitsspeicher verarbeitet und nie auf die Festplatte oder in eine Datenbank geschrieben.
- Eigenes Token. Der Browser hält das Hugging-Face-Token im
localStorageund sendet es mit jeder Anfrage als Bearer-Header. Der Server greift nur auf ein eigenes Token zurück, wenn eines konfiguriert ist. - Eine öffentliche Demo. Jeder kann sie aufrufen, deshalb gibt es Rate Limits, Fragen sind auf 1.000 Zeichen begrenzt, Dateien in der Größe beschränkt, und nur
.txtund.pdfwerden angenommen. Die API-Dokumentation ist durch ein privates Token geschützt. - Gehostete Inferenz. Embedding und Generierung laufen über Inferenz-Endpunkte von Hugging Face, daher muss wiederholte Arbeit am selben Dokument vermieden werden.
Vorgehen und Architektur
React + Vite auf Cloudflare
Client
Das Hugging-Face-Token bleibt im localStorage des Browsers
- POST /rag/query · Datei + Frage + Bearer-Token
FastAPI-Eingang
API
Rate Limit · nur .txt/.pdf · Größen- und 1.000-Zeichen-Grenze
Textextraktion
Extraktion
pypdf für PDFs, UTF-8 für Text, nur im Arbeitsspeicher
- Cache-Schlüssel: SHA-256(Datei) + Modell + Chunking + Token-Hash
Vektorspeicher wiederverwenden
Cache-Treffer
Fünf Minuten gültig
Zerlegen und einbetten
Cache-Fehltreffer
Rekursiver Splitter → HF-Embeddings → In-Memory-Speicher
Top-k-Ähnlichkeitssuche
Abruf
k von 1 bis 10, pro Anfrage wählbar
Fundierte Antwort
Generierung
HF Chat Completion · Temperatur 0 · max. 512 Tokens
- gestreamte Antwort
Antwort, dann Metriken
Client
Tokens, Generierungszeit und Tokens pro Sekunde
Beschreibung des Diagramms
Der React-Client sendet Datei, Frage und das Hugging-Face-Token an einen FastAPI-Endpunkt. Die API prüft Rate Limits und Eingabegrenzen, extrahiert den Text im Arbeitsspeicher und sucht einen gecachten Vektorspeicher über einen Hash aus Datei, Modell, Chunking-Einstellungen und Token. Bei einem Fehltreffer wird der Text zerlegt und eingebettet, bei einem Treffer der gecachte Speicher wiederverwendet. Danach werden die Top-k-Chunks abgerufen, eine fundierte Antwort mit Temperatur 0 erzeugt und gestreamt, gefolgt von Generierungsmetriken.
Entscheidung
Den Vektorspeicher im Arbeitsspeicher halten, hinter einem kurzlebigen Cache
Der InMemoryVectorStore jedes Dokuments wird fünf Minuten lang gecacht, mit höchstens 64 Einträgen. Der Cache-Schlüssel kombiniert einen SHA-256 der Datei, das Embedding-Modell, die Chunking-Einstellungen und einen Hash des Tokens. Folgefragen sparen sich das erneute Einbetten, und Nutzer teilen nie die Vektoren anderer.
Ebenfalls erwogen
- Eine gehostete Vektordatenbank wie pgvector oder PineconeGespeicherte Embeddings würden das Versprechen brechen, nichts zu speichern.
Entscheidung
Modelle über Hugging-Face-Inferenz mit dem Token der Nutzer ausführen
Die Namen der Embedding- und Generierungsmodelle sind Umgebungsvariablen, sodass sich beide ohne Codeänderung austauschen lassen.
Ebenfalls erwogen
- Embedding- und Generierungsmodelle selbst hostenEine kostenlose öffentliche Demo bräuchte dafür GPU-Hosting.
Entscheidung
Nur aus dem abgerufenen Kontext antworten
Der Prompt weist das Modell an zu sagen, dass es die Antwort nicht weiß, wenn sie nicht im Kontext steht. Die Generierung läuft mit Temperatur 0 und höchstens 512 Tokens.
Ebenfalls erwogen
- Das Modell eigenes Wissen einmischen lassenAntworten ließen sich nicht mehr am Dokument überprüfen.
Entscheidung
Tokens streamen, danach die Metriken anhängen
Der Stream endet mit einem kleinen Metadatenblock mit erzeugten Tokens, Generierungszeit und Tokens pro Sekunde, sodass die Oberfläche die Geschwindigkeit ohne weiteren Aufruf anzeigen kann.
Ebenfalls erwogen
- Eine separate Anfrage für MetrikenEin zusätzlicher Roundtrip, mit Zahlen, die getrennt von der Antwort berechnet werden.
Ergebnisse
0
gespeicherte Uploads
Text wird pro Anfrage im Arbeitsspeicher extrahiert und eingebettet
5 Min.
Cache-Lebensdauer pro Dokument
Folgefragen sparen sich das erneute Einbetten
2
CI-Prüfungen bei jedem Push
Backend mit pytest; Frontend mit unveränderlicher Yarn-4-Installation + Produktions-Build
Das System läuft unter rag.abdullahalrafi.com, der Quellcode liegt auf GitHub. Fehler gehen an Sentry, und Cache-Treffer und -Fehltreffer werden als Breadcrumbs protokolliert, sodass sich langsame Antworten auf erneutes Einbetten zurückführen lassen.
Was ich heute anders machen würde
- Tokens mit dem Tokenizer des Modells zählen. Tokens pro Sekunde werden derzeit durch Aufteilen an Leerzeichen geschätzt. Das unterschätzt Subword-Tokens und erschwert den Vergleich zwischen Modellen.
- Den Abruf an einem kleinen, annotierten Fragenset evaluieren und Chunk-Größe und Top-k daran ausrichten statt über Umgebungsvariablen.
- Mehr als einen Worker einplanen. Der Cache lebt in einem einzigen Prozess. Ein gemeinsamer, verschlüsselter Cache würde die Trefferquote erhöhen – zu einem echten Preis für das Versprechen, nichts zu speichern.