Aller au contenu
🔎 RAG #04

Vector stores : où ranger les vecteurs et comment les retrouver vite

Le composant 3 du RAG démonté. Vector store = index + storage + filtrage. ANN, HNSW, IVF expliqués sans formule. Matrice sqlite-vec / Qdrant / pgvector / FAISS / Chroma — pour choisir sans dette.

18 min de lecture Publié le 26 avril 2026 Révisé le 12 septembre 2026 v3.3

Accroche

Tu as suivi RAG-01 à RAG-03. Tes documents sont chunkés, tes chunks transformés en vecteurs (1024 floats chacun avec BGE-M3, par exemple). Tu te retrouves avec 10 000 vecteurs aujourd’hui, peut-être 100 000 dans deux ans. Question concrète : où tu les ranges, et comment tu retrouves les 5 plus proches d’une question utilisateur en moins de 100 ms ?

La réponse naïve — « je les stocke dans une liste Python et je compare un par un à chaque requête » — tient sur 1 000 vecteurs. À 100 000, une seconde par requête (hors budget interface). À 1 million, plusieurs secondes (inutilisable). Il te faut un composant spécialisé : le vector store.

Cette leçon démonte ce composant. Pas de formule. L’idée centrale, les algorithmes (ANN, HNSW, IVF) en intuition, la comparaison des cinq stores qui dominent en 2026, et la matrice de décision pour choisir lequel installer demain matin.

Vue d’ensemble : qu’est-ce qu’un vector store fait vraiment

Un vector store fait trois choses, dans cet ordre.

1. Stocker des vecteurs avec leurs identifiants et leurs metadata (catégorie, date, source, tags). Un vecteur seul ne sert à rien — sans son ID, tu ne sais pas à quel chunk il correspond ; sans ses metadata, tu ne peux pas filtrer.

2. Indexer ces vecteurs pour répondre vite à la question « quels sont les k vecteurs les plus proches de celui-ci ? ». L’index, c’est ce qui évite de comparer linéairement à tout le corpus à chaque requête.

3. Filtrer les résultats sur les metadata avant ou après la recherche par similarité (« les 5 chunks les plus proches, mais uniquement ceux datés de 2026 et catégorie juridique »).

Schéma : 1. Storage (vecteur + id + metadata), 2. Index ANN (lookup rapide), 3. Filtrage (metadata), Question embeddée, Candidats top-k, Chunks + scoresVector store1. Storage(vecteur + id + metadata)2. Index ANN(lookup rapide)3. Filtrage(metadata)QuestionembeddéeCandidatstop-kChunks+ scores
Le schéma en texte
  • Vector store : 1. Storage (vecteur + id + metadata), 2. Index ANN (lookup rapide), 3. Filtrage (metadata)
  • Question embeddée → 2. Index ANN (lookup rapide)
  • 2. Index ANN (lookup rapide) → Candidats top-k
  • Candidats top-k → 3. Filtrage (metadata)
  • 3. Filtrage (metadata) → Chunks + scores
  • Hors liens : 1. Storage (vecteur + id + metadata)
En clair

Un vector store n’est pas une magie. C’est un conteneur autour d’un algorithme de recherche par similarité, avec une couche de filtrage par metadata. Le « secret sauce » d’un store n’est pas son nom — c’est l’algorithme d’index qu’il utilise (vu plus loin) et la richesse du filtrage metadata qu’il propose.

Analogie infra : un vector store, c’est une base de données spécialisée, comme tu as ElasticSearch pour les mots-clés, Redis pour les clés-valeurs, Postgres pour le relationnel. Ici, l’opération native n’est ni « match texte » ni « get clé », c’est « trouve les k voisins géométriques d’un point dans un espace à 1024 dimensions ».

Le vrai problème que résout un vector store : l’index ANN

Une recherche brute sur N vecteurs, c’est FLAT (ou brute force) : tu calcules la similarité entre la requête et tous les vecteurs du corpus, tu tries, tu prends les k premiers. Recall garanti 100 %, mais coût linéaire en N.

Calcul d’ordres de grandeur. Pour comparer deux vecteurs de 1024 floats par similarité cosinus (rappel RAG-01 : l’angle entre les deux flèches), tu fais ~1024 multiplications :

  • 10 000 vecteurs × 1024 dim ≈ 10 millions de mults → ~30 ms sur un i5 récent. Acceptable.
  • 1 million × 1024 dim ≈ 1 milliard de mults → ~3 secondes. Hors budget.
  • 100 millions : impensable en FLAT.

