Personal data in LLM prompts and traces

You cannot stop users from pasting personal data into a chat box, and you usually do not want to stop retrieval from pulling it in. What you can control is how many copies survive the request, what they contain, and how long they live. Most of that control sits in the logging path, not the model call.

This article is the engineering companion to the AI privacy guide. It covers where personal data enters an LLM request, the three families of detectors and what each misses, the difference between redaction and reversible pseudonymization, where in the pipeline to apply them, and how to set retention and sampling on traces.

Three ways personal data gets in

Users paste it. People put their account number, their partner's medical history, a screenshot of a bank statement or a colleague's performance review into a chat box because that is the fastest way to get help. A support assistant for a pharmacy will receive health data whether or not the product team planned for it. Treat the user turn as untrusted free text that can contain any category of data, including special categories under Article 9 of the GDPR.

Retrieval pulls it. A RAG pipeline indexes tickets, contracts, CRM notes or email threads. When a chunk is retrieved, everything in it travels into the prompt: names in signatures, phone numbers in footers, third parties mentioned in passing. Retrieval is also where cross-user leakage starts. If the index is shared and retrieval is not filtered by the requesting user's permissions, one customer's documents can land in another customer's prompt, and from there in the completion.

Tools return it. An agent that calls get_order(id) or search_customers(query) receives structured records, often more fields than it needs. Tool outputs are appended to the conversation, so they are sent back to the model on every subsequent turn and captured by every span in the trace. A single customer lookup at turn two can appear in eight model calls by turn ten.

Add the system prompt, which many apps build by templating customer profile fields into instructions, and the completion, which can restate or infer personal data. The completion is also where incorrect personal data appears: a model summarizing an account may attribute a complaint to the wrong person.

Where the copies end up

Follow one request through a typical stack and count the places its content is written:

  • the application's conversation table;
  • the gateway or proxy's request log, if you run one;
  • the tracing backend: a root span plus child spans for retrieval, each model call and each tool call, each carrying inputs and outputs;
  • general application logs, if anyone logs the request body on error;
  • the model provider's abuse-monitoring log, retained for a period set by its terms;
  • any provider-side state you enabled, such as stored responses or files;
  • downstream: a warehouse export of traces for analytics, an eval dataset, a fine-tuning file.

The provider copy is governed by contract and settings; see the vendor comparison. The rest are yours. Traces deserve the most attention because they are the most complete record (every turn, every tool payload, every retrieved chunk) and the least governed. They are also where engineers go to debug, so access tends to be broad.

The OpenTelemetry semantic conventions for generative AI treat message content as opt-in. The output messages attribute carries the warning that it "is likely to contain sensitive information including user/PII data", and the retrieval query text and tool definitions attributes are flagged as possibly sensitive too (OpenTelemetry GenAI conventions, still in Development status). Vendor SDKs do not all follow that default. Check what yours captures before production traffic reaches it.

Detection approaches and their tradeoffs

