Hybrid-Suche (Vektor + FTS5-Keyword), Recency-Boost, Query-Erweiterung (letzte 2-3 User-Messages), Chunk-Overlapping, FullText-Speicherung, konfigurierbares Embedding-Modell/MaxSeqLen, deduplizierter AutoContext- Code (Streaming + CLI shared), Admin-Diagnose-Seite mit Re-Embed-Funktion. Threshold von 0.45 auf 0.35 gesenkt (Hybrid-Suche ist präziser). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.0 KiB
4.0 KiB
Plan: SentencePiece-Tokenizer-Support für multilinguales Embedding-Modell
Ziel
paraphrase-multilingual-MiniLM-L12-v2 als alternatives Embedding-Modell ermöglichen, indem der EmbeddingService neben WordPiece (BERT) auch SentencePiece (XLM-RoBERTa) Tokenizer unterstützt.
Hintergrund
- Aktuell:
all-MiniLM-L6-v2(BERT, WordPiece,vocab.txt, 384 Dims) - Ziel-Modell:
paraphrase-multilingual-MiniLM-L12-v2(XLM-RoBERTa, SentencePiece,sentencepiece.bpe.model, 384 Dims) - Problem: Unser Tokenizer ist hardcoded auf WordPiece – Special Tokens (
[CLS]/[SEP]),token_type_ids,vocab.txt
Unterschiede zwischen den Tokenizern
| Aspekt | WordPiece (BERT) | SentencePiece (XLM-R) |
|---|---|---|
| Vocab-Datei | vocab.txt |
sentencepiece.bpe.model |
| Special Tokens | [CLS]=101, [SEP]=102, [PAD]=0 |
<s>=0, </s>=2, <pad>=1 |
| token_type_ids | Required (alle 0) | Nicht verwendet |
| Subword-Prefix | ## |
▁ (U+2581) |
| Vocab-Groesse | ~30.522 | ~250.002 |
Schritte
Schritt 1: NuGet-Paket hinzufuegen
Microsoft.ML.Tokenizers.SentencePieceBpeoderSentencePieceSharpevaluieren- Kriterien: .NET 10 Kompatibilitaet, kein native-Dependency-Problem auf Linux, Lizenz
- Paket zu
gott.csprojhinzufuegen
Schritt 2: Tokenizer-Abstraktion in EmbeddingService.cs
- Interface oder Strategie-Pattern:
ITextTokenizermit MethodeTokenize(string text, int maxLen) -> (long[] inputIds, long[] attentionMask, long[]? tokenTypeIds) WordPieceTokenizer– bestehende Logik extrahieren (Zeilen 127-161)SentencePieceTokenizer– neue Implementierung- Constructor-Logik: Wenn
vocab.txtexistiert -> WordPiece; wennsentencepiece.bpe.modelexistiert -> SentencePiece - Property
TokenizerTypefuer Debug/Logging
Schritt 3: ONNX-Session Input anpassen
- WordPiece: 3 Inputs (
input_ids,attention_mask,token_type_ids) - SentencePiece: 2 Inputs (
input_ids,attention_mask) – keintoken_type_ids GenerateEmbedding()muss dynamisch die richtigen Inputs erstellen basierend auf Tokenizer-Typ- ONNX Input-Names aus dem Modell lesen statt hardcoded (robuster)
Schritt 4: Special Token IDs konfigurierbar machen
[CLS]/[SEP]IDs vs.<s>/</s>IDs- Am besten aus
special_tokens_map.jsonodertokenizer_config.jsonlesen, falls vorhanden - Fallback: Hardcoded Mapping pro Tokenizer-Typ
Schritt 5: Modell-Dateien bereitstellen
- Von HuggingFace herunterladen:
model.onnx(ONNX-Export des Modells)sentencepiece.bpe.model
- Ablegen in
data/models/paraphrase-multilingual-MiniLM-L12-v2/ - Server-Setting
EmbeddingModelDirauf den neuen Ordner setzen
Schritt 6: Re-Embed-Kompatibilitaet
- Dimensionen sind identisch (384) -> Keine DB-Schema-Aenderung noetig
- Re-Embed-Endpoint muss weiterhin funktionieren
- Beim Modell-Wechsel: Alle Embeddings muessen neu berechnet werden (altes Modell-Vektoren sind inkompatibel mit neuem Modell)
- Warnung im Admin-UI wenn Modell gewechselt wurde aber Re-Embed noch nicht gelaufen ist
Schritt 7: Tests & Verifikation
- Embedding-Debug-Page nutzen: Gleiche Queries mit altem vs. neuem Modell vergleichen
- Testcases:
- Deutsche Query -> deutsche Messages ("weisst du was zum thema autocontext")
- Englische Query -> englische Messages ("skills")
- Gemischt -> gemischte Messages ("otter karten")
- Pruefen ob Scores sich verbessern (besonders fuer deutsche Queries)
Betroffene Dateien
gott.csproj– NuGet-PaketServices/EmbeddingService.cs– Tokenizer-Abstraktion, SentencePiece-Supportwwwroot/js/embedding-debug.js– ggf. Anzeige des aktiven Modells/Tokenizers
Risiken
- SentencePiece NuGet auf Linux: Native Dependencies koennten Probleme machen -> vorher testen
- Re-Embed Dauer: 14k Messages mit 256 Tokens -> je nach CPU 5-30 Minuten
- Dimensionen-Mismatch: Falls ein zukuenftiges Modell andere Dims hat -> DB-Migration noetig (aktuell nicht, beide 384)
Aufwand
- Geschaetzt: ~2-3 Stunden Implementierung
- Groesster Block: Tokenizer-Abstraktion sauber bauen