feat: KI-gestützte Inhaltsanalyse via OpenRouter (hash-gegated)

Optionale semantische Prüfung von Text und Bildern (inkl. OCR) auf
problematische Inhalte ohne Link-Signal: Pornografie, Propaganda,
diffamierende/strafbare Texte, versteckter Spam, themenfremde Werbung,
widersprüchliche Aussagen. Bewertet die thematische Passung zum
deklarierten site_context — eingestreute Heimat-Begriffe täuschen die
Erkennung nicht.

Kern:
- scanner/ai_analyzer.py: run_ai_analysis + score_ai_findings.
  Hash-Gate über data/ai_ledger.json → unveränderte Inhalte = Cache-
  Treffer = kein API-Call. Nur neue/geänderte Inhalte kosten etwas.
- Modell-Kette mit zweifacher Eskalation (Stufe 1+2 free, Stufe 3
  günstig bezahlt); eskaliert bei Fehler ODER Timeout (attempt_timeout).
  Erfolgreiches Modell wird im Ledger vermerkt.
- KI-Funde sind auf GELB gedeckelt — ROT bleibt harten Integritäts-
  Signalen vorbehalten. Graceful degradation: ohne Key/bei Fehler wird
  übersprungen, Scan läuft unverändert weiter.

Integration:
- baseline.py: load/save_ai_ledger, dismiss_ai_entries.
- config.py: ai_analysis-Block + ai_* Scoring-Schlüssel.
- __main__.py: Einhängung in cmd_scan/cmd_check, ai-dismiss-Subcommand,
  approve quittiert zugehörige Funde, status-Anzeige, diff-only + report.
- alerter.py + __main__.py: beanstandete Dateien erscheinen mit URL,
  Begründung und Quittier-Fingerprint in E-Mail UND Markdown-Report.
- plain.py: laienverständliche KI-Sätze.

API-Key nur aus Umgebungsvariable (OPENROUTER_API_KEY). Audio/Video als
abschaltbare Hooks vorbereitet (default aus).

Modelle live gegen OpenRouter verifiziert; Demo auf bredelar.info zeigte
korrekte Erkennung (echter Inhalt clean, eingeschleuster Casino-Spam mit
Heimat-Begriffen als hidden_spam erkannt).

Tests: 227 grün (+26 für ai_analyzer, +1 für E-Mail-KI-Abschnitt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-13 00:04:33 +02:00
commit cae3dbb985
11 changed files with 1301 additions and 7 deletions

View file

@ -59,6 +59,7 @@ dafür **nichts** extra einzurichten — ein täglicher Aufruf genügt für alle
| `report --diff-only` | Nur Textdiffs geänderter Seiten (farbig, kompakt) |
| `status` | Baseline-Datum, letzter Scan, Risiko-Level |
| `test-alert` | Test-E-Mail senden, ohne echten Scan (SMTP-Check) |
| `ai-dismiss --all` | KI-Funde als geprüft/akzeptiert quittieren (Fehlalarm) |
Alle Befehle akzeptieren `--config path/to/config.yaml` und `--verbose`.
@ -219,6 +220,84 @@ Nach dem ersten `init` alle legitimen externen Domains in
---
## KI-Inhaltsanalyse (optional)
Der Integritäts-Kern erkennt strukturelle Manipulationen (fremde Links, Hidden Content,
Webshells). Die optionale KI-Analyse ergänzt ihn um **semantische** Prüfung von Text und
Bildern: Pornografie, Propaganda, diffamierende/strafbare Inhalte, versteckter Spam,
thematisch unpassende Werbung, widersprüchliche Aussagen — auch ohne Link-Signal und
inklusive Text in Bildern (OCR).
**Kostengate:** Jeder Inhalt bekommt einen Hash. Unveränderte Inhalte liegen mit ihrem
Verdikt im Cache (`data/ai_ledger.json`) → **kein API-Call**. Nur neue oder geänderte
Inhalte lösen eine (günstige) OpenRouter-Abfrage aus. Dadurch läuft die Analyse bei jedem
Scan mit und kostet an den meisten Tagen 0 €.
KI-Funde sind **auf Gelb gedeckelt** — sie lösen nie allein ROT aus (das bleibt harten
Integritäts-Signalen vorbehalten). Fehlt der API-Key oder schlägt eine Abfrage fehl, wird
die Analyse übersprungen und der Scan läuft unverändert weiter.
### Aktivieren
In `config.yaml`:
```yaml
ai_analysis:
enabled: true
api_key_env: "OPENROUTER_API_KEY"
site_context: "Heimat- und Vereinswebsite über die Bergbaugeschichte in Bredelar."
ai_confidence_min: 0.7
attempt_timeout: 30 # Zeitlimit je Modell-Versuch → eskaliert bei Langsamkeit
text:
enabled: true
models: # Kette: Stufe 1+2 free, Stufe 3 günstig bezahlt
- "qwen/qwen3-next-80b-a3b-instruct:free"
- "meta-llama/llama-3.3-70b-instruct:free"
- "google/gemini-2.5-flash-lite"
image:
enabled: true
models: # multimodal: Inhalt + OCR
- "google/gemma-4-31b-it:free"
- "nvidia/nemotron-nano-12b-v2-vl:free"
- "google/gemini-2.5-flash-lite"
```
Die Modelle werden als **Kette mit zweifacher Eskalation** abgearbeitet: Stufe 1 und 2 sind
kostenlose Modelle, Stufe 3 ein günstiges Bezahlmodell. Schlägt ein Modell fehl oder antwortet
es langsamer als `attempt_timeout`, wird automatisch zur nächsten Stufe eskaliert. Im Report
und Ledger wird festgehalten, welche Stufe das Verdikt geliefert hat.
`site_context` ist entscheidend: Die KI bewertet die **thematische Passung** zur
deklarierten Beschreibung, nicht einzelne Schlüsselwörter — eingestreute Heimat-Begriffe
machen Spam so nicht unauffällig.
API-Key als Umgebungsvariable setzen (nie in config.yaml):
```bash
export OPENROUTER_API_KEY="sk-or-..."
```
### Fehlalarm quittieren
Stuft die KI legitimen Inhalt fälschlich als auffällig ein:
```bash
python -m scanner https://ihre-website.de ai-dismiss --all # alle offenen Funde
python -m scanner https://ihre-website.de ai-dismiss --hash <fingerprint>
```
Ein `approve --all` / `--url` quittiert die zugehörigen KI-Funde automatisch mit.
### Modelle (OpenRouter)
Pro Modalität eine Kette mit zweifacher Eskalation (Stufe 1+2 free, Stufe 3 bezahlt):
| Zweck | Stufe 1 (free) | Stufe 2 (free) | Stufe 3 (bezahlt) |
|---|---|---|---|
| Text/Semantik | `qwen3-next-80b-a3b-instruct:free` | `llama-3.3-70b-instruct:free` | `gemini-2.5-flash-lite` |
| Bild + OCR | `gemma-4-31b-it:free` | `nemotron-nano-12b-v2-vl:free` | `gemini-2.5-flash-lite` |
Audio/Video sind als abschaltbare Hooks vorbereitet (`ai_analysis.audio/video`,
default aus).
---
## Verzeichnisstruktur
```