Dateien nach "/" hochladen
This commit is contained in:
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
||||
OPENAI_API_KEY=sk-....
|
||||
149
generate_pii_texts.py
Normal file
149
generate_pii_texts.py
Normal file
@@ -0,0 +1,149 @@
|
||||
"""
|
||||
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()
|
||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
openai
|
||||
python-dotenv
|
||||
Reference in New Issue
Block a user