Skip to content

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.

Was: Deterministische Pattern-Erkennung. Microsoft Presidio plus ein Pack eigener deutscher Recognizer.

Findet:

EntityQuelle
DE_IBANCustom Recognizer + Mod-97-Prüfziffer
DE_TAX_IDCustom Recognizer + ISO-7064-Prüfziffer
DE_TAX_NUMBERCustom Recognizer (Bundesland-tolerant)
DE_SV_NUMBERCustom Recognizer
DE_PHONE_NUMBERCustom Recognizer
DE_STREETCustom Recognizer
DE_POSTAL_CODECustom Recognizer
DE_CITYCustom Recognizer (nur in Kombination mit PLZ)
DE_COMPANY_REGISTRYCustom Recognizer (HRB / HRA / …)
EMAIL_ADDRESSPresidio-Built-in
CREDIT_CARDPresidio-Built-in
URLPresidio-Built-in
IP_ADDRESSPresidio-Built-in

Latenz: Sub-Millisekunde pro Recognizer. Stage 1 ist effektiv gratis.

Wann nicht: Diese Stage läuft immer.

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 mit audit_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:

ReasonAudit-FlagOperativ
converged(keiner)Erfolgsfall.
max_iterre_id_risk_remainingBudget aufgebraucht — Output bleibt mit Risiko.
no_hintsre_id_risk_remainingAdversary findet Risiko, kann aber nichts vorschlagen.
oscillationre_id_oscillationStage 3 ↔ 4 sind sich uneinig — Pipeline instabil.
provider_erroradversarial_unavailableVertex / Bedrock down oder Timeout.
invalid_outputadversarial_invalid_outputLLM-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.

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 --> Out

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.

Per Query-Parameter lassen sich Stages selektiv abschalten — nützlich, wenn p99-Latenz wichtiger ist als maximaler Recall:

Terminal window
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:

FilterLatenzCoverage
1~1 msNur deterministische Pattern. Keine Personen / Orgs.
1,2~50 msPlus PERSON / ORG / LOCATION. Keine Art.-9-Coverage.
1,2,3~1–3 sPlus NATIONALITY / RELIGION / EVENT. Kein Adversarial-Loop.
(default)~3–12 sAlles. P99 mit Adversarial-Loop. Empfohlen für Storage / Audit-getriebene Workflows.

In den SDKs als stages: ["stage1", "stage2"] (TS) bzw. stages=["stage1", "stage2"] (Python).

  • 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.