Files
breakpilot-core/document-templates/generators/fria_template.py
Benjamin Admin f23b872c54 feat: FRIA Template (Art. 27 AI Act) — 7. Document Template
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>
2026-04-12 16:38:59 +02:00

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)