Multi-page crawl: scan 5-10 strategic pages (start, footer links) for chatbot widgets, AI text mentions, and tracking services. Feed results into relevance filter to reduce false positives. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12 KiB
Plan: Control Relevance Filter — Generische Controls kontextsensitiv filtern
Problem
Die UCCA-Engine empfiehlt Controls pauschal basierend auf Intake-Flags (Boolean-Felder wie
personal_data: true, marketing: true). Sie prueft NICHT, ob der analysierte Text die
Bedingungen fuer einen spezifischen Control tatsaechlich erfuellt.
Konkretes Beispiel (Opodo-Test, 2026-04-28)
- Control:
[C_TRANSPARENCY] Nutzer informieren dass sie mit KI interagieren - Quelle: AI Act Art. 52 — nur relevant wenn KI eingesetzt wird
- Opodo sagt: "automated processing" (kann regelbasierte Software sein, muss keine KI sein)
- Ergebnis: False Positive — Control wird empfohlen obwohl kein KI-Einsatz belegt ist
Skalierung
Von ~166.740 Controls in der RAG-Datenbank wird ein unbekannter Prozentsatz bei jeder Bewertung generisch empfohlen. Jedes False Positive untergräbt das Vertrauen des Nutzers und macht das Tool fuer Abmahnungen unbrauchbar.
Loesung: 3-Stufen Relevance Filter
Stufe 1: Regelbasierter Vorfilter (deterministisch, schnell)
Jeder Control bekommt ein relevance_conditions Feld (JSON):
{
"control_id": "C_TRANSPARENCY",
"relevance_conditions": {
"text_must_contain_any": ["KI", "kuenstliche Intelligenz", "artificial intelligence",
"machine learning", "maschinelles Lernen", "neural", "deep learning",
"AI system", "AI-System", "algorith"],
"text_must_not_contain": [],
"requires_intake_flag": "automation",
"min_confidence": 0.5
}
}
Implementierung:
- Neues Feld
relevance_conditionsincompliance.canonical_controls(JSONB) - Funktion
check_relevance(control, source_text) -> (relevant: bool, confidence: float) - Laeuft NACH dem UCCA-Assessment, BEVOR das Ergebnis zurueckgegeben wird
- Filtert Controls raus deren Keywords im Quelltext nicht vorkommen
Aufwand: ~200 LOC Python, kein LLM-Call noetig
Datei: ai-compliance-sdk/internal/ucca/relevance_filter.go oder backend-compliance/compliance/services/relevance_filter.py
Stufe 2: LLM-Validierung (fuer High-Value Controls)
Fuer Controls mit severity >= HIGH oder wenn der regelbasierte Filter unsicher ist
(confidence < 0.7), wird Qwen gefragt:
Gegeben dieser Dokumenttext:
"[...Auszug...]"
Ist der folgende Control relevant fuer dieses Dokument?
Control: "[C_TRANSPARENCY] Nutzer informieren dass sie mit KI interagieren"
Rechtsgrundlage: Art. 52 AI Act
Antworte NUR mit: JA (mit Begruendung) oder NEIN (mit Begruendung)
Implementierung:
- Neuer Endpoint:
POST /sdk/v1/ucca/validate-controls - Nimmt:
assessment_id,source_text,controls[] - Gibt zurueck:
controls[]mitrelevant: bool,reason: string - Cached: Gleicher Text + Control = gleiche Antwort (24h TTL)
Aufwand: ~150 LOC, 1 LLM-Call pro Control (parallelisierbar)
Stufe 3: Follow-Up-Fragen an den Nutzer (Hybrid)
Wenn weder Regel noch LLM sicher entscheiden koennen:
Follow-Up: "Setzt der Anbieter KI oder maschinelles Lernen ein?"
→ Ja: Control bleibt
→ Nein: Control wird entfernt
→ Unsicher: Control bleibt mit Hinweis "Nicht verifizierbar"
Bereits implementiert: Das follow_up_questions System im Agent-Endpoint.
Datenmodell-Aenderung
-- Neues Feld in canonical_controls
ALTER TABLE compliance.canonical_controls
ADD COLUMN IF NOT EXISTS relevance_conditions JSONB DEFAULT '{}';
-- Index fuer schnelle Abfrage
CREATE INDEX IF NOT EXISTS idx_controls_relevance
ON compliance.canonical_controls USING gin (relevance_conditions);
Architektur
UCCA Assessment
│
▼
┌────────────────────┐
│ Stufe 1: Regelfilter│ ← text_must_contain_any, intake_flags
│ (deterministisch) │
└────────┬───────────┘
│ unsicher oder high-severity
▼
┌────────────────────┐
│ Stufe 2: LLM-Check │ ← Qwen validiert Relevanz
│ (1 Call/Control) │
└────────┬───────────┘
│ immer noch unsicher
▼
┌────────────────────┐
│ Stufe 3: Follow-Up │ ← Nutzer beantwortet Frage
│ (Frontend) │
└────────────────────┘
Implementierungsreihenfolge
Phase 1: Regelfilter (1 Tag)
- Migration:
relevance_conditionsFeld zucanonical_controls - Seed-Script: Top-20 generische Controls mit Bedingungen versehen (C_TRANSPARENCY, C_EXPLICIT_CONSENT, C_DSFA_REQUIRED, etc.)
- Filter-Funktion in
agent_analyze_routes.py - Test: Opodo erneut analysieren — C_TRANSPARENCY sollte rausfallen
Phase 2: LLM-Validierung (1 Tag)
- Neuer SDK-Endpoint
/sdk/v1/ucca/validate-controls - Integration in den Agent-Workflow
- Caching-Layer (Redis/Valkey)
Phase 3: Batch-Seeding (2-3 Tage)
- Pipeline-Job: Fuer alle 166k Controls
relevance_conditionsgenerieren (LLM-gestuetzt: "Welche Keywords im Quelltext wuerden diesen Control relevant machen?") - Qualitaetspruefung: Stichprobe von 100 Controls manuell validieren
Betroffene Dateien
| Datei | Aenderung |
|---|---|
backend-compliance/compliance/api/agent_analyze_routes.py |
Filter-Integration |
backend-compliance/compliance/services/relevance_filter.py |
NEU: Regelfilter |
ai-compliance-sdk/internal/ucca/relevance_filter.go |
NEU: SDK-seitig (alternativ) |
ai-compliance-sdk/internal/api/handlers/ucca_handlers.go |
Neuer Endpoint |
| Migration | relevance_conditions Spalte |
control-pipeline/ |
Batch-Seeding Job (Phase 3) |
Phase 4: Website-Scan (Multi-Page Crawl)
Problem
Aktuell analysieren wir nur EINE URL (z.B. /datenschutz/). Aber relevante Hinweise
auf KI, Chatbots, automatisierte Entscheidungen oder Tracking koennen auf ANDEREN
Seiten der Website stehen:
- Chatbot-Widget auf der Startseite (nicht auf der Datenschutzseite)
- "Powered by ChatGPT" im Footer
- KI-gestuetzte Produktempfehlungen auf der Shopseite
- Cookie-Scripts die Tracking-Dienste laden (Google Analytics, Meta Pixel, etc.)
- Chatbot-Anbieter wie Intercom, Drift, Zendesk, Tidio im HTML
Loesung: Lightweight Website-Scan
Kein vollstaendiger Crawl (zu langsam, zu invasiv), sondern ein gezielter Scan von 5-10 strategischen Seiten:
Eingabe: https://www.opodo.de/datenschutz/
Automatisch gescannte Seiten:
1. Startseite: https://www.opodo.de/
2. Datenschutz (bereits): https://www.opodo.de/datenschutz/
3. Impressum: https://www.opodo.de/impressum/ (aus Footer-Links)
4. AGB: https://www.opodo.de/agb/ (aus Footer-Links)
5. Cookie-Policy: https://www.opodo.de/cookies/ (falls vorhanden)
Scan-Logik
Schritt 1: Startseite holen + Footer-Links extrahieren
# Aus der Startseite die typischen Footer-Links extrahieren:
footer_patterns = [
r'href="([^"]*(?:impressum|imprint|legal)[^"]*)"',
r'href="([^"]*(?:datenschutz|privacy|dsgvo)[^"]*)"',
r'href="([^"]*(?:agb|terms|nutzungsbedingungen)[^"]*)"',
r'href="([^"]*(?:cookie|cookies)[^"]*)"',
r'href="([^"]*(?:kontakt|contact)[^"]*)"',
]
Schritt 2: Jede Seite auf KI/Chatbot/Tracking-Indikatoren scannen
AI_INDICATORS = {
# Chatbot-Widgets (JavaScript-Einbindungen)
"chatbot_widgets": [
r"intercom", # Intercom (KI-gestuetzt)
r"drift\.com", # Drift Chatbot
r"tidio", # Tidio Chat
r"zendesk", # Zendesk Chat
r"crisp\.chat", # Crisp Chat
r"livechat", # LiveChat
r"hubspot.*chat", # HubSpot Chat
r"tawk\.to", # Tawk.to
r"freshchat", # Freshworks
r"dialogflow", # Google Dialogflow
r"watson.*assistant", # IBM Watson
r"chatgpt|openai", # OpenAI/ChatGPT
r"anthropic|claude", # Anthropic/Claude
],
# KI-Hinweise im Text
"ai_text_mentions": [
r"k(?:ue|ü)nstliche.?intelligenz",
r"artificial.?intelligence",
r"machine.?learning",
r"maschinelles.?lernen",
r"KI.?gest(?:ue|ü)tzt",
r"AI.?powered",
r"algorithm",
r"automatisierte.?entscheidung",
r"automated.?decision",
r"profiling",
r"personalisier", # Personalisierung
],
# Tracking-Dienste
"tracking_services": [
r"google.?analytics|gtag|UA-\d+|G-\w+",
r"facebook.?pixel|fbq\(",
r"meta.?pixel",
r"hotjar",
r"segment\.com",
r"mixpanel",
r"amplitude",
r"matomo|piwik",
r"plausible",
],
}
Schritt 3: Ergebnis aggregieren
scan_result = {
"pages_scanned": 5,
"chatbot_detected": True, # z.B. Intercom auf Startseite
"chatbot_provider": "intercom", # Identifizierter Anbieter
"ai_mentions_found": False, # Kein expliziter KI-Text
"tracking_services": ["google_analytics", "facebook_pixel"],
"tracking_count": 2,
}
Schritt 4: Scan-Ergebnis in Relevanzpruefung einbeziehen
- Chatbot erkannt → C_TRANSPARENCY wird relevant (auch ohne KI-Text)
- Tracking erkannt → C_EXPLICIT_CONSENT wird relevant
- Kein KI-Nachweis auf gesamter Website → C_TRANSPARENCY faellt weg
Implementierung
Neue Datei: backend-compliance/compliance/services/website_scanner.py (~200 LOC)
class WebsiteScanner:
async def scan(self, base_url: str) -> ScanResult:
"""Scan 5-10 pages for AI, chatbot, and tracking indicators."""
pages = await self._discover_pages(base_url)
indicators = {}
for page_url in pages[:10]:
html = await self._fetch(page_url)
indicators[page_url] = self._detect_indicators(html)
return self._aggregate(indicators)
Integration in Agent-Workflow:
- Zwischen Schritt 1 (Fetch) und Schritt 3 (UCCA Assess)
- Scan-Ergebnis fliesst in die Intake-Flags UND in den Relevanzfilter
- Scan-Ergebnis wird im Response zurueckgegeben (Transparenz)
Frontend-Erweiterung:
- "Erweiterte Analyse" Toggle: Nur Einzelseite vs. Website-Scan
- Scan-Ergebnis als aufklappbare Sektion: "5 Seiten gescannt, Chatbot auf Startseite erkannt"
Aufwand
| Komponente | LOC | Zeit |
|---|---|---|
website_scanner.py |
~200 | 0.5 Tage |
Integration in agent_analyze_routes.py |
~50 | 2h |
| Frontend: Scan-Ergebnis anzeigen | ~80 | 2h |
| Tests | ~100 | 2h |
Beispiel: Opodo mit Website-Scan
Seiten gescannt: 5
- https://www.opodo.de/ → Didomi Cookie-Consent, Google Analytics
- https://www.opodo.de/datenschutz/ → Datenschutzerklaerung
- https://www.opodo.de/impressum/ → 404 (FINDING!)
- https://www.opodo.de/agb/ → AGB vorhanden
- https://www.opodo.de/cookies/ → Cookie-Policy
Chatbot erkannt: Nein
KI-Hinweise: Nein
Tracking: Google Analytics (G-03F834EHLM), Didomi CMP
→ C_TRANSPARENCY: NICHT relevant (kein KI-Nachweis auf gesamter Website)
→ C_EXPLICIT_CONSENT: Relevant (Google Analytics + Didomi = Tracking aktiv)
→ Impressum-Finding: 404 auf /impressum/ (§5 TMG Verstoss)
Risiken
| Risiko | Mitigation |
|---|---|
| Zu aggressive Filterung (False Negatives) | Stufe 1 nur fuer klare Faelle, Stufe 2 als Fallback |
| LLM-Kosten bei vielen Controls | Caching + nur high-severity Controls |
| Datenbank-Migration auf Production | ADD COLUMN IF NOT EXISTS ist non-blocking |
| 166k Controls ohne relevance_conditions | Default {} = kein Filter = bisheriges Verhalten |
Testfaelle
- Opodo-Test: C_TRANSPARENCY sollte NICHT mehr empfohlen werden (kein KI-Nachweis)
- Chatbot-Anbieter: C_TRANSPARENCY SOLL empfohlen werden (KI explizit erwaehnt)
- Arztpraxis-Website: C_DSFA_REQUIRED SOLL empfohlen werden (Gesundheitsdaten)
- Blog ohne Tracking: Nur minimale Controls (Impressum, Datenschutzerklaerung)