Architecture du Service de Recherche : Application Flask Autonome avec Valkey
Cet article explique comment notre service de recherche fonctionne comme une application Flask autonome sur un serveur séparé, utilisant Valkey (un fork de Redis) pour la recherche vectorielle haute performance, la mise en cache et la saisie semi-automatique.
Le Problème : Performance et Évolutivité de la Recherche
Les opérations de recherche sont coûteuses en calcul :
-
Extraction de filtres : Faire correspondre plus de 2 500 expressions à chaque requête
-
Recherches associées : Calculer la similarité sur 65 000 requêtes
-
Saisie semi-automatique : Correspondance par préfixe sur 65 000 requêtes
-
Filtrage de produits : Filtrer plus de 5 000 produits selon plusieurs critères
Exécuter ces opérations sur le serveur web principal entraîne :
-
Lenteur du chargement des pages : La recherche bloque les autres requêtes
-
Pression mémoire : Les embeddings représentent une empreinte mémoire massive
-
Contention CPU : Le calcul de similarité est gourmand en CPU
-
Difficulté de mise à l'échelle : Impossible de mettre à l'échelle la recherche indépendamment
Nous avons besoin d'un service de recherche dédié pouvant évoluer indépendamment.
La Solution : Service de Recherche Autonome
Nous exécutons une application Flask séparée sur un serveur dédié :
Serveur Web Principal
↓ Appels API HTTP
Service de Recherche
↓ Requêtes Valkey
Serveur Valkey
Cette architecture offre :
-
Mise à l'échelle indépendante : Mettre à l'échelle le service de recherche sans affecter le serveur web principal
-
Isolation des ressources : Les opérations de recherche n'impactent pas le serveur web principal
-
Mise en cache : Valkey met en cache les résultats pour les requêtes répétées rapides
-
Haute disponibilité : Le service de recherche peut redémarrer sans affecter le serveur web principal
Composants du Service de Recherche
1. API d'Extraction de Filtres
-
Point de terminaison :
/api/extract_filters -
Objectif : Extraire des filtres structurés de requêtes en langage naturel
-
Exemple :
GET /api/extract_filters?q=mini+pc+16gb+ram
Réponse :
{
"Form Factor": "Mini PC",
"Main Memory": "16"
}
Implémentation :
-
Charger les correspondances expression-filtre depuis Valkey ou JSON
-
Faire correspondre les expressions en utilisant une regex de limite de mot
-
Renvoyer les filtres structurés
Mise en cache : Les correspondances d'expressions sont mises en cache dans Valkey (TTL de 30 jours)
2. API de Recherches Associées
-
Point de terminaison :
/api/related -
Objectif : Trouver des requêtes sémantiquement similaires en utilisant la recherche vectorielle
-
Exemple :
POST /api/related
{
"query": "mini pc",
"limit": 10
}
Réponse :
{
"related": [
{"query": "small computer", "similarity": 0.92},
{"query": "compact desktop", "similarity": 0.89},
{"query": "mini pc 8gb", "similarity": 0.87}
]
}
Implémentation :
-
Intégrer (embed) la requête en utilisant all-mpnet-base-v2
-
Interroger Valkey RediSearch pour les plus proches voisins
-
Renvoyer les N meilleurs résultats triés par similarité
Mise en cache : Les résultats sont mis en cache dans Valkey (TTL de 7 jours)
3. API de Saisie Semi-Automatique
-
Point de terminaison :
/api/autocomplete -
Objectif : Suggérer des requêtes pendant la saisie de l'utilisateur
-
Exemple :
GET /api/autocomplete?q=mini+p&limit=5
Réponse :
{
"suggestions": [
"mini pc",
"mini pc 16gb",
"mini pc 8gb ram",
"mini pc fanless",
"mini pc windows 11"
]
}
Implémentation :
-
Interroger Valkey RediSearch avec une correspondance par préfixe
-
Classer par popularité (score d'impressions + clics)
-
Renvoyer les N meilleures suggestions
Mise en cache : L'index de saisie semi-automatique dans Valkey (mis à jour quotidiennement)
4. API des Requêtes Populaires
-
Point de terminaison :
/api/popular -
Objectif : Obtenir les requêtes les plus populaires
-
Exemple :
GET /api/popular?limit=10
Réponse :
{
"queries": [
"mini pc",
"thin client",
"industrial pc",
"all in one pc"
]
}
Implémentation :
-
Charger les requêtes depuis Valkey ou JSON
-
Trier par score de trafic (impressions + clics)
-
Renvoyer les N meilleures requêtes
Mise en cache : Les requêtes populaires sont mises en cache dans Valkey (TTL de 30 jours)
Intégration de Valkey
Valkey est un fork de Redis qui fournit :
-
Recherche vectorielle : Module RediSearch pour la recherche par similarité
-
Mise en cache : Stockage clé-valeur en mémoire rapide
-
Saisie semi-automatique : Correspondance par préfixe avec des ensembles triés
-
Persistance : AOF (Append-Only File) pour la durabilité
Recherche Vectorielle avec RediSearch
Nous utilisons le module RediSearch de Valkey pour la recherche par similarité vectorielle :
Création d'Index :
client.ft("queries_idx").create_index([
VectorField("embedding", "FLAT", {
"TYPE": "FLOAT32",
"DIM": 768,
"DISTANCE_METRIC": "COSINE"
}),
TextField("query"),
NumericField("score")
])
Recherche Vectorielle :
query_embedding = model.encode(query)
results = client.ft("queries_idx").search(
Query("*=>[KNN 10 @embedding $vec AS score]")
.sort_by("score")
.return_fields("query", "score")
.dialect(2),
query_params={"vec": query_embedding.tobytes()}
)
Cela renvoie les 10 plus proches voisins par similarité cosinus.
Stratégie de Mise en Cache
Nous mettons en cache plusieurs types de données dans Valkey :
Correspondances d'Expressions (TTL de 30 jours) :
client.setex(
"seo:phrase_mappings",
30 * 24 * 3600,
json.dumps(phrase_mappings)
)
Recherches Associées (TTL de 7 jours) :
cache_key = f"related:{query_hash}"
client.setex(cache_key, 7 * 24 * 3600, json.dumps(results))
Requêtes Populaires (TTL de 30 jours) :
client.setex(
"seo:popular_queries",
30 * 24 * 3600,
json.dumps(popular_queries)
)
Index de Saisie Semi-Automatique (mis à jour quotidiennement) :
for query, score in queries:
client.zadd("autocomplete:mini", {query: score})
Saisie Semi-Automatique avec des Ensembles Triés
Nous utilisons les ensembles triés de Valkey pour la saisie semi-automatique :
Structure de l'Index :
autocomplete:m → ["mini pc": 5000, "mini computer": 3000]
autocomplete:mi → ["mini pc": 5000, "mini computer": 3000]
autocomplete:min → ["mini pc": 5000, "mini computer": 3000]
autocomplete:mini → ["mini pc": 5000, "mini computer": 3000]
Recherche par Préfixe :
prefix = "mini"
results = client.zrevrange(f"autocomplete:{prefix}", 0, 9, withscores=True)
Cela renvoie les 10 meilleures requêtes commençant par "mini", triées par score.
Communication API
Le serveur web principal appelle le service de recherche via HTTP :
Extraction de Filtres
from app.shared.filter_service import extract_filters_from_query
filters = extract_filters_from_query("mini pc 16gb ram")
# Appelle en interne : GET SEARCH_SERVICE_URL/api/extract_filters?q=...
Recherches Associées
import requests
response = requests.post(
"SEARCH_SERVICE_URL/api/related",
json={"query": "mini pc", "limit": 10},
timeout=2
)
related = response.json()["related"]
Saisie Semi-Automatique
response = requests.get(
"SEARCH_SERVICE_URL/api/autocomplete",
params={"q": "mini p", "limit": 5},
timeout=1
)
suggestions = response.json()["suggestions"]
Gestion des Erreurs et Solutions de Rechange
Le serveur web principal gère les défaillances du service de recherche avec élégance :
try:
filters = extract_filters_from_query(query)
except Exception as e:
logger.error(f"Échec du service de recherche : {e}")
filters = {} # Solution de rechange : filtres vides
Cela garantit que le serveur web principal continue de fonctionner même si le service de recherche est indisponible.
Configuration Réseau
Tous les serveurs sont sur un réseau privé :
-
Serveur web principal : Peut accéder au service de recherche et à Valkey
-
Service de recherche : Peut accéder à Valkey
-
Valkey : Accessible uniquement depuis le serveur web principal et le service de recherche
Aucun accès externe au service de recherche ou à Valkey.
Intégration avec le Pipeline SEO
Le service de recherche s'intègre au pipeline SEO :
Étape 11 : Migration vers Valkey
Le pipeline SEO charge les données dans Valkey :
# Charger les embeddings de requêtes
for query, embedding in zip(queries, embeddings):
client.hset(f"query:{query_hash}", mapping={
"query": query,
"embedding": embedding.tobytes(),
"score": score
})
# Créer l'index RediSearch
client.ft("queries_idx").create_index([...])
Voir Migration Valkey pour les détails.
Journalisation des Requêtes
Le service de recherche journalise les requêtes pour le pipeline SEO :
log_entry = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"query": query,
"filters_extracted": filters,
"results_count": len(results)
}
with open(SEO_LIVE_QUERIES_LOG, "a") as f:
f.write(json.dumps(log_entry) + "\n")
Ces journaux alimentent à nouveau l'Étape 1d : Récupérer les Requêtes en Direct.
Références
Concepts Techniques
-
Valkey - Site officiel
-
Redis - Site officiel (fork de Valkey)
-
RediSearch - Module de recherche vectorielle
-
Similarité Cosinus - Wikipedia
Articles Connexes
-
Extraction de Filtres - Comment les filtres sont extraits
-
Génération de Recherches Associées - Comment les recherches associées sont générées
-
Stratégie d'Embedding - Comment les embeddings sont générés
-
Vue d'Ensemble du Pipeline SEO - Architecture complète du pipeline
-
Migration Valkey - Chargement des données dans Valkey
Résumé
Notre service de recherche fonctionne comme une application Flask autonome sur un serveur séparé :
Architecture :
-
Application Flask autonome sur un serveur dédié
-
Valkey (fork de Redis) pour la mise en cache et la recherche vectorielle
-
API HTTP pour la communication avec le serveur web principal
APIs :
-
/api/extract_filters- Extraire des filtres des requêtes -
/api/related- Trouver des requêtes similaires (recherche vectorielle) -
/api/autocomplete- Suggérer des requêtes (correspondance par préfixe) -
/api/popular- Obtenir les requêtes populaires
Fonctionnalités de Valkey :
-
Recherche vectorielle (module RediSearch)
-
Mise en cache (TTL de 30 jours pour les correspondances d'expressions)
-
Saisie semi-automatique (ensembles triés)
-
Persistance (AOF)
Avantages :
-
Mise à l'échelle indépendante
-
Isolation des ressources
-
Haute performance (mise en cache Valkey)
-
Tolérance aux pannes (dégradation gracieuse)
Cette architecture permet une recherche rapide et évolutive tout en gardant le serveur web principal réactif.