Grundrechte-Folgenabschaetzung mit 8 Sektionen, ~26 Placeholders, Conditional Blocks fuer Bildung/HR/oeffentliche Stellen. Python-Generator mit Domain→Grundrechte-Mapping (Education, HR, Healthcare, Finance). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
228 lines
10 KiB
Python
228 lines
10 KiB
Python
"""FRIA template generator — creates Fundamental Rights Impact Assessment from UCCA context.
|
|
|
|
Generates a FRIA (Art. 27 AI Act) based on:
|
|
- UCCA Assessment result (risk level, triggered rules, domain)
|
|
- AI Act Decision Tree classification
|
|
- Company profile
|
|
|
|
Automatically maps domains to affected fundamental rights.
|
|
"""
|
|
|
|
from typing import Optional
|
|
|
|
# -- Domain → Fundamental Rights Mapping ------------------------------------
|
|
|
|
DOMAIN_RIGHTS_MAP = {
|
|
"education": [
|
|
{"right": "Recht auf Bildung", "charter": "Art. 14", "gg": "Art. 12",
|
|
"risk": "Chancengleichheit bei KI-gestuetzter Bewertung oder Auswahl"},
|
|
{"right": "Nicht-Diskriminierung", "charter": "Art. 21", "gg": "Art. 3",
|
|
"risk": "Bias bei Leistungsbewertung nach Herkunft, Sprache oder Geschlecht"},
|
|
{"right": "Rechte des Kindes", "charter": "Art. 24", "gg": "Art. 6 Abs. 2",
|
|
"risk": "Besonderer Schutz Minderjaehriger vor automatisierten Bewertungen"},
|
|
],
|
|
"hr": [
|
|
{"right": "Berufsfreiheit / Recht zu arbeiten", "charter": "Art. 15", "gg": "Art. 12",
|
|
"risk": "KI-gestuetzte Auswahl kann Zugang zum Arbeitsmarkt einschraenken"},
|
|
{"right": "Nicht-Diskriminierung", "charter": "Art. 21", "gg": "Art. 3",
|
|
"risk": "Bias bei Recruiting, Befoerderung oder Kuendigung"},
|
|
{"right": "Schutz personenbezogener Daten", "charter": "Art. 8", "gg": "Art. 2 Abs. 1",
|
|
"risk": "Umfangreiche Verarbeitung von Beschaeftigtendaten"},
|
|
],
|
|
"healthcare": [
|
|
{"right": "Menschenwuerde", "charter": "Art. 1", "gg": "Art. 1",
|
|
"risk": "KI-Diagnosen koennen existenzielle Auswirkungen haben"},
|
|
{"right": "Schutz personenbezogener Daten", "charter": "Art. 8", "gg": "Art. 2 Abs. 1",
|
|
"risk": "Gesundheitsdaten sind besondere Kategorien (Art. 9 DSGVO)"},
|
|
{"right": "Nicht-Diskriminierung", "charter": "Art. 21", "gg": "Art. 3",
|
|
"risk": "Bias bei Behandlungsempfehlungen nach Alter, Geschlecht oder Ethnie"},
|
|
],
|
|
"finance": [
|
|
{"right": "Recht auf soziale Sicherheit", "charter": "Art. 34", "gg": "Art. 20",
|
|
"risk": "Zugang zu Finanzdienstleistungen und Versicherungen"},
|
|
{"right": "Nicht-Diskriminierung", "charter": "Art. 21", "gg": "Art. 3",
|
|
"risk": "Scoring-Bias bei Kreditvergabe oder Versicherungspraemien"},
|
|
{"right": "Recht auf wirksamen Rechtsbehelf", "charter": "Art. 47", "gg": "Art. 19 Abs. 4",
|
|
"risk": "Anfechtbarkeit automatisierter Finanzentscheidungen"},
|
|
],
|
|
"law_enforcement": [
|
|
{"right": "Recht auf Freiheit und Sicherheit", "charter": "Art. 6", "gg": "Art. 2 Abs. 2",
|
|
"risk": "KI-gestuetzte Ueberwachung oder Vorhersage"},
|
|
{"right": "Unschuldsvermutung", "charter": "Art. 48", "gg": "Art. 20 Abs. 3",
|
|
"risk": "Predictive Policing kann Vorverurteilung erzeugen"},
|
|
{"right": "Recht auf Privatsphaere", "charter": "Art. 7", "gg": "Art. 2 Abs. 1",
|
|
"risk": "Biometrische Identifizierung im oeffentlichen Raum"},
|
|
],
|
|
"public_sector": [
|
|
{"right": "Recht auf eine gute Verwaltung", "charter": "Art. 41", "gg": "Art. 20 Abs. 3",
|
|
"risk": "Automatisierte Verwaltungsentscheidungen muessen nachvollziehbar sein"},
|
|
{"right": "Nicht-Diskriminierung", "charter": "Art. 21", "gg": "Art. 3",
|
|
"risk": "Gleichbehandlung aller Buerger bei KI-gestuetzten Verwaltungsakten"},
|
|
{"right": "Recht auf wirksamen Rechtsbehelf", "charter": "Art. 47", "gg": "Art. 19 Abs. 4",
|
|
"risk": "Widerspruchsmoeglichkeit gegen KI-gestuetzte Bescheide"},
|
|
],
|
|
}
|
|
|
|
# Universal rights (always relevant for High-Risk AI)
|
|
UNIVERSAL_RIGHTS = [
|
|
{"right": "Schutz personenbezogener Daten", "charter": "Art. 8", "gg": "Art. 2 Abs. 1 i.V.m. Art. 1 Abs. 1",
|
|
"risk": "Datenverarbeitung durch KI-System"},
|
|
{"right": "Menschenwuerde", "charter": "Art. 1", "gg": "Art. 1",
|
|
"risk": "KI darf Menschen nicht auf Datenpunkte reduzieren"},
|
|
]
|
|
|
|
# -- Default measures -------------------------------------------------------
|
|
|
|
DEFAULT_MEASURES = [
|
|
"Human-in-the-Loop: Menschliche Ueberpruefung aller KI-Empfehlungen vor Umsetzung",
|
|
"Transparenz: Betroffene werden ueber den Einsatz von KI informiert",
|
|
"Erklaerbarkeit: KI-Ergebnisse koennen nachvollzogen und begruendet werden",
|
|
"Beschwerdemechanismus: Betroffene koennen KI-Entscheidungen anfechten",
|
|
"Logging: Alle Eingaben und Ausgaben werden fuer Audit-Zwecke protokolliert",
|
|
"Regelmaessige Bias-Audits: Systematische Pruefung auf Diskriminierung",
|
|
]
|
|
|
|
HR_MEASURES = [
|
|
"AGG-konforme Gestaltung: Kein Bias bei Geschlecht, Alter, Herkunft, Behinderung",
|
|
"Betriebsrat gemaess §87 Abs.1 Nr.6 und §95 BetrVG beteiligt",
|
|
"Keine automatisierte Endentscheidung bei Personalangelegenheiten",
|
|
]
|
|
|
|
EDUCATION_MEASURES = [
|
|
"Lehrkraft ueberprueft und verantwortet alle KI-generierten Bewertungen",
|
|
"Chancengleichheit unabhaengig von sozioekonomischem Hintergrund",
|
|
"Schueler/Eltern koennen KI-gestuetzte Bewertungen anfechten",
|
|
]
|
|
|
|
|
|
def generate_fria_draft(ctx: dict) -> dict:
|
|
"""Generate a FRIA draft from UCCA assessment context.
|
|
|
|
Args:
|
|
ctx: Dict with keys:
|
|
Required:
|
|
- organisation_name: str
|
|
- system_name: str
|
|
- system_description: str
|
|
- einsatzzweck: str
|
|
Optional:
|
|
- organisation_address: str
|
|
- system_version: str
|
|
- system_provider: str
|
|
- domain: str (education, hr, healthcare, finance, etc.)
|
|
- affected_groups: list[str]
|
|
- affected_count: str
|
|
- ai_act_classification: str (high_risk, limited_risk, etc.)
|
|
- annex_iii_category: str
|
|
- is_public_entity: bool
|
|
- has_hr_context: bool
|
|
- has_education_context: bool
|
|
- risk_score: int
|
|
- dpo_name: str
|
|
- dpo_contact: str
|
|
- review_interval: str
|
|
|
|
Returns:
|
|
Dict with placeholder values for template substitution.
|
|
"""
|
|
result = {}
|
|
|
|
# Section 1: Basic info
|
|
result["ORGANISATION_NAME"] = ctx.get("organisation_name", "{{ORGANISATION_NAME}}")
|
|
result["ORGANISATION_ADRESSE"] = ctx.get("organisation_address", "{{ORGANISATION_ADRESSE}}")
|
|
result["VERANTWORTLICHER"] = ctx.get("responsible_person", "{{VERANTWORTLICHER}}")
|
|
result["ERSTELLT_VON"] = ctx.get("created_by", "{{ERSTELLT_VON}}")
|
|
result["ERSTELLT_AM"] = ctx.get("created_at", "{{ERSTELLT_AM}}")
|
|
result["SYSTEM_NAME"] = ctx.get("system_name", "{{SYSTEM_NAME}}")
|
|
result["SYSTEM_VERSION"] = ctx.get("system_version", "1.0")
|
|
result["SYSTEM_BESCHREIBUNG"] = ctx.get("system_description", "{{SYSTEM_BESCHREIBUNG}}")
|
|
result["SYSTEM_ANBIETER"] = ctx.get("system_provider", "{{SYSTEM_ANBIETER}}")
|
|
result["EINSATZZWECK"] = ctx.get("einsatzzweck", "{{EINSATZZWECK}}")
|
|
result["EINSATZKONTEXT"] = ctx.get("einsatzkontext", "{{EINSATZKONTEXT}}")
|
|
result["AI_ACT_KLASSIFIKATION"] = ctx.get("ai_act_classification", "High-Risk")
|
|
result["ANNEX_III_KATEGORIE"] = ctx.get("annex_iii_category", "")
|
|
result["DSB_NAME"] = ctx.get("dpo_name", "{{DSB_NAME}}")
|
|
result["DSB_KONTAKT"] = ctx.get("dpo_contact", "{{DSB_KONTAKT}}")
|
|
|
|
# Section 1.5: Affected groups
|
|
groups = ctx.get("affected_groups", [])
|
|
result["BETROFFENE_GRUPPEN"] = _bullet_list(groups) if groups else "{{BETROFFENE_GRUPPEN}}"
|
|
result["BETROFFENE_ANZAHL"] = ctx.get("affected_count", "{{BETROFFENE_ANZAHL}}")
|
|
|
|
# Section 2: Fundamental rights mapping
|
|
domain = ctx.get("domain", "")
|
|
rights = list(UNIVERSAL_RIGHTS)
|
|
if domain in DOMAIN_RIGHTS_MAP:
|
|
rights.extend(DOMAIN_RIGHTS_MAP[domain])
|
|
|
|
rights_table = []
|
|
for i, r in enumerate(rights, 1):
|
|
rights_table.append(
|
|
f"| {i} | {r['right']} | {r['charter']} | {r['gg']} | Ja | {r['risk']} |"
|
|
)
|
|
result["GRUNDRECHTE_ANALYSE"] = "\n".join(rights_table) if rights_table else "{{GRUNDRECHTE_ANALYSE}}"
|
|
|
|
# Section 3: Risk matrix
|
|
risk_rows = []
|
|
risk_score = ctx.get("risk_score", 0)
|
|
base_likelihood = min(3, 1 + risk_score // 30)
|
|
for r in rights:
|
|
severity = 3 if "Diskriminierung" in r["risk"] or "existenz" in r["risk"].lower() else 2
|
|
likelihood = base_likelihood
|
|
level = _risk_level(likelihood * severity)
|
|
risk_rows.append(
|
|
f"| {r['right']} | {r['risk']} | {likelihood} | {severity} | {level} | Basierend auf Systemanalyse |"
|
|
)
|
|
result["RISIKOMATRIX"] = "\n".join(risk_rows) if risk_rows else "{{RISIKOMATRIX}}"
|
|
|
|
# Section 4: Measures
|
|
measures = list(DEFAULT_MEASURES)
|
|
if ctx.get("has_hr_context") or domain == "hr":
|
|
measures.extend(HR_MEASURES)
|
|
if ctx.get("has_education_context") or domain == "education":
|
|
measures.extend(EDUCATION_MEASURES)
|
|
result["MASSNAHMEN_LISTE"] = _bullet_list(measures)
|
|
|
|
result["HUMAN_OVERSIGHT_BESCHREIBUNG"] = ctx.get("human_oversight",
|
|
"Das System unterstuetzt menschliche Entscheidungen, trifft jedoch keine eigenstaendigen Entscheidungen. "
|
|
"Alle KI-generierten Empfehlungen werden von qualifiziertem Personal geprueft.")
|
|
|
|
result["TRANSPARENZ_MASSNAHMEN"] = ctx.get("transparency_measures",
|
|
"Betroffene Personen werden ueber den Einsatz des KI-Systems informiert. "
|
|
"KI-generierte Ergebnisse werden als solche gekennzeichnet.")
|
|
|
|
# Section 5: Consultation
|
|
result["KONSULTATION_ERGEBNISSE"] = ctx.get("consultation_results",
|
|
"Konsultation steht aus — bitte vor Freigabe durchfuehren.")
|
|
|
|
# Section 6: Approval
|
|
result["GENEHMIGT_VON"] = ctx.get("approved_by", "{{GENEHMIGT_VON}}")
|
|
result["GENEHMIGT_AM"] = ctx.get("approved_at", "{{GENEHMIGT_AM}}")
|
|
|
|
# Section 7: Monitoring
|
|
result["NAECHSTE_UEBERPRUEFUNG"] = ctx.get("review_interval", "12 Monate nach Inbetriebnahme")
|
|
|
|
# Conditional flags
|
|
result["BILDUNGSKONTEXT"] = ctx.get("has_education_context", False) or domain == "education"
|
|
result["HR_KONTEXT"] = ctx.get("has_hr_context", False) or domain == "hr"
|
|
result["OEFFENTLICHE_STELLE"] = ctx.get("is_public_entity", False)
|
|
|
|
return result
|
|
|
|
|
|
def _risk_level(score: int) -> str:
|
|
"""Map risk score to level label."""
|
|
if score <= 6:
|
|
return "Niedrig"
|
|
elif score <= 12:
|
|
return "Mittel"
|
|
elif score <= 19:
|
|
return "Hoch"
|
|
else:
|
|
return "Kritisch"
|
|
|
|
|
|
def _bullet_list(items: list) -> str:
|
|
"""Format a list as markdown bullet points."""
|
|
return "\n".join(f"- {item}" for item in items)
|