D’où l’ANN : Approximate Nearest Neighbor, ou « plus proches voisins approximatifs ».

Définition : un index ANN, c’est une structure de données qui élimine 90-99 % des candidats sans les regarder, en échange d’un petit risque de rater un vrai voisin. Tu acceptes 95 à 99 % de précision (le recall, vu RAG-01) pour 100× à 1000× de vitesse.

Analogie infra : c’est la même logique qu’un index B-tree sur une colonne SQL. Sans index, un WHERE id = 12345 scanne toute la table. Avec un B-tree, tu descends en O(log N) — saut direct à la bonne page. L’ANN fait la même promesse, mais pour des points dans un espace à N dimensions au lieu de scalaires triés.

Schéma : Question 1 vecteur, Compare aux N vecteurs, Trie + top-k, O(N × dim) 1M × 1024 = 1B mults, Navigue la structure, Suit vers le proche, O(log N) ~640 multsANN (HNSW)FLAT (brute force)Question1 vecteurCompare aux N vecteursTrie + top-kO(N × dim)1M × 1024 = 1B multsNavigue la structureSuit vers le procheO(log N)~640 mults
Le schéma en texte
  • FLAT (brute force) : Compare aux N vecteurs, Trie + top-k, O(N × dim) 1M × 1024 = 1B mults
  • ANN (HNSW) : Navigue la structure, Suit vers le proche, O(log N) ~640 mults
  • Compare aux N vecteurs → Trie + top-k
  • Navigue la structure → Suit vers le proche
  • Question 1 vecteur → Compare aux N vecteurs
  • Question 1 vecteur → Navigue la structure
  • Hors liens : O(N × dim) 1M × 1024 = 1B mults, O(log N) ~640 mults

Le coût conceptuel à payer : tu sors du déterminisme. Sur FLAT, le top-5 est exact. Sur HNSW (algo ANN, vu plus bas), le top-5 est probable — typiquement 95-98 % des vrais voisins, soit 1 chunk pertinent sur 50 silencieusement raté. Invisible pour un chatbot grand public, critique pour un système juridique. À toi de mesurer le recall sur ton corpus, pas sur un benchmark générique.

Trois familles d’algorithmes ANN

Trois approches dominent en 2026, chacune avec un compromis différent vitesse / précision / mémoire. Tu n’as pas à les implémenter — chaque store en propose une ou plusieurs. Mais tu dois savoir laquelle ton store utilise pour comprendre ce que tu paies.

FLAT — pas vraiment un ANN, mais le défaut quand N est petit

Le calcul exhaustif décrit plus haut. Recall 100 %, complexité O(N), aucun build offline. Suffit tant que N < ~50 000 vecteurs et que ton budget latence est > 100 ms. C’est ce que sqlite-vec utilise par défaut. Pas un ANN à proprement parler — c’est l’absence d’index, l’option à battre.

IVF — Inverted File Index, on découpe l’espace en clusters

Idée : avant la recherche, tu groupes tous tes vecteurs en n_clusters clusters (typiquement √N) avec un k-means. À la recherche, tu trouves d’abord le cluster le plus proche de la requête (coût : N divisé par √N), puis tu fais un FLAT à l’intérieur de ce cluster (coût : √N vecteurs).

Analogie infra : c’est exactement le pattern sharding par hash. Au lieu de chercher dans 1 million de lignes, tu sais à quel shard la clé appartient, tu interroges ce shard, ~1000 lignes. Tu paies un calcul de hash, tu divises ton coût par √N.

Schéma : Question, Centroids (préca k-means), Cluster 1, Cluster 2 *, Cluster 3, ...Cluster K, Top-KFLAT insidecluster choisiQuestionCentroids(préca k-means)Cluster 1Cluster 2 *Cluster 3...Cluster KTop-K
Le schéma en texte
  • Question → Centroids (préca k-means)
  • Centroids (préca k-means) → Cluster 1
  • Centroids (préca k-means) → Cluster 2 *
  • Centroids (préca k-means) → Cluster 3
  • Centroids (préca k-means) → ...Cluster K
  • Cluster 2 * → Top-K (FLAT inside cluster choisi)

