Plugin dedup: LLM als zweite Stufe für Grenzfälle

Regelbasierte Dedup-Prüfung bleibt maßgeblich; nur Grenzfälle gehen an ein
LLM: Titel mit Wort-Überschneidung unter der Fuzzy-Schwelle (Editionen, Big
Boxes, Übersetzungen wie 'El Grande Big Box' vs. 'El Grande: 25 Jahre') und
ein ergänzender Call zum Verlags-/Titel-Hinweis bei Verlags-Konflikt
(Verlags-Aliase). Klare Treffer und Non-Treffer bleiben regelbasiert.

- Neu: ki_pruefung.py — Env-Konfiguration (SPIELE_DEDUP_LLM_*), robuster
  JSON-Parser (Code-Fences, Zusatztext), OpenAI-kompatibler Client via httpx
  mit Timeout und genau einem Retry; liefert bei jedem Fehler None.
- pruefung.py: bestätigte Grenzfälle laufen in die üblichen Prüfungen a)-d)
  zurück; deutscher Titel füllt die Titel-Empfehlung, Verlags-Empfehlung
  landet im Konflikt-Detail. Neues Feld PruefErgebnis.ki_befunde.
- __init__.py: Aktivierungsprüfung vor jeder Fabrik — aus/unvollständig
  heißt nie ein LLM-Aufruf; KI-Befunde werden über die audit-log-API
  protokolliert (Modell, Konfidenz, Entscheidung), best effort.
- Tests: gemockte HTTP-Antworten (MockTransport/Fakes), Fallback-Fälle
  (aktiv=0, kein Key, Timeout, kaputtes JSON), Konfidenz-Schwelle,
  Parsing-Robustheit; kein echter LLM-Call in CI.
- README.md um die neuen Env-Variablen und den Zweitprüfungs-Abschnitt
  ergänzt.
This commit is contained in:
Flo Hartmann
2026-08-22 23:43:33 +00:00
parent b6d67e04e5
commit 8e5f65d864
6 changed files with 1201 additions and 12 deletions

View File

@@ -59,6 +59,12 @@ Beim ersten Start wird automatisch ein Admin-Konto angelegt:
| `SPIELE_BGG_MAX_TREFFER_PRO_SUCHE` | `25` | Obergrenze Treffer je Suchbegriff (schont das BGG-Rate-Limit) |
| `SPIELE_BGG_TOKEN` | *(leer)* | API-Token für die BGG-XML-API2, wird als `Authorization: Bearer …`-Header gesendet; seit der Token-Pflicht von BGG erforderlich (siehe [BGG-Thread 3602374](https://boardgamegeek.com/thread/3602374)) — ohne Token wird der Sync übersprungen |
| `SPIELE_DEDUP_BGG_AKTIV` | `1` | BGG-Zusatzdaten für die Dedup-Prüfung an (`1`) oder aus (`0`): Alternate-Names und Erweiterungs-Relationen |
| `SPIELE_DEDUP_LLM_AKTIV` | `0` | LLM-Zweitprüfung der Dedup-Grenzfälle an (`1`) oder aus (`0`) |
| `SPIELE_DEDUP_LLM_BASIS_URL` | *(leer)* | Basis-URL einer OpenAI-kompatiblen Chat-Completions-API, z. B. `https://api.openai.com/v1` |
| `SPIELE_DEDUP_LLM_API_KEY` | *(leer)* | API-Key, wird als `Authorization: Bearer …` gesendet |
| `SPIELE_DEDUP_LLM_MODELL` | *(leer)* | Modellname, z. B. `gpt-4o-mini` |
| `SPIELE_DEDUP_LLM_KONFIDENZ_MIN` | `0.7` | Mindest-Konfidenz; darunter wird das LLM-Ergebnis verworfen und nur der Regelbefund angezeigt |
| `SPIELE_DEDUP_LLM_TIMEOUT_SEKUNDEN` | `20` | Timeout je LLM-Aufruf; Netzwerk-/Server-Fehler werden genau einmal wiederholt |
| `SPIELE_ARCHIV_JOB_AKTIV` | `1` | Täglicher Archivierungs-Job an (`1`) oder aus (`0`) |
| `SPIELE_ARCHIV_JOB_UHRZEIT` | `03:00` | Tageszeit des täglichen Archiv-Laufs im Format `HH:MM` |
| `SPIELE_ERINNERUNG_JOB_AKTIV` | `1` | Täglicher Erinnerungs-Check an (`1`) oder aus (`0`) |
@@ -356,6 +362,37 @@ Teilprüfung. BGG-Störungen blockieren nie: der Hilfsclient degradiert auf
die reine Heuristik und protokolliert nur. Abschaltbar über
`SPIELE_DEDUP_BGG_AKTIV=0` (Standard: an, Rate-Limit 1 s).
### LLM-Zweitprüfung für Grenzfälle
Regeln stoßen bei Editionen, Big Boxes, Übersetzungen und Verlags-Aliasen an
Grenzen („El Grande Big Box“ vs. „El Grande: 25 Jahre“, „Catan Das schnelle
Spiel“ vs. „Catan Fast Edition“). Deshalb kann optional ein LLM als **zweite
Stufe** eingeschaltet werden — die Regelprüfung bleibt maßgeblich:
- **Nur Grenzfälle gehen ans LLM:** Titel mit gemeinsamen Wort-Tokens, aber
Fuzzy-Score unter der Schwelle (max. 5 Paarungen je Prüfung), sowie ein
ergänzender Call zum Verlags-/Titel-Hinweis bei einem regelbasierten
Verlags-Konflikt. Klare Treffer und klare Non-Treffer kosten kein Token.
- **Kontext:** beide Titel inkl. Alternate-Names (BGG-Sync), Verlage und ggf.
BGG-Expansion-Relationen.
- **Strukturierte Antwort** (JSON): `ist_gleiches_spiel`, `konfidenz`,
`begruendung`, `empfohlener_verlag`, `deutscher_titel`. Bestätigte Treffer
laufen in die üblichen Prüfungen a)d) zurück; ein übernommener deutscher
Titel füllt die Titel-Empfehlung, ein Verlags-Hinweis landet im Detail des
Verlags-Konflikts. Unter der Mindest-Konfidenz
(`SPIELE_DEDUP_LLM_KONFIDENZ_MIN`, Standard 0.7) wird das Ergebnis
verworfen und nur der Regelbefund angezeigt.
- **Fallback-Pflicht:** Standardmäßig aus (`SPIELE_DEDUP_LLM_AKTIV=0`). Ohne
Key oder bei jedem Fehler (Timeout, Server-Störung nach genau einem Retry,
kaputtes JSON) verhält sich die Prüfung exakt wie rein regelbasiert — das
LLM kann die Prüfung nie blockieren.
- **Audit:** Jede Prüfung mit LLM-Befunden schreibt einen Eintrag ins
audit-log (`geprüft` / `dedup_ki`) mit Modell, Konfidenz und Entscheidung
je bewertetem Grenzfall.
Schnittstelle: OpenAI-kompatible Chat-Completions-API via httpx, konfiguriert
über die `SPIELE_DEDUP_LLM_*`-Variablen (siehe Tabelle oben).
### Prüfprotokoll & Prüfseite
Jede `check_titel`-Prüfung landet in der eigenen Migration