Zum Inhalt springen
Abdullah Al Rafi

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
  1. Überblick
  2. Problem
  3. Rahmenbedingungen
  4. Vorgehen und Architektur
  5. Ergebnisse
  6. Was ich heute anders machen würde

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 localStorage und 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 .txt und .pdf werden 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

  1. React + Vite auf Cloudflare

    Client

    Das Hugging-Face-Token bleibt im localStorage des Browsers

  2. POST /rag/query · Datei + Frage + Bearer-Token

    FastAPI-Eingang

    API

    Rate Limit · nur .txt/.pdf · Größen- und 1.000-Zeichen-Grenze

  3. Textextraktion

    Extraktion

    pypdf für PDFs, UTF-8 für Text, nur im Arbeitsspeicher

  4. 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

  5. Top-k-Ähnlichkeitssuche

    Abruf

    k von 1 bis 10, pro Anfrage wählbar

  6. Fundierte Antwort

    Generierung

    HF Chat Completion · Temperatur 0 · max. 512 Tokens

  7. gestreamte Antwort

    Antwort, dann Metriken

    Client

    Tokens, Generierungszeit und Tokens pro Sekunde

Abb. 1 · Ablauf einer einzelnen Frage. In keinem Schritt wird etwas auf die Festplatte geschrieben.
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.