DEV Community

Cover image for I thought redacting medical notes was enough until I saw what GPT-5 actually needed
Lars Winstand
Lars Winstand

Posted on • Originally published at standardcompute.com

I thought redacting medical notes was enough until I saw what GPT-5 actually needed

Most teams handling medical text with GPT-5 or Claude seem to make the same two mistakes:

  1. Send raw clinical notes to a cloud model and call it a day
  2. Redact so aggressively that the model can’t reason about the note anymore

Both are bad.

The pattern that actually makes sense for agent workflows is usually consistent, reversible pseudonymization.

Instead of this:

John Smith visited on 2024-01-12 and spoke with Dr. Patel.
Enter fullscreen mode Exit fullscreen mode

or this:

[NAME] visited on [DATE] and spoke with [NAME].
Enter fullscreen mode Exit fullscreen mode

use this:

[PATIENT_NAME_1] visited on [VISIT_DATE_1] and spoke with [CLINICIAN_NAME_1].
Enter fullscreen mode Exit fullscreen mode

That tiny change preserves the structure GPT-5, Claude, and downstream agents actually need.

If you’re building automations in n8n, OpenClaw, Make, Zapier, or a custom OpenAI-compatible stack, this matters a lot more than people think.

The real problem with classic redaction

Blunt redaction protects strings, but it often destroys relationships.

A clinical note is full of references the model needs to keep straight:

  • who the patient is
  • which clinician said what
  • whether the same person appears multiple times
  • whether two dates refer to the same event
  • whether an identifier repeats consistently across the note

If every person becomes [NAME] and every date becomes [DATE], the model loses co-reference.

That breaks a bunch of very normal workflows:

  • summarization
  • extraction
  • coding assistance
  • triage
  • routing
  • downstream agent handoffs

Here’s the difference.

Bad redaction

[NAME] was admitted on [DATE]. [NAME] reported chest pain. [NAME] discussed family history with [NAME].
Enter fullscreen mode Exit fullscreen mode

Who is who? The patient? A parent? The cardiologist? No idea.

Better pseudonymization

[PATIENT_NAME_1] was admitted on [ADMISSION_DATE_1]. [PATIENT_NAME_1] reported chest pain. [PATIENT_NAME_1] discussed family history with [CLINICIAN_NAME_1].
Enter fullscreen mode Exit fullscreen mode

Now GPT-5 can still follow the story.

That’s the whole point.

The Reddit thread that made this click for me

I found a thread on r/openclaw about an OpenClaw skill called Redacta:

https://reddit.com/r/openclaw/comments/1vidd43/i_built_an_openclaw_skill_that_pseudonymises/

The key idea was simple:

instead of simply deleting information: John Smith → [NAME] you can preserve useful context: John Smith → PERSON_001

That sounds minor.

It isn’t.

That is the difference between a note that still works for an LLM and one that turns into static.

What good pseudonymization looks like

The practical pattern is:

  1. detect identifiers locally
  2. replace them with stable typed tokens
  3. keep the mapping table separate
  4. send only pseudonymized text to the model
  5. re-identify only if needed, and as late as possible

For example:

{
  "original": "Jane Doe, DOB 1982-04-16, MRN 123456, seen by Dr. Singh on 2025-02-01",
  "pseudonymized": "[PATIENT_NAME_1], DOB [DATE_OF_BIRTH_1], MRN [MRN_1], seen by [CLINICIAN_NAME_1] on [VISIT_DATE_1]"
}
Enter fullscreen mode Exit fullscreen mode

And then locally:

{
  "[PATIENT_NAME_1]": "Jane Doe",
  "[DATE_OF_BIRTH_1]": "1982-04-16",
  "[MRN_1]": "123456",
  "[CLINICIAN_NAME_1]": "Dr. Singh",
  "[VISIT_DATE_1]": "2025-02-01"
}
Enter fullscreen mode Exit fullscreen mode

That local token map is the important part.

