Deep-dive technique · 2026-05-25

AI observability : redaction PII à la source, les trois invariants stricts et ce que nous ne faisons pas délibérément

Journaliser les appels LLM d'une manière utile pour le debugging ET conforme au RGPD, à l'AI Act et à NIS2. Redaction PII côté client, evidence packs signés Ed25519, et ce que ce n'est pas.

Quand votre backend appelle un LLM — OpenAI, Claude, Mistral, Azure OpenAI — vous avez un problème de logging différent de celui des appels API traditionnels. Un appel REST vers un fournisseur de paiement vous log un request-ID et un status-code. Un appel LLM log potentiellement le texte intégral qu'un utilisateur final a saisi, y compris tout ce qu'il y a dedans.

Le RGPD appelle ça « données personnelles ». L'AI Act article 12 appelle ça « événements générés par le système IA ». NIS2 appelle ça « audit trail ». Trois régulations, un problème : comment loguer les appels LLM d'une manière utile pour le debugging ET conforme aux trois ?

C'est ce que fait le module AI observability monsys — et ce qu'il ne fait pas délibérément.

Les trois invariants stricts

Trois principes non négociables, quel que soit le client ou la configuration :

1. Passif, jamais autonome monsys n'exécute aucun prompt, ne bloque jamais inline, ne prend jamais de décisions de lui-même sur la base de ce qui est logué. C'est une couche d'observability — de la preuve a posteriori, pas un control plane. Si vous avez besoin de filtrage inline contre les prompt-injections ou de guardrails, combinez-nous avec Lakera ou Protect AI.

2. PII redactée à la source IBAN belge, registre national, BTW-BE, KBO, NL BSN, FR NIR, e-mails et numéros de téléphone sont reconnus avec validation par checksum avant stockage. Le contenu brut n'atteint jamais la base de données.

3. Evidence packs avec Ed25519 Chaque session de logging est vérifiable offline via une tarball signée. Un inspecteur DPA sans compte monsys peut prouver que les données n'ont pas été modifiées.

La spec wire envelope : ce qui va de votre code vers le hub

Quand votre SDK ferme un span, un seul HTTPS POST est envoyé :

{
  "schema_version": "1",
  "app_token": "aiv_...",
  "trace_id": "0e22f4a1-...",
  "span_id": "b3c1d2e4-...",
  "span_name": "openai.chat",
  "provider": "openai",
  "model": "gpt-4o",
  "prompt_hash": "sha256:a3f2c1...",
  "completion_hash": "sha256:b7e4d2...",
  "prompt_text": "Quel est le solde de l'IBAN [REDACTED-IBAN-BE] à ce jour ?",
  "completion_text": "Le solde de votre compte [REDACTED-IBAN-BE] est de 1 847,32 €.",
  "input_tokens": 23,
  "output_tokens": 18,
  "cost_eur": 0.000041,
  "pii_hits": [
    {
      "type": "IBAN_BE",
      "offset_start": 28,
      "offset_end": 46,
      "token": "sha256:c1d2e3f4..."
    }
  ],
  "pii_hits_count": 1,
  "started_at": "2026-05-25T09:14:02.341Z",
  "ended_at": "2026-05-25T09:14:03.887Z"
}

Remarquez : prompt_text et completion_text contiennent [REDACTED-IBAN-BE] — pas l'IBAN original. La redaction se fait dans le SDK, côté client, avant l'envoi du POST. Le hub ne voit jamais la valeur brute.

prompt_hash et completion_hash sont les hashes SHA256 du texte original (avant redaction). Vous pouvez utiliser ces hashes plus tard pour prouver que deux spans avaient le même prompt, sans conserver le contenu du prompt lui-même.

Détection PII : comment fonctionne la validation par checksum

Pour la détection IBAN, un regex ne suffit pas — BE68539007547034 ressemble à un IBAN, mais BE00123456789012 n'en est pas un (échec checksum). monsys utilise la checksum mod-97 ISO 13616 :

def validate_iban(iban: str) -> bool:
    # Déplacer les 4 premiers caractères vers la fin
    rearranged = iban[4:] + iban[:4]
    # Remplacer les lettres par des chiffres (A=10, B=11, ...)
    numeric = ''.join(
        str(ord(c) - ord('A') + 10) if c.isalpha() else c
        for c in rearranged
    )
    # Check mod-97
    return int(numeric) % 97 == 1

Même principe pour le numéro de registre national (mod-97), BTW-BE (mod-97), KBO (mod-97), NL BSN (mod-11), FR NIR (mod-97).

Cela élimine les faux positifs : un numéro qui ressemble par hasard à un IBAN mais échoue la checksum n'est pas redacté.

