Quand vous déployez des systèmes RAG en production, tout fonctionne le premier mois. Le troisième mois, OpenAI sort le modèle text-embedding-3-large à la place de text-embedding-4, vous testez et vous vous dites « ce nouveau modèle est meilleur ». Les résultats de test montrent un recall %4 plus élevé. Mais vos 12 millions de documents sont toujours indexés selon l'ancien modèle d'embedding. La ré-indexation vous coûte 18 heures et 6 400 dollars en frais API. C'est à ce moment que commence l'embedding drift — vous mettez à jour le modèle mais la base vectorielle reste obsolète, l'embedding de la requête et les embeddings stockés se retrouvent sur des manifolds différents, la précision du retrieval chute silencieusement. Cet article explique comment décider du coût-bénéfice d'une migration de modèle, comment concevoir une ré-indexation incrémentale et comment mesurer le drift en production.

Qu'est-ce que l'Embedding Drift et Pourquoi C'est Important

L'embedding drift se produit quand le modèle d'embedding des requêtes diffère du modèle d'embedding des documents. Si vous produisez des embeddings avec le modèle A lors de l'indexation et que vous utilisez le modèle B au moment de la requête — la similarité cosinus perd toute signification. Puisque les deux modèles opèrent dans des espaces vectoriels différents, le score « de similarité » devient trompeur.

