Alle Projekte

RAG-Wissensdatenbank

pacod-kb

Dateien und Webseiten in versioniertes, durchsuchbares Wissen verwandeln — mit einem stabilen Vertrag für die Modelle, die es lesen.

pacod-kb nimmt Dateien und Webseiten auf, wandelt sie in kanonisches Markdown um, indexiert sie für hybride semantische und Schlüsselwortsuche, bewahrt jede Version auf und liefert nachgelagerten Modellen Chunks mit einem stabilen Metadatenvertrag. Es läuft allein auf der CPU.

  • Apache-2.0
  • Version 1.0.0
  • Stabil

Funktionen

  • Alles aufnehmen

    PDF, DOCX, HTML, Markdown, Text und JSON — hochgeladen, in einen Ordner gelegt, von einer URL abgerufen oder auf Änderungen überwacht.

  • Kanonisches Markdown

    Strukturbewusste Überschriften für Rechtstexte in acht Sprachen, wissenschaftliche Arbeiten und Artikel; Boilerplate wird herausgefiltert.

  • Inhaltssicherheit

    Ein mehrstufiger Scan auf Prompt Injection und Spam blockiert Dokumente oder stellt sie unter Quarantäne, bevor sie indexiert werden; Ergebnisse werden als nicht vertrauenswürdige Daten abgegrenzt.

  • Hybride Suche

    Dichte BGE-M3-Vektoren in Qdrant plus BM25-Schlüsselwörter, zusammengeführt per Reciprocal Rank Fusion.

  • Jede Version bleibt erhalten

    Abgelöste Versionen bleiben über Abfragen zu einem Stichtag durchsuchbar; ein Git-Archiv zeichnet jede Änderung auf.

  • Kalibrierte Relevanz

    Ein optionaler Cross-Encoder-Reranker liefert einen kalibrierten Relevanzwert von 0–1.

  • Nur CPU

    Embedding, OCR und Suche bleiben allein auf der CPU reaktionsschnell, auch während Dokumente aufgenommen werden.

  • UI und CLI

    Eine Web-UI und ein CLI für Dokumente, Projekte, Jobs, KI-Nutzung und -Kosten sowie Mitglieder mit den Rollen Viewer, Editor und Admin.

Einordnung

Diagramm: wie die pacod-Projekte zusammenpassen LLM-Aufrufe Tippe auf ein Projekt, um zu sehen, womit es verbunden ist.

Warum es das gibt

Der größte Teil der Arbeit in Retrieval-Augmented Generation passiert, bevor ein Modell ein Wort liest: Dokumente hineinbekommen, sie sauber halten, die richtige Passage finden und dem Modell sagen, woher sie stammt. pacod-kb ist genau dieser Teil, gebaut als eigener Dienst.

Drei Anforderungen haben es geprägt. Antworten müssen zitierbar sein, deshalb trägt jeder Chunk seinen Überschriftenpfad und Byte-Offsets in einen stabilen Markdown-Text. Quellen ändern sich, deshalb wird nichts überschrieben: Eine neue Version löst die alte ab, die nach Datum durchsuchbar bleibt. Und aufgenommener Text ist für ein Modell nicht vertrauenswürdige Eingabe, deshalb wird er vor dem Indexieren auf Prompt Injection gescannt und bei der Ausgabe als Daten eingezäunt.

Von Dokumenten zu Antworten

Ein Dokument nimmt denselben Weg, ob es hochgeladen, im Dropzone-Ordner abgelegt, von einer URL abgerufen oder von einer überwachten Webquelle erneut geholt wurde:

  1. Erfassen. Der SHA-256 der Quellbytes ist die Dokument-ID, dieselbe Datei wird also nie zweimal aufgenommen.
  2. Parsen. PDF (docling, mit optionaler OCR für gescannte Seiten), DOCX, HTML, Markdown, Text und JSON werden zu Markdown mit YAML-Front-Matter.
  3. Sprache prüfen. Englisch, Deutsch, Bulgarisch, Russisch, Serbisch, Mazedonisch, Kroatisch und Slowenisch werden angenommen; andere Sprachen werden abgelehnt.
  4. Scannen des deterministischen Parsing-Ergebnisses auf Prompt Injection und Spam, bevor irgendein LLM den Text sieht.
  5. Umwandeln (optional). Ein LLM-Durchlauf über pacod-gw räumt das Markdown auf. Eine abgeschnittene, verweigerte oder leere Antwort lässt das deterministische Parsing-Ergebnis stehen.
  6. Bereinigen und strukturieren. Ein Rauschfilter entfernt Seitenzahlen, laufende Kopfzeilen und Boilerplate; die Überschriften-Erkennung erfasst die Struktur von Rechtstexten, Papers und Artikeln.
  7. Speichern von document.md, einem Manifest und einem Snapshot der Quelle.
  8. Indexieren. Überschriftenbewusste Chunks von bis zu 500 Tokens werden mit BGE-M3 in Qdrant eingebettet und in einen Schlüsselwortindex geschrieben, in der eigenen Collection des Projekts.