Ce qui n'est pas encore couvert :

Ces éléments sont sur la roadmap Q3-Q4 2026. Nous le nommons explicitement parce que « redaction PII » est une promesse dangereusement vague si vous ne dites pas quels types de PII vous couvrez.

Le SDK : ~150 lignes par langage, sans dépendances

Le SDK Python (et les équivalents Node/Go) a une seule dépendance externe : cryptography pour la signature Ed25519 du payload de span. C'est tout.

# monsys_ai/tracer.py — simplifié
import hashlib, json, time
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Optional
import httpx

@dataclass
class Span:
    name: str
    provider: str
    model: str
    prompt: Optional[str] = None
    completion: Optional[str] = None
    input_tokens: Optional[int] = None
    output_tokens: Optional[int] = None
    _started_at: float = field(default_factory=time.time)

    def _redact_pii(self, text: str) -> tuple[str, list]:
        """Redacte PII côté client avant transmission."""
        hits = []
        result = text
        import re
        iban_pattern = re.compile(r'\bBE\d{2}[\s]?\d{4}[\s]?\d{4}[\s]?\d{4}\b')
        for match in iban_pattern.finditer(result):
            candidate = match.group().replace(' ', '')
            if self._validate_iban(candidate):
                hits.append({"type": "IBAN_BE", "token": hashlib.sha256(candidate.encode()).hexdigest()})
                result = result[:match.start()] + "[REDACTED-IBAN-BE]" + result[match.end():]
        return result, hits

Le SDK n'échoue jamais : si le POST HTTP vers le hub échoue (timeout, erreur réseau), c'est logué mais le code appelant ne reçoit aucune exception. La fonctionnalité LLM de votre application n'est jamais bloquée par la couche observability.

Alertes : quand le système vous réveille

Quatre types d'alertes pour l'AI observability :

Cost spike : cost_per_minute > threshold_eur. Défaut : 1 €/minute. Configurable par app. Tire via ntfy/webhook, pas par e-mail (trop lent pour un bug de prompt qui brûle 500 €/heure).

Refusal rate : refusal_count / total_spans > threshold sur une fenêtre glissante de 15 minutes. Le hub détecte les refus en regardant si la completion commence par des motifs comme « I cannot », « I'm sorry, I can't », ou des équivalents NL/FR. Pas parfait, mais suffisant pour signaler la dérive de modèle.

PII rate : pii_hits_count / total_spans > threshold. Si tout à coup 40 % de vos spans ont des hits PII alors que c'est normalement 2 %, quelque chose a changé dans la manière dont les utilisateurs finaux utilisent l'application.

Anomalie Z-score : même méthode z-score que la surveillance serveur (|z| > 2.5). Calculée sur cost_per_span, input_tokens_per_span, duration_ms par app par modèle. Capte les régressions de prompt qui font monter les coûts sans dépasser un seuil absolu.

Evidence packs pour l'AI Act article 12

L'article 12 de l'AI Act exige que les systèmes IA à haut risque conservent automatiquement des logs de leur fonctionnement, y compris les données d'entrée dans la mesure où cela permet d'identifier la cause des problèmes.

Un evidence pack monsys pour l'AI observability contient par période :

evidence_<tenant>_<period>.tar.gz
├── manifest.json              ← période, tenant, inputs_hash
├── manifest.sig               ← signature Ed25519
├── signing_key.pub
├── verify.py
└── data/
    └── ai_traces/
        ├── spans.ndjson       ← tous les spans, PII redactée
        ├── pii_summary.json   ← agrégat : X hits de type Y
        ├── cost_summary.json  ← coût par app, par modèle, par jour
        └── alerts.ndjson      ← cost spikes, refusals, anomalies

Le spans.ndjson contient les textes redactés ET les hashes SHA256 des textes originaux. Un régulateur peut prouver qu'un span spécifique a traité l'IBAN d'un client (le hash correspond), sans voir l'IBAN lui-même.

Pour le scénario où vous avez bien besoin du contenu original (ex : traitement de plainte) : la fonction TOTP-gated « unlock content » donne un accès unique au span non redacté, journalisé dans l'audit trail.

Ce que ce n'est pas

Nous le nommons explicitement parce que le marché est plein de produits qui promettent « la conformité IA » sans dire ce qu'ils offrent réellement :

La valeur est la preuve, pas la prévention. Pour la prévention, vous avez besoin d'autres outils.


L'AI observability est documentée dans docs.monsys.ai/fr/ai/quick-start. Première app IA gratuite par tenant : monsys.ai/fr/ai.

Retour au blog