ADR 0003 — Embeddings BGE Multilingual Gemma2 via fournisseur externe
Date : initial Statut : Accepté
Contexte
Pour la recherche vectorielle, il faut produire un embedding stable et de qualité pour :
- Chaque voyage du catalogue (calculé au reindex sur la concaténation
name + subtitle + description + servicesIncluded + highLights). - Chaque requête utilisateur arrivant via
GET /travels?search=….
Contraintes :
- Français natif : le contenu et les requêtes sont en français à 95%. Le modèle doit comprendre la sémantique française au moins aussi bien que l'anglais.
- Pas d'infra ML à opérer : pas de GPU, pas de service d'inférence à maintenir côté équipe.
- Coût modéré : quelques centaines de voyages × reindex/jour + ~quelques milliers de requêtes/jour.
- Latence acceptable :
< 500mspour la requête utilisateur (l'embedding est sur le path critique).
Décision
Utiliser BGE Multilingual Gemma2 (3584 dimensions) servi par l'API embeddings d'Infomaniak (compatibilité OpenAI).
- Modèle :
bge_multilingual_gemma2. - Hébergement : Infomaniak (fournisseur cloud suisse, datacentre Suisse — important pour la conformité données Buchard).
- Authentification : Bearer token (
INFOMANIAK_TOKEN) + product ID (INFOMANIAK_PID). Détails du provisioning hors documentation publique — voir coffre-fort équipe. - Wrapper Python :
src/infomaniak.py(~40 lignes, deux fonctionsget_embedding(text)etget_embeddings(texts)).
Conséquences
Positives
- ✅ Qualité francophone solide. BGE Multilingual a été entraîné massivement sur des données multilingues, dont français. Tests qualitatifs en interne confirment de bons matches sémantiques.
- ✅ Dimensions élevées (3584) → capacité de représentation suffisante pour distinguer des nuances dans un petit catalogue voyages.
- ✅ Pas de stack ML interne. Pas de Triton, Ollama, TGI à opérer.
- ✅ Données chez Infomaniak (Suisse) — aligné avec les contraintes Buchard.
- ✅ API OpenAI-compatible. Le wrapper Python est trivial. Si on bascule sur OpenAI ou autre, c'est un changement minime.
Négatives
- ❌ Couplage à un fournisseur externe. Une panne Infomaniak casse le path
?search=…(la branche filtre-seul continue). Pas de fallback offline. - ❌ 3584 dimensions = stockage non trivial. 3584 × 4 octets = 14 Ko par embedding. Pour 1000 voyages, 14 Mo de vecteurs. Pas dramatique mais à connaître si le catalogue grossit (10k records ≈ 140 Mo).
- ❌ Pas d'index ANN (
hnsw) : la dimensionnalité élevée pénalise les index approximatifs. Pour l'instant, seq scan suffit ; à reconsidérer si la table dépasse ~10k lignes. - ❌ Changement de modèle = reindex complet. Les embeddings d'un modèle A ne sont pas comparables à ceux d'un modèle B. Migrer = (a) migration SQL
ALTER COLUMN embedding TYPE vector(new_dim), (b) vider et re-ingérer. - ❌ Latence ~100-300ms par appel. Pas négligeable sur un endpoint qui doit répondre rapidement. Mitigeable par batch côté reindex (10 textes par appel).
Alternatives écartées
- OpenAI
text-embedding-3-large(3072D) : qualité similaire, datacentre US — bloquant pour les contraintes data Buchard. - Sentence-transformers self-hosted (
paraphrase-multilingual-mpnet) : auto-hébergeable, 768D, qualité correcte. Mais nécessite un service d'inférence à opérer. Refusé pour le coût opérationnel. - Mistral Embed (1024D) : alternative française. Pas encore mature au moment du choix initial.
- Cohere Embed Multilingual (1024D) : qualité comparable. Hosting hors UE.
- Encodage TF-IDF / SVD côté Python : zero-cost, mais qualité sémantique très inférieure — c'est ce que FTS fait déjà via
tsvector, autant ne pas le doubler.
Coût / Quotas
À surveiller (pas exposé en métriques aujourd'hui) :
- Nombre d'appels Infomaniak / jour. Coût =
(reindex_par_jour × pages × 10 records)+(recherches_par_jour × 1). - Avec
REINDEX_FREQUENCY=900(15 min) et un catalogue de ~300 voyages :96 reindex × 30 pages = 2880 appels/jourcôté reindex. - Côté requêtes : un appel par recherche avec
?search=…. Les recherchesfiltre-onlyn'embedderont rien.
Si le coût devient problématique :
- Ajouter un hash content-based dans
_ingest_sourcepour skipper l'embedding des records inchangés (cf.architecture/reindex-lifecycle.md§ « Ce qui n'est PAS fait »). - Augmenter
REINDEX_FREQUENCY(45 min ou 1h sont raisonnables si le catalogue ne bouge pas vite). - Cacher les embeddings des requêtes fréquentes (memcached / redis devant
infomaniak.get_embedding).
Note de sécurité
Le token Infomaniak ne doit jamais apparaître dans les logs ni dans les réponses HTTP. Aucun log applicatif n'inclut le header Authorization. Le secret est monté via Docker secrets en prod (cf. architecture/deployment.md).
Contributors
No contributors
Changelog
No recent changes

