ADRs: - 0006 Embedding-Modell-Migration v3->v4 (#123) - 0008 DDD-Lightweight-Migration (#136) Analysen: - ddd-bewertung.md (1237 Zeilen) — vollstaendige DDD-Analyse mit Tages-Roadmap - protokoll-parser-v6-machbarkeit.md (418 Zeilen) — #106 Phase 2 Vorbereitung Reference: - zugriffsrechte.md — 63 Routes x 3 User-Status, UI-Sichtbarkeits-Matrix Ops: - scripts/deploy.sh — mit Uptime-Kuma-Wartungsmodus (#149) - scripts/run-digest.sh — taeglicher Mail-Digest-Cron - scripts/run-monitoring-scan.sh — Monitoring-Scan-Cron (noch nicht aktiv) - scripts/smoke-test.sh — Gesamt-Funktionspruefung - pytest.ini: integration/slow/e2e Markers, addopts not-integration Tests/integration/: Live-Adapter-Tests + Frontend-XRef + Citation-Substring + Wahlprogramm-Indexed (4 Live-Test-Suites, marker-opt-in) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
105 lines
4.2 KiB
Markdown
105 lines
4.2 KiB
Markdown
# ADR 0006: Embedding-Modell-Migration text-embedding-v3 → v4
|
||
|
||
**Status:** accepted
|
||
**Datum:** 2026-04-11
|
||
**Issue:** #123
|
||
**Kontext:** #105 Clustering, #108 Empfehlungen
|
||
|
||
## Kontext
|
||
|
||
Bis 2026-04-11 wurden alle Wahlprogramm-Chunks (69 Programme, ~18k Chunks) mit
|
||
`text-embedding-v3` (1024 Dimensionen) indexiert. Für die geplanten Features
|
||
**#105 Antrag-Clustering** und **#108 Empfehlungen ähnlicher Anträge** brauchen
|
||
wir Embeddings für die Assessments selbst, nicht nur für Programm-Chunks.
|
||
|
||
Bei der Modellwahl stellte sich die Frage: dasselbe Modell für beide Seiten
|
||
oder darf eine Seite neuer sein?
|
||
|
||
**Kritisch:** v3- und v4-Embeddings liegen in unterschiedlichen Vektorräumen.
|
||
Cosine-Distance zwischen einem v3- und einem v4-Vektor ist mathematisch
|
||
gültig, aber semantisch Unsinn. Eine Vermischung macht die Citation-Binding-
|
||
Logik aus ADR 0001 kaputt und blockiert jede zukünftige Feature, die
|
||
Antrag-zu-Programm-Ähnlichkeit berechnet.
|
||
|
||
## Entscheidung
|
||
|
||
Vollständige Migration **beider Seiten** (Wahlprogramm-Chunks + Assessments)
|
||
auf `text-embedding-v4` (1024 Dimensionen, gleiches Default wie v3).
|
||
|
||
**Begründung:**
|
||
|
||
- Alibaba DashScope preist v3 und v4 **identisch** ($0.07 / 1M Tokens)
|
||
- v4 ist strikt besser auf MTEB (68.36 vs 63.39 overall, 59.30 vs 55.41 retrieval)
|
||
- v4 unterstützt 100+ Sprachen (v3: 50+), relevant für Zitate mit Fremdwörtern
|
||
- v4 bietet flexible Dimensionen 64–2048, wir bleiben aber auf 1024 (v3-kompatibel)
|
||
- Die einmaligen Reindex-Kosten belaufen sich auf **~$0.50** für alle Programme
|
||
- v3 wird vermutlich mittelfristig deprecated; wir vermeiden eine zweite Migration später
|
||
|
||
Die Alternative "v3 für Programme, v4 für Assessments" wurde verworfen, weil
|
||
sie die zukünftige Kompatibilität für Antrag-zu-Programm-Ähnlichkeit
|
||
permanent blockiert.
|
||
|
||
## Migrations-Strategie (Zero-Downtime)
|
||
|
||
**Zwei getrennte Settings** (`settings.embedding_model_write`,
|
||
`settings.embedding_model_read`) ermöglichen einen stufenweisen Switch ohne
|
||
Prod-Downtime:
|
||
|
||
| Phase | WRITE | READ | Zustand |
|
||
|---|---|---|---|
|
||
| 0 | v3 | v3 | Pre-Migration (alter Zustand) |
|
||
| 1 | **v4** | v3 | Code deployed, Reindex läuft im Hintergrund, Prod läuft weiter auf v3-Rows |
|
||
| 2 | v4 | **v4** | Reindex fertig, Flag geflippt, neue Queries nutzen v4-Rows |
|
||
| 3 | v4 | v4 | Cleanup: alte v3-Rows gelöscht |
|
||
|
||
**Schema-Änderungen:**
|
||
|
||
- `chunks`: neue Spalte `model TEXT NOT NULL DEFAULT 'text-embedding-v3'`
|
||
+ Index `idx_chunks_model`
|
||
- `assessments`: neue Spalten `summary_embedding BLOB`, `embedding_model TEXT`
|
||
|
||
**Reindex-Skript:** `app/reindex_embeddings.py` (Ausführung via
|
||
`docker exec gwoe-antragspruefer python -m app.reindex_embeddings`).
|
||
Schreibt v4-Rows parallel zu den v3-Rows, mit 100ms Rate-Limit zwischen
|
||
Calls (= max 10 req/sec). Bereits mit v4 indexierte Programme werden
|
||
übersprungen, damit das Skript idempotent ist und nach Abbruch nahtlos
|
||
fortgesetzt werden kann.
|
||
|
||
**Query-Pfad:** `find_relevant_chunks` filtert jetzt explizit
|
||
`WHERE model = ?` mit `EMBEDDING_MODEL_READ`. Query-Embeddings werden mit
|
||
demselben READ-Modell erzeugt (via neuer `model`-Parameter in
|
||
`create_embedding`), damit Query und Chunks im selben Vektorraum liegen.
|
||
|
||
## Konsequenzen
|
||
|
||
**Positiv:**
|
||
|
||
- Einheitlicher Vektorraum für Chunks und Assessments → #105, #108, und künftige
|
||
Ähnlichkeits-Features funktionieren out-of-the-box
|
||
- Bessere Retrieval-Qualität (MTEB +3.9 Punkte)
|
||
- Einmaliger Schritt, danach kein Mental-Load für Modell-Drift
|
||
|
||
**Negativ:**
|
||
|
||
- Reindex-Zeit: ~1–2h Wall-Time für alle 69 Programme (rate-limited)
|
||
- Kurzzeitig doppelter Storage (v3 + v4 Chunks parallel) bis Phase 3
|
||
- Assessment-Embedding-Generation adds ~100ms Latenz pro neuer Analyse (ein
|
||
zusätzlicher API-Call), der aber non-blocking fehlertolerant ist — Backfill
|
||
zieht später nach
|
||
|
||
**Neutral:**
|
||
|
||
- Die beiden Settings (`write` / `read`) bleiben langfristig im Code bestehen
|
||
als Infrastruktur für zukünftige Modell-Migrationen (v5, v6, …). Das Pattern
|
||
ist wiederverwendbar.
|
||
|
||
## Alternative: "v3 einfrieren"
|
||
|
||
Verworfen, weil v3 kein aktuell unterstütztes Flaggschiff mehr ist und
|
||
Deprecation wahrscheinlich ist. Besser jetzt migrieren, wenn noch beide
|
||
Modelle verfügbar sind, als später unter Zeitdruck.
|
||
|
||
## Alternative: "nur Assessments auf v4"
|
||
|
||
Verworfen wegen der Vektorraum-Fragmentierung (siehe Kontext).
|