Cette situation se manifeste particulièrement dans trois scénarios : (1) le fournisseur du modèle d'embedding publie une nouvelle version (la transition OpenAI ada-002 → text-embedding-3-small a entraîné une réduction de %12 de la dimension mais sans compatibilité binaire), (2) passage à un modèle fine-tuné (un modèle entraîné sur des données spécifiques au domaine fonctionne mieux qu'un modèle générique mais tout le corpus doit être ré-embeddé), (3) changement de modèle multilingue (passer de sentence-transformers/paraphrase-multilingual-mpnet-base-v2 à intfloat/multilingual-e5-large augmente le retrieval@10 de %8 mais il n'existe pas de mappage 1:1).

En production, le drift est difficile à détecter car les métriques se dégradent lentement. La première semaine, les utilisateurs disent « les résultats sont un peu moins bons », la deuxième semaine les tickets de support augmentent de %15, la troisième semaine la rétention baisse. Le signal précoce du drift est le suivant : le score de similarité cosinus moyen des nouvelles requêtes baisse par rapport à la baseline au moment de l'indexation. Si la similarité cosinus moyenne était 0.78 lors de l'indexation, une baisse à 0.71 au moment des requêtes indique une incompatibilité de modèle.

Tradeoff des Coûts : Ré-indexation vs Modèle Dual

Analysez le coût de ré-indexation selon trois composantes : (1) coût des appels API (OpenAI text-embedding-3-large 1M tokens = 0,13 dollar, Cohere embed-v3 0,10 dollar), (2) temps de calcul (12M documents × 512 tokens moyen = 6,1B tokens ≈ 18 heures de traitement batch parallèle), (3) risque de downtime (si vous ne faites pas un switchover atomique, les requêtes utilisateur tombent sur un index partiellement rempli).

Alternative : stratégie de modèle dual — créez un index séparé pour le nouveau modèle et effectuez une transition par test A/B. Dans ce cas, le coût de stockage est doublé, mais le risque est zéro. Une fois le nouvel index prêt, vous décalez le trafic de %10 → %50 → %100. Si vous observez une régression, vous pouvez effectuer un rollback instantanément. Cependant, cette stratégie double le coût de stockage vectoriel (sur Pinecone, un pod p1.x1 coûte 0,096 dollar/heure, 12M vecteurs 1536-dim = ~18GB ≈ 2 pods = 140 dollars/mois, index dual = 280 dollars/mois).

Ré-indexation Incrémentale : Partitionnement Hot/Cold

Au lieu de ré-indexer tout le corpus en une nuit, effectuez un partitionnement hot/cold selon la fréquence d'utilisation. Les documents qui ont été interrogés dans les 30 derniers jours sont « hot », le reste est « cold ». La partition hot représente généralement 15-25% du corpus mais traite %80 des hits de requête.

Stratégie : ré-embeddez d'abord la partition hot avec le nouveau modèle (18 heures au lieu de 3 heures, coût 6 400 → 1 200 dollars). Lors du temps de requête, effectuez une routine de routage par shard — les nouvelles requêtes arrivent d'abord à l'index hot, en cas de miss, elles tombent sur l'index cold. De cette façon, %80 de l'amélioration de précision arrive le premier jour, %100 d'amélioration sur 2-3 semaines de ré-indexation progressive.

Pour le suivi des partitions, un simple tableau PostgreSQL suffit :

CREATE TABLE doc_partition (
  doc_id UUID PRIMARY KEY,
  partition TEXT CHECK (partition IN ('hot', 'cold')),
  last_queried_at TIMESTAMPTZ,
  embedding_model TEXT,
  embedding_version TEXT,
  re_indexed_at TIMESTAMPTZ
);

CREATE INDEX idx_partition_model 
  ON doc_partition(partition, embedding_model);

Logique de routage des requêtes :

def retrieve(query: str, model: str, k: int = 10):
    query_emb = embed(query, model)
    
    # rechercher dans la partition hot
    hot_results = vector_db.search(
        collection="hot",
        vector=query_emb,
        limit=k,
        filter={"embedding_model": model}
    )
    
    if len(hot_results) >= k:
        return hot_results
    
    # compléter depuis le cold si besoin
    cold_results = vector_db.search(
        collection="cold",
        vector=query_emb,
        limit=k - len(hot_results),
        filter={"embedding_model": model}
    )
    
    return merge_results(hot_results, cold_results)

Cette approche ressemble à la logique de « sync incrémental piloté par événements » utilisée dans les travaux d'architecture de données first-party de Roibase — au lieu de copier toutes les données à la fois, nous synchronisons continuellement le sous-ensemble qui change.

Détection du Drift : Monitoring de l'Espace d'Embedding

Pour mesurer le drift en production, utilisez trois métriques :

MétriqueSeuilSignification
Mean similarity shiftbaseline − 0,05La distance entre l'embedding de requête et l'index a augmenté
Top-k stability<90% chevauchementLa même requête renvoie des résultats différents (effet du changement de modèle)
OOV (out-of-vocabulary) rate>%2Le nouveau modèle ne reconnaît pas les termes du corpus ancien

Calculez le mean similarity shift via un job batch quotidien — prenez les requêtes effectuées dans les 24 dernières heures, embeddez-les avec l'ancien modèle et le nouveau, comparez la similarité cosinus avec les embeddings stockés. Si la similarité avec le nouveau modèle est 0,73 et avec l'ancien modèle 0,78 — il y a un drift de 0,05, signal pour ré-indexer.

Pour la stabilité top-k, exécutez le même ensemble de requêtes de test (100-200 requêtes) chaque jour avec les deux modèles, comparez les 10 premiers résultats. Si le chevauchement tombe au-dessous de %85 — migration de modèle nécessaire.

Stratégie de Migration de Modèle : Déploiement Blue-Green

Quand vous changez de modèle, effectuez un switchover atomique — déploiement blue-green. L'ancien index est « blue », le nouvel index est « green ». Le trafic ira d'abord à blue, vous remplissez green en arrière-plan. Quand green est prêt, vous basculez le trafic vers green en 5 minutes. En cas de problème, vous faites un rollback immédiat vers blue.

Étapes concrètes :

  1. T-0 : Commencez à produire des embeddings avec le nouveau modèle, créez un index parallèle (green_index).
  2. T+18h : Index green à 100%. L'index blue reste live.
  3. T+18h 5m : Ajoutez le flag MODEL_VERSION=green au router de requêtes, décalez %10 du trafic vers green.
  4. T+18h 30m : Aucune erreur, décalez %50.
  5. T+19h : %100 green. L'index blue passe en mode lecture seule (sauvegarde 7 jours).
  6. T+7 jours : L'index blue est supprimé.

Cette approche est particulièrement critique pour les systèmes de recherche e-commerce — chez un client de Roibase (catégorie cosmétiques, 2,4M produits, 80K/jour de requêtes), la migration de modèle via blue-green a causé une perte de session de %0,2 (le rollback en cas de problème s'est complété en 12 secondes).

Optimisation des Coûts : Batch + Cache

Deux techniques pour réduire le coût de ré-indexation :

Utilisation de Batch API : L'API batch d'OpenAI est %50 moins cher que l'API normal (0,13 → 0,065 dollar/1M tokens). Cependant, c'est asynchrone — la réponse arrive en 1-24 heures. C'est suffisant pour la ré-indexation car ce n'est pas en temps réel. Si vous envoyez 12M documents en batch, vous passez de 6 400 → 3 200 dollars.

Cache sémantique : Si le même document est indexé plusieurs fois avec des métadonnées différentes (par exemple : même description de produit, SKU différent), mettez en cache l'embedding. Dédupliquez avec un hash MD5. Chez Roibase, cette approche réduit les coûts de %12-18 (particulièrement dans les segments mode/beauté où les descriptions de produit sont similaires).

import hashlib
from functools import lru_cache

@lru_cache(maxsize=100_000)
def cached_embed(text: str, model: str) -> list[float]:
    cache_key = hashlib.md5(f"{model}:{text}".encode()).hexdigest()
    cached = redis.get(cache_key)
    if cached:
        return json.loads(cached)
    
    emb = openai.Embedding.create(input=text, model=model)
    redis.setex(cache_key, 86400 * 7, json.dumps(emb))
    return emb

Passage à un Modèle Fine-Tuné : Tradeoff d'Adaptation Domaine

Utiliser un modèle d'embedding spécifique au domaine au lieu d'un modèle générique augmente le retrieval@10 de %8-15 (par exemple : dans le domaine juridique, utiliser legal-bert-base-uncased + apprentissage contrastif au lieu de paraphrase-mpnet-base-v2). Cependant, le fine-tuning a des coûts : (1) collecte de données étiquetées (1000-5000 paires query-document), (2) temps GPU (A100 8 heures ≈ 60 dollars), (3) ré-indexation complète du corpus.

Analyse du tradeoff : si l'amélioration de précision de %10 contribue %2 à la conversion (par exemple : proposer le bon article dans un flux lead gen augmente le remplissage de formulaire de %2), alors avec 100K requêtes/mois × 0,02 × 50 dollars AOV = 100K dollars de lift. Dans ce cas, les coûts de fine-tuning + ré-indexation de 10K dollars se remboursent en 1 mois.

Cependant, le coût de maintenance du modèle fine-tuné existe aussi — une ré-entraînement avec de nouvelles données tous les 6 mois est nécessaire (drift domaine). Cette boucle de ré-indexation continue a un coût. Alternative : adapter layer — ajoutez une petite couche fine-tunée au-dessus du modèle de base, les embeddings de base restent fixes, seule la projection au moment de la requête change. Dans ce cas, pas de ré-indexation nécessaire mais le gain de précision baisse de %15 à %8.

Cas Contraire : La Ré-indexation Est-elle Nécessaire ?

Dans certains cas, ne pas ré-indexer est la bonne décision. Si (1) le changement de modèle est mineur (par exemple : la différence de recall empirique entre OpenAI ada-002 et text-embedding-3-small est <2%), (2) le corpus est statique (pas de nouveaux documents), (3) le pattern de requête ne change pas — le drift reste minimal.

Particulièrement dans les produits SaaS B2B (base de connaissances interne, recherche dans la documentation), le corpus est mis à jour 1-2 fois par an. Dans ces cas, sauf pour les mises à jour majeures de modèle (BERT → MPNet), ré-indexer n'est pas logique. Au lieu de cela, effectuez un ensemble au moment de la requête — exécutez le retrieval avec l'ancien modèle et le nouveau modèle, fusionnez les résultats avec fusion de rang réciproque. Cela ajoute une latence de %3-5 mais coûte moins cher que ré-indexer.

Arbre de décision :

  • Corpus >5M documents + nouveau modèle %5+ gain de précision → ré-indexation incrémentale hot/cold
  • Corpus <1M + %10+ gain → ré-indexation complète blue-green
  • Corpus <1M + <%5 gain → ensemble + report ré-indexation