Anonymiser avant le LLM: un problème d'organisation, pas de discipline
TL;DR
Dire aux gens “ne collez pas de données sensibles dans ChatGPT”, c’est une affichette, pas une politique. À l’échelle d’une organisation, la seule approche qui tient, c’est d’intercepter, détecter et anonymiser avant que la requête ne sorte.
Cet article explique pourquoi j’ai choisi un modèle CRF, un truc des années 2000, plutôt qu’un LLM local pour faire ce travail dans go-anon, la brique d’anonymisation de Xolo. Une démo est disponible à l’adresse anonymize.cadoles.com pour se faire une idée concrète.
La fuite par mille petits prompts
Il y a un truc que je vois arriver dans à peu près toutes les organisations qui adoptent les outils d’IA générative, et qui ne ressemble à rien de ce qu’on avait l’habitude d’appeler une fuite de données. Pas de base exfiltrée, pas de dump sur un forum, pas d’incident à déclarer. À la place: des dizaines, des centaines de prompts qui partent chaque jour vers des fournisseurs tiers, et qui embarquent à chaque fois un petit bout d’information qui n’aurait pas dû sortir.
Un développeur qui demande de l’aide pour reformuler un ticket client. Une chargée de projet qui fait résumer un compte-rendu. Un commercial qui demande à un modèle de relire sa proposition commerciale. À chaque fois, les noms, les adresses, les numéros de dossier, les références internes partent avec.
Personne n’agit mal. Au contraire: ces gestes sont parfaitement rationnels, l’outil aide à mieux travailler. Le problème, c’est que ce qui sort de l’organisation ne revient pas. C’est journalisé quelque part, peut-être utilisé pour de l’entraînement futur, conservé selon des politiques de rétention sur lesquelles personne en interne n’a la moindre prise.
Et là, dès qu’il y a des données personnelles dans le lot, un nom de client, un mail, un numéro de dossier rattaché à une personne identifiable, on n’est plus dans l’inconfort éthique, on est dans le traitement de données à caractère personnel au sens du RGPD. Avec tout ce que ça implique: base légale, finalité, transfert hors UE, durée de conservation, droits des personnes concernées. La CNIL a d’ailleurs commencé à publier des recommandations spécifiques sur l’IA, et son message est assez clair: utiliser un service tiers d’IA générative avec des données personnelles, c’est un traitement comme un autre, et il faut le traiter comme tel.
La bonne volonté ne fait pas une politique
La réaction réflexe, dans la plupart des structures, c’est l’affichette: “ne collez pas de données personnelles ou confidentielles dans les IA génératives. “ J’en ai vu passer un paquet. Ça ne marche pas, et la raison est assez simple: ça transfère à l’utilisateur un arbitrage qu’il n’est pas en position de faire à chaque requête.
Concrètement, on demande à quelqu’un qui veut juste finir sa journée de:
- Identifier dans son texte ce qui est “sensible” sachant que la définition varie selon le contexte, et que le RGPD a sa propre notion de donnée personnelle qui n’est pas forcément intuitive.
- Réécrire mentalement son prompt en remplaçant ces éléments.
- Évaluer si la réponse sera encore utile une fois ces informations retirées.
- Faire ça à chaque interaction, des dizaines de fois par jour.
C’est ingérable. Et comme c’est ingérable, ça ne se fait pas. Pas par mauvaise volonté, juste par friction. Je le sais, les utilisateurs le savent, et la CNIL le sait aussi: on ne construit pas une politique de protection des données sur la vigilance permanente des gens.
La seule approche qui tient, c’est de rendre l’action sûre par défaut. L’utilisateur tape son prompt normalement. Quelque chose, entre lui et le fournisseur LLM, fait le sale boulot de détecter et masquer ce qui ne doit pas sortir. Dans l’architecture que je construis, ce quelque chose, c’est la passerelle Xolo, et la brique qui fait le travail de détection, c’est go-anon.
Pourquoi pas un LLM local pour détecter ?
Première objection qui arrive quand je présente le projet: pourquoi ne pas utiliser un petit LLM local pour faire la détection ? Les LLM sont bons pour comprendre du texte, il existe plein de modèles open-source, on peut les faire tourner sur ses propres machines, fin de l’histoire.
L’idée se défend, mais elle se cogne à plusieurs murs.
Le serpent qui se mord la queue. Déployer un LLM local pour décider ce qu’on peut envoyer à un LLM distant, c’est déplacer le problème, pas le résoudre. J’ajoute une dépendance lourde au cœur d’un dispositif dont l’objectif est précisément la sobriété et le contrôle.
Le coût. Un LLM local de taille raisonnable, c’est du GPU, de la RAM, de l’énergie. Multiplié par le débit d’une passerelle qui doit traiter chaque requête de chaque utilisateur, en temps quasi-réel, ça représente une infrastructure non triviale. Et un coût écologique en contradiction directe avec ce que je défends par ailleurs sur le numérique responsable.
La latence. Une passerelle qui ajoute 800 ms à chaque requête pour faire de la détection, c’est une passerelle que les gens vont contourner. On retombe sur le problème de la friction.
Le non-déterminisme. Un LLM peut halluciner, rater une entité aujourd’hui qu’il détectait hier, inventer une catégorie qui n’existe pas. Pour une brique de sécurité, c’est rédhibitoire. Je veux un comportement prédictible, reproductible, auditable, y compris devant un délégué à la protection des données qui voudrait comprendre ce qui sort et ce qui ne sort pas.
Le bon outil pour le bon problème: les CRF
La reconnaissance d’entités nommées (NER) est un problème étudié depuis des décennies. Bien avant les LLM, on savait déjà extraire des noms de personnes, de lieux ou d’organisations dans un texte. L’une des approches les plus efficaces et les plus économes s’appelle CRF, Conditional Random Fields, ou champs aléatoires conditionnels.
L’idée
Un CRF, c’est un modèle qui apprend à étiqueter chaque mot d’une phrase en fonction de ses caractéristiques et de ce qui l’entoure.
Prenons une phrase:
Marie Curie a obtenu son doctorat à Paris en 1903.
Un CRF entraîné produit, mot par mot, une suite d’étiquettes:
Marie → B-PER (début d'un nom de personne)
Curie → I-PER (suite du nom de personne)
a → O (rien)
obtenu → O
son → O
doctorat → O
à → O
Paris → B-LOC (lieu)
en → O
1903 → O
Le schéma BIO (B- pour Begin, I- pour Inside, O pour Outside) permet de capturer des entités qui s’étendent sur plusieurs mots.
Ce qui distingue un CRF d’un classifieur naïf
Un classifieur naïf regarderait chaque mot indépendamment et déciderait: ” est-ce un nom de personne ?". Le CRF, lui, modélise les dépendances entre étiquettes voisines. Il sait par exemple qu’un B-PER suivi d’un I-LOC n’a aucun sens, et il va donc chercher la séquence d’étiquettes globalement la plus probable, pas la meilleure étiquette pour chaque mot pris isolément.
Cette décision globale, qui pondère à la fois les indices propres à chaque mot et la cohérence de l’enchaînement, c’est ce qui rend les CRF particulièrement adaptés à la NER.
Et pourquoi pas BERT, RoBERTa ou FLAIR ?
Question légitime, il existe des modèles NER plus modernes et plus précis. Trois raisons de s’en passer ici:
- Coût d’inférence: quelques millisecondes sur un CPU modeste pour un CRF, un à deux ordres de grandeur au-dessus pour un transformer. Sur le chemin critique d’une requête LLM, ça compte.
- Empreinte de déploiement: quelques Mo et zéro dépendance lourde, contre plusieurs centaines de Mo et une stack Python conséquente. La passerelle reste légère et auto-hébergeable.
- Auditabilité: un CRF, ça s’inspecte. Pour un composant qui touche au RGPD, c’est un argument qui pèse face à l’opacité d’un réseau profond.
La précision pure n’est pas tout et rien n’empêche d’ajouter un backend transformer plus tard pour les cas qui le justifient.
Les indices que le modèle utilise
Pour décider, le CRF s’appuie sur un grand nombre de petits indicateurs (des features) extraits pour chaque mot:
- sa forme exacte (
Marie) ; - sa casse (commence-t-il par une majuscule ?) ;
- ses préfixes et suffixes (
-ie,Mar-) ; - les mots qui le précèdent et le suivent ;
- son appartenance à des listes connues (un dictionnaire de prénoms, par exemple) ;
- sa catégorie grammaticale ;
- des représentations sémantiques compactes (clusters de mots similaires).
Le modèle apprend, sur un corpus annoté, à pondérer ces milliers d’indices pour produire les bonnes étiquettes. Une fois entraîné, il devient un fichier de quelques mégaoctets qui s’exécute en quelques millisecondes par phrase, sur CPU, sans dépendance externe.
Ce que ça donne en pratique
Pour donner un ordre d’idée, le modèle français de go-anon:
- pèse quelques mégaoctets une fois compressé ;
- traite plusieurs centaines de phrases par seconde sur un CPU standard ;
- ne nécessite ni GPU, ni connexion réseau, ni mise à jour quotidienne ;
- produit toujours la même sortie pour la même entrée.
Comparé à un LLM, c’est une autre catégorie d’objet. Moins polyvalent, beaucoup plus adapté à la tâche précise qu’on lui confie. Et accessoirement, beaucoup plus simple à documenter dans un registre de traitements.
Le flux complet, vu de la passerelle
Intégré à Xolo, le flux devient le suivant:
- L’utilisateur envoie son prompt à la passerelle.
go-anondétecte les entités sensibles et les remplace par des jetons cohérents ([PERSON_1],[LOCATION_2]…).- Le prompt anonymisé est relayé au fournisseur LLM.
- La réponse revient, contenant éventuellement les jetons.
- La passerelle restaure les valeurs originales avant de rendre la réponse à l’utilisateur.
Du point de vue de l’utilisateur, rien ne change. Du point de vue du fournisseur, il n’a jamais vu les données originales. Du point de vue de l’organisation, la donnée sensible n’est pas sortie du périmètre.
Un mot de vocabulaire, quand même. Au sens strict du RGPD, ce que je fais ici relève davantage de la pseudonymisation que de l’anonymisation: je conserve, côté passerelle, la table de correspondance qui permet de restaurer les valeurs originales. Une vraie anonymisation, au sens de la CNIL, suppose qu’il soit impossible de remonter à la personne. Ici, je remonte volontairement, c’est même tout l’intérêt. La donnée à caractère personnel ne sort pas vers le tiers, mais elle reste sous mon contrôle. C’est précisément ce que je veux.
90 % vaut mieux que 0 %
Il faut être honnête: aucun modèle de NER n’est parfait. go-anon va rater des entités, en inventer parfois, mal classer des cas limites. C’est inhérent à la tâche, et ce serait vrai aussi avec un LLM, sans doute davantage, d’ailleurs, parce qu’un LLM est plus difficile à auditer.
Mais le bon point de comparaison n’est pas “un système parfait”. C’est “rien du tout”, c’est-à-dire l’état actuel de la plupart des organisations qui laissent leurs utilisateurs causer directement avec des LLM tiers, sans aucun filtre.
Dans ce référentiel-là, un système qui attrape correctement la grande majorité des noms, adresses, identifiants et références sensibles fait basculer le risque d’un ordre de grandeur. On passe d’une fuite quasi-systématique à une fuite résiduelle, sur laquelle je peux continuer de bosser, en enrichissant les dictionnaires, en ajoutant des détecteurs métier, en affinant le post-traitement. Et surtout, on passe d’une situation où il est très difficile de répondre honnêtement à la question “qu’est-ce qui sort de chez nous ?" à une situation où on peut le mesurer, le documenter, et l’améliorer.
C’est l’approche que je défends: livrer une brique imparfaite mais utile, transparente et améliorable, plutôt qu’attendre une solution parfaite qui n’arrivera jamais. Le code est libre, auto-hébergeable, sans dépendance lourde, et auditable en une après-midi.