It lets agents reason over the note without exposing raw identifiers upstream.

Why stable tokens beat generic placeholders

Stable tokens do three useful things.

1. They preserve co-reference

[PATIENT_NAME_1] in line 3 is the same [PATIENT_NAME_1] in line 40.

2. They preserve role information

[PATIENT_NAME_1] and [CLINICIAN_NAME_1] are not interchangeable.

3. They preserve downstream usability

Your extraction agent, summarizer, coding workflow, or human review queue can still make sense of the note.

For agent pipelines, this is a huge difference.

Regex alone won’t solve this

A lot of engineers start here:

import re

text = re.sub(r"\b\d{3}-\d{2}-\d{4}\b", "[SSN]", text)
text = re.sub(r"\b\d{10}\b", "[PHONE]", text)
Enter fullscreen mode Exit fullscreen mode

That’s fine for fixed patterns.

It is not enough for the whole problem.

Regex is decent for:

  • SSNs
  • NHS numbers
  • MRNs
  • phone numbers
  • emails
  • dates of birth
  • ZIP codes or postcodes

Regex is bad at:

  • distinguishing patient names from clinician names
  • relatives and carers
  • free-text addresses
  • indirect age references
  • contextual identifiers like profession or location

The realistic architecture is a two-layer pipeline.

A sane architecture: deterministic first, model second

Layer 1: local deterministic matching

Use pattern matching for the obvious stuff.

- NHS numbers
- National Insurance numbers
- MRNs
- SSNs
- DOBs
- phone numbers
- emails
- postcodes
Enter fullscreen mode Exit fullscreen mode

Layer 2: contextual entity detection

Use a local model or tightly controlled service for ambiguous entities.

- patient vs clinician names
- relatives
- carers
- professions
- free-text addresses
- indirect identifiers
Enter fullscreen mode Exit fullscreen mode

Then replace everything with stable typed tokens.

That split is just honest engineering.

Pseudonymization is not the same as de-identification

This is where teams get sloppy.

If you can reverse the mapping, the data may still be regulated.

So no, pseudonymization is not magic.

If you keep a token map, you still need to:

  • protect it
  • control access
  • audit usage
  • define where re-identification is allowed
  • think about whether reversible mapping is even acceptable for the use case

Sometimes reversible pseudonyms are correct.

Sometimes you need one-way tokenization or stricter de-identification.

Google Sensitive Data Protection is pretty clear on this distinction. It supports:

  • AES-SIV deterministic encryption
  • FPE-FFX format-preserving encryption
  • HMAC-SHA-256 hashing

That’s useful because it frames the problem correctly: reduce exposure without destroying utility.

The trap on the other side: preserving too much

You can pseudonymize a note and still leak identity.

If the note keeps:

  • a rare profession
  • a tiny town
  • an unusual diagnosis date
  • a very specific age
  • a distinctive clinical event

then the note may still be identifiable.

So “we removed names” is not enough.

Sometimes you need a stricter pass.

Sometimes you need Safe Harbor-style removal.

Sometimes you need a human review step.

AWS Comprehend Medical’s PHI categories are a good reminder that privacy is broader than names. It includes things like:

  • age
  • date
  • name
  • phone or fax
  • email
  • ID
  • URL
  • address
  • profession

That’s closer to the real problem.

Tools I’d actually consider

If I were building a gateway in front of GPT-5, Claude, Grok, Qwen, or Llama for internal workflows, I’d look at these:

Option What it’s best at
Redacta Clinical pseudonymization with stable labeled tokens, local-first workflows, and local re-identification
Microsoft Presidio General-purpose PII detection and anonymization with strong building blocks for custom services
Google Sensitive Data Protection Managed pseudonymization and tokenization with enterprise-friendly cryptographic primitives

My opinionated take:

  • Redacta is the most interesting if the text is actually clinical
  • Microsoft Presidio is the best starting point if you want to build your own service
  • Google Sensitive Data Protection is strong if you want managed tokenization primitives and enterprise controls

