Recherche vectorielle Redis
Conseils pour stocker et rechercher des embeddings dans Redis. Couvre la configuration des index, le choix d'algorithme, le filtrage hybride et le pattern RAG avec RedisVL.
Quand appliquer
- Définir un champ
VECTORdansFT.CREATE(RQE brut) ou unIndexSchemaRedisVL. - Choisir entre HNSW et FLAT et ajuster les paramètres HNSW.
- Ajouter des filtres de catégorie, date ou tenant à une requête vectorielle.
- Construire un pipeline de retrieval-augmented generation (RAG) sur Redis.
Cette skill s'appuie sur la skill redis-query-engine — les champs vectoriels vivent à l'intérieur des index RQE et partagent la même machinerie FT.CREATE / FT.SEARCH.
1. Configurer correctement l'index vectoriel
Trois paramètres doivent correspondre au modèle d'embedding :
DIM— la dimensionnalité de sortie du modèle (ex. 1536 pourtext-embedding-3-smalld'OpenAI). Une non-correspondance produit du garbage silencieux.DISTANCE_METRIC—COSINEpour les embeddings textes normalisés (le cas courant),IPpour le produit scalaire non-normalisé,L2pour l'Euclidien brut.TYPE/datatype— généralementFLOAT32. UtiliserFLOAT16ou des variantes quantifiées seulement quand le coût mémoire est une contrainte dure.
RQE brut :
FT.CREATE idx:docs ON HASH PREFIX 1 doc:
SCHEMA
content TEXT
embedding VECTOR HNSW 6
TYPE FLOAT32
DIM 1536
DISTANCE_METRIC COSINE
RedisVL :
schema = IndexSchema.from_dict({
"index": {"name": "idx:docs", "prefix": "doc:"},
"fields": [
{"name": "content", "type": "text"},
{"name": "embedding", "type": "vector", "attrs": {
"dims": 1536, "algorithm": "HNSW",
"datatype": "FLOAT32", "distance_metric": "COSINE",
}},
]
})
Voir references/index-creation.md pour les variantes redis-py et RedisVL.
2. HNSW vs FLAT
| Algorithme | Vitesse | Précision | Mémoire | Meilleur pour |
|---|---|---|---|---|
| HNSW | Rapide (approximé) | ~95%+ de rappel (ajustable) | Plus élevée | Grands jeux de données (>10k vecteurs), sensibles à la latence |
| FLAT | Lent (exact) | 100% | Plus faible | Petits jeux de données (<10k), critiques pour la précision |
Par défaut, HNSW pour toute charge de travail de production. Leviers d'ajustement :
M— connexions par nœud (16–64). Plus haut = meilleur rappel, plus de mémoire.EF_CONSTRUCTION— qualité du graphe à la construction (100–500). Plus haut = meilleur index, construction plus lente.EF_RUNTIME— taille de la liste de candidats au moment de la requête. Plus haut = meilleur rappel, requêtes plus lentes.
Utiliser FLAT quand le corpus est petit et que vous avez besoin de résultats exacts (ex. déduplication sémantique sur quelques milliers d'éléments).
Voir references/algorithm-choice.md.
3. Recherche hybride — filtrer avant le vecteur
Appliquer des filtres d'attributs (TAG / NUMERIC) pour que le moteur réduise l'espace de recherche avant la comparaison vectorielle. Ne pas récupérer un large ensemble de résultats puis filtrer côté client — c'est plus lent et moins précis.
from redisvl.query import VectorQuery
from redisvl.query.filter import Num, Tag
filters = (Tag("category") == "technology") & (Num("date") >= 2024)
query = VectorQuery(
vector=query_embedding,
vector_field_name="embedding",
return_fields=["content", "category", "date"],
num_results=10,
filter_expression=filters,
)
results = index.query(query)
Pour fusion texte + vecteur (score textuel pondéré BM25 combiné avec similarité vectorielle), utiliser HybridQuery sur Redis ≥ 8.4 avec redis-py ≥ 7.1, ou AggregateHybridQuery sur Redis plus anciens. C'est un type de « hybride » différent de la recherche vectorielle filtrée ci-dessus.
Voir references/hybrid-search.md.
4. Pattern RAG
Pipeline standard : embedder la requête utilisateur → recherche vectorielle Redis → passer le contexte top-K au LLM.
# Indexer les documents avec embeddings
records = [{"content": doc.content,
"embedding": embed_model.encode(doc.content).tolist(),
"source": doc.source}
for doc in documents]
index.load(records)
# Récupérer le contexte pertinent pour une question utilisateur
q_emb = embed_model.encode(user_question)
results = index.query(VectorQuery(
vector=q_emb,
vector_field_name="embedding",
return_fields=["content", "source"],
num_results=5,
))
# Générer avec le contexte récupéré
context = "\n".join(r["content"] for r in results)
response = llm.generate(f"Context: {context}\n\nQuestion: {user_question}")
Conseils pratiques :
- Faire correspondre la métrique au modèle. La plupart des modèles modernes d'embedding texte s'associent au mieux à
COSINE. - Chunker les documents longs avant l'indexation — la retrieval sur des chunks de 200–500 tokens surpasse généralement l'indexation de pages entières.
- Insérer par lots avec
index.load([...])au lieu d'un appel par enregistrement. - Pré-filtrer par attributs (tenant, récence, type de document) avant la recherche vectorielle.
Voir references/rag-pattern.md.