""" 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()