Quick Presidio example

Presidio is practical because you can run it as libraries or HTTP services.

The docs show an anonymization endpoint like this:

curl -XPOST http://localhost:3000/anonymize \
  -H "Content-Type: application/json" \
  -d '{
    "text":"hello world, my name is Jane Doe. My number is: 034453334",
    "anonymizers":{
      "PHONE_NUMBER":{
        "type":"mask",
        "masking_char":"*",
        "chars_to_mask":4,
        "from_end":true
      }
    },
    "analyzer_results":[
      {
        "start":24,
        "end":32,
        "score":0.8,
        "entity_type":"NAME"
      }
    ]
  }'
Enter fullscreen mode Exit fullscreen mode

That’s not a full clinical pseudonymization pipeline, but it’s a useful foundation.

Quick Redacta example

If you’re already working in OpenClaw-style automation environments:

openclaw skills install redacta
npx -y redacta-mcp
Enter fullscreen mode Exit fullscreen mode

That packaging is better than I expected.

The project is described as shipping across multiple surfaces, including:

  • OpenClaw skill
  • MCP server
  • TypeScript library
  • Python library
  • CLI

That matters because developers don’t want privacy tooling trapped in one UI.

What a production pipeline should look like

If I were implementing this for a real automation stack, I’d do something like this:

1. Detect obvious identifiers locally

Use deterministic matching for MRNs, NHS numbers, SSNs, DOBs, emails, phone numbers, and postcodes.

2. Run contextual detection in a tightly controlled layer

Separate patient names from clinician names, relatives, carers, and ambiguous references.

3. Replace with stable typed tokens

Use:

[PATIENT_NAME_1]
[CLINICIAN_NAME_1]
[DOB_1]
[MRN_1]
[VISIT_DATE_1]
Enter fullscreen mode Exit fullscreen mode

Not:

[NAME]
[DATE]
Enter fullscreen mode Exit fullscreen mode

4. Store the mapping table separately

Keep it local. Lock it down. Audit it.

5. Send only pseudonymized notes to the model

That can be GPT-5, Claude, or any OpenAI-compatible endpoint.

6. Re-identify only at the end

Do it only if needed, and inside the smallest controlled scope possible.

Why this matters even more for agent workflows

This problem gets worse when you move from one-off prompts to always-on automations.

A real pipeline might be:

local detection -> pseudonymization -> GPT-5 extraction -> n8n routing -> human review -> controlled re-identification
Enter fullscreen mode Exit fullscreen mode

That’s how teams actually build systems.

Not:

paste note into chatbot
Enter fullscreen mode Exit fullscreen mode

And once you accept that, economics matter too.

Because now you’re not paying for one prompt. You’re paying for a workflow with multiple passes:

  • detection
  • pseudonymization
  • extraction
  • validation
  • summarization
  • routing
  • maybe a second review pass

That’s where flat-rate compute gets interesting.

If your stack uses an OpenAI-compatible endpoint, Standard Compute is relevant here because it lets teams run multi-step agent flows without per-token billing anxiety. That matters when safety steps stop being optional and become part of the default pipeline.

For n8n, Make, Zapier, OpenClaw, and custom automations, that changes behavior. Teams are less likely to cut corners when every extra pass doesn’t feel like another billing event.

My practical recommendation

If you’re building LLM workflows around medical notes, don’t choose between these two bad options:

  • raw-note prompting
  • context-destroying redaction

Use consistent pseudonymization first.

Then decide where you need stricter de-identification.

That’s the middle path that actually works.

It preserves enough structure for GPT-5 or Claude to reason correctly, while reducing exposure enough to build a sane pipeline.

For developers, the takeaway is simple:

privacy and utility are not enemies
bad implementations of both are
Enter fullscreen mode Exit fullscreen mode

If your agents need to understand the note, give them structure.

Just don’t give them the patient’s identity unless they truly need it.

Top comments (0)