EntityGuard: Warum LLMs im Gesundheitswesen eine ‚Sicherheitsleine‘ brauchen

Ich habe in den letzten Monaten intensiv an der Integration von Large Language Models (LLMs) in produktive Umgebungen gearbeitet. Dabei ist mir ein Muster aufgefallen: Die Begeisterung über die Fähigkeiten von Modellen wie GPT-4 oder Claude wird oft von einer tiefen, berechtigten Angst vor Datenleaks überlagert – besonders im Gesundheitswesen.

Wenn wir über Patientendaten sprechen, bewegen wir uns im Bereich der „besonderen Kategorien personenbezogener Daten“ gemäß DSGVO. Hier ist ein „Ich hoffe, das Modell behält die Daten für sich“ nicht nur fahrlässig, sondern rechtlich riskant. Die Lösung ist nicht, den Zugriff auf die KI zu verbieten, sondern eine technische Barriere zu errichten: eine Guardrail.

Genau hier setzt EntityGuard an. Es ist nicht einfach nur ein Anonymisierungs-Tool, sondern eine bewusste Architektur-Entscheidung, um den Datenfluss zwischen Mensch und Modell zu kontrollieren.

Das Problem: Die Illusion der Anonymität

Viele Entwickler verlassen sich darauf, dass sie den Nutzern sagen: „Bitte geben Sie keine Patientennamen ein.“ In der Realität passiert das Gegenteil. In einem hektischen Klinikalltag wird der Arzt den Fallbericht kopieren und fragen: „Fasse mir diesen Bericht für Patient Max Mustermann, geb. 15.03.1980, zusammen.“

In diesem Moment verlassen die Daten die kontrollierte Umgebung. Selbst wenn der API-Provider verspricht, die Daten nicht für das Training zu verwenden, bleibt ein Risiko: Logging, Intercepts oder Fehlkonfigurationen.

Ein echter Schutz muss vor der API-Schnittstelle liegen. Er muss deterministisch sein und darf im Zweifelsfall lieber die Anfrage blockieren, als sensible Daten im Klartext durchzulassen.

Die Architektur von EntityGuard

EntityGuard fungiert als Inline-Proxy. Es sitzt zwischen dem Interface (z. B. OpenWebUI) und dem LLM-Backend. Der gesamte Prozess folgt einem strengen Fluss:

  1. Interception: Der Prompt des Nutzers wird abgefangen.
  2. Analyse: Der Text wird durch die Analyse-Engine geschickt.
  3. Maskierung: Identifizierte Entitäten werden durch Platzhalter ersetzt.
  4. Weitergabe: Nur der anonymisierte Text erreicht das Modell.

Der technische Stack

Um dies performant und präzise umzusetzen, habe ich mich für eine Kombination aus bewährten Frameworks entschieden:

  • FastAPI: Für eine extrem schnelle, asynchrone API.
  • Microsoft Presidio: Als Herzstück der PII-Erkennung (Personally Identifiable Information).
  • spaCy (de_core_news_lg): Um die Herausforderungen der deutschen Sprache (Großschreibung, komplexe Entitäten) zu meistern.
  • SQLAlchemy & SQLite: Damit die Erkennungsregeln nicht im Code hartkodiert sind, sondern in einer Datenbank leben.

Deep Dive: Die „Fail-Closed“-Philosophie

Einer der kritischsten Punkte in der Entwicklung von EntityGuard war die Fehlerbehandlung. In den meisten Web-Apps ist das Ziel „High Availability“. Wenn ein Modul abstürzt, wird versucht, den Fehler zu ignorieren oder eine Standardantwort zu geben.

In einem Datenschutz-Szenario ist das fatal. Wenn der Anonymisierer abstürzt, darf der Text auf keinen Fall einfach ungefiltert durchgereicht werden.

Ich habe EntityGuard daher nach dem Fail-Closed-Prinzip gebaut. Schauen wir uns den relevanten Teil in src/views/anonymizer.py an:

except Exception as e:
    logger.error(f"Error in guardrail routing: {e}")
    raise HTTPException(
        status_code=500,
        detail="Security abort: Data sanitization failed"
    )

Das bedeutet: Wenn irgendetwas schiefgeht – sei es ein Datenbankfehler, ein Absturz des spaCy-Modells oder ein ungültiger Regex – antwortet die API mit einem HTTP 500. Die Anfrage an das LLM wird blockiert. Für den Nutzer bedeutet das eine kurze Fehlermeldung, für den Datenschutzbeauftragten bedeutet es die Gewissheit, dass keine Daten geleakt wurden.

Flexibilität durch Entkoppelung: Code vs. Regelwerk

Ein großes Problem bei klassischen PII-Tools ist, dass jede neue Erkennungsregel (z. B. eine spezifische Fallnummer eines Krankenhauses) einen neuen Deployment-Zyklus erfordert. Code ändern -> Testen -> CI/CD $\rightarrow$ Deployment.