Architektur

pacod-kb besteht aus zwei Diensten, kb-pipeline und kb-ui, dazu dem Modell und dem Speicher, die sie nutzen:

Teil Aufgabe
Pipeline-API (kb-pipeline, :8000) Aufnahme, Indexierung, Suche, Jobs, Einstellungen; der einzige Teil, der das KI-Gateway aufruft
Web-UI (kb-ui, :8080) Anmeldung über pacod-id, Sitzungen, Mitglieder, die Seiten; ruft die Pipeline mit einem API-Schlüssel auf
BGE-M3 Embeddings, im eigenen Prozess der Pipeline oder über einen gemeinsamen Embeddings-Dienst
Qdrant Dichte Vektoren, eingebettet in die Pipeline oder als Server, den auch andere Produkte nutzen können
SQLite FTS5 oder PostgreSQL Der BM25-Schlüsselwortindex
Dateisystem oder PostgreSQL Dokumente, Manifeste und das Ledger der KI-Nutzung und -Kosten

Die Trennung ist gewollt. Die Pipeline parst nicht vertrauenswürdige Dokumente und ruft beliebige URLs ab, deshalb erhält sie keines der Geheimnisse von pacod-id; die UI bekommt den Gateway-Schlüssel nie zu sehen. Alle Container hängen an einem gemeinsamen Docker-Netzwerk, pacod-net, die Trennung beruht also auf Geheimnissen, nicht auf Netzwerken: Jeder Container erhält eine explizite Liste von Umgebungsvariablen, und ein Test prüft sie.

Jeder Aufruf aus der UI trägt X-KB-Actor, die ID der angemeldeten Person. Die Pipeline vertraut ihm nur zusammen mit dem API-Schlüssel, vermerkt ihn an Jobs, Einstellungsänderungen und Nutzung und reicht ihn an das Gateway weiter. Löscht pacod-id ein Konto, ersetzt kb diese ID überall durch deleted-user, bevor es die Löschung bestätigt.

Sicherheitsmodell

Inhaltssicherheit. Der Scan normalisiert den Text, um Verschleierung auszuhebeln, gleicht ihn mit einem mehrsprachigen Regelwerk ab und liefert eines von drei Urteilen:

  • block: ein einzelner schwerwiegender Injection- oder Verschleierungsbefund oder ein hoher Bedrohungswert. Die Aufnahme schlägt fehl, nichts wird gespeichert.
  • quarantine: Das Dokument wird mit Status review behalten und nie indexiert. Spam allein führt nie zu einer Blockierung.
  • clean: Das Dokument wird indexiert.

Eine optionale zweite Meinung eines LLM über das Gateway wird nur eingeholt, wenn die Regeln nicht blockiert haben; sie hat 15 Sekunden Zeit und lässt im Fehlerfall durch. Als sicher bekanntes Material kann den Scan mit kb ingest --trusted überspringen. Bei der Ausgabe umschließt /query jeden Chunk mit einer Markierung für nicht vertrauenswürdige Daten.

Abrufen. WEB_FETCH_ALLOW_PRIVATE=false, für die Produktion empfohlen, verweigert URL-Aufnahmen, die zu privaten Adressen auflösen — ein Schutz gegen Server-Side Request Forgery.

Personen. Die UI meldet Personen über pacod-id mit OIDC und PKCE an und hält die Tokens in einer verschlüsselten serverseitigen Sitzung; das Cookie trägt nur ein zufälliges Geheimnis. Rollen kommen aus dem Autorisierungs-Feed von pacod-id. Ist die Kopie der UI älter als 60 Sekunden, antworten die Seiten mit 503, statt zu raten, wer was darf.

Rolle Darf
viewer Lesen und suchen
editor Dokumente aufnehmen, neu indexieren und löschen; Projekte verwalten
admin AI Management, Prompts, Laufzeitschalter, Mitglieder

Gemessene Suche

Eine Abfrage läuft gegen Qdrant und den Schlüsselwortindex, und die beiden Rankings werden per Reciprocal Rank Fusion (k = 60) zusammengeführt. Datums- und Verlaufsfilter greifen innerhalb beider Suchen. Ein optionaler Cross-Encoder, BAAI/bge-reranker-v2-m3, ordnet die besten Kandidaten neu und liefert einen Relevanzwert von 0 bis 1, der über Abfragen hinweg vergleichbar ist, sodass min_score Nicht-Antworten aussortieren kann.

