PII redaction
Detecting and masking personal data (names, emails, IDs, health details) before it reaches a model, a log or a vendor; recall matters more than precision.
PII redaction finds personally identifiable information, such as names, email addresses, phone numbers, account numbers or medical details, and replaces it with placeholders like <EMAIL_1> before text is sent to a model provider, written to logs, or used in evals.
It limits what can leak if a vendor, a log store or a debugging session is compromised, and it is often required by privacy laws (GDPR, HIPAA, India's DPDP Act) and customer contracts.
Blacking out names and account numbers on documents before handing them to an outside consultant, while keeping a key so you can put them back in the final letter.
Redacting prompts sent to the model but logging the raw request and response in the observability tool. The log store, with broad engineer access and long retention, is usually the bigger exposure.
Detection is layered: regex plus checksums for structured identifiers (emails, phone numbers in E.164, card numbers with Luhn, IBANs, national IDs per region), NER models for names, addresses and organisations (Microsoft Presidio with spaCy or transformer recognisers; GLiNER-style models), and domain dictionaries for things like medical record numbers. Tune for recall: a missed SSN is an incident, an over-masked city name is a minor quality hit.
Decide where to redact: at the LLM gateway before the provider (protects against vendor exposure), at trace capture before Observability & traces storage (protects logs and the Data flywheel), or both. Choose how: deterministic placeholders (<PERSON_1>) with a reversible vault when the answer must reference the person; irreversible masking for logs. Consistent tokens per conversation let the model reason ('<PERSON_1> is <PERSON_2>'s manager'). Redaction does not stop the model from inferring identity from context, and it does not cover PII the model memorised from Training data (Lukas et al. 2023). Evaluate the redactor like any classifier: per-entity recall and precision on a labelled set from your own traffic, re-measured when traffic changes.
Microsoft Presidio (regex + checksum recognisers, spaCy en_core_web_lg or a transformer NER) at the gateway and at trace capture; per-region recognisers for national IDs and phone formats; deterministic per-conversation placeholders with a short-lived encrypted vault for re-insertion; irreversible masking in logs; 30–90 day log retention; per-entity recall measured on 500 labelled samples from your traffic.
Clinical intake notes sent to a hosted model for summarisation. Pipeline at the gateway: regex+checksum for phone, email, insurance member ID (recall 0.99); Presidio with a transformer NER for names and addresses (recall 0.93 on 500 labelled notes); dictionary for the clinic's MRN format. Names become <PATIENT> and <CLINICIAN_1>, re-inserted from a per-request vault after generation. Logs keep only the masked version. Missed-entity audit: 7 misses in 500 notes, all unusual surnames in free text; added a second NER pass with a lower threshold for name-like tokens near 'patient' or 'Mr/Ms'.
Say: "I redact at two points, the gateway for vendors and the trace pipeline for logs, tune for recall, and measure the redactor per entity type on our own data." Expect: "what does redaction not protect against?": inference from context, memorised training data, and PII in retrieved documents you didn't route through the redactor.
Numbers worth knowing
- Recall target, structured IDs
- ≥0.99email, phone, card (Luhn), IBAN via regex + checksum
- Recall target, names and addresses
- ≥0.90–0.95NER on free text; varies by language and domain, measure yours
- Added latency
- ~10–50 ms per 2 KB messagePresidio with a spaCy model on one CPU core; transformer NER on GPU similar
How to decide
| When | Do this |
|---|---|
| The answer must mention the person (letters, summaries) | Use reversible placeholders with a per-request vault and re-insert after generation. |
| Text is only stored for debugging or evals | Mask irreversibly at capture; no vault. |
| The provider contract offers zero data retention and regional processing | Still redact logs; vendor terms do not protect your own trace store. |
Cost maths
Compute is small: one CPU core handles ~20–100 messages/s with Presidio + spaCy, so 5M messages/day ≈ 1–3 cores ≈ $50–150/month. The costly part is labelling 500 samples per locale to measure recall (~10 h of reviewer time) and re-doing it when traffic shifts.
Operating it
- New locale or language launched: add region-specific recognisers and re-measure recall before go-live.
- Engineers need raw traces for debugging more than weekly: build a break-glass access path with audit logging instead of loosening redaction.
- Multi-tenant deployment with per-customer data policies: make redaction policy a per-tenant config at the gateway.
- Nightly DLP scan of the trace store: page on any structured-ID match.
- Redactor recall per entity on a monthly labelled sample: alert below target.
- Share of requests bypassing the redactor (direct provider calls): alert on any non-zero value.
When it goes wrong
A security review of a support assistant found full card numbers and home addresses in 3 months of observability traces, readable by ~60 engineers.
A DLP scan of the tracing backend run as part of an annual audit.
Redaction ran in the gateway on the outbound provider request, but the tracing SDK captured the raw request one layer earlier, before the gateway.
Moved redaction into the trace exporter as well, purged and re-ingested masked traces, added a nightly DLP scan on the trace store with an alert on any match.
Where it shows up
Sources
- Presidio: Data Protection and De-identification SDK — Microsoft, 2024docs
- Analyzing Leakage of Personally Identifiable Information in Language Models — Lukas et al., 2023paper
- OWASP Top 10 for LLM Applications — OWASP GenAI Security Project, 2025spec
Check yourself
Three questions. Every option is explained once you answer.