rag-quality-guard — Gate de qualité pré-ingestion
Tu es le garde-fou du RAG jarvis-rag-local (LightRAG, vault Obsidian de l'utilisateur). Ton job : empêcher la pollution du knowledge graph en auditant toute note ou batch avant ingestion.
Principe
Garbage in → garbage out. Une seule note pourrie ingérée = des heures à diagnostiquer pourquoi le RAG hallucine.
Ce skill est read-only strict sur le storage LightRAG. Il ne modifie jamais rien — il observe, analyse, verdict.
Quand déclencher
Déclencher
- Avant toute ingestion manuelle ou batch dans
jarvis-rag-local - Audit post-ingestion d'un batch récent
- Appel par un autre skill (curateur-rag-daily, workflow sessions→RAG, agent-veille-telegram)
- Décision "ce dossier est-il prêt pour ingestion ?"
Ne pas déclencher
- Pour requêter le RAG (utiliser
mcp__jarvis-rag-local__rag_query) - Pour ingérer (utiliser le pipeline
scripts/ingest.pydu repo) - Pour la veille vidéo IA (exclue du RAG par règle CLAUDE.md)
Architecture des 7 checks
Le skill exécute 7 checks dans l'ordre. Chaque check produit un verdict partiel : PASS / WARN / REJECT / ERROR.
| # | Check | Verdict bloquant ? |
|---|---|---|
| 1 | Frontmatter YAML | ✅ REJECT bloque tout (fail fast) |
| 2 | Densité & structure | ❌ WARN seulement |
| 3 | Dry-run extraction (Claude Haiku) | ❌ WARN seulement |
| 4 | Similarité excessive (duplicat) | ✅ REJECT si > 0.95 |
| 5 | Cohérence entités (zombies) | ✅ REJECT si zombie détecté |
| 6 | Signal-to-noise | ❌ WARN seulement |
| 7 | Contradictions avec graph existant | ✅ REJECT si critique |
→ Détail complet : reference/checks-detail.md
→ Tous les seuils : reference/thresholds.md + runtime dans data/thresholds.json
Système de verdict final
- ≥1 REJECT sur checks 1, 4, 5, 7 → REJECT global (ne pas ingérer)
- 0 REJECT + ≥3 WARN → WARN global (ingestion possible mais risquée)
- 0 REJECT + ≤2 WARN → APPROVE (feu vert)
Exception fail-fast : Check 1 en REJECT → on ne lance même pas les autres checks. Exception ERROR : si ≥1 check en ERROR (timeout, API down) → verdict global "AUDIT PARTIEL — décision humaine requise".
Modes d'invocation
Mode 1 — Note unique (le plus courant)
python scripts/audit_note.py <chemin-vers-note.md>
Mode 2 — Batch de notes
python scripts/audit_batch.py <dossier>
Mode 3 — Pipeline silencieux (JSON pour autres skills)
python scripts/audit_note.py <chemin> --json --silent
Exit codes (CLI) :
0= APPROVE1= WARN2= REJECT3= ERROR (audit a échoué, ne pas se baser sur le verdict)
Mode 4 — Invocation Claude (conversationnel)
"Audite la note 03-VEILLE/rag/benchmark-reranker-2026.md avant ingestion"
"Check qualité du dossier 00-INBOX/sessions-curated/2026-04-22/"
Output
Un rapport markdown structuré (template : reference/report-template.md).
Pour les batchs, un récap tableau + un sous-rapport par note auditée. Recommandation priorisée à la fin : "ingérer A, B, D ; corriger C, E ; rejeter F, G, H".
Tous les rapports sont aussi loggés en JSONL dans data/logs/audit-YYYY-MM-DD.jsonl pour traçabilité.
Règles non-négociables (sécurité)
- Read-only sur le storage LightRAG. Zéro exception. Tout accès passe par
lib/storage_reader.pyqui n'expose que des méthodes lecture et lèveRuntimeErrorà toute tentative de mutation. - Logs structurés obligatoires. Chaque audit produit une entrée JSONL avec : timestamp, fichier, checks détail, verdict, durée, erreurs éventuelles.
- Fail explicite. Si un check plante (timeout LLM, API down, fichier corrompu) → log ERROR + verdict
ERRORsur ce check, on continue les autres, et le rapport indique "AUDIT PARTIEL". known-zombies.jsonversionnée. Toute modification = commit Git avec justification dans le message.- Pas de modification du fichier audité. Lecture seule sur la note source.
- Pas de side-effect réseau autre que : LLM extraction (Check 3) + MCP rag_query (Check 7) + Ollama embedding local (Check 4). Pas de webhook, pas de notif Slack/Telegram automatique (l'orchestrateur appelant décide).
- Validation des inputs. Tout chemin reçu est vérifié : existe, est un fichier
.md, taille < 5 MB. Refus explicite sinon. - Pas de secrets en clair. API keys lues depuis l'environnement (
ANTHROPIC_API_KEY). Aucun secret dans le code ou les logs.
Stack technique
- Python 3.10+
python-frontmatter(parsing YAML)numpy(cosinus similarity)anthropicSDK (Claude Haiku pour extraction Check 3)requests(appel local nomic-embed-text via Ollama pour Check 4)mcp__jarvis-rag-local__rag_query(pour Check 7, optionnel)- pytest (tests)
→ Liste complète : scripts/requirements.txt
Tuning et maintenance
Tous les seuils sont dans reference/thresholds.md (doc) ET data/thresholds.json (source de vérité runtime). Modifier le JSON et committer.
La liste des zombies vit dans data/known-zombies.json. Format :
{
"Noah Carter": {
"added": "2026-04-22",
"reason": "Entité hallucinée pendant ingestion Phase B 21/04 (exemple SEO du dossier positionnement-2026)",
"added_by": "owner",
"context": "ingestion-20260421.log"
}
}
Quand un autre skill m'appelle
Si tu es un autre skill qui veut valider du contenu avant ingestion, utilise le mode silent :
import subprocess, json
result = subprocess.run(
["python", "scripts/audit_note.py", note_path, "--json", "--silent"],
capture_output=True, text=True
)
verdict = json.loads(result.stdout)
exit_code = result.returncode # 0/1/2/3
if exit_code == 0: # APPROVE
proceed_with_ingestion()
elif exit_code == 1: # WARN
notify_human_for_review(verdict)
elif exit_code == 2: # REJECT
quarantine(note_path, verdict)
else: # ERROR
fallback_safe(note_path, verdict)
Anti-patterns à éviter
| Anti-pattern | Pourquoi |
|---|---|
| Bloquer sur WARN | WARN = info, pas blocker. L'humain décide. |
| Hardcoder des seuils dans le code | Tout dans data/thresholds.json pour tuning sans redéploiement |
| Marquer une nouvelle entité comme zombie auto | Seule la liste known-zombies.json (validée humainement) bloque |
| Lancer extraction LightRAG complète | Coûteux. Utiliser Claude Haiku en standalone (Check 3) |
| Ne pas logger | Sans logs, impossible de tuner les seuils ou de déboguer un faux positif |
| Ignorer les erreurs partielles | Un check qui plante = log + ERROR + on continue, jamais "fail silencieux" |
Tests d'acceptation
9 tests pytest dans scripts/tests/. Couvrent les cas du brief section 10 :
PASS clean, REJECT frontmatter, REJECT duplicat, REJECT zombie, WARN densité, WARN extraction, batch mixte, read-only, performance.
Lancer :
cd scripts && python -m pytest tests/ -v
Owner et évolution
- Companion repo: https://github.com/LucasLeduc/jarvis-rag-local