Recall : 90-99 % selon combien de clusters tu sondes (paramètre n_probe, typiquement 1 à 10). Plus tu en sondes, plus c’est précis et lent. Variante prod : IVF-PQ (Product Quantization) qui compresse les vecteurs ×8 à ×32 en mémoire — utile au-delà de 10 millions de vecteurs.

HNSW — Hierarchical Navigable Small World, le défaut moderne

L’algorithme dominant en 2026 (Malkov & Yashunin 2018). Idée : un graphe à plusieurs niveaux où les vecteurs sont reliés à leurs voisins proches. Au sommet, peu de nœuds, longues arêtes (vue d’avion). En bas, tous les nœuds, courtes arêtes (vue de détail). Pour une requête, tu pars du sommet, tu suis l’arête vers le voisin le plus proche, tu descends d’un niveau, tu recommences. En quelques sauts, tu arrives sur les vrais voisins.

Analogie infra : c’est l’organisation d’un CDN multi-niveaux. Le routing DNS te trouve d’abord la région (sommet), puis le serveur edge (intermédiaire), puis le contenu (base). Tu n’as jamais à connaître l’adresse exacte du serveur final — tu descends par approximations successives.

Complexité empirique O(log N) jusqu’à ~100 millions de vecteurs. Coût : build 5-10× plus lent qu’IVF, mémoire ~1.5× le brut.

AlgorithmeRecall@10SearchMémoireCas d’usage
FLAT100 %O(N)×1< 50 000 vecteurs, latence > 100 ms
IVF90-99 %O(√N)×1.05100 000 - 1 million, RAM serrée
IVF-PQ85-95 %O(√N)×0.05-0.25> 10 millions, RAM très serrée
HNSW95-99 %O(log N)×1.510 000 - 100 millions, latence prio
En clair

Les trois algos ne sont pas concurrents — ils couvrent des zones différentes. FLAT : démarrage. HNSW : production standard. IVF / IVF-PQ : volumes très gros avec contrainte mémoire. Quand un store annonce « ANN search » sans préciser, c’est presque toujours HNSW.

Cinq vector stores comparés — la matrice 2026

Cinq stores méritent d’être nommés en 2026. Les autres (Weaviate, Milvus, Pinecone) restent valides dans des cas spécifiques (multi-modal enterprise, milliards de vecteurs, SaaS managé) hors scope d’un copilote interne.

Schéma : sqlite-vec extension SQLite, FAISS lib C++/Python, Chroma embedded ou C/S, Qdrant service Rust, pgvector extension PostgresService réseauQdrantservice Rustpgvectorextension PostgresEmbedded (1 process)sqlite-vecextension SQLiteFAISSlib C++/PythonChromaembedded ou C/S
Le schéma en texte
  • Embedded (1 process) : sqlite-vec extension SQLite, FAISS lib C++/Python, Chroma embedded ou C/S
  • Service réseau : Qdrant service Rust, pgvector extension Postgres
  • Hors liens : sqlite-vec extension SQLite, FAISS lib C++/Python, Chroma embedded ou C/S, Qdrant service Rust, pgvector extension Postgres

sqlite-vec — l’extension SQLite single-file

Extension SQLite qui ajoute une table virtuelle vec0 pour stocker des embeddings et faire du k-NN par opérateur SQL MATCH. Mode FLAT par défaut (IVF en preview). Toute ta donnée — texte, vecteur, BM25 lexical (FTS5) — vit dans un seul fichier .db. Zéro infra, backup cp file.db, transactionnel. Limite : 1 writer concurrent max (héritage SQLite), dégradation au-delà de ~100 000 vecteurs en FLAT. Le bon défaut pour un projet RAG démarrage.

FAISS — la bibliothèque pure de Facebook AI

C’est une librairie, pas une DB. Tu instancies un index (IndexFlatIP, IndexHNSWFlat…), tu add() des vecteurs, tu search(). Pas de persistance native (tu sérialises toi-même), pas de filtrage metadata sans code custom. Le plus rapide sur les benchmarks pure-search. Cas d’usage : pipelines ML serving custom, batch offline, recherche académique. Pas le bon choix pour une stack RAG prête à l’emploi.

Qdrant — le service standalone en Rust

Service séparé en Rust, exposé en REST + gRPC. HNSW custom optimisé, filtrage riche (conditions metadata appliquées pendant la recherche, pas après — voir AP-3), multi-tenant via collections, snapshots de backup. Setup : docker run -p 6333:6333 qdrant/qdrant. Latence k=20 sur 100 000 vecteurs : ~10 ms. Idéal multi-tenant, filtrage complexe, scaling au-delà de 1 million de vecteurs.

