891fc5bea0
mandatory_content_checker.py keywords break with alternative formulations. Solution: LLM-based check per mandatory field (9 calls, parallelizable). For other session to implement alongside Dict→Control migration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
154 lines
6.1 KiB
Markdown
154 lines
6.1 KiB
Markdown
# Instruktion: Hartkodiertes Wissen → Control Library Migration
|
||
|
||
**Branch:** `feat/zeroclaw-compliance-agent`
|
||
**Repo:** `/Users/benjaminadmin/Projekte/breakpilot-compliance/`
|
||
**Erstellt:** 2026-04-29
|
||
**Review-Deadline:** 2026-07-01
|
||
|
||
## Problem
|
||
|
||
Der Compliance Agent hat 6 Dateien mit hartkodiertem Rechtswissen in Python-Dicts.
|
||
Das Wissen veraltet und wird nicht von der Pipeline aktualisiert. Langfristig muss
|
||
alles aus der Control Library kommen (166k+ Controls in `compliance.canonical_controls`).
|
||
|
||
## Inventar (alle im Backend: `backend-compliance/compliance/services/`)
|
||
|
||
| Datei | Hartkodiert | Zeilen | Prioritaet |
|
||
|-------|------------|--------|-----------|
|
||
| `legal_basis_validator.py` | `CORRECT_BASIS` dict — 7 Lit-Zuordnungen (Art. 6 lit. a-f pro Zweck) | ~50 LOC | HOCH |
|
||
| `service_registry.py` | `SERVICE_REGISTRY` dict — 82 Services mit Legal Refs | ~500 LOC | MITTEL |
|
||
| `mandatory_content_checker.py` | `MANDATORY_DSE_CONTENT` (9 Felder) + `MANDATORY_IMPRESSUM_CONTENT` (5 Felder) | ~80 LOC | MITTEL |
|
||
| `relevance_filter.py` | `CONTROL_RELEVANCE` dict — 7 Controls mit Keyword-Listen | ~60 LOC | MITTEL |
|
||
| `consent-tester/services/script_analyzer.py` | `SERVICE_PATTERNS` — 19 Services (Duplikat von Registry) | ~70 LOC | NIEDRIG |
|
||
| `consent-tester/services/banner_detector.py` | `CMP_SELECTORS` — 10 CMPs | PERMANENT (technisch) | — |
|
||
|
||
## Migrationspfad pro Datei
|
||
|
||
### Schritt 1: Controls in der Pipeline generieren
|
||
|
||
Fuer jedes Dict-Entry einen entsprechenden Control in der Pipeline generieren lassen.
|
||
|
||
Beispiel fuer `legal_basis_validator.py`:
|
||
|
||
```sql
|
||
-- Neuer Control in canonical_controls:
|
||
INSERT INTO compliance.canonical_controls (title, objective, requirements, scope_conditions, tags)
|
||
VALUES (
|
||
'Cookie-Tracking erfordert Einwilligung (lit. a)',
|
||
'Cookie-Tracking und Webanalyse duerfen nur mit ausdruecklicher Einwilligung (Art. 6 Abs. 1 lit. a DSGVO) erfolgen.',
|
||
'Rechtsgrundlage fuer Cookie-Tracking muss Art. 6(1)(a) sein. Art. 6(1)(f) (berechtigtes Interesse) ist nach EuGH C-673/17 (Planet49) nicht zulaessig.',
|
||
'{"applies_when": "cookie_tracking OR web_analytics"}',
|
||
ARRAY['legal_basis', 'lit_mapping', 'cookie', 'planet49']
|
||
);
|
||
```
|
||
|
||
Benoetigte Controls (aus legal_basis_validator.py CORRECT_BASIS):
|
||
1. `cookie_tracking` → lit. a (Planet49)
|
||
2. `web_analytics` → lit. a (DSK Orientierungshilfe, §25 TDDDG)
|
||
3. `marketing_email` → lit. a (Art. 7 DSGVO, §7 UWG)
|
||
4. `remarketing` → lit. a (§25 TDDDG)
|
||
5. `credit_check` → lit. b/f + Art. 22 Pflichthinweis
|
||
6. `social_media_embed` → lit. a (Fashion ID Urteil)
|
||
7. `session_recording` → lit. a (§25 TDDDG)
|
||
|
||
Benoetigte Controls (aus mandatory_content_checker.py):
|
||
1. DSE muss Verantwortlichen nennen (Art. 13(1)(a))
|
||
2. DSE muss DSB-Kontakt nennen (Art. 13(1)(b))
|
||
3. DSE muss Zwecke nennen (Art. 13(1)(c))
|
||
4. DSE muss Rechtsgrundlagen nennen (Art. 13(1)(c))
|
||
5. DSE muss Speicherdauer nennen (Art. 13(2)(a))
|
||
6. DSE muss Betroffenenrechte nennen (Art. 13(2)(b-d))
|
||
7. DSE muss Beschwerderecht nennen (Art. 13(2)(d))
|
||
8. DSE muss Drittlandtransfer nennen (Art. 13(1)(f))
|
||
9. DSE muss automatisierte Entscheidungen nennen (Art. 13(2)(f))
|
||
|
||
### Schritt 2: Agent-Code aendern — Control Library first, Dict as Fallback
|
||
|
||
```python
|
||
# VORHER (hartkodiert):
|
||
CORRECT_BASIS = {"cookie_tracking": {"correct": "lit. a", ...}}
|
||
|
||
# NACHHER (Control Library first):
|
||
async def get_legal_basis_rule(purpose: str) -> dict | None:
|
||
controls = await query_controls(tags=["lit_mapping", purpose])
|
||
if controls:
|
||
return controls[0] # Control Library hat Vorrang
|
||
logger.warning("No control found for %s — using hardcoded fallback", purpose)
|
||
return CORRECT_BASIS.get(purpose) # Fallback
|
||
```
|
||
|
||
### Schritt 3: Dicts entfernen
|
||
|
||
Wenn alle Controls in der Library sind und der Agent sie zuverlaessig findet:
|
||
- Dicts loeschen
|
||
- Warning-Logs entfernen
|
||
- Tests aktualisieren
|
||
|
||
## Welche Datei NICHT migriert wird
|
||
|
||
`banner_detector.py` — die CMP-Selektoren sind technische CSS-Patterns, kein
|
||
Rechtswissen. Die bleiben hartkodiert und werden aktualisiert wenn CMPs ihre UI aendern.
|
||
|
||
## Erkennungszeichen im Code
|
||
|
||
Alle betroffenen Dateien haben:
|
||
- `⚠️ TECHNISCHE SCHULD` im Docstring
|
||
- `Review-Datum: 2026-07-01` im Header
|
||
- `logger.warning("... HARDCODED rules ...")` bei Nutzung
|
||
|
||
## Zusaetzliches Problem: Keyword-basierte Pflichtinhalte-Pruefung
|
||
|
||
### Problem (identifiziert beim IHK Konstanz Test, 2026-04-29)
|
||
|
||
Der `mandatory_content_checker.py` prueft ob DSE-Pflichtangaben vorhanden sind
|
||
per Keyword-Matching: `"zweck" in text.lower()`. Das bricht wenn:
|
||
|
||
- Andere Formulierung: "Verarbeitungszwecke" statt "Zwecke"
|
||
- Andere Sprache: Englische DSE auf deutscher Website
|
||
- Umschreibung: "Wozu wir Ihre Daten nutzen" statt "Zwecke"
|
||
|
||
Gleiches Problem bei `ECOMMERCE_INDICATORS` in der gleichen Datei — hartkodierte
|
||
Shop-Erkennung die neue Shop-Systeme nicht kennt.
|
||
|
||
### Betroffene Stellen
|
||
|
||
| Datei | Dict/Liste | Zeilen |
|
||
|-------|-----------|--------|
|
||
| `mandatory_content_checker.py` | `MANDATORY_DSE_CONTENT` keywords (9 Felder) | ~60 LOC |
|
||
| `mandatory_content_checker.py` | `MANDATORY_IMPRESSUM_CONTENT` keywords (5 Felder) | ~30 LOC |
|
||
| `mandatory_content_checker.py` | `ECOMMERCE_INDICATORS` | ~10 LOC |
|
||
|
||
### Loesung: LLM-basierte Pflichtinhalte-Pruefung
|
||
|
||
Statt hartkodierter Keywords → Qwen fragen:
|
||
|
||
```python
|
||
# VORHER (hartkodiert, bricht bei neuer Formulierung):
|
||
found = any(kw in text_lower for kw in ["zweck", "purpose", "verarbeitungszweck"])
|
||
|
||
# NACHHER (LLM-basiert, sprachunabhaengig):
|
||
prompt = f"""
|
||
Pruefe ob der folgende Text Angaben zu den Zwecken der Datenverarbeitung
|
||
enthaelt (Art. 13 Abs. 1 lit. c DSGVO).
|
||
Antworte NUR mit: JA (mit kurzem Zitat) oder NEIN.
|
||
Text: {dse_text[:2000]}
|
||
"""
|
||
result = await query_qwen(prompt)
|
||
found = result.startswith("JA")
|
||
```
|
||
|
||
### Aufwand
|
||
|
||
- 9 Pflichtfelder × 1 LLM-Call = 9 Calls (parallelisierbar)
|
||
- ~100 LOC Umbau
|
||
- Fallback auf Keywords wenn LLM nicht verfuegbar
|
||
|
||
### Prioritaet
|
||
|
||
MITTEL — die erweiterten Keywords funktionieren fuer 90% der Faelle.
|
||
LLM-Pruefung ist robuster aber langsamer (~30s statt <1s).
|
||
|
||
## Memory-Datei
|
||
|
||
Details: `/Users/benjaminadmin/.claude/projects/-Users-benjaminadmin-Projekte-breakpilot-lehrer/memory/hardcoded_knowledge_debt.md`
|