RAG et agents sans fuite de PII : la tokenisation réversible en production
Comment garder les données personnelles hors des LLM, des vector stores et des logs sans casser le retrieval. Tokenisation sémantique, modes de restauration, sessions et RAG scopé par corpus.
Le problème : le RAG fuit
Soyons directs : dès que tu construis un pipeline RAG sur de vrais documents métier, tu as presque certainement construit une machine à fuites de données. Noms de clients, IBAN, identifiants de santé et numéros de dossier partent de tes documents vers trois endroits non fiables : le fournisseur du modèle, le vector store et tes logs. La plupart des équipes ne s'en rendent compte que quand une revue de sécurité ou un régulateur demande où sont passées les données personnelles.
Le correctif naïf, c'est de tout supprimer. Tu remplaces chaque nom par [REDACTED] et tu passes à autre chose. Ça casse deux choses à la fois. Le modèle perd le contexte dont il a besoin pour bien répondre, et le retrieval s'effondre parce que la requête masquée ne matche plus les documents masqués. Il te faut les données personnelles hors des systèmes non fiables, mais le sens préservé. C'est un problème plus dur, et il est soluble.
Le but n'est pas de supprimer les données personnelles de ton pipeline IA. C'est de garantir que les valeurs brutes ne franchissent jamais la frontière vers un système auquel tu ne fais pas confiance, pendant que le modèle garde assez de contexte pour faire son travail.
On construit cette discipline dans les systèmes de nos clients, et on a construit un runtime pour ça : OGuardAI, une couche propriétaire de protection des données qui se place entre ton application et le modèle. Ce guide, c'est l'architecture d'une IA PII-safe faite proprement. Pour l'angle business, lis notre guide sur la prévention des fuites de données IA, et c'est au cœur de nos services IA.
Qui se soucie de quoi
| Rôle | La vraie question | À quoi ressemble le bon niveau |
|---|---|---|
| Ingénieur IA | Est-ce que je peux protéger les données sans casser le retrieval ? | La requête masquée matche toujours les docs masqués |
| Responsable sécurité | Où va vraiment la PII brute ? | Jamais au-delà de la frontière de confiance |
| DPO | Peut-on prouver l'effacement et la base légale ? | Suppression by design, décisions loguées |
| Architecte | Peut-on ajouter ça sans réécrire l'app ? | Un proxy drop-in ou un SDK léger |
| CTO | Est-ce que ça fail safe sous charge ou en panne ? | Fail closed, jamais de fuite sur erreur |
La frontière de confiance
Tout commence par une ligne tracée à travers ton architecture. D'un côté, la zone de confiance : ton application et le runtime de protection. De l'autre, la zone non fiable : le fournisseur du modèle, le vector store, les outils tiers et tes logs.
TRUSTED │ UNTRUSTED
│
App ──▶ Protection runtime ──────▶│──▶ LLM provider
(detect, tokenize) │──▶ Vector store
(restore on the way back) │──▶ Logs, tools
│
raw PII lives here, transiently │ only tokens cross this line
Les données personnelles brutes n'existent que de façon transitoire dans le runtime, le temps d'une requête. Ce qui franchit la frontière, ce sont des tokens et des métadonnées sûres, jamais des valeurs brutes. Le principe de design qui rend ça digne de confiance : la configuration ne peut qu'ajouter ou durcir la protection, jamais assouplir le plancher intégré. Tu ne peux pas reconfigurer la fuite par accident. Notre guide sur la gouvernance IA explique pourquoi cette posture qui fait échouer vers le côté sûr compte pour les systèmes agentiques.
Détection : trouver les morceaux sensibles
Tu ne peux pas protéger ce que tu ne trouves pas. La détection tourne en deux couches.
La première est un plancher regex déterministe : des dizaines de patterns basés sur le format, indépendants de la langue. Emails, numéros de téléphone, IBAN, numéros de TVA, cartes bancaires validées par checksum, IP, identifiants nationaux, identifiants fiscaux. C'est rapide, de l'ordre de la milliseconde, et ça ne rate jamais un IBAN bien formé parce que ça ne dépend pas de l'humeur d'un modèle.
La deuxième est la reconnaissance d'entités nommées pour ce que la regex ne peut pas attraper : noms de personnes, organisations, lieux, adresses. Ça passe par un détecteur basé sur un modèle, largement multilingue, avec un coût de latence plus élevé.
Le point important, c'est la politique d'échec. Tu choisis un mode : builtin seul, both avec repli propre sur le plancher regex si le NER est down, ou advanced où le NER est obligatoire et où la requête fail closed s'il ne peut pas tourner. Un système qui dégrade silencieusement vers "pas de détection de PII" parce qu'un sidecar a fait un timeout est pire qu'un système qui refuse la requête. Fail closed.
| Mode | Comportement | À utiliser quand |
|---|---|---|
| builtin | Plancher regex uniquement | La PII structurée est tout le risque |
| both | Regex plus NER, replie sur la regex | Tu veux attraper les noms mais tu ne peux pas hard-fail |
| advanced | NER obligatoire, fail closed s'il est indisponible | Les noms et adresses sont non négociables |
Des tokens sémantiques, pas du caviardage
Voilà l'idée clé qui garde le modèle utile. Au lieu de supprimer une entité détectée, tu la remplaces par un token structuré qui porte son type et une identité unique, mais pas sa valeur.
Input: "Email the invoice to Max Mustermann at max.mustermann@beispiel.de"
Masked: "Email the invoice to {{person:p_001:ad4f97}} at {{email:e_002:5873cc}}"
Le token dit au modèle "il y a une personne ici" et "il y a un email ici", et il les garde distincts et référençables, pour que le modèle puisse écrire une réponse cohérente à leur sujet. Le vrai nom et la vraie adresse ne quittent jamais ta frontière de confiance. Au retour, le runtime restaure les vraies valeurs dans la sortie finale.
Ça bat [REDACTED] sur tous les plans qui comptent. Le modèle garde la structure et les relations. Les valeurs identiques se replient sur le même token, donc le modèle comprend que deux mentions sont la même personne. Les entités proches peuvent être liées, donc un nom et son email se restaurent de façon cohérente. Le caviardage jette tout ça.
Les six modes de restauration
Tout ne doit pas revenir en entier. La restauration est gouvernée par entité et par canal de sortie, avec six modes.
| Mode | Ce qui revient | Exemple |
|---|---|---|
| full | La valeur originale complète | Le vrai nom |
| partial | Un sous-ensemble déterministe | Les quatre derniers chiffres seulement |
| masked | Un masquage adapté au type | La carte affichée en chiffres masqués |
| formatted | La valeur avec une forme contextuelle | Civilité selon le genre plus le nom |
| abstract | Un label sémantique, jamais la valeur | "(email au dossier)" |
| none | Retiré entièrement | Un placeholder de caviardage |
Donc une réponse support au client peut restaurer son nom en entier, pendant que la même interaction écrite dans un log interne ne restaure qu'une forme masquée. C'est le canal qui décide. C'est comme ça que tu sers une réponse utile à la personne tout en gardant tes logs propres.
Le problème RAG : le retrieval doit continuer à marcher
C'est la partie qui piège toutes les équipes qui veulent le faire elles-mêmes. Si tu masques un document avec des tokens aléatoires à l'ingestion, puis que tu masques la requête de l'utilisateur avec d'autres tokens aléatoires au moment de la recherche, le retrieval casse. Le token de requête pour "Mustermann" ne matche pas le token de document pour "Mustermann", parce qu'ils ont été frappés indépendamment.
Le correctif, ce sont des tokens déterministes, scopés par corpus. Dans un corpus, une valeur est tokenisée via un hash à clé, pour que la même valeur produise toujours le même token, dans chaque document et dans la requête. Maintenant une requête masquée matche des chunks masqués, parce que "Mustermann" se tokenise à l'identique des deux côtés.
Ingestion: doc chunk "...contract with Mustermann..." ──▶ "...contract with {{person:h_9f3a...}}..."
Query: "what did Mustermann sign" ──▶ "what did {{person:h_9f3a...}} sign"
│
same value ──▶ same token ──▶ retrieval matches
Les tokens sont dérivés par corpus depuis un secret, donc la même valeur dans un autre corpus donne un token sans rapport, et une collision de hash fail closed au lieu de fusionner deux personnes différentes. C'est ce qui te permet de faire tourner le retrieval sur des documents masqués avec des requêtes masquées et d'avoir quand même des résultats corrects. On couvre le côté retrieval en général dans notre guide sur les systèmes RAG d'entreprise et notre guide d'architecture de recherche vectorielle. Combiné aux patterns de mémoire d'agent de notre guide sur la mémoire des agents, c'est comme ça que tu construis des agents qui se souviennent et retrouvent sans accumuler de données personnelles.
Sessions : où vit le mapping
Le mapping des tokens vers les vraies valeurs doit vivre quelque part de sécurisé et rester utilisable entre réplicas. Le défaut, c'est une session scellée : le mapping est chiffré en AES-256-GCM dans un blob qui voyage avec la requête, donc le serveur ne garde aucun état et n'importe quel réplica peut traiter n'importe quelle requête. Pour les setups partagés, un store chiffré ne garde que du ciphertext plus des métadonnées de routage non personnelles.
Chaque chargement de session revalide le tenant auquel elle appartient et fail closed en cas de mismatch, donc un tenant ne peut jamais desceller le mapping d'un autre. Les sessions expirent sur un TTL, et un blob altéré ou expiré est rejeté par son tag d'authentification. C'est la plomberie cryptographique ennuyeuse qui rend le tout digne de confiance, et se tromper là-dessus, c'est comme ça que "on tokenise la PII" devient "on a une fuite de secret partagé". Notre guide sur la conception de systèmes pour la panne couvre cet état d'esprit défensif.
Policy : déclarer ce qui se passe
Tu ne hard-codes pas les décisions de protection. Tu les déclares en policy, pour qu'un responsable sécurité ou conformité puisse les lire et les changer sans toucher au code applicatif.
name: support-desk
version: 1.0.0
defaults:
restore_mode: masked
rules:
- entity_type: credit_card
action: redact # jamais réversible, n'atteint jamais le modèle
- entity_type: person
action: tokenize # réversible, restauré pour le client
restore_mode: full
- entity_type: email
action: tokenize
restore_mode: masked # masqué dans les logs, complet pour l'utilisateur selon le canal
Les actions sont explicites : tokenize pour la protection réversible, redact pour la suppression irréversible, abstract pour un label grossier. Un nom de policy inconnu fait échouer la requête au lieu de retomber sur quelque chose de permissif. Le plancher ne peut pas être affaibli par la policy, seulement durci. C'est de l'autorisation par configuration, et ça sort la décision de sécurité du code éparpillé pour la mettre dans un endroit unique et reviewable. Notre guide sur la gouvernance IA explique pourquoi cette séparation compte.
Le proxy drop-in
Le moyen le plus rapide de protéger une app existante, c'est de ne rien y changer. Un proxy transparent parle la même API que ton fournisseur de modèle, donc tu pointes ton SDK vers l'URL du proxy et il masque à l'aller et restaure au retour. Pas de réécriture d'application.
Your app ──▶ proxy (mask) ──▶ OpenAI / Anthropic ──▶ proxy (restore) ──▶ your app
Le proxy tient la session, donc ton app ne manipule jamais le mapping de tokens. Pour les équipes qui veulent un contrôle plus fin, des SDK légers dans plusieurs langages te donnent les mêmes primitives avec des intégrations framework pour les stacks agent et RAG courantes. Dans les deux cas, l'intégration est une couture, pas une réécriture, et c'est ce qui rend l'adoption pratique. Nos équipes logiciel sur mesure et conseil câblent ça dans les systèmes existants.
GDPR : l'effacement par révocation
Une demande de droit à l'effacement a une réponse propre dans ce modèle. Comme le mapping du token vers la valeur est le seul lien vers les vraies données dans ton pipeline, tu le révoques. Le mapping stocké ne garde que des hash à clé des valeurs, jamais les valeurs brutes, et une fois une valeur révoquée, elle se restaure en marqueur de suppression, peu importe ce que dit n'importe quel blob de session. Une session rejouée ou revenue en arrière ne peut pas annuler la révocation.
Ça te donne un effacement ciblé et prouvable à travers le pipeline IA, avec un reçu d'audit et aucune donnée personnelle brute dans la piste d'audit elle-même. On couvre le tableau conformité plus large dans notre guide GDPR et IA, et la façon dont on le gère en delivery sur nos pages confiance et GDPR.
Par où commencer
- Cartographie tes flux de données. Trouve chaque point où des documents ou des messages atteignent le modèle, le vector store ou les logs.
- Commence par le plancher regex. La PII structurée, IBAN, cartes, emails, c'est le gain le plus rapide et le plus sûr.
- Ajoute le NER là où les noms et adresses comptent. Choisis ton mode d'échec délibérément.
- Utilise des tokens scopés par corpus pour le RAG. C'est ce qui garde le retrieval fonctionnel sur des données masquées.
- Déclare la policy, pas le code. Mets les décisions de protection là où un responsable conformité peut les lire.
C'est un travail par étapes, à faible risque, et ça colle à la delivery par phases de notre méthodologie. Commence par contact ou demande un devis cadré.
Les façons classiques de se planter
- Caviarder au lieu de tokeniser. Tu perds le contexte du modèle et tu casses le retrieval. Utilise des tokens sémantiques réversibles.
- Des tokens indépendants pour la requête et les documents. Le retrieval casse. Utilise des tokens déterministes scopés par corpus.
- Une dégradation silencieuse quand le détecteur tombe. "Pas de détection", c'est une fuite. Fail closed.
- La logique de protection éparpillée dans le code. Personne ne peut l'auditer. Déclare-la en policy.
- Pas de chemin d'effacement. Si tu ne peux pas révoquer un mapping, tu ne peux pas honorer une demande GDPR. Conçois la suppression dès le départ.
Qui construit ça
Oronts est une société de logiciels dirigée par son fondateur, basée à Munich. Refaat Al Ktifan, notre fondateur et architecte solution, mène une équipe senior en backend, sécurité et IA. OGuardAI est notre propre runtime de protection des données, construit parce qu'on avait besoin de cette discipline dans notre propre travail IA avant de pouvoir la recommander à nos clients. On cartographie tes flux de données, on ajoute la couche de protection comme une couture, et on te livre un pipeline défendable sous le GDPR et l'AI Act européen. Va voir nos pages services et solutions.
À retenir
- Un pipeline RAG sur de vrais documents fuit de la PII vers le modèle, le vector store et tes logs par défaut.
- Tokenise, ne caviarde pas, pour que le modèle garde le contexte et que les valeurs identiques restent liées.
- Utilise des tokens déterministes scopés par corpus pour que le retrieval marche toujours sur des données masquées.
- Garde le mapping de tokens dans des sessions chiffrées, liées au tenant, et fail closed sur toute erreur.
- Déclare la protection en policy, et honore l'effacement en révoquant le mapping.
"On tokenise la PII", c'est facile à dire et dur à faire correctement. Les détails, tokens scopés par corpus, détection fail-closed, sessions liées au tenant, sont ce qui sépare une vraie couche de protection d'un faux sentiment de sécurité.
Si ton pipeline IA touche des données personnelles, dis-nous comment elles circulent aujourd'hui. Commence par contact ou demande un devis cadré.
Sujets couverts
Guides connexes
Les 9 endroits où ton système IA laisse fuir des données (et comment colmater chacun)
Cartographie systématique de chaque point de fuite de données dans les systèmes IA. Prompts, embeddings, logs, appels d'outils, mémoire d'agent, messages d'erreur, cache, données de fine-tuning et transferts entre agents.
Lire le guideSystèmes IA dans l'UE : Concevoir pour le RGPD dès le Jour Un
Guide d'architecture pour construire des systèmes IA conformes au RGPD. Frontières de confiance, tokenisation sémantique, restauration pilotée par politique, et scénarios de production réels.
Lire le guideDéfense contre l'injection de prompt pour les systèmes agentiques
Comment défendre les agents IA contre l'injection de prompt. Sépare les instructions de confiance des données non fiables, clôture le contenu récupéré, scanne les entrées et échoue en sécurité.
Lire le guideVous construisez quelque chose de similaire ?
Nous concevons et exploitons des systèmes de production comme celui de ce guide. Parlez aux ingénieurs qui l'ont écrit, sans argumentaire commercial.
Démarrer une conversation