pgvector — l’extension PostgreSQL

Extension qui ajoute un type colonne vector(N) à PostgreSQL, et trois opérateurs de distance (<-> L2, <=> cosine, <#> produit scalaire). Index HNSW depuis 0.5.0, IVFFlat avant. Le bon choix si tu as déjà Postgres : tu joins ta table chunks_vec à documents et tags dans une seule requête SQL avec push-down des filtres metadata, et tu réutilises pg_dump, transactions, monitoring existants. Latence ~5-20 ms sur 100 000 vecteurs en HNSW.

Chroma — l’embedded pythonic

Embedded ou client/serveur, HNSW via hnswlib sous le capot, API très pythonic (add(), query()). Limite : couche Python obligatoire, metadata séparées du vecteur dans le format de stockage. Apprentissage moins « bare-metal » que sqlite-vec.

StoreTypeAlgo défautLatence k=20 @100kSetupFiltrageBackupQuand l’utiliser
sqlite-vecextension SQLiteFLAT~250 mspip install sqlite-vecSQL WHEREcp file.db< 100 000 vecteurs, single-user
FAISSlib C++/Pythonau choix~20 ms (HNSW)lib importnon (custom)sérialisation manuellepipeline ML, latence dure, in-memory pur
Qdrantservice RustHNSW custom~10 msdocker runriche, push-downsnapshot APIprod multi-tenant
pgvectorextension PostgresHNSW (0.5+)~20 msCREATE EXTENSIONSQL WHEREpg_dumpPostgres déjà déployé
Chromaembedded ou C/SHNSW (hnswlib)~15 mspip installpartielcp dirquick local ML pythonic
En clair

Quatre axes te font choisir entre ces stores : volume du corpus, latence cible, stack existante (Postgres ou pas), et compétence DevOps que tu acceptes d’investir. Le tableau ci-dessous donne les recommandations directes.

Matrice de décision — quel store pour quel contexte

Volume corpusLatence cibleStack existanteRecommandationPourquoi
< 10 000 chunks< 1 sAucunesqlite-vec FLATZéro infra, single-file, suffit largement.
10 000 - 100 000< 500 msAucunesqlite-vec ou ChromaToujours embedded, FLAT acceptable < 50k.
100 000 - 1 million< 200 msPostgrespgvectorRéutilise infra, transactions, backups.
100 000 - 1 million< 200 msAgnostiqueQdrantFiltering riche, multi-tenant, scaling natif.
> 1 million< 100 msML pipelineFAISS ou QdrantFAISS si in-memory pur acceptable, sinon Qdrant.
Latence < 10 msdurML servingFAISS in-memoryPas d’overhead réseau, indices custom.

Cas pratique labo (le mien) : ~1 800 chunks aujourd’hui, projection 18 000 à 5 ans, latence p95 < 10 s tolérée, single-user. Verdict : sqlite-vec FLAT — confortable jusqu’en 2030. Plan de migration vers pgvector si une de ces trois conditions devient vraie : (a) multi-utilisateurs concurrents, (b) intégration avec un schéma SQL riche, (c) volume > 100 000 chunks avec contrainte latence < 200 ms.

Heuristiques transverses :

  • Démarre toujours en FLAT. C’est gratuit (pas de tuning), tu mesures recall et latence sur ton corpus, tu passes à HNSW seulement quand la latence dépasse ton budget.
  • Pousse les filtres metadata dans la requête, pas en post-traitement Python (cf. AP-3).
  • Stocke la version du modèle d’embedding dans les metadata. Si tu changes d’embedder, tu dois rebuilder l’index — la version te dit lesquels.

Code minimal — sqlite-vec en pratique

Voici le squelette d’un système RAG single-file complet en Python, avec sqlite-vec, qui marche en moins de 30 lignes utiles.

import sqlite3, sqlite_vec, struct

# 1. Connexion + chargement de l'extension sqlite-vec
db = sqlite3.connect("rag.db")
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)

# 2. Schéma — table classique pour le texte+metadata, table virtuelle vec0 pour les vecteurs
db.executescript("""
    CREATE TABLE IF NOT EXISTS chunks (
        chunk_id INTEGER PRIMARY KEY,
        text TEXT NOT NULL,
        category TEXT
    );
    CREATE VIRTUAL TABLE IF NOT EXISTS chunks_vec USING vec0(
        chunk_id INTEGER PRIMARY KEY,
        embedding FLOAT[1024]
    );
""")

