rag-sanitizer v0.2: escáner de corpus RAG pre-ingestión con embeddings reales y CLIP
Detecta documentos poison en un corpus RAG antes del chunk+embed, no después de la recuperación. v0.2 añade embeddings reales (MiniLM) y detección multimodal (CLIP).
El problema
La mayoría de la defensa contra envenenamiento de RAG opera post-retrieval o en inferencia. Pero el corpus se puede envenar antes de la ingesta: un documento insertado con texto que imita el estilo del corpus (semantic mimicry), sustituye entidades conocidas (entity-swap), o lleva una imagen cuyo contenido contradice el texto (visual poison). Una vez embebido, el daño ya está en el vector store.
rag-sanitizer cubre ese tramo: un escáner determinista y barato que marca o pone en cuarentena el corpus antes de chunk + embed.
Qué hace
Tres detectores, combinables:
-
Semantic mimicry: mide la distancia del documento al centroide del perfil de confianza en espacio de embeddings. Umbral
k·sigma. -
Entity-swap: marca entidades que no aparecen en el perfil conocido del usuario (cierra el caso de una sola palabra, p.ej.
Madrid → Beijing). - Multimodal (CLIP): compara el texto del documento con su imagen declarada en espacio CLIP; si la similitud cae por debajo del umbral, sospecha de visual poison.
Cómo funciona (v0.2)
# install
pip install -e '.[real-embeddings,multimodal]'
# fast suite (Dummy + heurístico, sin descargar modelos)
rag-sanitizer fixtures/corpus.jsonl --embedder dummy
# batería real
rag-sanitizer fixtures/corpus.jsonl --embedder real --multimodal clip
El reporte es por documento: CLEAN / SUSPECT / POISON, con las razones. La salida va por --out (.json o .md); el corpus es un dir, un .jsonl, o un .txt/.md.
Resultados medidos (sin inflar)
- Batería
real_embeddings(MiniLM + CLIP, contrasentence-transformers/torch): 22 tests pasan. - En clone limpio sin extras pesados: 15 tests fast + 7 skipped (los
real_embeddingsse saltan).ruff checkyruff format --checklimpios;bandit0 issues. - CLIP carga el modelo con
revisionpinneada (commit3d74acf9...deopenai/clip-vit-base-patch32), así la mitigación de B615 es el pin, no un#nosec.
Lo que queda abierto (honestamente)
Documentado en KNOWN_ISSUES.md, no oculto:
- KI-9: MiniLM real no separa gibberish fluido de prosa real cuando la longitud es similar. No te fíes de mimicry para ese vector de ataque concreto; combínalo con entity-swap.
- KI-10: el umbral de CLIP (0.25) está calibrado sobre imágenes sintéticas de 64px; el margen es estrecho (~0.03). Recalíbralo contra tu corpus real antes de producción.
-
Perfil pequeño: el detector de mimicry exige
n >= 10documentos de confianza; con menos, el veredicto llevalow_confidencey el scanner lo señala enScanReport.warningsen vez de emitir un veredicto silencioso.
Gobernanza: el bug lo encontró la auditoría, no el self-report
El ciclo siguió implementa → audita en clone limpio → corrige → re-audita → aprueba → merge. En la auditoría independiente (Claude, sobre SHA 22db1fb) apareció un bug real: tras corregir sigmoid(logit) (que saturaba a 1.0 para todos los pares) por coseno escalado por temperatura en clip_embedder.py, el umbral de ClipMultimodalDetector se había quedado en 0.5 y con la nueva escala (match ~0.27–0.32, mismatch ~0.16–0.24) habría marcado SUSPECT prácticamente cualquier imagen. Se recalibró a 0.25, se añadió el test de caso positivo que faltaba, y se reverificó. Ese es el valor de la auditoría externa: atrapa lo que el self-report no ve.
Stack
| Componente | Uso |
|---|---|
| Typer | CLI |
| sentence-transformers (MiniLM) | embeddings reales |
| transformers + torch | CLIP |
| ruff + bandit | calidad y SAST |
Enlaces
- Repo: https://github.com/amurlaniakea/rag-sanitizer
- Tag v0.2: https://github.com/amurlaniakea/rag-sanitizer/releases/tag/v0.2
- KNOWN_ISSUES: https://github.com/amurlaniakea/rag-sanitizer/blob/main/KNOWN_ISSUES.md
Licencia: AGPL-3.0-or-later. Autor: Pedro Sordo Martínez.
This article was originally published by DEV Community and written by Fenix.
Read original article on DEV Community