← Retour au blog

Privacy by design avec IA et RAG

Lucian BLETAN

Privacy by design signifie intégrer la protection des personnes dès la conception: minimiser les données, protéger les flux et contrôler les usages. Avec RAG (retrieval-augmented generation), l’enjeu est double: maîtriser ce qui entre (index, requêtes) et ce qui sort (réponses, citations, journaux). Objectif: utilité maximale, exposition minimale.

objectifs et périmètre

  • cadrer la finalité: à quoi sert l’assistant et à quoi il ne doit pas servir.
  • limiter l’entrée: n’indexer et n’envoyer que l’utile.
  • filtrer la sortie: supprimer les PII résiduelles et forcer les citations.
  • prouver la conformité: journaux, rétention courte, droit à l’oubli.

privacy by design rag

minimiser

champs utiles

masquage en amont

rétention courte

protéger

chiffrement

rls cls

journalisation

contrôler

sources en liste blanche

citations obligatoires

refus hors périmètre

corriger

réclamations

droit à l'oubli

post mortem

minimiser

  • n’envoyer que les champs utiles à la tâche (ex: id, sujet, catégorie).
  • masquer les PII bien avant l’inférence (email, téléphone, adresse).
  • définir une rétention courte et automatique des journaux.
-- champs autorisés vers le pipeline RAG (exemple)
SELECT id, subject, category
FROM tickets
WHERE pii_masked = true
  AND updated_at >= NOW() - INTERVAL '90 day';
ingest_policy:
  purpose: "support"
  allow_fields: ["id","subject","category","created_at"]
  deny_fields: ["email","phone","address","iban"]
  retention_days: 30

protéger

  • chiffrer au repos et en transit (TLS et clés managées).
  • appliquer des contrôles d’accès par rôles (RBAC) et RLS/CLS côté entrepôt.
  • centraliser les journaux d’accès, sans PII, avec rétention courte.
-- vue masquée pour CLS simple
CREATE OR REPLACE VIEW support.tickets_view AS
SELECT
  id,
  subject,
  category,
  CASE WHEN has_privilege('see_email') THEN email ELSE 'masked' END AS email
FROM support.tickets_src;

-- RLS: lecture par périmètre d'équipe
ALTER TABLE support.tickets_src ENABLE ROW LEVEL SECURITY;
CREATE POLICY rls_support ON support.tickets_src
  FOR SELECT TO role_support
  USING (team = current_setting('app.team', true));

contrôler

  • restreindre les sources: seul l’index autorisé est interrogeable.
  • imposer les citations: chaque réponse doit référencer les documents utilisés.
  • refuser poliment hors périmètre: mieux vaut ne pas répondre que halluciner.
rag_policy:
  source_allowlist: ["support_faq_v2","kb_internal_v1"]
  max_chunks: 6
  require_citations: true
  outguard:
    pii_redaction: true
    banned_patterns: ["num_secu","iban","carte_bancaire"]
  refusals:
    out_of_scope_message: "Désolé, cette demande dépasse le périmètre défini."

pipeline RAG avec contrôles (vue verticale)

entrée utilisateur

normalisation

filtre pii en entrée

recherche sur index autorisé

génération avec citations

filtre de sortie

journaux sans pii, ttl court

réponse à l'utilisateur

boucle de correction

  • canal de réclamation simple (lien “corriger ceci” dans l’interface).
  • droit à l’oubli: suppression dans l’index et propagation vers les caches.
  • post-mortem après incident: causes, correctifs, prévention.
-- droit à l'oubli: marquer et purger de l'index
UPDATE support.kb_internal_v1
SET forget_flag = true, forget_ts = NOW()
WHERE doc_id = $1;

-- job de purge périodique
DELETE FROM support.kb_internal_v1
WHERE forget_flag = true
  AND NOW() - forget_ts >= INTERVAL '1 day';

métriques et SLO de confidentialité

  • taux de réponses avec citation valide.
  • fuites PII détectées par le filtre de sortie (objectif: 0).
  • délai de traitement droit à l’oubli (SLO, ex: 24 h).
  • couverture de l’allowlist (toutes les requêtes proviennent de sources autorisées).
privacy_slo:
  citations_rate_min: 0.98
  pii_leak_rate_max: 0.0
  forget_request_mttr_hours: 24
  allowlist_coverage: 1.0
-- suivi du taux de citations (extrait)
SELECT DATE_TRUNC('day', ts) AS d,
       AVG(CASE WHEN citations >= 1 THEN 1 ELSE 0 END) AS citations_rate
FROM rag.responses_metrics
WHERE ts >= NOW() - INTERVAL '30 day'
GROUP BY 1 ORDER BY 1;

exemples complets

cas 1: masquage d’email et de téléphone en entrée

import re

EMAIL = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
PHONE = re.compile(r"\+?\d[\d\s\-]{7,}\d")

def mask_input(text: str) -> str:
    text = EMAIL.sub("[email_masqué]", text)
    text = PHONE.sub("[téléphone_masqué]", text)
    return text

cas 2: vérification des citations en sortie

def has_valid_citations(answer, citations):
    if not citations:
        return False
    # Exige au moins un identifiant de source référencé dans la réponse
    return any(cid in answer for cid in citations)

cas 3: configuration d’un index limité

index_config:
  name: "support_faq_v2"
  include_paths:
    - "faq/howto/"
    - "faq/policies/"
  exclude_paths:
    - "drafts/"
    - "personnel/"
  tokenizer: "fr_core_news_md"
  max_chunk_tokens: 512

bonnes pratiques

  • écrire des politiques simples et testables; valider en CI la présence des champs critiques.
  • tracer les versions de l’index et des filtres; consigner les changements.
  • privilégier des messages de refus clairs plutôt qu’une réponse incertaine.
  • vérifier régulièrement la dérive des journaux: volumes, champs, durée de rétention.
  • aligner les SLO privacy avec la sécurité et le juridique (mêmes seuils, même calendrier).

pièges

  • copier “tout” vers l’index -> fuite -> minimiser strictement et masquer en amont.
  • logs verbeux -> PII en clair -> filtrer avant stockage, rétention courte.
  • sources non revues -> RAG à risque -> revue éditoriale et étiquettes “autorisé”.
  • citations optionnelles -> opacité -> rendre les citations obligatoires et contrôlées.
  • droit à l’oubli lent -> non-conformité -> mécaniser marquage et purge, SLO de 24 h.

faq

  • Comment limiter l’entrée sans perdre en qualité ? Définissez une allowlist de champs et de répertoires; masquez les PII; gardez des extraits courts mais pertinents.

  • Faut-il toujours imposer des citations ? Oui pour les usages décisionnels ou sensibles: elles apportent traçabilité et facilitent l’audit.

  • Que faire si une requête sort du périmètre ? Répondre par un refus explicite et proposer ce que l’assistant peut faire dans son périmètre.

  • Comment prouver la conformité au quotidien ? Exposez des métriques privacy (citations, fuites PII, MTTR oubli) et conservez des journaux sans PII avec TTL court.

  • Puis-je anonymiser au lieu de pseudonymiser pour l’index ? Préférez le masquage sélectif et la minimisation; l’anonymisation complète est souvent difficile à garantir en libre texte.