Home

/

Keep PII Out of Your LLM

/

Restoring safely

Restoring safely

Chapter 9
Part III
4
min read

Restoring safely

Restore only what this request tokenised. The scoping is the control.

public async Task<string> CompleteAsync(
    string conversationId, string userText, CancellationToken ct)
{
    var findings = await _detector.AnalyzeAsync(userText, ct: ct);
    var d = await _deidentifier.ApplyAsync(conversationId, userText, findings, ct);

    var completion = await _model.CompleteAsync(d.Text, ct);

    // Scope-limited: only this conversation's map is in play.
    var map = await _vault.MapForAsync(conversationId, ct);
    var answer = Reidentify(completion, map);

    if (TokenPattern.IsMatch(answer))
        _logger.LogWarning("Unresolved token in completion for {Conversation}", conversationId);

    return answer;
}

That last check matters. An unresolved token in the output means the model invented a token name, or your map was incomplete, or the restore missed a reformatted token. Users will report it as gibberish. Catch it yourself first.

Do not log userText, d.Map, or answer in that method. Chapter 16 explains why at length; for now, note that the most natural place to add a debug log is the one place that sees both the tokens and the values.

Fail closed

The detector is a network call to a container. Containers restart, get OOM-killed, and hit their connection limits. What your code does in that two-second timeout is a policy decision, and the default that most code falls into is the wrong one.

try
{
    findings = await _detector.AnalyzeAsync(text, ct: ct);
}
catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException)
{
    _logger.LogError(ex, "PII detector unavailable");
    throw new DetectorUnavailableException();   // do not proceed
}

An empty findings list and a failed call are indistinguishable downstream. A catch that logs and continues with zero findings sends the raw text to the model, and it does so precisely when your protection is broken. The system appears healthy, because from the user's perspective the feature is working better than usual.

Failing closed has a cost and you should choose it deliberately. Options, roughly in order of preference:

Reject the request with a clear error. Correct for anything handling sensitive data.

Degrade to deterministic-only and proceed with reduced coverage, logging that you did. Defensible when Chapter 5's layer covers your highest-consequence entities and the remainder is low blast radius. Record the degradation, because it is a fact an auditor will ask about.

Queue for later. Works for asynchronous jobs, useless for interactive chat.

What you never do is proceed as though the check passed. If that means your LLM feature has an availability dependency on your detection container, that is true and you should say so in the design document rather than discovering it during an incident.

Multi-turn drift

One last failure mode, easy to miss and unpleasant in production.

A user says "Sarah Whitfield" in turn one and "Sarah" in turn four. Your detector finds PERSON in both, but the strings differ, so byValue assigns a second token. The model now believes there are two people.

The fix is normalisation at the vault boundary: before assigning a token, check whether the value is a subset or variant of an existing value in this conversation. Surname-only, given-name-only, and initialised forms cover most real cases.

private static bool IsLikelySamePerson(string candidate, string known) =>
    known.Contains(candidate, StringComparison.OrdinalIgnoreCase)
    || candidate.Contains(known, StringComparison.OrdinalIgnoreCase);

That heuristic is crude and it is better than nothing. Get it wrong in the merging direction and you conflate two people, which is worse than splitting one, so bias it towards splitting and accept the occasional duplicate.

The round trip works, it is reversible, and it keeps real values away from the model. It also, sometimes, makes the model worse at the job you asked it to do.

Sources for this chapter

  • Presidio's 64% reversibility pass rate, caused by character offset misalignment, against 100% for regex, Piiranha and both GLiNER models — Sikkema benchmark: https://albertsikkema.com/python/security/privacy/2026/06/01/benchmarking-open-source-pii-detection.html · This specific result is why the round-trip invariant test exists in this chapter.
  • Sanitising sensitive prompts with reversible mappingsPreempt, arXiv 2504.05147: https://arxiv.org/pdf/2504.05147
  • Keeping the token vault outside model reach, preserving only the relationships the task needs — practitioner consensus; see the architecture sources in Chapter 13.

The implementation details are the author's: replacing from the end of the string, the byValue map for coreference, angle-bracket delimiters surviving paraphrase, the four vault properties, and the multi-turn name-variant heuristic. These are engineering positions, argued in the text rather than cited.

the-leak-you-cant-see
blast-radius
what-counts-as-pii
the-five-doors
the-accuracy-reckoning
the-hybrid-that-does-not-work
deterministic-detection
npi-in-c
the-three-way-choice
calling-the-analyzer-from-c
measuring-your-own-demo-gap
choosing-the-operating-point
the-ladder-of-safeguards
pseudonymisation
the-round-trip
restoring-safely
when-masking-breaks-the-task
plausibility-hazard
the-architecture-that-holds
the-reference-architecture
dont-send-it-at-all
structure-beats-prose
the-gateway
failure-is-a-policy-decision
the-sidecar-you-can-trust
egress-deny-it-at-the-network
rag-and-agents
de-identify-before-you-embed
dual-model-separation
the-boring-controls
evidence-and-the-first-thirty-days
week-two-the-chokepoint-and-the-fast-layer
entity-catalogue-and-c-validators
mbi-positional-rules
tooling-at-a-glance
azure-ai-language-pii-in-detail
container-trust-checklist
sources
azure-ai-language
provider-retention

Download the full PDF for free?

Free download — no account required

Get the PDF
Get the PDF
Related Chapters
Free Download
Get the full PDF
All pages, including all code examples, diagrams, and the appendix reference card.
No spam. Unsubscribe at any time.
Your email won't be shared.
Oops! There's a problem with your request. We're working on fixing it. Please try again later.