# 3. Sérialisation float32 little-endian (format attendu par vec0)
def to_blob(vec): return struct.pack(f"{len(vec)}f", *vec)

# 4. Recherche top-k avec filtre metadata push-down (rapide, dans la requête SQL)
def search_topk(query_emb, k=5, category=None):
    sql = """SELECT c.chunk_id, c.text, distance FROM chunks_vec v
             JOIN chunks c USING (chunk_id)
             WHERE v.embedding MATCH ? AND k = ?"""
    params = [to_blob(query_emb), k]
    if category:
        sql += " AND c.category = ?"; params.append(category)
    return [dict(zip(["id", "text", "score"], r))
            for r in db.execute(sql + " ORDER BY distance", params)]

Trace exécution sur 1 800 chunks (i5-12500, BGE-M3 1024 dim) : insertion batch ~3 s, build index nul (FLAT), search top-k ~12 ms par requête. Production sur ton poste de travail demain matin sans Docker.

Pièges et anti-patterns

Anti-pattern

AP-1 — Confondre vector DB et index ANN. Quand tu compares Qdrant et pgvector, tu compares deux implémentations de HNSW. Le « secret sauce » est dans le tuning des paramètres (M, ef_construction, ef_search), pas dans le nom du produit.

Anti-pattern

AP-2 — Choisir HNSW sur 5 000 chunks. HNSW est conçu pour > 100 000 vecteurs. Sur petit corpus, FLAT bat HNSW : pas de build, latence comparable, recall 100 %. Démarre toujours en FLAT et migre quand la latence dépasse 200 ms.

Anti-pattern

AP-3 — Filtrer metadata après le retrieval. Si tu fais search(k=20) puis tu filtres category == 'fundamentals' en Python post-hoc, tu rates les chunks pertinents qui n’étaient pas dans le top-20 dense. Pousse toujours le filtre dans la requête (WHERE SQL ou condition Qdrant).

Anti-pattern

AP-4 — Réindexer sur changement d’embedder. Si tu passes de BGE-M3 (1024 dim) à OpenAI text-embedding-3-large (3072 dim), tu dois rebuilder l’index complet. Les espaces vectoriels ne sont pas comparables. Stocke la version du modèle dans les metadata pour détecter les drifts.

Anti-pattern

AP-5 — Mesurer recall sur un benchmark générique. SIFT-1M, GloVe-100 — datasets génériques. Ton corpus FR de conventions techniques a une distribution différente. Construis ton eval set (30 paires Q/A gold), mesure recall@5 sur ton pipeline. Si HNSW tape 0.95 sur SIFT mais 0.80 chez toi, FLAT vaut peut-être mieux.

Anti-pattern

AP-6 — Ignorer la concurrence d’écriture SQLite. sqlite-vec hérite des limites SQLite : 1 writer max simultané. Ingest concurrent + queries serving → database is locked. Solution : queue d’ingest séquentielle, ou pivot vers pgvector / Qdrant.

📌 Débat ouvert — embedding propriétaire vs vector store agnostique. La fragilité d’un système RAG est rarement le vector store (HNSW est devenu un commodity). C’est l’embedder. Si OpenAI déprécie text-embedding-3-large, tu dois ré-embedder ton corpus entier. Le vector store, lui, accepte n’importe quel vecteur. Choisis ton embedder avec soin, ton vector store avec pragmatisme.

Récap visuel

Schéma : Documents, chunking RAG-02, embedding RAG-03, Vector store (cette leçon), Question, embedding, search top_k, Chunks + scoreslookupDocumentschunkingRAG-02embeddingRAG-03Vector store(cette leçon)Questionembeddingsearch top_kChunks+ scores
Le schéma en texte
  • Documents → chunking RAG-02
  • chunking RAG-02 → embedding RAG-03
  • embedding RAG-03 → Vector store (cette leçon)
  • Question → embedding
  • embedding → search top_k
  • Vector store (cette leçon) → search top_k (lookup)
  • search top_k → Chunks + scores

Le vector store est le pivot du RAG. L’indexation y dépose, la requête y lit. Tout le reste (hybrid search, reranking, query rewriting) est de l’optimisation autour de ce pivot.

Quel store changer quand le retrieval pose problème

