AAI Agentic Handbook
Evals & LLMOps

PII redaction

Mid levelReviewed Oct 2026In 7 interview questionsIn 3 posts

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.

In plain English

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.

Analogy

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.

Common pitfall

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.

How it actually works

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.

What I'd ship on day one

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.

Worked example

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'.

In the interview

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.

01Raw textapp or retrieved doc
02Regex + checksumemail, card, IBAN
03NER passnames, addresses
04Placeholders<PERSON_1> + vault
05Model / logsmasked only
06Re-insertonly where needed
Cheap exact patterns first, models for the fuzzy entities, and only masked text leaves the boundary.

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

WhenDo 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 evalsMask irreversibly at capture; no vault.
The provider contract offers zero data retention and regional processingStill 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

Time to scale up when…
  • 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.
What to monitor
  • 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

What users saw

A security review of a support assistant found full card numbers and home addresses in 3 months of observability traces, readable by ~60 engineers.

How it was caught

A DLP scan of the tracing backend run as part of an annual audit.

Root cause

Redaction ran in the gateway on the outbound provider request, but the tracing SDK captured the raw request one layer earlier, before the gateway.

Fix

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.

Sources

  1. Presidio: Data Protection and De-identification SDK — Microsoft, 2024docs
  2. Analyzing Leakage of Personally Identifiable Information in Language Models — Lukas et al., 2023paper
  3. OWASP Top 10 for LLM Applications — OWASP GenAI Security Project, 2025spec

Check yourself

Three questions. Every option is explained once you answer.

1ConceptWhy are redactors tuned for recall over precision?
2ScenarioRedaction runs in the gateway, yet an audit finds card numbers in your tracing backend. Most likely cause?
3CalculationOne CPU core redacts ~50 messages/s. How many cores for 5M messages/day at a steady rate?