Aller au contenu principal

Conformité RGPD et protection des données

Mis à jour le 29 juillet 2026

IA et données personnelles : un cadre strict

Il y a un paradoxe au cœur de cette leçon, et le voir clairement évite bien des erreurs de conception : votre système de modération est lui-même un traitement de données personnelles. Pour protéger vos utilisateurs, vous analysez leurs textes, vous produisez des scores, vous conservez des journaux — autant d’opérations que le RGPD encadre exactement comme n’importe quel autre traitement.

La conformité ne consiste donc pas à ajouter une couche de protection par-dessus la modération, mais à concevoir la modération elle-même pour qu’elle traite le minimum de données, le moins longtemps possible, avec une trace de ses décisions. C’est le fil conducteur des sections qui suivent.

Le RGPD (Règlement Général sur la Protection des Données) s’applique pleinement aux applications d’IA qui traitent des données de résidents européens. La modération de contenu elle-même génère des données qui tombent sous le scope du RGPD. Vous devez comprendre vos obligations pour éviter des sanctions pouvant atteindre 4% du chiffre d’affaires mondial.

La catégorie PII dans Mistral Moderation

La catégorie pii du classifieur Mistral détecte les demandes ou divulgations d’informations personnelles. Un point d’architecture mérite d’être posé d’emblée : ce classifieur est un filet, pas une garantie. Il attrape les formulations variées qu’une regex manquerait (« mon numéro c’est le… »), mais comme tout modèle il a ses faux négatifs. À l’inverse, une regex attrape à coup sûr les formats structurés — IBAN, numéro de sécurité sociale, carte bancaire — mais rien d’autre. La bonne pratique combine les deux : regex pour les formats connus, classifieur pour le reste. Les types détectés :

  • Noms et adresses
  • Numéros de sécurité sociale
  • Numéros de carte bancaire
  • Adresses email et téléphones
  • Données de santé
  • Données biométriques
from mistralai import Mistral
import os
import re

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

def detecter_pii(texte: str) -> dict:
    """Détecte les données personnelles dans un texte."""
    # Couche 1 : Classifieur Mistral
    response = client.classifiers.moderate(
        model="mistral-moderation-2603",
        inputs=[texte]
    )
    pii_score = response.results[0].category_scores.get("pii", 0)

    # Couche 2 : Regex pour les patterns courants
    patterns_pii = {
        "email": r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
        "telephone_fr": r"(?:0|\+33)[1-9](?:\d{2}){4}",
        "secu_sociale": r"\d{13,15}",
        "carte_bancaire": r"\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}",
        "iban": r"FR\d{2}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\s?\d{3}"
    }

    pii_detectees = {}
    for nom, pattern in patterns_pii.items():
        matches = re.findall(pattern, texte)
        if matches:
            pii_detectees[nom] = len(matches)

    return {
        "score_classifieur": round(pii_score, 3),
        "pii_regex": pii_detectees,
        "contient_pii": pii_score > 0.2 or len(pii_detectees) > 0
    }

Anonymisation avant traitement

L’anonymisation en amont est la mesure la plus rentable de toute cette leçon, parce qu’elle change la nature du problème : une donnée qui ne quitte jamais votre infrastructure ne pose aucune question de transfert. Remplacer noms, e-mails et numéros par des jetons ([PERSONNE_1], [EMAIL_1]) avant l’appel au modèle préserve presque toujours le sens pour l’analyse — et si vous conservez la table de correspondance localement, vous pouvez réinjecter les valeurs réelles dans la réponse.

def anonymiser_texte(texte: str) -> tuple:
    """Anonymise un texte et conserve un mapping de restauration."""
    mapping = {}
    texte_anonyme = texte

    # Anonymiser les emails
    emails = re.findall(
        r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
        texte
    )
    for i, email in enumerate(set(emails)):
        placeholder = f"[EMAIL_{i+1}]"
        mapping[placeholder] = email
        texte_anonyme = texte_anonyme.replace(email, placeholder)

    # Anonymiser les téléphones
    telephones = re.findall(
        r"(?:0|\+33)[1-9](?:[\s.-]?\d{2}){4}",
        texte
    )
    for i, tel in enumerate(set(telephones)):
        placeholder = f"[TEL_{i+1}]"
        mapping[placeholder] = tel
        texte_anonyme = texte_anonyme.replace(tel, placeholder)

    # Anonymiser les noms (si détectés par NER en amont)
    # Ajoutez votre propre logique NER ici

    return texte_anonyme, mapping

def restaurer_texte(texte_anonyme: str, mapping: dict) -> str:
    """Restaure les données anonymisées après traitement."""
    texte_restaure = texte_anonyme
    for placeholder, valeur in mapping.items():
        texte_restaure = texte_restaure.replace(placeholder, valeur)
    return texte_restaure

# Utilisation dans le pipeline
texte_original = "Contactez [email protected] au 06 12 34 56 78"
texte_anonyme, mapping = anonymiser_texte(texte_original)
# → "Contactez [EMAIL_1] au [TEL_1]"