Quand le retrieval déraille, le vector store n’est pas toujours coupable — c’est même rarement le cas. Table debug à parcourir avant de migrer.

Symptôme observéPremière chose à challenger
Latence > budgetAlgo d’index (FLAT → HNSW) ou tuning HNSW (ef_search)
Recall qui chute après scalingAlgo d’index (FLAT dégrade en O(N), HNSW reste log N)
Filtres metadata cassés ou ignorésVérifier le push-down du store (sinon post-filter rate des chunks)
RAM saturéePasser IVF-PQ (compression ×8 à ×32) ou sharder en collections
database is locked (sqlite-vec)Sérialiser les writes ou pivoter vers pgvector / Qdrant
Recall stable mais réponses faussesPas le vector store — chunking (RAG-02) ou embedder (RAG-03)
Question

Tu démarres un projet interne : 40 000 documents, une dizaine d’utilisateurs, pas d’équipe d’exploitation dédiée. Un collègue propose de monter un cluster distribué « pour ne pas avoir à migrer plus tard ». Quel est le coût réel de ce conseil ?

Réponse

Il t’ajoute un composant à exploiter avant d’avoir prouvé que le produit sert à quelque chose. À cette échelle, un store embarqué tient sans effort — et il n’a ni cluster, ni sauvegarde, ni montée de version, ni astreinte.

L’argument « pour ne pas migrer plus tard » suppose que la migration soit coûteuse. Elle ne l’est pas tant que : tes chunks et tes métadonnées sont stockés hors du store, et que tu peux réindexer depuis la source. Si tu tiens cette discipline, changer de store est une réindexation — pas une reconstruction.

La vraie question n’est donc pas « quel store va me porter à cinq ans » mais « qu’est-ce que je garde en dehors du store pour pouvoir en changer ». Réponse : le corpus, les métadonnées, et le script qui reconstruit l’index.

À toi de jouer

Note en trois lignes ce que tu devrais garder hors de ton vector store pour pouvoir le remplacer un lundi matin. Si la liste est vide, ton index est devenu ta source de vérité — et c’est le moment de corriger ça, pas dans deux ans.

Glossaire

Format : TERME — définition courte + analogie infra quand pertinente.

  • Vector store (rappel RAG-01) — Base de données spécialisée pour stocker des vecteurs et chercher rapidement les plus proches d’un vecteur cible. Analogie : Redis, mais indexé par similarité au lieu de clé.
  • Index — Structure de données accessoire qui accélère la recherche au prix d’un coût de build et de stockage. Analogie : un B-tree sur une colonne SQL.
  • ANN (Approximate Nearest Neighbor) — Famille d’algorithmes qui trouvent les k voisins les plus proches d’un point en acceptant 1-5 % d’erreur en échange de 100× à 1000× de vitesse. Analogie : un cache à hit-rate 95 % qui répond en O(log N) au lieu d’un store exhaustif en O(N).
  • FLAT — Recherche brute exhaustive : on calcule la similarité avec tous les vecteurs, on trie. Recall 100 %, complexité O(N), aucun index. Le défaut quand N est petit.
  • IVF (Inverted File Index) — Algorithme ANN qui pré-clustérise les vecteurs en groupes (k-means), puis fait un FLAT à l’intérieur du ou des clusters les plus proches de la requête. Analogie : sharding par hash.
  • HNSW (Hierarchical Navigable Small World) — Algorithme ANN à base de graphe multi-niveaux. Le défaut moderne 2026, complexité O(log N) jusqu’à 100 millions de vecteurs. Analogie : un CDN multi-niveaux où on descend par approximations successives.
  • Recall@k — Pourcentage des k vrais voisins qu’on retrouve effectivement dans le top-k retourné par l’index ANN. Mesure clé de qualité d’un index ANN. Analogie : taux de hit dans un cache.
  • Push-down filter — Application d’un filtre sur les metadata dans la requête (pendant le scan de l’index), par opposition à un post-filtre appliqué après. Plus rapide et plus précis : tu ne rates pas les chunks pertinents qui auraient été éjectés du top-k brut.
  • Quantization — Compression des vecteurs (typiquement de float32 à int8 ou moins) pour réduire la mémoire. Variante : Product Quantization (PQ) utilisée dans IVF-PQ. Compromis : ×8 à ×32 de mémoire économisée pour 5-15 pts de recall en moins.
  • Embedding (rappel RAG-01) — Représentation d’un texte sous forme de vecteur de nombres. Le vector store stocke et indexe ces vecteurs.
  • Similarité cosinus (rappel RAG-01) — Mesure de proximité entre deux vecteurs basée sur l’angle entre eux. Métrique standard utilisée par la plupart des vector stores.
  • top_k (rappel RAG-01) — Nombre de chunks ramenés par le retrieval. Cible typique : 5 à 20.

