Files
Pii/generate_pii_texts.py
2026-02-23 10:48:57 +00:00

150 lines
5.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Generiert synthetische deutsche Texte mit PII-Daten via OpenAI.
Speichert das Ergebnis als JSON.
Usage:
pip install openai python-dotenv
set OPENAI_API_KEY=sk-...
python generate_pii_texts.py
"""
import json
import os
import random
from pathlib import Path
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
SYSTEM_PROMPT = """\
Du bist ein Testdaten-Generator. Du erzeugst realistische deutsche Texte \
die personenbezogene Daten (PII) enthalten. Alle Daten sind FIKTIV.
Wichtig:
- Sei KREATIV bei den PII-Typen. Nicht nur Name+Email, sondern auch:
Geburtsdaten, Steuernummern, Kennzeichen, Kundennummern, IP-Adressen,
Benutzernamen, Bankverbindungen, Sozialversicherungsnummern, Adressen,
Telefonnummern, Kreditkarten, Personalausweisnummern, Reisepassnummern,
Führerscheinnummern, Krankenkassennummern, URLs mit PII-Parametern,
Geburtsorte, Nationalitäten, Religionszugehörigkeit usw.
- Baue EDGE CASES ein:
* PII in Tabellenform, Aufzählungen, Signaturen
* Abgekürzte Namen (M. Müller), Spitznamen, Doppelnamen
* Telefonnummern in verschiedenen Formaten (+49, 0049, 089/, mit/ohne Leerzeichen)
* Adressen in verschiedenen Formaten (mit/ohne Bundesland, Postfach, c/o)
* Teilweise geschwärzte Daten (DE** **** **** 1234 56)
* PII eingebettet in Code-Snippets, Log-Dateien, SQL-Queries
* PII in E-Mail-Headern, Chat-Verläufen
* Mehrere Personen im selben Text
* PII die sich über Zeilenumbrüche erstreckt
* Tippfehler in PII-nahen Kontexten
* IBANs/Kartennummern mit und ohne Leerzeichen
- Variiere die Textarten: Emails, Chatverläufe, Briefe, Logs, Formulare,
Berichte, Notizen, Protokolle, Tickets, Datenbankeinträge
"""
SCENARIOS = [
"Kundenanfrage per Email mit Signatur",
"IT-Support-Ticket mit Log-Auszügen",
"Internes Chat-Protokoll zwischen Kollegen",
"Behördliches Schreiben / Bescheid",
"Arztbrief mit Patientendaten",
"Polizeibericht / Unfallprotokoll",
"Bewerbung mit Lebenslauf-Auszügen",
"Bankmitteilung / Kontoeröffnung",
"Versicherungsmeldung / Schadensfall",
"Datenbankexport / CSV-artige Daten mit Kopfzeile",
"Handschriftliche Notiz (transkribiert) mit PII",
"Server-Logfile mit User-Daten und IPs",
"SQL-Query oder Code-Snippet mit hardkodierten PII",
"Meeting-Protokoll mit Teilnehmerliste",
"Mietvertrag / Wohnungsübergabe-Protokoll",
]
def generate(count=5, scenario=None, model="gpt-4o"):
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
scenario = scenario or random.choice(SCENARIOS)
user_prompt = f"""\
Generiere {count} verschiedene deutsche Texte (je 50-250 Wörter).
Szenario: {scenario}
Jeder Text soll VERSCHIEDENE PII-Typen enthalten nicht immer die gleichen.
Baue bewusst Edge Cases und ungewöhnliche Formate ein.
Antwort als JSON:
{{
"samples": [
{{
"text": "...",
"pii_spans": [
{{"type": "PERSON", "value": "Max Müller", "start": 10, "end": 20}},
{{"type": "PHONE", "value": "+49 170 1234567", "start": 55, "end": 70}}
],
"scenario": "Kundenanfrage"
}}
]
}}
Regeln für pii_spans:
- type: freier String, z.B. PERSON, EMAIL, PHONE, IBAN, ADDRESS, DATE_OF_BIRTH,
LICENSE_PLATE, IP_ADDRESS, CREDIT_CARD, TAX_ID, SSN, ID_CARD, PASSPORT, URL, USERNAME, ...
- start/end: Zeichenpositionen im text (0-basiert). MÜSSEN exakt dem value entsprechen.
- JEDE PII im Text muss als span annotiert sein.
"""
print(f" → Generiere {count}x '{scenario}' ...")
resp = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_prompt},
],
temperature=1.0,
response_format={"type": "json_object"},
)
raw = json.loads(resp.choices[0].message.content or "{}")
samples = raw.get("samples", [])
# Fix offsets: LLMs liegen oft daneben bei start/end
for s in samples:
for span in s.get("pii_spans", []):
val = span.get("value", "")
idx = s["text"].find(val)
if idx != -1:
span["start"] = idx
span["end"] = idx + len(val)
return samples
def main():
total = 50
batch_size = 5
all_samples = []
print(f"Generiere {total} synthetische PII-Texte...\n")
for i in range(0, total, batch_size):
scenario = random.choice(SCENARIOS)
n = min(batch_size, total - i)
try:
batch = generate(count=n, scenario=scenario)
all_samples.extend(batch)
print(f"{len(all_samples)}/{total} fertig")
except Exception as e:
print(f" ✗ Fehler: {e}")
out = Path("output/synthetic_pii_data.json")
out.parent.mkdir(exist_ok=True)
out.write_text(json.dumps(all_samples, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"\n{len(all_samples)} Texte gespeichert → {out}")
if __name__ == "__main__":
main()