ner · regex · pii detection · onnx · local inference
Regex vs NER for PII detection: when patterns aren't enough
Evaluate patterns, labels, known terms and NER for PII detection. Build representative fixtures and review local model deployment boundaries.
In this article
- Where do validated patterns help?
- What does label anchoring add?
- When does free-prose entity recognition help?
- What do the trade-offs actually look like?
- Why should NER run locally?
- How do you keep the core small if NER is heavy?
- What are the honest limits of NER here?
- So which should you use?
- Frequently asked questions
- Comments
Sether™ / Field notes
ner
Different text. Different evidence.
Compare structured patterns with prose recognition.
Use validated patterns for structured candidates such as card numbers and IBANs, labels for fields with explicit cues, and evaluate NER for entities in free prose. Known-term lists and custom detectors can help with specific vocabularies. Measure missed and unwanted matches on representative data; local inference can avoid sending text to a separate detection service.
- Checksums and format validation reject some invalid candidates; they do not guarantee near-zero false positives or complete coverage.
- Label anchoring uses explicit field cues. Coverage depends on the labels, scripts and formats you enable.
- NER can help with prose names, while known-term lists and custom detectors handle specific vocabularies. No single method catches every case.
- Local inference can keep prompt text out of a separate detection API. Model download, cache and deployment settings still need review.
- Start with the required data classes, build labeled fixtures and add techniques when those fixtures demonstrate a gap.
Where do validated patterns help?
Some PII announces itself by shape. An email address has a grammar. A credit card number is a digit sequence that must pass a Luhn checksum. An IBAN carries a mod-97 check built into the standard. A phone number parses against real country metadata.
Patterns identify candidates; validation rejects some malformed ones. Passing a checksum does not establish that digits are a real customer’s card, and failing one does not mean the surrounding text is harmless. Measure both false positives and missed values for the formats your application accepts.
Sether's basic pack works this way: email, phone (via libphonenumber-js, the one runtime dependency), card with Luhn, SSN, IPv4/IPv6, IBAN with mod-97. The secrets pack does the same for API keys: AWS, OpenAI, Anthropic, GitHub, Slack, Stripe, JWTs, plus a Shannon-entropy catch-all for high-entropy strings.
Patterns avoid model inference and can be a practical first layer for structured values. They still have CPU and memory costs, format limits and adversarial-input concerns. Use bounded patterns, test them and measure the actual request path before making latency claims.
What does label anchoring add?
A name has no checksum. “Sarah Chen”, “New York” and “Prime Minister” can have similar capitalization. A broad capitalization rule can mistake ordinary phrases for people; an evaluation set needs those harmless lookalikes.
But a lot of real data labels itself. Forms, tickets, CRM exports, and copied records read like "name: Sarah Chen", "DOB: 12/04/1991", "passport: A1234567". The label is the anchor: match the label, capture the value that follows.
Sether’s identity pack uses multilingual labels across Latin, CJK, Cyrillic and Arabic scripts. An explicit cue helps distinguish a candidate value from ordinary words, but labels do not guarantee accuracy. Unlabelled text and unsupported value formats remain gaps to test.
When does free-prose entity recognition help?
A message such as "tell Chidi I’ll send the money on Friday" lacks an explicit name field. General shape and label detectors may not identify the person. NER is one option; a known-term list can also match Chidi when your application already has the relevant vocabulary.
A named-entity model predicts spans and classes from context. It can identify names, organisations and locations, but it can also misclassify ordinary words or miss an unfamiliar name. Evaluate its predictions in the languages and situations your users actually produce.
This is what @raeven-co/sether-ner exists for. It detects names, organisations, and locations in running prose, and emits the same token format as the core library, so a <NAME_...> token restores through the core Sether vault with no extra wiring.
import { Sether } from '@raeven-co/sether';
import { createNerRedactor } from '@raeven-co/sether-ner';
const sether = new Sether();
const ner = createNerRedactor(); // default model: Xenova/bert-base-NER
// Batched pass over the outbound prompt, locally, before it leaves
const safe = await ner.redact("tell Chidi I'll send the money on Friday");
// -> "tell <NAME_1a2b> I'll send the money on Friday"
// The model reply restores through the core Sether vault,
// same as pattern-based tokens, no extra wiring:
modelReply.pipe(sether.restore());
What do the trade-offs actually look like?
| Technique | Useful signal | Failure to test | Operational cost |
|---|---|---|---|
| Validated patterns | Shape, checksums and format rules | Valid-looking order IDs; unusual but accepted formats | Pattern work on every input; no inference model |
| Label anchoring | A cue such as name: or DOB: | Missing or ambiguous labels; unsupported scripts | Rules and value validators |
| Local NER | Contextual entity predictions in prose | Names outside training coverage; ambiguous words | Model artifacts, startup, inference and cache |
| Known-term/custom detector | Application-specific names or identifiers | Overlapping terms; ordinary-word collisions | Maintained vocabulary/rules and regression tests |
| Cloud detection service | The selected service’s classifiers | Coverage, access, retention and errors | Network request, service dependency and billing |
A separate detection API adds a recipient for the text it classifies. That may be acceptable under an assessed design, but it is a different privacy boundary from local detection. Do not describe a cloud-assisted pipeline as entirely local.
Why should NER run locally?
If your requirement is to avoid a separate text-analysis recipient, local inference is a useful design choice. It shifts model execution and operations to your environment; it does not remove evaluation, access-control or model-supply responsibilities.
The add-on runs configured inference through transformers.js, with Xenova/bert-base-NER as its default model. The inference path processes text in your environment, but initial model loading can fetch artifacts. Review caching, offline deployment and the chosen inference function. The GDPR discussion explains why the full data flow matters.
How do you keep the core small if NER is heavy?
Keep optional model dependencies separate from applications that do not need them. Sether’s TypeScript core has one declared runtime dependency; @raeven-co/sether-ner uses an optional @huggingface/transformers peer and loads its model when needed. Actual package and model sizes depend on the version and artifacts selected.
The NER add-on uses the core token/vault restoration model. Wire both paths to the same retained vault and test the entity output. Keeping model dependencies optional lets applications that do not need NER avoid those artifacts and startup costs.
What are the honest limits of NER here?
- Plan model loading. Test a cold start as well as a cached one. Preload approved artifacts if the production environment cannot download them.
- Benchmark your runtime. This article does not establish a universal ONNX-versus-PyTorch speed ratio. Hardware, model, quantization and workload change the result.
- Keep the integration scope clear. The add-on is a batched outbound-text pass, not transformer inference on every streaming chunk. See the chunk-boundary discussion for the separate stream problem.
- Evaluate coverage. Include local names, multiple scripts and ambiguous words. A default model and confidence threshold are starting configurations, not proof of fairness or accuracy.
- Test custom inference too. The
infer,thresholdandlabelsoptions allow customization. A replacement model needs its own compatibility and quality checks.
| Fixture | What it tells you |
|---|---|
| A checksum-valid order ID that is not a card | Whether structured validation over-replaces harmless values. |
| An unlabelled name in a locally relevant language | Whether the configured NER or known-term path covers your users. |
| Name: followed by a placeholder or ordinary word | Whether labels and deny-lists behave as intended. |
| The same sample under a cold/offline start | Whether model loading matches your deployment promise. |
Keep the test corpus separate from public demos. Use fictional or appropriately controlled examples, record the expected spans and review disagreements. The result you want is an understood error profile for the task, not a “perfect detector” claim.
So which should you use?
It is a ladder, not a choice. Climb only as far as your data requires.
- Start with the data classes. Inventory structured identifiers, labels, prose entities and business terms the task can do without.
- Enable and evaluate patterns and labels. Add both positive cases and harmless lookalikes before selecting thresholds or rules.
- Add NER or known-term detection where fixtures reveal a gap. Use a representative corpus rather than a single impressive example.
- Review the execution boundary. Know whether text, model artifacts or account metadata leave your environment, and describe each accurately.
For where detection fits in the full redact-then-restore pipeline, the complete guide to PII redaction for LLM apps covers the whole picture. If you are comparing this approach to Presidio's analyzer stack, see lightweight Presidio alternatives for TypeScript.
Frequently asked questions
Can I skip patterns and just use NER for everything?
You can evaluate that approach, but NER is not automatically better at every structured format and costs inference work. Compare it with validated patterns on the actual data classes; a hybrid can cover different gaps without treating either technique as infallible.
How big is the NER model download?
It depends on the selected model artifacts, format and quantization. Measure the actual download, cached footprint and cold-start behavior of your deployment. Avoid treating one historical artifact size as a package guarantee.
Can I use a different model than bert-base-NER?
Yes. createNerRedactor accepts custom inference through infer, plus threshold and labels. Evaluate the replacement model’s outputs, runtime needs and licensing on your own corpus before relying on it.
Does NER run on streaming responses?
No. It runs as a batched pass on the outbound prompt before it leaves. The per-chunk streaming path stays pattern-based, because transformer inference inside a hold-back window would add latency streaming exists to avoid.
Written by Godfrey Lebo
Founder of Sether. Ships production software for a living and reads every comment below. More about the team.
Comments
Emails and phone numbers in comments are hidden automatically. We take our own medicine.
No comments yet. Ask a question or share how you handle this. First comment gets our undivided attention.
Join the conversation
Questions, corrections and war stories are all welcome. Be kind, stay on topic.