Die Standardwerte stammen aus einem Evaluierungs-Harness: 14 Dokumente in sechs Sprachen (DSGVO, die Richtlinie zum Datenschutz bei Polizei und Justiz, vier Verfassungen, Wikipedia-Artikel) und 92 handgeschriebene Abfragen — Paraphrasen, exakte Begriffe, sprachübergreifende, muttersprachliche und unbeantwortbare. Gemessen auf der CPU eines Entwicklungsrechners — die Doku betont, dass das keine Produktionszahlen sind:

  • Ohne Reranker liegt der richtige Abschnitt bei 98,7 % der beantwortbaren Abfragen unter den ersten fünf; R@1 beträgt 0,83, bei etwa 50 ms.
  • Mit Reranker steigt R@1 auf 0,91, bei etwa 2 s pro Abfrage.
  • Unter hoher Aufnahmelast wartete eine Suche früher etwa 24 s auf einen ganzen Embedding-Batch. Heute haben Abfragen Vorrang vor der Aufnahmearbeit und warten höchstens auf einen kleinen Mikro-Batch: etwa 0,4 s (p50), wenn Aufnahme-Jobs nacheinander laufen, wie die Pipeline sie ausführt.
  • Nach Länge sortierte Batches machen das Einbetten bei der Aufnahme etwa 2,2× schneller, bei unveränderten Vektoren.

Der Reranker ist standardmäßig aus: Sein Gewinn blieb auf den zurückgehaltenen Abfragen knapp unter der Schwelle, und seine Latenz muss erst auf der Produktions-CPU bestätigt werden.

Was ein Modell bekommt

POST /query liefert Chunks unter einem stabilen, dokumentierten Metadatenvertrag:

{"query": "maximum fine for a data breach", "top_k": 5, "as_of": "2025-12-30"}

Die Antwort ist {"results": [...], "reranked": false, "score_kind": "rrf"}. Jedes Ergebnis enthält:

Feld Wofür
chunk_id Eine stabile ID, die die Überschriftenstruktur kodiert
source_id Die Dokument-ID: SHA-256 der Quellbytes
heading_path Ein Brotkrumenpfad wie ["Chapter 1", "§ 5"], für Zitate und Abschnittsfilter
byte_start, byte_end UTF-8-Offsets in den gespeicherten Markdown-Text
valid_until Wann diese Version abgelöst wurde, oder null, solange sie aktuell ist
text Der eingebettete Text, als nicht vertrauenswürdige Daten eingezäunt
score, rrf_score, rerank_score Der Sortierwert und seine Bestandteile

Jedes Dokument hat außerdem ein Manifest: Titel, Quelle, Dokumenttyp, Status, Projekt, der Parser oder das Modell, das den Text erzeugt hat, und ein Lebenszyklus-Protokoll (ingested, superseded, security_flagged, llm_usage und weitere).

Versionen statt Überschreiben

Eine mit --supersedes aufgenommene neue Version oder eine geänderte überwachte Quelle markiert die alte mit valid_until — im Front Matter, im Manifest, im Schlüsselwortindex und in den Vektor-Payloads zugleich. Suchen liefern standardmäßig aktuelle Chunks; as_of liefert, was an einem bestimmten Tag gültig war; include_historical liefert alles. Kehrt eine überwachte Seite zu einem früheren Stand zurück, wird die frühere Version wieder in Kraft gesetzt. Ablösungen und Neuaufbauten verwenden die Vektoren unveränderter Chunks wieder.

Im Betrieb

kb folgt der gemeinsamen Netzwerkkonvention von pacod: Jeder Container hängt an einem Docker-Netzwerk, pacod-net. docker-compose.yml enthält die eigenen Dienste von kb, kb-pipeline und kb-ui. docker-compose.standalone.yml enthält die Kopien der gemeinsamen Komponenten, die Docker-Compose-Profile einschalten: Qdrant, PostgreSQL, pacod-id mit seiner Datenbank neben der von kb, einen Mail-Catcher und ein Entwicklungs-Gateway, dessen Mock-Provider alles auf der Maschine halten. make up lädt beide Dateien. Von jeder gemeinsamen Komponente kann auf einer Maschine nur eine Kopie laufen. In Produktion verweist man kb auf ein echtes pacod-gw, stellt pacod-id hinter TLS und lässt die Entwicklungsprofile weg.