Das ist in einer regulierten Umgebung viel zu langsam.

In EntityGuard habe ich das Regelwerk vom Code getrennt. Die Recognizer (Erkenner) basieren auf drei Säulen in der Datenbank:
1. Entities: Definition der Platzhalter (z. B. [NAME], [FALLNUMMER]).
2. Patterns: Reguläre Ausdrücke (Regex), die definieren, wie eine Fallnummer oder eine Versicherungsnummer aussieht.
3. Context Words: Wörter, die die Wahrscheinlichkeit erhöhen, dass ein Pattern zutrifft (z. B. „Patient“, „Versichert bei“, „geboren am“).

Durch die integrierte HTML-Admin-Oberfläche können Administratoren neue Patterns hinzufügen, ohne eine einzige Zeile Python-Code zu schreiben. Ein Klick auf den „Reload“-Button löscht den Cache im CustomAnalyzer und lädt die neuen Regeln aus der SQLite-DB.

Beispiel für einen Custom-Analyzer-Fluss

Wenn ein Text wie „Fallnr. 48291“ analysiert wird, passiert folgendes:
– Das System sucht in der Tabelle patterns nach passenden Regex.
– Es prüft die context_words. Wenn „Fallnr.“ im Text vorkommt, wird der Confidence-Score für das entsprechende Pattern erhöht.
– Wenn der Score einen Schwellenwert überschreitet, wird die Entität [MED_IDENTIFIKATOR] zugewiesen.

Warum AGPL-3.0? Eine Entscheidung für die Transparenz

Die Entscheidung für die GNU Affero General Public License (AGPL-3.0) war ganz bewusst getroffen.

Software, die über Datenschutz und Sicherheit entscheidet, darf keine „Black Box“ sein. Besonders im Gesundheitswesen müssen wir darauf vertrauen können, dass die Anonymisierungslogik tatsächlich funktioniert. Die AGPL stellt sicher, dass:
– Der Quellcode für jeden prüfbar bleibt.
– Verbesserungen an den Erkennungsmustern der Gemeinschaft zugutekommen.
– Kein Anbieter eine proprietäre Version baut, die als „Sicherheits-Tool“ verkauft wird, ohne die Community-Verbesserungen zurückzugeben.

Wenn man EntityGuard in einem Krankenhausnetzwerk betreibt und Modifikationen am Code vornimmt, verpflichtet die AGPL dazu, diese Änderungen offen zu legen. Das schafft eine Ebene von Vertrauen und technischer Validierung, die kommerzielle Produkte oft nicht bieten können.

Integration in die Praxis: OpenWebUI

Die theoretische Sicherheit ist wertlos, wenn die Hürde zur Nutzung zu hoch ist. Deshalb ist EntityGuard als Inlet-Filter für OpenWebUI konzipiert.

Für den Endnutzer ändert sich nichts. Er tippt seine Frage in den Chat. Im Hintergrund sendet OpenWebUI den Text an den EntityGuard-Endpunkt /api/v1/entityguard/sanitize. Erst wenn die Antwort „sanitized“ zurückkommt, wird sie an das LLM weitergeleitet.

Beispiel-Transformationskette:
Input: "Patient Max Mustermann, geb. 15.03.1980, AOK-versichert, Fallnr. 48291"
-> EntityGuard -> Output: "Patient [NAME], geb. [DATUM/ZEIT], [MED_IDENTIFIKATOR]-versichert, Fallnr. [MED_IDENTIFIKATOR]"

Das LLM erhält nun alle kontextuellen Informationen, um die Aufgabe zu lösen, kennt aber die Identität der Person nicht.

Ohne Guardrails

Mit Guardrails

Key Takeaways

Wenn ihr LLMs in sensiblen Bereichen einsetzt, solltet ihr folgende Prinzipien beachten:

  • Vertraue niemals dem Prompt: Nutzer werden (absichtlich oder unabsichtlich) PII eingeben.
  • Implementiere eine Guardrail: Nutze Tools wie Microsoft Presidio, aber passe sie an die lokale Sprache (Deutsch) und spezifische Domänen-Patterns an.
  • Wähle „Fail-Closed“: Sicherheit geht vor Verfügbarkeit. Ein Fehler im Filter muss die Kommunikation stoppen, nicht erlauben.
  • Trenne Logik von Daten: Halte Regex-Patterns und Kontextwörter in einer Datenbank, um schnell auf neue Anforderungen reagieren zu können.
  • Setze auf Open Source: Gerade bei Security-Software ist Transparenz (wie durch die AGPL) die beste Form der Qualitätssicherung.

EntityGuard ist ein schlanker, aber effektiver Weg, um die Lücke zwischen der enormen Produktivität von LLMs und den strengen Anforderungen des Datenschutzes zu schließen. Es beweist, dass Compliance kein Hindernis für Innovation sein muss, sondern durch kluge Architektur zum Enabler wird.

Link zum Github Repository: https://github.com/daemolition/entityguard