Architettura del Servizio di Ricerca: Applicazione Flask Standalone con Valkey
Questo articolo spiega come il nostro servizio di ricerca funzioni come un'applicazione Flask standalone su un server separato, utilizzando Valkey (un fork di Redis) per la ricerca vettoriale ad alte prestazioni, la cache e l'autocompletamento.
Il Problema: Prestazioni e Scalabilità della Ricerca
Le operazioni di ricerca sono computazionalmente costose:
-
Estrazione filtri: Confrontare oltre 2.500 frasi con ogni query
-
Ricerche correlate: Calcolare la similarità su 65K query
-
Autocompletamento: Corrispondenza per prefisso su 65K query
-
Filtraggio prodotti: Filtrare oltre 5K prodotti per molteplici criteri
Eseguire queste operazioni sul server web principale causa:
-
Lentezza nel caricamento delle pagine: La ricerca blocca altre richieste
-
Pressione sulla memoria: Gli embedding occupano un'enorme quantità di memoria
-
Contesa della CPU: Il calcolo della similarità è intensivo per la CPU
-
Difficoltà di scalabilità: Impossibile scalare la ricerca in modo indipendente
Abbiamo bisogno di un servizio di ricerca dedicato che possa scalare indipendentemente.
La Soluzione: Servizio di Ricerca Standalone
Eseguiamo un'applicazione Flask separata su un server dedicato:
Server Web Principale
↓ Chiamate API HTTP
Servizio di Ricerca
↓ Query Valkey
Server Valkey
Questa architettura fornisce:
-
Scalabilità indipendente: Scalare il servizio di ricerca senza influenzare il server web principale
-
Isolamento delle risorse: Le operazioni di ricerca non impattano il server web principale
-
Caching: Valkey memorizza in cache i risultati per query ripetute veloci
-
Alta disponibilità: Il servizio di ricerca può riavviarsi senza influenzare il server web principale
Componenti del Servizio di Ricerca
1. API Estrazione Filtri
-
Endpoint:
/api/extract_filters -
Scopo: Estrarre filtri strutturati da query in linguaggio naturale
-
Esempio:
GET /api/extract_filters?q=mini+pc+16gb+ram
Risposta:
{
"Form Factor": "Mini PC",
"Main Memory": "16"
}
Implementazione:
-
Caricare mappature frase-filtro da Valkey o JSON
-
Trovare corrispondenze delle frasi usando regex con confine di parola
-
Restituire filtri strutturati
Caching: Mappature frasi memorizzate in cache in Valkey (TTL 30 giorni)
2. API Ricerche Correlate
-
Endpoint:
/api/related -
Scopo: Trovare query semanticamente simili usando la ricerca vettoriale
-
Esempio:
POST /api/related
{
"query": "mini pc",
"limit": 10
}
Risposta:
{
"related": [
{"query": "small computer", "similarity": 0.92},
{"query": "compact desktop", "similarity": 0.89},
{"query": "mini pc 8gb", "similarity": 0.87}
]
}
Implementazione:
-
Creare embedding della query usando all-mpnet-base-v2
-
Interrogare RediSearch di Valkey per i vicini più prossimi
-
Restituire i primi N risultati ordinati per similarità
Caching: Risultati memorizzati in cache in Valkey (TTL 7 giorni)
3. API Autocompletamento
-
Endpoint:
/api/autocomplete -
Scopo: Suggerire query mentre l'utente digita
-
Esempio:
GET /api/autocomplete?q=mini+p&limit=5
Risposta:
{
"suggestions": [
"mini pc",
"mini pc 16gb",
"mini pc 8gb ram",
"mini pc fanless",
"mini pc windows 11"
]
}
Implementazione:
-
Interrogare RediSearch di Valkey con corrispondenza per prefisso
-
Classificare per popolarità (punteggio impressioni + clic)
-
Restituire i primi N suggerimenti
Caching: Indice di autocompletamento in Valkey (aggiornato giornalmente)
4. API Query Popolari
-
Endpoint:
/api/popular -
Scopo: Ottenere le query più popolari
-
Esempio:
GET /api/popular?limit=10
Risposta:
{
"queries": [
"mini pc",
"thin client",
"industrial pc",
"all in one pc"
]
}
Implementazione:
-
Caricare query da Valkey o JSON
-
Ordinare per punteggio di traffico (impressioni + clic)
-
Restituire le prime N query
Caching: Query popolari memorizzate in cache in Valkey (TTL 30 giorni)
Integrazione con Valkey
Valkey è un fork di Redis che fornisce:
-
Ricerca vettoriale: Modulo RediSearch per la ricerca per similarità
-
Caching: Archivio chiave-valore in memoria veloce
-
Autocompletamento: Corrispondenza per prefisso con insiemi ordinati
-
Persistenza: AOF (Append-Only File) per durabilità
Ricerca Vettoriale con RediSearch
Utilizziamo il modulo RediSearch di Valkey per la ricerca per similarità vettoriale:
Creazione Indice:
client.ft("queries_idx").create_index([
VectorField("embedding", "FLAT", {
"TYPE": "FLOAT32",
"DIM": 768,
"DISTANCE_METRIC": "COSINE"
}),
TextField("query"),
NumericField("score")
])
Ricerca Vettoriale:
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()}
)
Questo restituisce i 10 vicini più prossimi per similarità coseno.
Strategia di Caching
Memorizziamo in cache in Valkey diversi tipi di dati:
Mappature Frasi (TTL 30 giorni):
client.setex(
"seo:phrase_mappings",
30 * 24 * 3600,
json.dumps(phrase_mappings)
)
Ricerche Correlate (TTL 7 giorni):
cache_key = f"related:{query_hash}"
client.setex(cache_key, 7 * 24 * 3600, json.dumps(results))
Query Popolari (TTL 30 giorni):
client.setex(
"seo:popular_queries",
30 * 24 * 3600,
json.dumps(popular_queries)
)
Indice Autocompletamento (aggiornato giornalmente):
for query, score in queries:
client.zadd("autocomplete:mini", {query: score})
Autocompletamento con Insiemi Ordinati
Utilizziamo insiemi ordinati di Valkey per l'autocompletamento:
Struttura Indice:
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]
Ricerca per Prefisso:
prefix = "mini"
results = client.zrevrange(f"autocomplete:{prefix}", 0, 9, withscores=True)
Questo restituisce le prime 10 query che iniziano con "mini", ordinate per punteggio.
Comunicazione API
Il server web principale chiama il servizio di ricerca via HTTP:
Estrazione Filtri
from app.shared.filter_service import extract_filters_from_query
filters = extract_filters_from_query("mini pc 16gb ram")
# Internamente chiama: GET SEARCH_SERVICE_URL/api/extract_filters?q=...
Ricerche Correlate
import requests
response = requests.post(
"SEARCH_SERVICE_URL/api/related",
json={"query": "mini pc", "limit": 10},
timeout=2
)
related = response.json()["related"]
Autocompletamento
response = requests.get(
"SEARCH_SERVICE_URL/api/autocomplete",
params={"q": "mini p", "limit": 5},
timeout=1
)
suggestions = response.json()["suggestions"]
Gestione Errori e Fallback
Il server web principale gestisce con garbo i fallimenti del servizio di ricerca:
try:
filters = extract_filters_from_query(query)
except Exception as e:
logger.error(f"Search service failed: {e}")
filters = {} # Fallback a filtri vuoti
Ciò garantisce che il server web principale continui a funzionare anche se il servizio di ricerca è fuori servizio.
### Configurazione di Rete
Tutti i server sono su una rete privata:
- **Server web principale**: Può accedere al servizio di ricerca e a Valkey
- **Servizio di ricerca**: Può accedere a Valkey
- **Valkey**: Accessibile solo dal server web principale e dal servizio di ricerca
Nessun accesso esterno al servizio di ricerca o a Valkey.
## Integrazione con la Pipeline SEO
Il servizio di ricerca si integra con la pipeline SEO:
### Step 11: Migrazione a Valkey
La pipeline SEO carica i dati in Valkey:
```python
# Caricare embedding delle query
for query, embedding in zip(queries, embeddings):
client.hset(f"query:{query_hash}", mapping={
"query": query,
"embedding": embedding.tobytes(),
"score": score
})
# Creare indice RediSearch
client.ft("queries_idx").create_index([...])
Vedi Migrazione Valkey per i dettagli.
Registrazione delle Query
Il servizio di ricerca registra le query per la 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")
Questi log alimentano nuovamente lo Step 1d: Recupero Query Live.
Riferimenti
Concetti Tecnici
-
Valkey - Sito ufficiale
-
Redis - Sito ufficiale (fork di Valkey)
-
RediSearch - Modulo per ricerca vettoriale
-
Similarità Coseno - Wikipedia
Articoli Correlati
-
Estrazione Filtri - Come vengono estratti i filtri
-
Generazione Ricerche Correlate - Come vengono generate le ricerche correlate
-
Strategia di Embedding - Come vengono generati gli embedding
-
Panoramica Pipeline SEO - Architettura completa della pipeline
-
Migrazione Valkey - Caricamento dati in Valkey
Riepilogo
Il nostro servizio di ricerca funziona come un'applicazione Flask standalone su un server separato:
Architettura:
-
Applicazione Flask standalone su server dedicato
-
Valkey (fork di Redis) per caching e ricerca vettoriale
-
API HTTP per la comunicazione con il server web principale
API:
-
/api/extract_filters- Estrai filtri dalle query -
/api/related- Trova query simili (ricerca vettoriale) -
/api/autocomplete- Suggerisci query (corrispondenza per prefisso) -
/api/popular- Ottieni query popolari
Funzionalità Valkey:
-
Ricerca vettoriale (modulo RediSearch)
-
Caching (TTL 30 giorni per mappature frasi)
-
Autocompletamento (insiemi ordinati)
-
Persistenza (AOF)
Vantaggi:
-
Scalabilità indipendente
-
Isolamento delle risorse
-
Alte prestazioni (caching Valkey)
-
Tolleranza ai guasti (degradazione graduale)
Questa architettura consente una ricerca veloce e scalabile mantenendo reattivo il server web principale.