Recette : Mise en cache des embeddings de documents avec Workflow v6 pour accélérer la recherche sémantique
Découvrez comment exploiter le cache distribué de Workflow v6 afin de réduire la latence d’inférence lors de la recherche sémantique en réutilisant les embeddings déjà calculés.
Contexte et objectifs
La recherche sémantique repose souvent sur le calcul d’embeddings de documents à la volée. Lorsque le même document est interrogé plusieurs fois, le recalcul des vecteurs devient un goulet d’étranglement, surtout sous forte charge. Cette recette montre comment mettre en place une couche de cache transparente avec Workflow v6, afin de :
- réduire la latence de réponse d’au moins un ordre de grandeur ;
- limiter le nombre d’appels au modèle d’embedding (et donc le coût associé) ;
- garantir la cohérence du cache lors de l’évolution des modèles.
Architecture du cache
Nous utilisons un cache distribué (Redis ou Memcached) comme store persistant. Workflow v6 orchestre les étapes suivantes :
- Pré‑traitement : extraction du texte brut et génération d’une clé de hachage (SHA‑256) à partir du contenu du document.
- Lookup : interrogation du cache avec la clé. Si le vecteur existe, il est renvoyé immédiatement.
- Calcul : sinon, appel au modèle d’embedding (ex. OpenAI, HuggingFace) via un worker dédié.
- Write‑back : le vecteur calculé est stocké dans le cache avec un TTL configurable.
Le diagramme simplifié :
+----------------+ +-----------+ +-------------------+
| Client API | ---> | Workflow | ---> | Cache (Redis) |
+----------------+ +-----------+ +-------------------+
| | |
| cache miss | cache hit |
v v v
Compute embedding Retrieve embedding Store embedding
Implémentation avec Workflow v6
1. Définition du pipeline (YAML)
# workflow.yml
name: embedding-cache-pipeline
steps:
- name: compute-hash
type: python
script: |
import hashlib, json
def run(context):
doc = context['payload']['text']
h = hashlib.sha256(doc.encode('utf-8')).hexdigest()
return {'hash': h, 'text': doc}
- name: cache-get
type: redis
operation: get
key: "embedding:${hash}"
on_missing: continue # passe à l'étape suivante si aucune entrée
- name: embed
type: python
condition: ${cache-get.result} == null
script: |
from transformers import AutoTokenizer, AutoModel
import torch
def run(context):
tokenizer = AutoTokenizer.from_pretrained('sentence-transformers/all-MiniLM-L6-v2')
model = AutoModel.from_pretrained('sentence-transformers/all-MiniLM-L6-v2')
inputs = tokenizer(context['payload']['text'], return_tensors='pt')
with torch.no_grad():
embeddings = model(**inputs).last_hidden_state.mean(dim=1).squeeze().numpy()
return {'embedding': embeddings.tolist()}
- name: cache-set
type: redis
operation: set
key: "embedding:${hash}"
value: ${embed.result.embedding}
ttl: 86400 # 24 h
- name: output
type: python
script: |
def run(context):
if context['cache-get'].get('result'):
return {'embedding': context['cache-get']['result']}
else:
return {'embedding': context['embed']['result']['embedding']}
2. Lancement du pipeline
workflow run embedding-cache-pipeline \
--payload '{"text": "Le texte à indexer..."}'
Le CLI renvoie le vecteur, provenant soit du cache, soit du modèle.
3. Gestion du TTL et de l’invalidation
- TTL dynamique : ajustez le temps de vie en fonction du cycle de vie du modèle (ex. 7 jours après un déploiement de version).
- Invalidation par version : préfixez la clé avec le version tag du modèle (
v1.2:embedding:${hash}) pour forcer le recalcul lors d’une mise à jour.
Validation et bonnes pratiques
- Idempotence du hash : le même contenu doit toujours produire la même clé. Utilisez un hachage cryptographique (SHA‑256) plutôt qu’un simple hash Python.
- Surveillance du taux de hit : exposez la métrique
cache_hit_ratiovia Prometheus. Un ratio inférieur à 80 % indique un problème de granularité des clés ou de TTL trop court. - Limitation de taille : configurez Redis avec une politique
maxmemory-policy volatile-lrupour évincer les entrées les plus anciennes lorsque la capacité est atteinte. - Sécurité : chiffrez les données en transit (TLS) et activez l’authentification Redis. Le vecteur lui‑même n’est pas sensible, mais la clé dérivée du texte peut l’être.
- Tests d’intégration : incluez un scénario où le même document est soumis deux fois consécutives et vérifiez que la seconde exécution ne déclenche pas la section
embed(ex. via les logs de Workflow).
Conclusion
En intégrant un cache d’embeddings directement dans le pipeline Workflow v6, vous obtenez une amélioration notable de la latence tout en maîtrisant les coûts d’inférence. La démarche repose sur des primitives déjà présentes dans Workflow v6 (étapes Python, connecteur Redis, conditions dynamiques), ce qui minimise la charge opérationnelle. Adaptez le TTL, la stratégie d’invalidation et le monitoring à votre contexte ; la même architecture peut être réutilisée pour d’autres vecteurs (audio, image) en ne changeant que le composant d’embedding.
Envie d’aller plus loin avec WORKFLOW v6 ?
Découvrir WORKFLOW v6