// Outil
Presidio en français
Ce qu'il faut apprendre à Presidio pour trouver noms, adresses, NIR et IBAN dans des textes français, sans LLM : recognizers, clés de contrôle et mesures sur 100 textes.
- Publié
- Lecture
- 14 min
Sommaire
- Ce que Presidio voit d’origine
- Comment Presidio décide
- Les numéros qui se vérifient tout seuls
- Le numéro de sécurité sociale
- Le numéro fiscal
- L’IBAN et la carte bancaire
- Les adresses, sans filet
- Les noms, le plus difficile
- Trois recognizers d’origine à retoucher
- Les chiffres
- Ce qui passe encore
- À vous
- Pour finir
Un matin, le service client reçoit une lettre de Camille Roussel. Elle écrit au sujet des remboursements de soins de sa mère et donne tout ce qu’il faut pour traiter la demande : son adresse, le numéro de sécurité sociale et le numéro fiscal de sa mère, un IBAN, un numéro de carte, son téléphone et son email. Le conseiller voudrait qu’un modèle de langue l’aide à rédiger la réponse. Mais avant d’envoyer cette lettre à un modèle, il faut repérer chacune de ces données pour la remplacer par une étiquette.
Dans un premier article, j’ai raconté la gateway qui fait ce travail pour toute une entreprise fictive. Celui-ci ouvre le capot de la pièce qui repère les données : Presidio, un outil open source de Microsoft. Presidio sait très bien chercher des données personnelles dans un texte anglais. En français, il en rate beaucoup, et cet article raconte ce que j’ai dû lui apprendre, chiffres à l’appui. Tout ce qui suit se reproduit avec le repo de la gateway, et même sans la gateway, puisque Presidio fonctionne très bien seul.
Presidio tel quel
59,1%
Part des données personnelles entièrement masquées sur 100 textes français, avec l'image officielle et son modèle anglais.
Avec un modèle français
72,8%
Le modèle de langue français trouve les villes et la plupart des noms, mais toujours aucune adresse ni aucun numéro de sécurité sociale.
Avec des recognizers français
99,2%
Sept détecteurs écrits pour le projet, qui vérifient les clés de contrôle et lisent les indices autour des noms.
Par texte
9ms
Temps médian d'analyse sur un processeur, sans carte graphique. Un LLM local de 7 milliards de paramètres met plus de 8 secondes.
Ce que Presidio voit d’origine
La façon la plus simple d’essayer Presidio, c’est de lancer son image officielle et de lui envoyer un texte. C’est ce que j’ai fait avec la lettre de Camille Roussel. Ses numéros sont inventés, mais leurs clés de contrôle sont justes, comme pour de vrais numéros.
Le résultat est déroutant. L’image officielle ne contient qu’un modèle de langue anglais, alors elle lit la lettre comme un texte anglais. Elle trouve bien l’IBAN, l’email et les deux noms complets. Elle voit en revanche une personne dans « joindre au 06 12 » et un lieu dans « Cordialement ». Surtout, elle passe à côté de l’adresse, du numéro de sécurité sociale, du numéro fiscal et de la carte bancaire.
Le visuel ci-dessous montre la même lettre, et deux autres textes, vus par trois versions de Presidio. Ce sont les vraies réponses de Presidio, enregistrées par le script just presidio-steps du repo.
Camille Roussel 8 allée des Tilleuls 33000 Bordeaux Bonjour, Je vous écris au sujet du remboursement de soins de ma mère, Mme Josiane Roussel. Son numéro de sécurité sociale est le 2 52 07 33 063 112 03 et son numéro fiscal le 30 23 217 600 053. Le remboursement peut partir sur le compte FR76 3000 6000 0112 3456 7890 189, ou sur la carte 2221 0000 1234 5673. Vous pouvez me joindre au 06 12 34 56 78 ou à camille.roussel@example.fr. Cordialement, Camille
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Camille Roussel | SpacyRecognizer | 0,85 | |
| PERSON | Mme Josiane Roussel | SpacyRecognizer | 0,85 | |
| IBAN_CODE | FR76 3000 6000 0112 3456 7890 189 | IbanRecognizer | 0,5 → 1 | clé valide |
| PERSON | joindre au 06 12 | SpacyRecognizer | 0,85 | faux positif |
| PHONE_NUMBER | 06 12 34 56 78 | PhoneRecognizer | 0,4 | |
| EMAIL_ADDRESS | camille.roussel@example.fr | EmailRecognizer | 0,5 → 1 | clé valide |
| LOCATION | Cordialement | SpacyRecognizer | 0,85 | faux positif |
| PERSON | Camille | SpacyRecognizer | 0,85 |
Camille Roussel 8 allée des Tilleuls 33000 Bordeaux Bonjour, Je vous écris au sujet du remboursement de soins de ma mère, Mme Josiane Roussel. Son numéro de sécurité sociale est le 2 52 07 33 063 112 03 et son numéro fiscal le 30 23 217 600 053. Le remboursement peut partir sur le compte FR76 3000 6000 0112 3456 7890 189, ou sur la carte 2221 0000 1234 5673. Vous pouvez me joindre au 06 12 34 56 78 ou à camille.roussel@example.fr. Cordialement, Camille
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Camille Roussel | SpacyRecognizer | 0,85 | |
| PERSON | Mme Josiane Roussel | SpacyRecognizer | 0,85 | |
| IBAN_CODE | FR76 3000 6000 0112 3456 7890 189 | IbanRecognizer | 0,5 → 1 | clé valide |
| LOCATION | FR76 | SpacyRecognizer | 0,85 | |
| PHONE_NUMBER | 06 12 34 56 78 | PhoneRecognizer | 0,4 | |
| EMAIL_ADDRESS | camille.roussel@example.fr | EmailRecognizer | 0,5 → 1 | clé valide |
| PERSON | Camille | SpacyRecognizer | 0,85 |
Camille Roussel 8 allée des Tilleuls 33000 Bordeaux Bonjour, Je vous écris au sujet du remboursement de soins de ma mère, Mme Josiane Roussel. Son numéro de sécurité sociale est le 2 52 07 33 063 112 03 et son numéro fiscal le 30 23 217 600 053. Le remboursement peut partir sur le compte FR76 3000 6000 0112 3456 7890 189, ou sur la carte 2221 0000 1234 5673. Vous pouvez me joindre au 06 12 34 56 78 ou à camille.roussel@example.fr. Cordialement, Camille
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Camille Roussel | FrPersonRecognizer | 0,85 → 1 | contexte « ami » |
| FR_ADDRESS | 8 allée des Tilleuls ⏎ 33000 Bordeaux | FrAddressRecognizer | 0,6 | |
| PERSON | Mme Josiane Roussel | SpacyRecognizer | 0,85 | |
| FR_NIR | 2 52 07 33 063 112 03 | FrNirRecognizer | 0,3 → 1 | contexte « sécurité » · clé valide |
| FR_FISCAL_NUMBER | 30 23 217 600 053 | FrFiscalNumberRecognizer | 0,3 → 1 | contexte « fiscal » · clé valide |
| IBAN_CODE | FR76 3000 6000 0112 3456 7890 189 | IbanRecognizer | 0,5 → 1 | contexte « compte » · clé valide |
| LOCATION | FR76 | SpacyRecognizer | 0,85 | |
| CREDIT_CARD | 2221 0000 1234 5673 | CardRecognizer | 0,3 → 1 | contexte « carte » · clé valide |
| PHONE_NUMBER | 06 12 34 56 78 | PhoneRecognizer | 0,4 → 0,75 | contexte « joindre » |
| EMAIL_ADDRESS | camille.roussel@example.fr | EmailRecognizer | 0,5 → 1 | clé valide |
| PERSON | Camille | FrPersonRecognizer | 0,85 → 1 | contexte « ami » |
Salut Anaïs, le Docteur Camus a validé l'arrêt. Tu peux le déposer chez Étienne avant vendredi ? Sinon appelle-moi au 01 46 39 90 88. Bises, Olivier
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Salut Anaïs | SpacyRecognizer | 0,85 | |
| LOCATION | Sinon appelle-moi au 01 | SpacyRecognizer | 0,85 | faux positif |
| PERSON | Olivier | SpacyRecognizer | 0,85 |
Salut Anaïs, le Docteur Camus a validé l'arrêt. Tu peux le déposer chez Étienne avant vendredi ? Sinon appelle-moi au 01 46 39 90 88. Bises, Olivier
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Olivier | SpacyRecognizer | 0,85 |
Salut Anaïs, le Docteur Camus a validé l'arrêt. Tu peux le déposer chez Étienne avant vendredi ? Sinon appelle-moi au 01 46 39 90 88. Bises, Olivier
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Anaïs | FrPersonRecognizer | 0,3 → 0,65 | contexte « salut » |
| PERSON | Camus | FrPersonRecognizer | 0,85 → 1 | contexte « salut » |
| PHONE_NUMBER | 01 46 39 90 88 | PhoneRecognizer | 0,4 → 0,75 | contexte « appel » |
| PERSON | Olivier | SpacyRecognizer | 0,85 |
Nom : Buisson Prénom : Hortense Adresse : 21 rue Jean Moulin 74000 Annecy N° de sécurité sociale : 1 85 05 78 006 084 91 Téléphone : 04 50 12 34 56
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Hortense ⏎ Adresse | SpacyRecognizer | 0,85 | |
| PERSON | Jean Moulin | SpacyRecognizer | 0,85 | |
| LOCATION | Annecy | SpacyRecognizer | 0,85 | |
| PHONE_NUMBER | 04 50 12 34 56 | PhoneRecognizer | 0,4 → 0,75 | contexte « phone » |
Nom : Buisson Prénom : Hortense Adresse : 21 rue Jean Moulin 74000 Annecy N° de sécurité sociale : 1 85 05 78 006 084 91 Téléphone : 04 50 12 34 56
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Buisson ⏎ Prénom | SpacyRecognizer | 0,85 | |
| PERSON | Hortense ⏎ Adresse | SpacyRecognizer | 0,85 | |
| LOCATION | Annecy | SpacyRecognizer | 0,85 | |
| PHONE_NUMBER | 04 50 12 34 56 | PhoneRecognizer | 0,4 → 0,75 | contexte « phone » |
Nom : Buisson Prénom : Hortense Adresse : 21 rue Jean Moulin 74000 Annecy N° de sécurité sociale : 1 85 05 78 006 084 91 Téléphone : 04 50 12 34 56
Surligné : trouvé. Pointillés : resté en clair. Barré : faux positif.
| Type | Passage | Recognizer | Score | Indice |
|---|---|---|---|---|
| PERSON | Buisson ⏎ Prénom | SpacyRecognizer | 0,85 | |
| PERSON | Hortense | FrPersonRecognizer | 0,85 → 1 | contexte « prénom » |
| PERSON | Hortense ⏎ Adresse | SpacyRecognizer | 0,85 | |
| FR_ADDRESS | 21 rue Jean Moulin ⏎ 74000 Annecy | FrAddressRecognizer | 0,6 → 0,95 | contexte « adresse » |
| PERSON | Jean Moulin | FrPersonRecognizer | 0,6 | |
| LOCATION | Annecy | SpacyRecognizer | 0,85 | |
| FR_NIR | 1 85 05 78 006 084 91 | FrNirRecognizer | 0,3 → 1 | contexte « sécurité » · clé valide |
| PHONE_NUMBER | 04 50 12 34 56 | PhoneRecognizer | 0,4 → 0,75 | contexte « téléphon » |
Ajouter le modèle de langue français fait disparaître la plupart de ces faux positifs, mais pas les données manquantes. Aucun détecteur d’origine ne connaît le NIR, le numéro fiscal ou la forme d’une adresse française. Et dans le petit message, « Anaïs », « Étienne » et le « Docteur Camus » échappent au modèle. Un nom isolé, sans phrase autour pour l’annoncer, ne lui suffit pas.
Comment Presidio décide
Pour comprendre ce qu’il faut ajouter, il faut d’abord voir comment Presidio travaille. On peut l’imaginer comme un jury de petits spécialistes qui lisent le texte en même temps.
Le premier est un modèle de langue, spaCy, entraîné sur de grandes quantités de textes annotés à reconnaître les noms de personnes et de lieux. Il juge d’après la phrase : « Mme Josiane Roussel » ressemble à une personne parce que les mots autour le suggèrent. Les autres spécialistes sont des recognizers, de petits détecteurs qui ne cherchent qu’une seule chose. Celui des emails cherche un arobase entouré de la bonne façon, celui des IBAN cherche deux lettres suivies de chiffres et vérifie leur clé de contrôle.
Chaque spécialiste qui pense avoir trouvé quelque chose propose un passage du texte avec un score, entre 0 et 1, qui dit à quel point il en est sûr. Deux mécanismes font ensuite bouger ce score :
- La clé de contrôle. Quand une donnée en possède une, le recognizer la vérifie. Si elle est juste, le score passe à 1. Si elle est fausse, le passage est éliminé, quel que soit son score de départ.
- Le contexte. Chaque recognizer a sa liste de mots d’indice. Si l’un d’eux apparaît dans les cinq mots qui précèdent le passage, le score gagne 0,35. « domicilié » annonce une adresse, « salut » annonce un prénom.
Enfin, un seuil fait le tri. Dans la gateway, il est réglé à 0,4 : tout ce qui reste en dessous est ignoré.
Toute ma stratégie tient dans ces trois règles. Un motif qui ressemble à une donnée, mais pourrait aussi bien être autre chose, part avec un score de 0,3, juste sous le seuil. Il ne passe que si une clé de contrôle ou un mot d’indice vient le confirmer. Voici ce que ça donne sur de vraies requêtes :
-
2 52 07 33 063 112 04
forme d’un NIR, clé fausse
0,3 → 0
éliminé
-
2 52 07 33 063 112 03
forme d’un NIR, bonne clé
0,3 → 1
gardé
-
« Il vit à 33000 Bordeaux »
aucun mot d’indice
0,3
écarté
-
« Il est domicilié à 33000 Bordeaux »
indice « domicilié »
0,3 → 0,65
gardé
-
« Tu peux le déposer chez Étienne ? »
prénom seul, sans indice
0,3
écarté
-
« Salut Étienne, tu peux le déposer ? »
indice « salut »
0,3 → 0,65
gardé
-
« Le Docteur Camus a validé. »
après un titre
0,85
gardé
Presidio peut expliquer chacune de ses décisions, à condition de le lui demander avec l’option return_decision_process. On y reviendra à la fin de l’article, au moment de lancer les commandes soi-même.
Les numéros qui se vérifient tout seuls
Beaucoup d’identifiants français ont une propriété très pratique : leurs derniers chiffres sont calculés à partir des premiers. C’est la clé de contrôle. Elle a été conçue pour détecter les fautes de frappe, et elle permet aussi de distinguer un vrai numéro d’un nombre quelconque de la même longueur.
Le numéro de sécurité sociale
Le NIR compte 15 caractères qui racontent une petite histoire : le sexe, l’année et le mois de naissance, le département et la commune, un numéro d’ordre, puis une clé de deux chiffres. Cette clé vaut 97 moins le reste de la division des 13 premiers chiffres par 97. Les départements corses compliquent un peu le calcul, puisque 2A et 2B contiennent une lettre. On les remplace par 19 et 18 avant de diviser.
Le calculateur ci-dessous refait ce calcul pas à pas. Essayez de changer un seul chiffre : la clé attendue change, et le numéro devient invalide.
15 caractères attendus : 13 chiffres (ou 2A, 2B pour la Corse) et une clé de 2 chiffres.
- Sexe
- 2
- Année
- 52
- Mois
- 07
- Département
- 33
- Commune
- 063
- Ordre
- 112
- Clé
- 03
- Sans les espaces
252073306311203 - 13 premiers chiffres, 2A → 19 et 2B → 18
2520733063112 - Reste de la division par 97
94 - Clé attendue
97 − 94 = 03 - Clé écrite
03valide
Le recognizer reprend exactement ce calcul. Le motif décrit la structure du numéro, avec ou sans espaces, et validate_result vérifie la clé :
def validate_result(self, pattern_text: str) -> bool:
nir = pattern_text.replace(" ", "").upper()
digits = nir[:13].replace("2A", "19").replace("2B", "18")
return 97 - int(digits) % 97 == int(nir[13:])Un nombre de 15 chiffres pris au hasard n’a qu’environ une chance sur 97 de passer ce contrôle, et encore faut-il qu’il ait d’abord la structure d’un NIR, avec un mois et un département plausibles. Les faux positifs deviennent si rares qu’un numéro valide peut être masqué même sans aucun mot d’indice autour.
Le numéro fiscal
Le numéro fiscal, celui qui figure sur les avis d’impôt, compte 13 chiffres et commence par 0, 1, 2 ou 3. Ses trois derniers chiffres valent les dix premiers modulo 511. C’est une règle peu connue, et pourtant très efficace : sur 511 nombres de 13 chiffres tirés au hasard, un seul la respecte.
L’IBAN et la carte bancaire
Presidio sait déjà vérifier la clé d’un IBAN, et celle d’une carte bancaire avec l’algorithme de Luhn. Pour l’IBAN, il a simplement fallu ajouter des mots d’indice français comme « RIB », « virement » ou « prélèvement ».
La carte bancaire réservait une surprise. Avec le recognizer d’origine, le benchmark laisse passer quatre numéros de carte sur dix, tous des Mastercard commençant par 2. Mastercard émet des numéros entre 2221 et 2720 depuis 2017, et le motif de Presidio n’accepte que les cartes commençant par 1, 3, 4, 5 ou 6. J’ai écrit un recognizer qui hérite de celui de Presidio et n’en change que le motif, en gardant la vérification de Luhn. Les dix cartes du jeu de test sont désormais trouvées.
Les adresses, sans filet
Une adresse n’a pas de clé de contrôle. Rien ne distingue mathématiquement « 12 rue de la Paix » de « 12 pages de la notice ». Le recognizer s’appuie donc sur la forme : un numéro, éventuellement suivi de bis ou ter, puis un type de voie pris dans une liste (rue, avenue, allée, impasse, chemin, lieu-dit et une vingtaine d’autres), puis des mots qui commencent par une majuscule, éventuellement reliés par de la, du ou des. Le code postal et la ville peuvent suivre.
Ces majuscules cachent un piège. Presidio applique à tous les motifs l’option qui ignore la différence entre majuscules et minuscules, si bien qu’une règle « un mot qui commence par une majuscule » accepterait n’importe quel mot, et l’adresse déborderait sur la suite de la phrase. La parade consiste à désactiver cette option à l’intérieur du motif, pour ce seul morceau, avec la syntaxe (?-i:...).
Le benchmark a révélé un autre cas. Dans l’en-tête d’une lettre, l’adresse tient sur deux lignes, avec la rue sur la première et le code postal et la ville sur la seconde. Le motif n’acceptait qu’un espace entre les deux, alors le code postal et la ville restaient en clair. Il accepte maintenant un retour à la ligne.
Un code postal suivi d’une ville, seul, peut aussi être une adresse. Mais « 33000 Bordeaux » peut tout autant apparaître dans une phrase banale sur une agence ou un salon. Ce motif part donc à 0,3, sous le seuil, et ne passe qu’après un mot comme « domicilié », « adresse » ou « habite ».
Les noms, le plus difficile
Pour les noms, il n’y a ni clé de contrôle, ni forme fixe. Le modèle spaCy s’en sort bien quand la phrase l’aide, comme dans « je vous écris au sujet de ma mère, Mme Josiane Roussel ». Il rate en revanche les noms isolés. C’est le cas d’un nom seul sur la première ligne d’une lettre, d’une signature, d’un prénom après « Salut », ou d’un nom de famille après « Docteur ». Sur les 100 textes de test, il en laissait passer 16.
J’ai écrit un recognizer qui cherche les mêmes indices qu’un lecteur humain. Il connaît quatre situations, et chacune a son score :
- après un titre ou un champ de formulaire (« M. », « Docteur », « Maître », « Nom : »), le mot suivant est très probablement un nom : 0,85 ;
- un nom seul sur sa ligne qui commence par un prénom connu, comme dans un en-tête ou une signature : 0,85 ;
- un prénom connu suivi d’un autre mot à majuscule, comme « Hortense Rodriguez » : 0,6 ;
- un prénom connu tout seul : 0,3, sous le seuil. Il ne passe qu’avec un mot d’indice comme « salut », « bonjour », « mon fils » ou « cordialement ».
Les prénoms connus viennent du fichier des prénoms de l’INSEE, qui recense les prénoms donnés en France depuis 1900. J’ai gardé les 5 114 orthographes attribuées à au moins 500 enfants. Deux en sont exclues à la main, Paris et France : ce sont aussi des prénoms, mais dans une lettre, ils désignent presque toujours la capitale ou le pays.
Le recognizer ne remplace pas spaCy, il le complète. Quand les deux trouvent le même nom, Presidio garde un seul résultat. Avec ce recognizer, la part des noms trouvés est passée de 90,5 % à 98,5 %.
Trois recognizers d’origine à retoucher
Certains recognizers de Presidio faisaient presque l’affaire et n’avaient besoin que d’une retouche.
Le recognizer des téléphones s’appuie sur phonenumbers, la version Python de la bibliothèque de Google qui valide les numéros du monde entier. Mais par défaut, il ne cherche que des numéros américains, britanniques, allemands, israéliens, indiens, canadiens ou brésiliens. Un numéro écrit avec +33 passe quand même, puisque l’indicatif suffit à le situer. Un « 01 46 39 90 88 » écrit à la française, en revanche, n’est reconnu dans aucune de ces régions. Le réglage qui ajoute la France existe bien, mais le chargeur de configuration de Presidio l’ignore. D’où une petite sous-classe de cinq lignes.
Le recognizer des URL, lui, signalait aussi le domaine de chaque adresse email : dans marie@example.fr, il voyait une URL example.fr en plus de l’email. J’ai écarté ces doublons.
Et il y a la carte Mastercard, déjà racontée plus haut.
Les chiffres
Le benchmark compte, pour chaque type de donnée, la part trouvée par chacune des trois versions de Presidio. La différence se joue presque entièrement sur ce qui est français.
| Type de donnée | Données | Image officielle (%) | + modèle français (%) | + recognizers FR (%) |
|---|---|---|---|---|
| Noms de personnes | 201 | 76,6 | 90,5 | 98,5 |
| Villes et pays | 51 | 9,8 | 96,1 | 96,1 |
| Adresses | 74 | 0 | 0 | 100 |
| Téléphones | 61 | 88,5 | 91,8 | 100 |
| Emails | 35 | 100 | 100 | 100 |
| IBAN | 30 | 100 | 100 | 100 |
| Numéros de sécurité sociale | 30 | 0 | 0 | 100 |
| Cartes bancaires | 10 | 60 | 60 | 100 |
| Numéros fiscaux | 10 | 0 | 0 | 100 |
| Adresses IP | 6 | 100 | 100 | 100 |
Ces 100 textes sont des lettres, des emails, des messages courts et des formulaires fictifs que j’ai générés avec leurs 508 données personnelles déjà repérées, ce qui permet de vérifier chaque détection. Le chiffre qui compte vraiment est la part des données dont plus aucun caractère n’atteint le modèle, quelle que soit la catégorie retenue. Une adresse prise pour une ville reste cachée. C’est ce chiffre qui passe de 59,1 % à 99,2 %.
Ces recognizers n’étaient pas aussi bons du premier coup. Avant les corrections sur les noms isolés, les adresses sur deux lignes et les cartes en 2, le taux plafonnait à 93,9 %. Corriger des règles en regardant les erreurs d’un jeu de test fait courir un risque connu, celui de les ajuster à ces textes-là et à eux seuls. J’ai donc généré un second jeu avec une autre graine aléatoire et je ne l’ai jamais regardé pendant les corrections. Il est passé de 94,1 % à 99,4 %, ce qui montre que les règles tiennent sur des noms et des numéros qu’elles n’avaient jamais vus.
La précision, elle, est de 88,1 %. Autrement dit, environ une détection sur huit n’est pas une donnée personnelle : la rue Jean Moulin prise pour une personne, ou une ville qui ne désigne personne en particulier. Pour ce travail, c’est le bon compromis. Un mot masqué à tort fait perdre un peu de contexte au modèle, alors qu’une donnée oubliée part chez lui.
Le tout reste léger. L’analyse prend 9 millisecondes par texte en médiane, et le conteneur occupe 1,6 Go de mémoire avec les modèles français et anglais chargés. Dans le premier article, j’avais comparé Presidio à des LLM qu’on chargeait de trouver les mêmes données. Le meilleur, avec 7 milliards de paramètres, en laissait passer une sur six et mettait plus de 8 secondes par texte.
Ce qui passe encore
Quatre données sur 508 restent en clair dans le benchmark, et d’autres textes en feront sûrement apparaître d’autres. Voici ce que j’en sais.
Un prénom seul, sans indice. « Tu peux le déposer chez Étienne ? » : le prénom est bien dans la liste, mais rien autour ne l’annonce, alors son score reste à 0,3. Abaisser le seuil ferait passer ce cas, mais aussi tous les mots qui sont à la fois des prénoms et des mots courants.
Un prénom absent de la liste. Alexandrie n’a pas été donné à 500 enfants. Dans « Alexandrie Toussaint », spaCy a vu une ville dans le prénom et l’a masqué comme un lieu, si bien que le nom de famille est resté visible. C’est le test de non-fuite de la gateway qui l’a repéré, parce qu’il cherche chaque mot séparément.
Une ville sans contexte. Paris et Caen, cités au détour d’une phrase, ont échappé à spaCy, et aucun recognizer ne cherche une ville seule. Ils identifient rarement quelqu’un à eux seuls, mais ils restent comptés comme des fuites.
Des mots d’indice trop gourmands. Presidio cherche ses mots d’indice comme des morceaux de mots, et il inclut dans sa recherche le premier mot du passage trouvé. Le mot « ami » se trouve dans « Camille », alors tout prénom qui contient « ami » reçoit le bonus de contexte d’office. De la même façon, l’indice anglais « phone » se déclenche dans « Téléphone ». Ici, ça ne fait que renforcer des détections justes. Mais un recognizer avec des mots d’indice trop courts peut se mettre à valider n’importe quoi.
À vous
Il n’est pas nécessaire d’utiliser la gateway pour essayer tout ça. L’Analyzer est un conteneur à part, avec une API HTTP.
- Lancer l’Analyzer. Depuis le repo, copiez
.env.exampleen.env, que Docker Compose lit au démarrage, puis lancezdocker compose up -d presidio-analyzer. L’image part de l’image officielle, y ajoute le modèle français et les recognizers, et répond sur le port 5002. - Analyser un texte. Envoyez le texte à
/analyzeavec"language": "fr". L’optionreturn_decision_processajoute à chaque résultat l’explication de Presidio. - Ajouter votre propre recognizer. Pour essayer un motif, passez-le directement dans la requête. Pour le garder, ajoutez-le dans
config/presidio/recognizers.yaml, ou écrivez une classe Python comme celles depii_recognizers/quand il faut un calcul.
Voici l’explication pour un NIR. Le motif seul donnait 0,3, le mot « sécurité » a été reconnu comme indice, et la clé est juste, d’où le score final de 1. Avec une clé fausse, la même requête renvoie une liste vide.
curl -s localhost:5002/analyze \
-H 'content-type: application/json' \
-d '{"text": "Mon numéro de sécurité sociale est le 2 52 07 33 063 112 03.", "language": "fr", "return_decision_process": true}' \
| jq '.[] | {entity_type, score, recognizer: .analysis_explanation.recognizer, original_score: .analysis_explanation.original_score, context: .analysis_explanation.supportive_context_word, checksum: .analysis_explanation.validation_result}' {
"entity_type": "FR_NIR",
"score": 1.0,
"recognizer": "FrNirRecognizer",
"original_score": 0.3,
"context": "sécurité",
"checksum": true
} Chaque entreprise a aussi ses propres identifiants, comme un numéro client ou un numéro de dossier, que personne d’autre ne connaît. Pour les essayer, un recognizer peut être passé directement dans la requête, avec son motif, son score de départ et ses mots d’indice. Ici, le numéro client part à 0,3 et monte à 0,65 grâce aux mots « dossier » et « client ».
curl -s localhost:5002/analyze \
-H 'content-type: application/json' \
-d '{"text": "Votre dossier CLI-2026-00042 est clos.", "language": "fr", "score_threshold": 0.4,
"ad_hoc_recognizers": [{"name": "Numéro client", "supported_language": "fr", "supported_entity": "CUSTOMER_ID",
"patterns": [{"name": "numéro client", "regex": "\\bCLI-20\\d{2}-\\d{5}\\b", "score": 0.3}],
"context": ["dossier", "client"]}]}' \
| jq -c '.[] | {entity_type, start, end, score}' {"entity_type":"CUSTOMER_ID","start":14,"end":28,"score":0.6499999999999999} Le même recognizer, écrit dans config/presidio/recognizers.yaml avec type: custom, s’applique à toutes les requêtes après un simple redémarrage du conteneur. Les recognizers en Python, eux, demandent de reconstruire l’image.
Pour finir
Presidio n’est pas un outil magique. C’est un cadre, avec un bon modèle de langue et une façon claire de combiner des règles. Pour le français, presque tout le travail consiste à écrire ces règles : des motifs pour la forme des données, des clés de contrôle quand elles existent, et des mots d’indice pour tout le reste. Avec sept recognizers et environ 300 lignes de Python, la part des données masquées passe de 59 % à 99 %, en 9 millisecondes par texte et sans aucun appel à un modèle génératif.
Le code des recognizers, leurs tests et les deux benchmarks sont sur GitHub. La commande just presidio-steps refait les mesures de cet article en une minute environ.