AI observability: PII-redactie aan de bron, de drie harde invariants en wat we bewust niet doen
LLM-calls loggen op een manier die bruikbaar is voor debugging én compliant met GDPR, AI Act en NIS2. PII-redactie client-side, Ed25519-getekende evidence packs, en wat dit niet is.
Wanneer je backend een LLM aanroept — OpenAI, Claude, Mistral, Azure OpenAI — heb je een logging-probleem dat anders is dan bij traditionele API-calls. Een REST-call naar een payment provider logt je een request-ID en een status-code. Een LLM-call logt potentieel de volledige tekst die een eindgebruiker heeft ingevoerd, inclusief alles wat daarin zit.
GDPR noemt dit "persoonsgegevens". AI Act artikel 12 noemt dit "events die het AI-systeem genereert". NIS2 noemt dit "audit trail". Drie regelgevingen, één probleem: hoe log je LLM-calls op een manier die bruikbaar is voor debugging én compliant is met alle drie?
Dit is wat de monsys AI observability module doet — en wat ze bewust niet doet.
De drie harde invariants
Drie principes die niet onderhandelbaar zijn, ongeacht klant of configuratie:
1. Passief, nooit autonoom monsys voert geen prompts uit, blokkeert nooit inline, neemt nooit zelf beslissingen op basis van wat er gelogd wordt. Het is een observability-laag — bewijsmateriaal achteraf, geen control plane. Als je inline prompt-injection-filtering of guardrails nodig hebt, combineer ons met Lakera of Protect AI.
2. PII geredacteerd aan de bron Belgische IBAN, Rijksregister, BTW-BE, KBO, NL BSN, FR NIR, e-mails en telefoonnummers worden herkend met checksum-validatie vóór opslag. Raw inhoud bereikt de database nooit.
3. Evidence packs met Ed25519 Elke loggingsessie is offline verifieerbaar via een getekende tarball. Een DPA-inspecteur zonder monsys-account kan bewijzen dat de data niet is aangepast.
De wire envelope spec: wat er van jouw code naar de hub gaat
Wanneer je SDK een span sluit, wordt één HTTPS POST verstuurd:
{
"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": "Wat is het saldo van IBAN [REDACTED-IBAN-BE] per vandaag?",
"completion_text": "Het saldo van uw rekening [REDACTED-IBAN-BE] is €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"
}
Merk op: prompt_text en completion_text bevatten [REDACTED-IBAN-BE] — niet het originele IBAN. De redactie gebeurt in de SDK, client-side, voordat de POST verstuurd wordt. De hub ziet de raw waarde nooit.
prompt_hash en completion_hash zijn de SHA256-hashes van de originele tekst (vóór redactie). Die hashes kun je gebruiken om later te bewijzen dat twee spans dezelfde prompt hadden, zonder de promptinhoud zelf te bewaren.
PII-detectie: hoe de checksum-validatie werkt
Voor IBAN-detectie is een regex niet voldoende — BE68539007547034 ziet eruit als een IBAN, maar BE00123456789012 is er geen (checksum faalt). monsys gebruikt de ISO 13616 mod-97 checksum:
def validate_iban(iban: str) -> bool:
# Verplaats de eerste 4 karakters naar achteren
rearranged = iban[4:] + iban[:4]
# Vervang letters door cijfers (A=10, B=11, ...)
numeric = ''.join(
str(ord(c) - ord('A') + 10) if c.isalpha() else c
for c in rearranged
)
# Mod-97 check
return int(numeric) % 97 == 1
Hetzelfde principe voor Rijksregisternummer (mod-97), BTW-BE (mod-97), KBO (mod-97), NL BSN (mod-11), FR NIR (mod-97).
Dit elimineert false positives: een nummer dat toevallig op een IBAN lijkt maar de checksum faalt, wordt niet geredacteerd.
Wat nog niet gedekt is:
- DE Steuer-ID
- IT Codice Fiscale
- ES NIF
- US SSN
- Kredietkaartnummers (Luhn-check beschikbaar maar nog niet geïmplementeerd)
Deze staan op de roadmap voor Q3-Q4 2026. We benoemen dit expliciet omdat "PII-redactie" een gevaarlijk vage belofte is als je niet zegt welke PII-types je bedoelt.
De SDK: ~150 regels per taal, geen dependencies
De Python SDK (en Node/Go equivalent) heeft één externe dependency: cryptography voor de Ed25519-signing van het span-payload. Dat is het.
# monsys_ai/tracer.py — vereenvoudigd
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]:
"""Redacteer PII client-side voor verzending."""
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
De SDK faalt nooit: als de HTTP POST naar de hub mislukt (timeout, network error), wordt het gelogd maar de aanroepende code krijgt geen exception. De LLM-functionaliteit van je applicatie wordt nooit geblokkeerd door de observability-laag.
Alerts: wanneer het systeem je wekt
Vier alert-types voor AI observability:
Cost-spike: cost_per_minute > threshold_eur. Standaard: €1/minuut. Configureerbaar per app. Vuurt via ntfy/webhook, niet als e-mail (te traag voor een prompt-bug die €500/uur verbrandt).
Refusal-rate: refusal_count / total_spans > threshold over een sliding window van 15 minuten. De hub detecteert refusals door te kijken of de completion begint met patronen als "I cannot", "I'm sorry, I can't", of equivalenten in NL/FR. Niet perfect, maar goed genoeg om model-drift te signaleren.
PII-rate: pii_hits_count / total_spans > threshold. Als plots 40% van je spans PII-hits heeft waar dat normaal 2% is, is er iets veranderd in hoe eindgebruikers de applicatie gebruiken.
Z-score anomalie: dezelfde z-score methode als de server-monitoring (|z| > 2.5). Berekend over cost_per_span, input_tokens_per_span, duration_ms per app per model. Vangt prompt-regressions die de kosten opdrijven zonder een absolute drempel te overschrijden.
Evidence packs voor AI Act artikel 12
Artikel 12 van de AI Act vereist dat high-risk AI-systemen automatisch logs bijhouden van de werking, inclusief de invoer-data voor zover dat de identificatie van de oorzaak van problemen mogelijk maakt.
Een monsys evidence pack voor AI observability bevat per periode:
evidence_<tenant>_<period>.tar.gz
├── manifest.json ← periode, tenant, inputs_hash
├── manifest.sig ← Ed25519 signature
├── signing_key.pub
├── verify.py
└── data/
└── ai_traces/
├── spans.ndjson ← alle spans, PII-geredacteerd
├── pii_summary.json ← aggregaat: X hits van type Y
├── cost_summary.json ← kosten per app, per model, per dag
└── alerts.ndjson ← cost-spikes, refusals, anomalies
De spans.ndjson bevat de geredacteerde teksten én de SHA256-hashes van de originele teksten. Een toezichthouder kan bewijzen dat een specifieke span de IBAN van een klant heeft verwerkt (hash matcht), zonder de IBAN zelf te zien.
Voor het scenario waarbij je wél de originele inhoud nodig hebt (bijv. voor klachtbehandeling): de TOTP-gated "unlock content" functie geeft eenmalig toegang tot de unredacted span, gelogd in het audit trail.
Wat dit niet is
We benoemen dit expliciet omdat de markt vol is met producten die "AI compliance" beloven zonder te zeggen wat ze precies bieden:
- Geen NIS2-certificering — die bestaat niet voor leveranciers. Wij helpen met artikel 21 bewijslast.
- Geen DPIA-vervanger — wij zijn één input voor je impact assessment.
- Geen guardrail of prompt-injection filter — passieve audit-laag, nooit blokkerend inline.
- Geen detectie van jailbreaks — we loggen refusals als het model weigert; misuse-patroon-detectie valt buiten scope.
- Geen garantie op wat OpenAI/Anthropic doet met jouw data — dat valt onder jouw contract met hen.
De waarde is bewijsvoering, niet preventie. Voor preventie heb je andere tools.
AI observability is gedocumenteerd in docs.monsys.ai/nl/ai/quick-start. Eerste AI-app gratis per tenant: monsys.ai/nl/ai.