Pour aller plus loin

Sources principales :

Prochaine leçon dans l’axe RAG :

  • RAG-05 — Retrieval avancé (à venir) : hybrid search (BM25 + dense + RRF), reranking, query rewriting. Comment combiner recherche sémantique (vector store) et recherche par mots-clés pour gagner 5-15 points de recall.

Vérifie-toi

5 questions, tirées de cette leçon et, pour la dernière, d’une leçon précédente.

  1. Question 1Cinq mille chunks, un seul utilisateur. Index HNSW ou recherche FLAT ?
    Voir le corrigé
    • Non : IVF-PQ, pour économiser la mémoire.La compression sert au-delà de dix millions de vecteurs. Ici, elle ne ferait que coûter des points de recall.
    • Juste : FLAT : recall de 100 %, aucun build, latence largement suffisante. Tu migres quand la latence dépasse ton budget.On démarre toujours en FLAT, on mesure, et on ne passe à un index approximatif que sur un besoin constaté.
    • Non : HNSW, pour être prêt quand le corpus grandira.Sur un petit corpus, HNSW ajoute un build et un réglage sans rien gagner, et tu perds le recall exact.
  2. Question 2Ton code fait search(k=20), puis filtre la catégorie en Python sur les vingt résultats. Quel est le défaut ?
    Voir le corrigé
    • Juste : Les chunks pertinents de la catégorie qui n'étaient pas dans le top-20 sont perdus. Le filtre doit être poussé dans la requête.Un filtre appliqué pendant la recherche garde les bons candidats. Appliqué après, il peut ne rien laisser.
    • Non : Aucun, c'est le fonctionnement normal d'un vector store.Le filtre arrive trop tard : il ne voit que ce que la recherche a déjà écarté ou gardé.
    • Non : C'est plus lent, mais le résultat est identique.Le résultat n'est pas identique : des chunks pertinents manquent, sans aucun message.
  3. Question 3Postgres est déjà en production, trois cent mille chunks, une latence cible sous 200 ms. Quel store ?
    Voir le corrigé
    • Non : FAISS.C'est une bibliothèque, pas une base : ni persistance native, ni filtrage de métadonnées sans code maison.
    • Juste : pgvector.Tu réutilises l'infrastructure, les transactions, les sauvegardes et le filtrage SQL que tu as déjà.
    • Non : sqlite-vec.En FLAT, il se dégrade au-delà de cent mille vecteurs, et il n'accepte qu'un écrivain à la fois.
  4. Question 4Un index HNSW annonce un recall de 95 à 98 %. Qu'est-ce que cela veut dire pour toi ?
    Voir le corrigé
    • Non : Que 2 à 5 % des requêtes échoueront avec une erreur.Aucune erreur n'est levée. Le voisin manquant est simplement absent du résultat.
    • Non : Que 2 à 5 % des réponses du modèle seront fausses.Le recall mesure les voisins retrouvés par l'index, pas la justesse des réponses générées.
    • Juste : Que de vrais voisins peuvent être ratés sans que rien ne le signale : à mesurer sur ton corpus, et critique selon ton domaine.Invisible pour un chatbot grand public, grave pour un système juridique. Le chiffre d'un banc générique ne dit pas ce qu'il vaut chez toi.
  5. Question 5 · rappel RAG-03Tu passes de BGE-M3 (1024 dimensions) à un modèle qui produit 3072 dimensions. Que fais-tu côté vector store ?
    Voir le corrigé
    • Juste : Tu réembeddes le corpus et tu reconstruis l'index en entier.Changer de modèle d'embedding, c'est réindexer. D'où l'intérêt de stocker la version du modèle dans les métadonnées.
    • Non : Tu convertis les vecteurs existants en 3072 dimensions.Aucune conversion ne transforme un vecteur d'un modèle en vecteur d'un autre. Il faut repartir du texte.
    • Non : Rien : le store accepte n'importe quel vecteur.Il accepte les nouveaux vecteurs, mais les anciens vivent dans un autre espace : les comparer ne veut plus rien dire.