feat(251): NLI-Debug-Log-Endpoint für Kalibrierungs-Session #252

Merged
admin-mrrm merged 3 commits from fix/251-nli-debug-logging into main 2026-05-14 14:52:50 +02:00
Owner

Summary

  • Neuer temporärer Endpoint POST /api/mail/_debug/nli-log (gated via NLI_DEBUG_LOG=true, sonst 404). Schreibt JSONL nach NLI_DEBUG_LOG_FILE mit Mail-Meta + allen NLI-Kandidaten-Scores + verwendetem Hypothese-Template.
  • Mobile (model-manager.ts) sendet fire-and-forget, wenn EXPO_PUBLIC_NLI_DEBUG_LOG=1 gesetzt ist — Errors werden geschluckt, blockieren die Klassifikation nicht.
  • Llama wird für die Datenerhebung bewusst nicht ausgelöst. Wir sammeln pur NLI-Daten für die Kalibrierung; der Llama-Fallback im Produktionspfad bleibt unverändert.
  • mail-batch-categorizer reicht jetzt Mail-Meta (subject/from/date/uid/accountId/folder/messageId) an suggest durch, damit das automatische Tagging die Debug-Daten liefert.

Adressiert Issue #251 (Schritt 1: 'Logging erweitern' als eigener Mini-PR). Threshold-Senkung, Template-A/B und Snippet-Cleanup folgen in eigenen PRs, sobald wir gemeinsam ~50 Mails mit dem Log ausgewertet haben.

Test plan

  • API: pnpm -F @mrrmlab/api test — 200/200 grün (7 neu)
  • Mobile: pnpm -F @mrrmlab/mobile test — 18/18 grün (4 neu)
  • Typecheck: api, mobile, api-client
  • Smoke gegen lokales API: POST /mail/_debug/nli-log mit gültigem Body → 404 ohne Flag, geplante 204 mit Flag
  • Manuell: Flag aktivieren, App durchscrollen, JSONL in apps/api/.tmp/nli-debug.jsonl prüfen — passiert in der anschließenden Kalibrierungs-Session

Datenschutz

Der Endpoint schreibt Mail-Snippets im Klartext. Nach Abschluss der Kalibrierung wird er per Folge-PR wieder entfernt (oder dauerhaft auf false belassen).

Closes #nothing — gehört zu #251.

## Summary - Neuer temporärer Endpoint `POST /api/mail/_debug/nli-log` (gated via `NLI_DEBUG_LOG=true`, sonst 404). Schreibt JSONL nach `NLI_DEBUG_LOG_FILE` mit Mail-Meta + allen NLI-Kandidaten-Scores + verwendetem Hypothese-Template. - Mobile (`model-manager.ts`) sendet fire-and-forget, wenn `EXPO_PUBLIC_NLI_DEBUG_LOG=1` gesetzt ist — Errors werden geschluckt, blockieren die Klassifikation nicht. - **Llama wird für die Datenerhebung bewusst nicht ausgelöst.** Wir sammeln pur NLI-Daten für die Kalibrierung; der Llama-Fallback im Produktionspfad bleibt unverändert. - `mail-batch-categorizer` reicht jetzt Mail-Meta (subject/from/date/uid/accountId/folder/messageId) an `suggest` durch, damit das automatische Tagging die Debug-Daten liefert. Adressiert Issue #251 (Schritt 1: 'Logging erweitern' als eigener Mini-PR). Threshold-Senkung, Template-A/B und Snippet-Cleanup folgen in eigenen PRs, sobald wir gemeinsam ~50 Mails mit dem Log ausgewertet haben. ## Test plan - [x] API: `pnpm -F @mrrmlab/api test` — 200/200 grün (7 neu) - [x] Mobile: `pnpm -F @mrrmlab/mobile test` — 18/18 grün (4 neu) - [x] Typecheck: api, mobile, api-client - [x] Smoke gegen lokales API: `POST /mail/_debug/nli-log` mit gültigem Body → 404 ohne Flag, geplante 204 mit Flag - [ ] Manuell: Flag aktivieren, App durchscrollen, JSONL in `apps/api/.tmp/nli-debug.jsonl` prüfen — passiert in der anschließenden Kalibrierungs-Session ## Datenschutz Der Endpoint schreibt Mail-Snippets im Klartext. Nach Abschluss der Kalibrierung wird er per Folge-PR wieder entfernt (oder dauerhaft auf `false` belassen). Closes #nothing — gehört zu #251.
feat(251): temporärer NLI-Debug-Log-Endpoint für Threshold-/Template-Kalibrierung
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
31c29851ea
POST /api/mail/_debug/nli-log nimmt pro Klassifikation Mail-Meta + alle
Kandidaten-Scores entgegen und schreibt sie als JSONL nach
NLI_DEBUG_LOG_FILE. Mobile sendet fire-and-forget, wenn
EXPO_PUBLIC_NLI_DEBUG_LOG=1 gesetzt ist. Llama wird für die Erhebung
bewusst nicht ausgelöst — wir sammeln nur NLI-Daten für die Kalibrierung
(Issue #251).

Gate: NLI_DEBUG_LOG=true (sonst 404) — Endpoint enthält Mail-Snippets im
Klartext und ist nur für temporäre Kalibrierungs-Sessions gedacht.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(251): bodyText-Fallback aus bodyHtml für HTML-only Mails
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
ef2419e33b
Viele transaktionale Mails (Shop Apotheke, Renegade, Kleinanzeigen, …)
haben keinen text/plain-Part. getMessage lieferte bodyText=null, und das
Mobile-Snippet bestand dann nur aus dem Subject — NLI hatte zu wenig
Text, alle Scores ~0.09 ohne erkennbares Signal.

Lazy htmlToText-Fallback in beiden getMessage-Pfaden (IMAP-Fetch und
Cache-Read); kein DB-Backfill nötig. htmlToText-Helper aus mail-tags
nach modules/mail/html-to-text.ts gezogen und dort wiederverwendet.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
fix(251): NLI-Kalibrierung — Threshold 0.30, Konto-Kategorie, OTTO-Shop-Blocklist
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
29c2e9278d
Auswertung der Debug-Log-Daten (36 Mails) zeigte drei Probleme:
- NLI_CONFIDENCE_THRESHOLD = 0.7 wurde nie erreicht (max. korrekt: 0.443)
- "OTTO-Shop" (user-confirmed Tag) gewann jede Mail mit "OTTO" im Subject
- Account-/Login-Mails hatten keine passende Default-Kategorie

Änderungen:
- NLI_CONFIDENCE_THRESHOLD 0.7 → 0.30, NLI_KEEP_THRESHOLD 0.5 → 0.25
- NLI_BLOCKLIST mit "OTTO-Shop" — bias-erzeugende Tags raus aus Kandidaten
- DEFAULT_CATEGORIES: "Paket" → "Sendung", neu "Konto"
- CATEGORY_DESCRIPTIONS für Sendung/Konto/Finanzen ergänzt/angepasst

Template-Reform (Punkt 4 der Auswertung) folgt separat.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
admin-mrrm/mrrmlabapp!252
No description provided.