# Envoyer au modèle le texte anonymisé
response = client.chat.complete(
    model="mistral-large-latest",
    messages=[{"role": "user", "content": texte_anonyme}]
)

Logs de modération conformes RGPD

Les journaux sont le point aveugle classique : on pense « données personnelles » pour les messages des utilisateurs, rarement pour les traces techniques qui les copient. Or un log de modération qui conserve le texte analysé est une base de données personnelles, avec toutes les obligations qui suivent. La règle pratique : ne journaliser le texte que pour les contenus bloqués — vous en aurez besoin en cas de contestation — et seulement les scores pour les contenus acceptés.

import hashlib
from datetime import datetime

class LogModerationRGPD:
    """Logger de modération conforme RGPD."""

    def __init__(self, retention_jours: int = 90):
        self.retention_jours = retention_jours

    def _pseudonymiser(self, user_id: str) -> str:
        """Pseudonymise un identifiant utilisateur."""
        salt = os.environ.get("PII_SALT", "default_salt")
        return hashlib.sha256(
            f"{user_id}{salt}".encode()
        ).hexdigest()[:16]

    def log_moderation(self, user_id: str, scores: dict,
                       decision: str, texte: str = None) -> dict:
        """Enregistre une décision de modération."""
        entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "user_hash": self._pseudonymiser(user_id),
            "scores": scores,
            "decision": decision,
            # Ne PAS stocker le texte complet par défaut
            "texte_longueur": len(texte) if texte else 0,
            "retention_jusqu_a": (
                datetime.utcnow().isoformat()  # + retention
            )
        }

        # Stocker le texte uniquement si bloqué (pour audit)
        if decision == "blocked" and texte:
            entry["texte_tronque"] = texte[:200]

        return entry

    def purger_anciens_logs(self, logs: list) -> list:
        """Purge les logs au-delà de la durée de rétention."""
        cutoff = datetime.utcnow()  # - timedelta(days=retention)
        return [
            log for log in logs
            if log.get("retention_jusqu_a", "") > cutoff.isoformat()
        ]

Audit trail pour la conformité

L’audit trail répond à une question précise qu’un utilisateur ou une autorité peut vous poser : « pourquoi ce contenu a-t-il été bloqué ? ». Sans trace, votre seule réponse honnête est « je ne sais plus » — intenable dès lors que le RGPD donne un droit d’explication sur les décisions automatisées, et que l’AI Act renforce cette exigence. L’audit trail conserve la décision, les scores qui l’ont motivée et la version de la politique appliquée — sans nécessairement conserver le texte lui-même.

class AuditTrail:
    """Audit trail pour les décisions de modération."""

    def __init__(self, storage_path: str):
        self.storage_path = storage_path

    def enregistrer_decision(self, data: dict) -> str:
        """Enregistre une décision avec un identifiant unique."""
        import uuid

        audit_id = str(uuid.uuid4())
        entry = {
            "audit_id": audit_id,
            "timestamp": datetime.utcnow().isoformat(),
            "type_decision": data.get("decision"),
            "modele_moderation": "mistral-moderation-2603",
            "scores": data.get("scores"),
            "seuils_appliques": data.get("seuils"),
            "politique_version": data.get("politique_version"),
            "action_prise": data.get("action"),
            "base_legale": "interet_legitime",
            "responsable": "systeme_automatise"
        }

        # Écrire dans le fichier d'audit
        import json
        with open(self.storage_path, "a") as f:
            f.write(json.dumps(entry, ensure_ascii=False) + "\n")

        return audit_id

Checklist RGPD pour votre application IA

Voici les points à vérifier pour assurer la conformité de votre système de modération :

Base légale

  • Intérêt légitime documenté pour la modération de contenu
  • Information des utilisateurs sur le traitement automatisé
  • Possibilité de contester une décision automatisée

Minimisation des données

  • Ne stocker que les données strictement nécessaires
  • Anonymiser ou pseudonymiser les identifiants utilisateur
  • Tronquer les textes dans les logs de modération
  • Ne pas conserver les textes acceptés (uniquement les scores)

Durée de conservation

  • Définir une durée de rétention pour chaque type de donnée
  • Logs de modération : 90 jours maximum (sauf obligation légale)
  • Audit trail : selon la réglementation sectorielle
  • Purge automatique programmée

Droits des utilisateurs

  • Droit d’accès aux décisions de modération les concernant
  • Droit de rectification si la décision est contestée
  • Droit à l’explication de la décision automatisée

Points clés à retenir

  • Le RGPD s’applique pleinement à vos systèmes de modération IA
  • Utilisez la catégorie pii de Mistral ET des regex pour détecter les données personnelles
  • Anonymisez les textes avant de les envoyer au modèle quand possible
  • Pseudonymisez les identifiants utilisateur dans les logs
  • Implémentez un audit trail pour la traçabilité des décisions
  • Définissez une durée de rétention et purgez automatiquement les logs