Every redaction scheme starts with detection, and every detector makes two kinds of mistake. A false negative is personal data that slips through. A false positive is a harmless token flagged as personal data, which degrades the logged record (or, if you redact the prompt itself, the model's answer). You trade one against the other, and the right balance differs by store: aggressive for long-lived traces, conservative for the live prompt.

Patterns and checksums

Regular expressions catch identifiers with a fixed shape: email addresses, phone numbers, IP addresses, IBANs, payment card numbers, national ID formats. Adding validation cuts false positives sharply. A 16-digit string that passes the Luhn check is far more likely to be a card number than one that does not; an IBAN has a mod-97 check digit; many national IDs have their own.

Strengths: fast enough to run on every span, deterministic, easy to audit, no model to host. Weaknesses: they find only what has a shape. They miss names, addresses written in prose, health conditions, and anything a user types in an unexpected format ("my card ends 4 4 1 2"). Phone number patterns in particular over-match on order numbers and timestamps unless you add context rules.

Named-entity recognition models

NER models tag spans of text as person, location, organization, date and so on. Presidio, the open-source PII toolkit originally released by Microsoft, combines NER with regex, rule-based logic and checksums, and uses surrounding context words to raise or lower confidence (Presidio documentation). It ships an analyzer that returns entity spans with scores and an anonymizer with operators including replace, redact, hash, mask and encrypt, plus decrypt for reversal.

Strengths: catches names and locations in free text, configurable per entity type and score threshold, runs locally. Weaknesses: accuracy depends on the language and domain the underlying model was trained on. Product names, usernames and unusual names get missed or mis-tagged. Short chat messages without context are harder than full sentences. And the project says plainly that there is no guarantee it will find all sensitive information and recommends additional protections. That caveat applies to every automated detector.

LLM-based classifiers

You can ask a model, often a small one, to identify personal data in a passage and return spans or a redacted copy. These handle context that NER misses: "my neighbour, the one with the dialysis appointments" contains health data about a third party with no named entity at all.

Weaknesses: cost and latency per call, non-determinism, and the awkward fact that if the classifier is a hosted model, you have just sent the unredacted text to another processor. They can also hallucinate spans that are not in the input, so validate that every returned span exists at the stated offsets. Use them where context matters and volume is manageable, such as scanning eval datasets before they are shared, rather than on every trace.

ApproachGood atMissesCost per spanBest placement
Regex + checksumEmails, phones, cards, IBANs, IDs, IPsNames, prose addresses, health and other context-dependent dataNegligibleEvery span, in the SDK or collector
NER (e.g. Presidio)Names, locations, organizations in sentencesUnusual names, short fragments, domain jargon, non-supported languagesLow to moderate (local model)Trace export path, batch scans of stores
LLM classifierContext: third parties, health, implied identityNon-deterministic; can invent spansHighEval sets, fine-tuning files, sampled audits

In practice, layer them: regex and checksums everywhere, NER on content that is going to be stored, an LLM pass on datasets that will be reused. Measure each layer on a labeled sample of your own traffic, because published benchmark numbers on news text say little about how a detector performs on support chats in your product's language.

Redaction, masking, pseudonymization and tokenization

Once a span is detected, you decide what to replace it with. The choice determines whether the record is still useful and whether it is still personal data.

  • Redaction removes the value or replaces it with a type label: [EMAIL]. Irreversible and simple. The trace stays readable but you cannot tell whether two redacted emails were the same person.
  • Masking hides part of the value: j***@example.com, **** 4412. Useful for support staff who need a hint. Partial values can still identify someone in combination.
  • Hashing replaces the value with a digest. Consistent across records, so you can count distinct users. Unsalted hashes of emails or phone numbers are trivially reversible by hashing a list of candidates, so use a keyed hash (HMAC) with a secret kept outside the logging system.
  • Pseudonymization with a reversible vault replaces each value with a token (<PERSON_7f3a>) and stores the mapping in a separate service. Authorized processes can re-identify; everyone else sees tokens. Format-preserving encryption is a variant that keeps the shape of the original so downstream validators still pass.

The legal status matters. Pseudonymized data remains personal data for anyone who can link it back, which includes you if you hold the vault. The EDPB's Guidelines 01/2025 on pseudonymisation make that point and also note that pseudonymization can support a legitimate-interest balancing test. The Court of Justice's September 4, 2025 judgment in EDPS v SRB (C-413/23 P) held that whether pseudonymized data is personal data for a recipient depends on whether that recipient can reasonably re-identify. For you as the vault holder, nothing changes: your traces are still in scope for access and deletion. What pseudonymization buys you is a smaller blast radius if the tracing backend is breached, narrower access for engineers, and, potentially, a better position for the observability vendor.

Pseudonymizing the prompt itself

Some teams tokenize identifiers before the model call and rehydrate the completion afterwards: "Refund order <ORDER_1> for <PERSON_1>" goes to the provider; the app swaps the real values back into the answer. This keeps identifiers away from the provider, but has costs. The model cannot reason about what it cannot see (it will not notice that a name suggests a language preference, or that an address is outside the delivery area). Tokens must be consistent within a conversation so the model can refer back to them. And free-text personal data that the detector misses still goes through. It works best for structured identifiers in workflows where the model only needs to refer to entities, not interpret them.

Redact before logging, not after

The most important design decision is where in the pipeline you scrub. There are three places, and only the first keeps raw content out of your storage.

  1. In the application, before export. The tracing SDK applies a masking function to span inputs, outputs and metadata before they leave the process. Langfuse's SDKs, for example, accept (as of October 2026) a mask function that runs on every observation before trace data is sent (Langfuse masking docs). Raw content never reaches the tracing backend or the network.
  2. In a collector or pipeline. An OpenTelemetry Collector with attribute processors, or a log pipeline, rewrites spans in transit. Centralized and language-agnostic, but the raw data has already left the application, so it is in transit buffers and any collector-side logs. Langfuse's documentation makes the same point: collector-side masking happens after telemetry leaves the application.
  3. In the backend, after ingestion. A scheduled job scans stored traces and rewrites them. This is cleanup, not prevention. Raw content sits in the database, its replicas and its backups until the job runs, and backup copies may never be rewritten.

Do the first, add the second as a safety net if you have multiple services emitting spans, and use the third only to remediate history. Also check that masking applies to every exporter: if you send spans to two backends, each may receive its own unmasked copy unless masking happens before the fan-out.

The same logic applies outside the tracing SDK. Error handlers that log the full request body on a 500, HTTP access logs that capture query strings, and debug-level logging left on in production are common ways raw prompts reach general-purpose log storage with long retention.

Retention and sampling for traces

Detection is imperfect, so the second line of defence is keeping less for less time.

Split content from metadata. Latency, token counts, model name, cost, error codes and evaluation scores rarely contain personal data and are what most dashboards need. Keep them for months. Keep the content fields (inputs, outputs, retrieved chunks, tool payloads) for days. Many tracing backends let you set retention per project; where they do not, run separate projects for content-bearing and metrics-only traces.

Pick a number and write down why. Storage limitation under Article 5(1)(e) does not specify a period. A defensible content retention is the time your team actually needs to investigate an incident or a user complaint. If nobody looks at traces older than two weeks, two weeks plus a margin is easier to justify than ninety days.

Sample content capture. You do not need the full text of every request to monitor quality. Capture content on a sample of traffic, on requests that errored, on requests flagged by users, and on a small random slice for evaluation. Record metadata for everything. Sampling cuts both the volume of personal data stored and the cost of the tracing backend.

Gate access to raw content. Give most engineers metrics views and redacted traces. Restrict raw content to a smaller group, log who opens it, and review the access list. Traces are the most concentrated personal data in an LLM stack; protect them like a production database. The security controls guide covers the baseline.

Make deletion possible. Attach a stable user or tenant identifier to every trace so you can find and delete one person's traces on request without scanning text. The DSAR article covers the rest of the deletion path.

A minimal configuration to start from

  • Regex and checksum masking in the tracing SDK for emails, phones, cards, IBANs, IPs and the ID formats of your main markets.
  • NER-based masking of person names and addresses on span inputs and outputs before export, with thresholds tuned on a labeled sample.
  • Tool outputs trimmed to the fields the model needs before they are appended to the conversation.
  • Content capture on errors, flagged requests and a small sample; metadata on all requests.
  • Content retention measured in days, metadata in months, both documented in the ROPA.
  • Raw content access restricted and logged.
  • Every trace tagged with a user or tenant ID; a tested deletion query.
  • An LLM-assisted scan before any production data is copied into eval or fine-tuning sets.

None of this makes the system leak-proof. It shrinks the number of copies, shortens their life, and makes the remaining ones findable, which is most of what the law asks.