4-Stage Pipeline
Cortina ist eine vierstufige Pipeline, in der jede Stage einen klaren Job hat und die früheren Stages bei Span-Überlappung gewinnen. Das ist absichtlich: ein per Mod-97 validiertes IBAN überstimmt eine LLM-„glaube nicht, dass das PII ist”-Einschätzung — Cryptographic-Grade Validation rangiert vor statistischer Confidence.
Die Stages
Section titled “Die Stages”Stage 1 — Presidio + DE-Recognizer-Pack
Section titled “Stage 1 — Presidio + DE-Recognizer-Pack”Was: Deterministische Pattern-Erkennung. Microsoft Presidio plus ein Pack eigener deutscher Recognizer.
Findet:
| Entity | Quelle |
|---|---|
DE_IBAN | Custom Recognizer + Mod-97-Prüfziffer |
DE_TAX_ID | Custom Recognizer + ISO-7064-Prüfziffer |
DE_TAX_NUMBER | Custom Recognizer (Bundesland-tolerant) |
DE_SV_NUMBER | Custom Recognizer |
DE_PHONE_NUMBER | Custom Recognizer |
DE_STREET | Custom Recognizer |
DE_POSTAL_CODE | Custom Recognizer |
DE_CITY | Custom Recognizer (nur in Kombination mit PLZ) |
DE_COMPANY_REGISTRY | Custom Recognizer (HRB / HRA / …) |
EMAIL_ADDRESS | Presidio-Built-in |
CREDIT_CARD | Presidio-Built-in |
URL | Presidio-Built-in |
IP_ADDRESS | Presidio-Built-in |
Latenz: Sub-Millisekunde pro Recognizer. Stage 1 ist effektiv gratis.
Wann nicht: Diese Stage läuft immer.
Stage 2 — spaCy NER (de_core_news_md)
Section titled “Stage 2 — spaCy NER (de_core_news_md)”Was: Statistical Named Entity Recognition über das deutsche spaCy- Modell. Findet die offenen Klassen, die Pattern-Recognizer per Defi- nition verfehlen.
Findet: PERSON, ORG, LOCATION.
Was Stage 2 absichtlich nicht findet: Das spaCy-MISC-Label wird
verworfen — es ist auf deutschem Text zu unpräzise (mischt Nationali-
täten, Religionen, Veranstaltungen, Fachbegriffe). Stage 3 schließt
diese Lücke gezielt mit den schärferen Sub-Klassen.
Latenz: ~30–80 ms je nach Textlänge. Ein einziges Modell pro Worker, geteilt mit Presidios Tokenizer.
Wann nicht: Diese Stage läuft immer.
Stage 3 — Context-LLM (Bedrock Claude, additiv)
Section titled “Stage 3 — Context-LLM (Bedrock Claude, additiv)”Was: Ein LLM-Validator über AWS Bedrock Claude in eu-central-1,
EU regional inference profile. Liest den Text mit den bisherigen Spans
als Kontext und ergänzt PII-Klassen, die Stage 1+2 strukturell nicht
erfassen können.
Findet: NATIONALITY, RELIGION, EVENT, DATE, MISC_OTHER —
das deckt unter anderem die GDPR-Art.-9-Spezialkategorien ab, die
beim spaCy-MISC-Drop liegen bleiben würden.
Strict-additiv: Stage 3 darf nur hinzufügen, nicht entfernen oder schrumpfen. Damit kann ein Prompt-Injection-Versuch keinen real gefundenen IBAN-Span „raussprechen”, und Mod-97-validierte Treffer bleiben gesetzt.
Latenz: ~1–3 s pro Call. LLM-Stages sind die teuren.
Wann nicht:
STAGE3_PROVIDER=""(deaktiviert) →audit_flag=context_llm_disabled, Pipeline gibt Stage-1+2-Output zurück.- Provider-Outage →
audit_flag=context_llm_unavailable, gleiches Verhalten. - Stage-Filter (siehe unten) lässt Stage 3 weg.
Stage 4 — Adversarial-Loop (Vertex Gemini)
Section titled “Stage 4 — Adversarial-Loop (Vertex Gemini)”Was: Ein zweiter LLM auf einem anderen Provider — Google Vertex
Gemini in europe-west3. Bekommt nicht den Original-, sondern den
bereits anonymisierten Text und versucht, das Subjekt zu
re-identifizieren. Findet er ein Re-ID-Risiko, schlägt er weitere
Spans vor; die werden additiv eingemerged, die Schleife läuft erneut.
Warum ein zweiter Provider: Bedrock und Vertex haben unter- schiedliche Priors über das, was in deutschem Text identifizierend ist. Ein Bedrock-only-Setup hätte das Risiko, dass Stage 3 und Stage 4 „dieselben blinden Flecken” haben.
Loop-Tuning:
- ε =
0.15— Re-ID-Score unter dem Threshold = konvergiert. max_iter = 3— danach Abbruch mitaudit_flag=re_id_risk_remaining.- Cycle-Detection über einen Span-Set-Hash — wenn die Span-Menge sich
wiederholt, Abbruch mit
audit_flag=re_id_oscillation.
Termination-Matrix:
| Reason | Audit-Flag | Operativ |
|---|---|---|
converged | (keiner) | Erfolgsfall. |
max_iter | re_id_risk_remaining | Budget aufgebraucht — Output bleibt mit Risiko. |
no_hints | re_id_risk_remaining | Adversary findet Risiko, kann aber nichts vorschlagen. |
oscillation | re_id_oscillation | Stage 3 ↔ 4 sind sich uneinig — Pipeline instabil. |
provider_error | adversarial_unavailable | Vertex / Bedrock down oder Timeout. |
invalid_output | adversarial_invalid_output | LLM-Output zweimal nicht parsebar. |
Latenz: ~2 s pro LLM-Call, bei drei Iterationen Worst-Case ~12 s P99. Wenn das nicht passt: Stage-Filter setzen.
Wann nicht: Wie Stage 3 — über STAGE4_PROVIDER, Outage oder
Stage-Filter.
Pipeline-Diagramm
Section titled “Pipeline-Diagramm”flowchart LR In([Input Text]) --> S1[Stage 1<br/>Presidio + DE-Pack] S1 --> S2[Stage 2<br/>spaCy NER] S2 --> Merge12((merge<br/>earlier wins)) Merge12 --> S3[Stage 3<br/>Bedrock Claude<br/>strict additive] S3 --> Merge123((merge<br/>1 > 2 > 3)) Merge123 --> S4{Stage 4<br/>Vertex Gemini<br/>adversarial loop} S4 -- score < ε --> Out([Anonymized Text<br/>+ Mapping in Redis]) S4 -- hints --> Merge123 S4 -- max_iter / cycle / error --> OutMerge-Regel
Section titled “Merge-Regel”Wenn zwei Stages denselben Text-Bereich treffen, gewinnt die frühere
Stage. Beispiel: „Frankfurt am Main” — Stage 1 erkennt es als
DE_CITY (zusammen mit dem PLZ-Kontext), Stage 2 würde es als
LOCATION labeln. Ergebnis: DE_CITY wird behalten, weil
spezifischer und cryptographic-grade-validiert.
Stage 3 darf nur Spans hinzufügen, nicht entfernen oder schrumpfen. Stage 4 trägt seine Hint-Spans über dieselbe additive Merge-Logik ein, sobald sie den Adversarial-Loop überleben.
Stage-Filter
Section titled “Stage-Filter”Per Query-Parameter lassen sich Stages selektiv abschalten — nützlich, wenn p99-Latenz wichtiger ist als maximaler Recall:
curl -X POST 'https://api.tup-ai.de/v1/cortina/anonymize?stages=1,2' \ -H "Authorization: Bearer $TUP_ANON_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "...", "session_id": "..." }'Trade-offs:
| Filter | Latenz | Coverage |
|---|---|---|
1 | ~1 ms | Nur deterministische Pattern. Keine Personen / Orgs. |
1,2 | ~50 ms | Plus PERSON / ORG / LOCATION. Keine Art.-9-Coverage. |
1,2,3 | ~1–3 s | Plus NATIONALITY / RELIGION / EVENT. Kein Adversarial-Loop. |
| (default) | ~3–12 s | Alles. P99 mit Adversarial-Loop. Empfohlen für Storage / Audit-getriebene Workflows. |
In den SDKs als stages: ["stage1", "stage2"] (TS) bzw.
stages=["stage1", "stage2"] (Python).
Quellen
Section titled “Quellen”- Phase-2-Decisions (D1–D5): spaCy-Modellwahl, Merge-Regel, MISC-Drop.
- Phase-3-Decisions (D6–D11): Provider-Split, Strict-Additive-Stage-3, Adversarial-Loop-Semantics, Cycle-Hash, Termination-Matrix.
Beide Dokumente sind interne Architektur-Records. Diese Seite ist die User-Sicht; wenn du den Implementation-Trail brauchst, frag uns nach den Decision-Docs.