pacod-demo, live unter demo.pacod.io, betreibt kb zusammen mit den anderen pacod-Projekten. Es lädt nur die App-Datei von kb: kb legt seine Datenbank im gemeinsamen PostgreSQL und seine Collections im gemeinsamen Qdrant ab, bettet über den gemeinsamen Embeddings-Dienst ein und stellt seine UI hinter dem gemeinsamen Caddy-Edge bereit.

  • Speicher. KB_STORAGE_BACKEND ist filesystem oder database; ein Wechsel migriert keine Dokumente.
  • Daten. Die Datenverzeichnisse sind Variablen (KB_ROOT, KB_MODEL_CACHE, KB_QDRANT_DATA, KB_POSTGRES_DATA). Ein Name ohne Schrägstrich ist ein Docker-Volume; unter macOS verwendet make setup Volumes.
  • CPU. TORCH_NUM_THREADS=0 folgt der CPU-Quote des Containers. EMBED_BATCH_TOKENS (1024) begrenzt, wie lange eine Abfrage hinter einer Aufnahme warten kann.
  • Modelle offline. Einmal mit Netzwerkzugang laufen lassen, um den Modell-Cache zu füllen, dann HF_HUB_OFFLINE=1 setzen.
  • Gemeinsame Modelle. Mit EMBEDDINGS_URL bettet die Pipeline über den gemeinsamen Embeddings-Dienst ein, statt BGE-M3 selbst zu laden. Die Vektoren sind dieselben, es wird also nichts neu eingebettet, und die Pipeline braucht etwa 2 GB weniger Arbeitsspeicher. RERANKER_URL tut dasselbe für den Reranker.
  • Gemeinsames Qdrant. Auf einem Qdrant, das auch andere Produkte nutzen, zeigen und berühren die Qdrant-Seiten der UI nur die Collections von kb (KB_QDRANT_COLLECTION_PREFIX, Standard kb_).
  • Laufzeitschalter. Das Dashboard schaltet Inhaltsscan, KI-Umwandlung und Sprachprüfung ohne Neustart ein und aus; beim nächsten Start gelten wieder die Umgebungsvariablen.
  • KI. Admins wählen Modelle und bearbeiten Prompts in AI Management. Jeder Aufruf trägt eine opake Zuordnung (Funktion, Job, Projekt, Person), und kb erfasst die Tokens mit den Kosten des Gateways. Ist kein Gateway konfiguriert, ist KI aus, und sonst geht nichts kaputt.

Backups umfassen KB_ROOT oder die PostgreSQL-Datenbank von kb, den Qdrant-Speicher (oder man baut ihn mit kb rebuild --all-projects neu auf), die Datenbank von pacod-id im selben PostgreSQL samt ihrem Schlüsselverschlüsselungsschlüssel und .env.

Grenzen und weiterführende Lektüre

  • Pipeline und UI laufen jeweils mit einem einzigen Worker: Das eingebettete Qdrant hält eine Dateisperre, und die Sitzungsverwaltung der UI setzt einen Prozess voraus.
  • Ein Reranker im eigenen Prozess der Pipeline verdoppelt ungefähr ihren Arbeitsspeicher; mit RERANKER_URL lädt die Pipeline keinen.
  • Serbisch wird angenommen, aber die Spracherkennung hat kein serbisches Profil, sodass serbischer Text falsch erkannt oder abgelehnt werden kann.
  • Für die nächste Neuindexierung dokumentiert, nicht ausgeliefert: BGE-M3-Sparse-Vektoren für die Schlüsselworthälfte, Aufräumarbeiten am Chunker und der Neuaufbau in einer Schatten-Collection vor dem Umschalten.

Mehr dazu:

  • Architektur: Container, Anmeldung, das Gateway, wer gehandelt hat.
  • Metadatenvertrag: jedes Chunk- und Manifest-Feld.
  • Retrieval-Evaluierung: die vollständigen Zahlen und die Entscheidungen hinter den Standardwerten.
  • Runbook: Entwicklung, Produktion, Betrieb und Fehlersuche.
  • Migrationen: das Upgrade einer bestehenden Installation, auch der Umzug auf pacod-net.

Tech-Stack

Geschrieben in
  • Python
Tech-Stack
  • FastAPI
  • Qdrant
  • BGE-M3
  • BM25
  • SQLite
  • PostgreSQL
  • HTMX
  • Docker

Schnellstart

Mit Docker starten
make setupmake upopen http://localhost:8080/kb
Entwickeln und evaluieren
uv syncmake checkmake eval

Funktioniert mit

  • pacod-id

    Personen melden sich über pacod-id an; die Rollen Viewer, Editor und Admin werden dort vergeben.

  • pacod-gw

    Der optionale LLM-Bereinigungsschritt läuft über das Gateway, das jeden Aufruf bepreist und zuordnet.

  • pacod-ap

    Läuft kb neben pacod-ap, kann es über dessen gemeinsamen Embeddings-Dienst einbetten und reranken, mit denselben BGE-M3-Vektoren.

Lizenz

Apache-2.0