Sirocco

RAG enterprise : le contrôle d'accès que tout le monde oublie

Piège flat-index, 3 patterns ACL (filtre métadonnées, index séparé, re-vérification source), safeguards.

4 min de lecture
RAG enterprise : le contrôle d'accès que tout le monde oublie

La quasi-totalité des projets RAG (Retrieval Augmented Generation) en entreprise commence par la même architecture naïve : un seul vector store (Qdrant, Pinecone, Weaviate) où tous les documents sont indexés avec un métachamp `user_id` ou `team_id`, et la requête applique un filtre `WHERE user_id = ?` au moment de la recherche. Cela fonctionne en démo, plait au PoC, et fuit en production dès qu'un développeur oublie d'appliquer le filtre sur un nouveau endpoint. Le contrôle d'accès dans un RAG enterprise est le sujet qu'on découvre en dernier et qui ruine les projets en premier ; il mérite d'être traité comme un problème de design d'architecture, pas comme une option de configuration. Trois patterns d'architecture coexistent et chacun fait des compromis différents. Le pattern 1, Metadata Filter, est celui décrit ci-dessus : un seul index, ACL sérialisée comme métadonnée Qdrant (`owner_list`, `groups`, `classification`), filtre obligatoire à la requête. C'est simple, dense, et efficace tant que tous les chemins de récupération passent par une bibliothèque commune qui force le filtre. Le risque structurel est réel : un nouveau endpoint qui appelle directement le SDK Qdrant sans le wrapper de l'équipe oublie le filtre, et toute la base devient lisible par tous les utilisateurs. La parade : interdire l'usage du SDK natif, exposer un client interne qui exige `user_context` en paramètre, et lint la base de code pour détecter les imports directs. Le pattern 2, Separate Vector Indexes, isole physiquement par groupe d'utilisateurs ou par tenant : un index Qdrant par équipe, par client, ou par niveau de classification. Le contrôle d'accès devient celui de la connexion à l'index lui-même — pas de filtre à oublier. Le pattern est secure by design mais a deux limites pratiques. D'abord, il scale mal au sharing fin : un document que trois équipes partagent doit être indexé trois fois, ce qui multiplie le coût de stockage et complique les mises à jour. Ensuite, les index Qdrant ont des coûts fixes (mémoire pour les graphes HNSW) qui rendent peu réaliste de faire un index par utilisateur. Le sweet spot est généralement un index par tenant ou par grand département. Le pattern 3, Re-check at Source, est le plus solide pour les données très sensibles. Le vector store sert uniquement de moteur de recherche sémantique : on récupère les `doc_id` candidats, puis on vérifie les droits à la source (base de données métier, SharePoint Graph, Notion API) avant de servir le contenu au LLM. Ce double passage coûte 200 à 500 ms supplémentaires par requête, mais il garantit que même si l'ACL stockée dans le vector store est obsolète — un employé licencié dont les droits n'ont pas encore été propagés — la source de vérité refuse l'accès. Pour les documents marqués C3+ ou équivalents, c'est la seule architecture défendable en audit. Les patterns ne s'excluent pas et peuvent être empilés. Une architecture solide combine pattern 2 pour la séparation grossière (un index par tenant ou par classification de sécurité), pattern 1 pour le filtrage fin à l'intérieur d'un index, et pattern 3 pour la vérification finale sur les documents sensibles avant envoi au LLM. C'est plus complexe à coder qu'un Qdrant nu, mais c'est aussi ce qui rend la sécurité présentable en audit ANSSI ou ISO 27001. Les garde-fous au-delà du contrôle d'accès méritent autant d'attention. Premièrement, l'historisation immuable de toutes les requêtes utilisateur : qui a demandé quoi, quels documents ont été retournés, quelle réponse a été générée, le tout dans un journal append-only signé. Cela protège contre l'exfiltration progressive (un employé qui pose mille questions ciblées pour reconstruire un document confidentiel) et permet l'audit en cas de fuite avérée. Deuxièmement, la redaction NER (Named Entity Recognition) avant envoi au LLM : un pipeline spaCy ou Presidio masque automatiquement les numéros de téléphone, IBANs, emails et noms propres marqués personnels dans les chunks récupérés. Cela limite ce qui sort vers le LLM et donc ce qui pourrait fuir si le provider est compromis ou logge les prompts. Le choix du LLM est un sujet de contrôle d'accès en soi. Pour les données très sensibles (C3+, RGPD santé, secret industriel), le LLM doit être hébergé en France ou en VPC dédié. Mistral Large sur GPU H100 en France via une plateforme comme Scaleway, OVH ou un déploiement Azure France-Centrale, est la solution canonique. Pour les données moins sensibles, GPT-4o via Azure OpenAI région européenne reste acceptable, à condition de signer un DPA et d'auditer périodiquement les engagements de Microsoft sur le non-entraînement. Un routeur qui décide du LLM cible à partir de la classification des documents retournés est l'élément qui permet d'arbitrer dynamiquement. La leçon transverse, sur une douzaine de projets RAG en entreprise : le retrieval marche presque toujours bien avec une stack standard (bge-m3 embeddings + Qdrant + cross-encoder reranker). Ce qui distingue un projet qui finit en production d'un projet abandonné, ce n'est presque jamais la qualité des réponses — c'est la capacité à démontrer en audit qu'aucune donnée sensible ne fuit, et la capacité opérationnelle à révoquer un accès et voir l'effet immédiat dans l'assistant. Quand le projet est conçu d'emblée autour de ces deux questions, il survit ; sinon, il s'arrête à la première remarque du RSSI.