Detector Capabilities
Entity detection capabilities by runtime mode, language support matrix, performance characteristics, and deployment modes
Entity Detection by Runtime Mode
| Entity Type | Builtin (Regex) | + GLiNER | + spaCy |
|---|---|---|---|
| High | High | High | |
| Phone | High (intl + DE) | High | High |
| SSN | High | High | High |
| IBAN | High | High | High |
| VAT ID (EU) | High | High | High |
| Credit Card | High (Luhn) | High | High |
| IP Address | High (v4 + v6) | High | High |
| URL | High | High | High |
| Customer ID | High (pattern) | High | High |
| Order Number | High (pattern) | High | High |
| Passport | Medium (pattern) | High | Medium |
| Health ID | Medium (pattern) | High | Medium |
| Date of Birth | High (with context) | High | High |
| German Tax ID | High | High | High |
| German Social Security | High | High | High |
| Money (ISO 4217) | High | High | High |
| Quote ID | High (pattern) | High | High |
| Case Reference | High (pattern) | High | High |
| Person Name | None | Good | Good |
| Company Name | None | Good | Moderate |
| Location | None | Good | Good |
| Address (street) | Medium (DE/US) | Better | Better |
German Tax ID, German Social Security, Money, Quote ID, and Case Reference are the five always-on
builtin_custom detectors: they run in every mode (including builtin) and emit typed
custom:<name> entities. Together with the named builtin formats, these are the builtin entity
types available before any NER, config, or policy additions.
Entity Source Provenance
Every entity type in GET /v1/capabilities carries a source field so a client can tell the fixed
built-in floor apart from the policy and config extensible surface. It answers "is this type baked
into the runtime, or did this deployment add it?" without reading the policy files back.
source | Meaning | Extensible by |
|---|---|---|
builtin | A universal PII format detector (email, phone, IBAN, SSN, IP, URL, credit card, ...). The safe floor. | Not removable; always on |
builtin_custom | A shipped custom regex detector (money, German tax ID, German social security, ...). | Not removable; always on |
ner | Detected by the NER model (person, company, location, or a policy mapped zero-shot label). Present only when detector.mode is both or advanced. | detection.ner_labels + ner_label_map |
config_custom | Added by this deployment's detector config (custom_detectors or vocab.extra_patterns). | Server config, no recompile |
policy_custom | Defined by a loaded policy's custom_patterns. | Policy YAML, no recompile |
The builtin and builtin_custom types are The Line: they always run and cannot be switched off by
config. Everything tagged config_custom or policy_custom is what this deployment added on top, so
a developer can confirm a new policy or vocab entry is live by querying capabilities. The field is
additive: an older server that omits it is read as builtin by the SDKs.
Language Support
Language support is not a fixed list; it is derived from the running deployment. There is no hardcoded set of supported languages. Three layers combine:
- Structured PII (builtin) is language-independent: email, IBAN, IP, URL, SSN, credit card,
customer ID, and order numbers are matched by pattern in any language. Phone and address have
locale-specific format coverage (international plus German, US, French, Italian, Spanish, Dutch,
UK formats today), extendable per locale via
detector.vocab.extra_patternswith no recompile. - NER detection (person, company, location) covers whatever the Python sidecar reports. GLiNER
is broadly multilingual; the exact set is the enrichment registry plus any loaded
GUARDAI_LANGUAGE_PACKS. Every language is data (a built-in JSON lexicon, no per-language code); operators add, extend, or override (_mode: replace) any language from data, with extend guaranteed to only widen the built-in floor. The server fetches this from the sidecar at startup. - Restoration (honorifics, gendered titles, abstract labels) ships built-in templates for ~30
languages and is extended or overridden for any locale via
restore_templates.
Do not rely on a static table: query GET /v1/capabilities for the live list this deployment
advertises. Each language reports detection_support (present when the NER sidecar covers it) and
rehydration_support (present when a restore template covers it). GET /v1/diagnostics (admin)
shows the deployment's vocab additions, custom detectors, suppressions, and restore-template
overrides.
NER Noise Suppression (Precision Floor)
Zero-shot NER (GLiNER) tends to over-tag common greeting and function words as person, company, or location entities. To keep precision high, OGuardAI applies a built-in noise floor after NER, additive over the raw model output:
- A universal acronym stoplist (~50 entries such as PII, API, CEO, IBAN, SWIFT, HR, SSN) drops NER spans that are just a known acronym.
- A per-language noise floor, shipped as data (German and Arabic today), suppresses NER spans whose tokens are all noise words, stopwords, or proclitics. This also catches glued phrases where the model tags a whole run of function words as one entity.
- A minimum-confidence floor for NER-only types (built-in 0.65) drops low-confidence names.
This is a precision floor, not a ceiling. A deployment may add acronyms to the stoplist, un-suppress a
specific built-in acronym so an acronym-shaped real organization name is detected, or raise the
confidence floor (stricter only). It cannot lower the floor or weaken detection below the built-in
baseline, and invalid overrides are rejected at startup. The active suppressions are visible via
GET /v1/diagnostics.
Performance Characteristics
Latency figures are indicative and hardware-dependent. The builtin path runs in low single-digit milliseconds; NER modes (GLiNER, spaCy) add model inference and are an order of magnitude slower.
| Metric | Builtin Only | + GLiNER | + spaCy |
|---|---|---|---|
| Cold start | <1ms | 5-30s (model load) | 3-15s (model load) |
| Idle memory | ~10MB | ~500MB (CPU) / ~2GB (GPU) | ~200MB |
| Latency p50 | low single-digit ms | ~80ms (up to several hundred ms p99) | ~80ms (hardware-dependent) |
| Deterministic | Yes | Mostly | Mostly |
| GPU required | No | Optional (faster) | No |
Deployment Modes
| Mode | Config | What Runs | Best For |
|---|---|---|---|
builtin | detector.mode: builtin | Rust regex only | Low-latency, structured data |
advanced | detector.mode: advanced + detector.advanced_url | Rust regex + required Python NER (fail-closed) | Maximum accuracy |
both | detector.mode: both + URL | Rust regex + Python NER merged | Best coverage |
Degraded Mode Behavior
Behavior on NER sidecar unavailability depends on the configured mode.
both mode (graceful fallback):
- Server logs:
ner_service_unavailable_falling_back_to_builtin - Detection continues with builtin regex only
- No crash, no hang, no data loss
- Entity types requiring NER (Person, Company, Location) will not be detected
advanced mode, or any policy required_for type (fail closed):
- NER is mandatory and is not downgraded to builtin
- The request fails closed with
GUARDAI_DETECTION_FAILED - A policy's
required_forgates on whether NER actually ran, not on the configured mode, so arequired_fortype that NER did not produce rejects the request rather than passing the input through undetected
Health endpoint: GET /v1/health reports the CONFIGURED mode, not live NER reachability. It never
flips to a detector degraded status on a live NER outage (this keeps Kubernetes liveness/readiness
probes stable). The detector component message reflects configuration only:
builtin_only (...) in builtin mode, builtin_and_ner (full entity detection) in both mode, and
ner_required (...) in advanced mode. To check whether NER is configured, read the ner_active
field from GET /v1/capabilities; to see whether NER actually ran for a given request, read the
detector_mode and warnings fields on the transform response.