Forschung

rag-integrity-guard

Eine von Grund auf gebaute Integritätsschicht für RAG-Systeme. Jeder Chunk bekommt beim Ingest einen Fingerprint und wird zur Abfragezeit erneut geprüft, sodass ein kompromittierter Vektorspeicher, ein vergiftetes Dokument oder eine direkte Datenbankänderung erkannt wird, bevor sie das LLM erreicht, statt ihr blind zu vertrauen.

100 % Richtig-Positive · 0 % Falsch-Positive über drei Angriffsarten in einem öffentlichen Benchmark mit 500 Paaren

Angriffssimulator, der einen manipulierten Chunk in Echtzeit erkennt, mit einem Benchmark von 100 % Richtig-Positiven und 0 % Falsch-Positiven

Das Problem

Standard-RAG vertraut ohne Rückfrage allem, was der Vektorspeicher zur Abfragezeit zurückgibt. Wird dieser Speicher manipuliert, über eine kompromittierte Ingest-Pipeline, ein vergiftetes Dokument oder eine direkte Datenbankänderung, hat das LLM keine Möglichkeit, echten Kontext von der Fälschung eines Angreifers zu unterscheiden. Es antwortet einfach selbstbewusst mit dem, was es bekommen hat.

So funktioniert es

  • Jeder Chunk erhält beim Ingest eine doppelte Signatur: einen kryptografischen Hash (BLAKE3/SHA-256) des normalisierten Textes zur exakten Manipulationserkennung sowie ein L2-normalisiertes semantisches Embedding für den Ähnlichkeitsvergleich.
  • IntegrityGuardRetriever wraps any LangChain BaseRetriever as a drop-in and re-verifies every chunk it returns. A hash match is GÜLTIG, full stop. A hash miss where the embedding is still highly similar gets flagged SEMANTIC_DRIFT: the content changed just enough to stay plausible, which is exactly what a disguised poisoning attempt looks like. A hash miss where the embedding has diverged too is the blunter HASH_MISMATCH. Only VALID chunks are ever treated as trustworthy.
  • A configurable StrictnessMode decides what happens next. STRICT blocks the whole query, FILTER quietly drops the offending chunk, and LOG_ONLY returns everything annotated for audit.
  • A zero-dependency HashingEmbedder ships by default: deterministic hashed n-grams, no API key, no downloaded weights, runs anywhere at zero cost. A real OpenAIEmbedder swaps in when genuine semantic comparison is worth the cost.

Gemessen, nicht behauptet

Ich habe es gegen 500 echte Paare aus Aussage und Belegtext aus dem FEVER-Datensatz laufen lassen, aufgeteilt in eine unauffällige Baseline und drei Angriffsarten: exakte Mutation, Einschleusen von Prompt-Injection-Payloads und vollständiger Austausch durch semantische Drift.

Benchmark-Dashboard mit 100 % Richtig-Positiv-Rate, 0 % Falsch-Positiv-Rate und einer Aufschlüsselung der Erkennung nach Angriffsart

Insgesamt: 100 % Richtig-Positiv-Rate, 0 % Falsch-Positiv-Rate, bei einem durchschnittlichen Prüf-Overhead von 2,73 ms (7,07 ms p95) mit dem kostenlosen Standard-Embedder. Das ist schnell genug, um standardmäßig im Retrieval-Pfad zu liegen, und nicht als Schalter, den man beim ersten Lastanstieg abschaltet.

One caveat worth stating plainly: the 100/0 numbers aren't a probabilistic score, they're a property of hash verification. Any byte-level change fails the hash, by construction, so that part was never really in doubt. The interesting number is the HASH_MISMATCH vs. SEMANTIC_DRIFT split. A threshold-sensitivity sweep shows the zero-cost HashingEmbedder struggles specifically with the heimtückischste attack, a wholesale topical swap, because it's reading lexical n-gram overlap rather than meaning. A real semantic embedder would probably make that number worse before it gets better, and that's the part that actually matters for a poisoning defense to get right.

So sieht es aus

Zur Bibliothek gehört ein Live-Dashboard. Beispielfakten einlesen, eine Abfrage starten, dann einen Angriff auf einen eingelesenen Chunk auslösen und zusehen, wie der Guard ihn in Echtzeit einordnet.

Oberfläche des Angriffssimulators: ein Chunk vor und nach der Manipulation, mit dem resultierenden Integritätsurteil

Engineering, nicht nur ein Notebook

Es ist ein richtiges Monorepo: das Python-Kernpaket (Hasher, ein Embedder-Protokoll passend zu LangChains eigener Embeddings-Schnittstelle, der Fingerprint-Speicher, der LangChain-Retriever-Wrapper), ein FastAPI-Backend, ausgeliefert als Python-Serverless-Function auf Vercel, und ein Next.js-Dashboard. Die letzten beiden laufen bewusst als zwei unabhängige Vercel-Projekte, nachdem die Variante mit einem Projekt die Next.js-Framework-Erkennung still verloren hatte. 89 Tests, über 96 % Abdeckung, ruff und mypy im Strict-Modus, in CI bei jedem Push erzwungen, über Python 3.11 und 3.12.

Zurück zu allen Projekten