Index & Embeddings
Choisir le vector store, l’encodeur d’embeddings, et un index lexical pour l’hybride. Tuner HNSW/IVF, dimension, batchs, cache. Objectif : pertinence↑, latence↓, coût maîtrisé.
pgvector / Qdrant Embeddings texte & code BM25 hybride HNSW params KPIs & sizing
Choisir le vector store — pgvector ou Qdrant ?
🧭 Règle pratique
- Déjà sur PostgreSQL & besoins simples → pgvector.
- Volumétrie élevée, multi-tenants, features avancées → Qdrant (Docker/K8s).
- Dans tous les cas, envisager un index lexical pour l’hybride.
Punchline : « On commence où on est (Postgres), on scale sur Qdrant quand la recherche devient un produit. »
🧪 Questions d’entretien
- Nb de chunks estimés (N), dimension (d), langues, multi-produits ?
- Latence p95 cible sur k=20 et filtres ?
- RBAC/ABAC sur métadonnées requis ? (Qdrant a des payload filters rapides)
✅ / ⛔ Signaux
- Capacité d’index incrémental, version d’index.
- Monitoring QPS/latence/ef_search.
- Backups et snapshots planifiés.
- pgvector sans index (scan séquentiel).
- Aucun index lexical pour mots rares.
- Pas de stratégie de rebuild/AB-test.
pgvector — installation, schéma & requêtes
📦 Setup
CREATE EXTENSION IF NOT EXISTS vector; -- PostgreSQL ≥ 15
-- Table de chunks
CREATE TABLE rag_chunk(
id BIGSERIAL PRIMARY KEY,
doc_id TEXT, lang TEXT, product TEXT, conf TEXT,
text_md TEXT NOT NULL,
emb VECTOR(768), -- dimension = encodage choisi
created_at TIMESTAMPTZ DEFAULT now()
);
-- Index : HNSW (si dispo) sinon IVFFlat
-- HNSW (pgvector ≥ 0.7)
CREATE INDEX ON rag_chunk USING hnsw (emb vector_cosine_ops);
-- IVFFlat (entraînement requis)
-- CREATE INDEX ON rag_chunk USING ivfflat (emb vector_cosine_ops) WITH (lists = 100);
ANALYZE rag_chunk;🔎 Requête (cosine)
-- :q_emb = embedding(question)
SELECT id, doc_id, lang, 1 - (emb <=> :q_emb) AS score
FROM rag_chunk
WHERE lang='fr' AND product='WebApp'
ORDER BY emb <=> :q_emb
LIMIT 50;⚙️ Conseils
- IVFFlat : nlist ≈ sqrt(N), SET ivfflat.probes (ex. 10–20).
- HNSW : hnsw.ef_search 64–128 pour k=20.
- Stocker les métadonnées en colonnes (filtres rapides).
- Maintenir ver_index pour le cache réponses.
Anti-patterns : emb NULL, dimension incohérente, pas d’ANALYZE, filtres en JSON non indexés.
Qdrant — HNSW params & filtres de métadonnées
🧱 Création de collection
{
"name":"rag_chunks",
"vectors":{"size":768,"distance":"Cosine"},
"hnsw":{"m":32,"ef_construct":128},
"optimizers_config":{"default_segment_number":4},
"quantization_config":{"scalar":{"type":"int8","always_ram":true}}
}🔎 Recherche
{
"vector": q_emb, "top": 50,
"filter":{"must":[{"key":"lang","match":{"value":"fr"}},
{"key":"product","match":{"value":"WebApp"}}]},
"params":{"hnsw_ef":96}
}⚙️ Tuning HNSW
- m≈32 (connectivité), ef_construct 128–256.
- ef_search 64–128 pour k=20 (p95 < 100 ms sur 1M vec).
- Quantization int8 pour RAM ÷4 (légère perte recall).
- Payload filters très rapides → tagger lang, product, version, conf.
Anti-patterns : pas de filtres; mélange fr/en; pas de snapshots; ef_search par défaut trop bas.
Embeddings (texte & code) — choix pragmatiques
📚 Textuels multilingues
- BGE-m3 (multi, robuste, dim 1024).
- e5-base-v2 (dim 768, bon recall).
- Si mono-lang → modèles spécifiques (dim ≤ 512 acceptable).
🧑💻 Code-friendly
- jina-embeddings-code (fonctions, symboles, signatures).
- Chunking code par symbol (fichier, classe, fonction).
⚖️ Trade-offs
- Dim ↑ → qualité↑ mais RAM & latence ↑ (4 bytes × dim × N).
- Multilingue utile si fr/en mélangés.
- Modèle unique pour homogénéité; versionner encoder_ver.
✅ Heuristiques
- Docs métiers FR/EN : e5-base-v2 (768) ou BGE-m3 (1024).
- Référentiels code : jina-code + index lexical (identifiants).
- Évaluer sur 100 Q/A réelles (Hit@k, nDCG).
Pipeline embeddings — batch, cache, normalisation
🛠️ Bonnes pratiques
- Batch 32–128 (selon VRAM/API quotas).
- Cache embeddings : clé = sha1(text)|encoder_ver.
- Normaliser L2 si exigé par l’index (cosine).
- Stocker lang, product, version, conf, chunk_id.
# pseudo-cache
key = sha1(clean(text) + "|" + encoder_ver).hexdigest()
if not cache.has(key): vec = embed(text); cache.set(key, vec)📏 Taille & coûts
- Mémoire ≈ N × dim × 4 bytes (float32) → int8/PQ pour ÷4 à ÷8.
- Throughput = embeds/s → dimensionner workers.
- Tracer encoder_ver et index_ver pour invalidation.
Anti-patterns : recalc embeddings identiques; mélanger plusieurs encodeurs; pas de L2 quand requis.
Index lexical (BM25) — indispensable pour l’hybride
🔤 Pourquoi
- Mots rares, noms de tables/champs, numéros de version.
- Équations, symboles, erreurs exactes (codes).
⚙️ Outils
- OpenSearch/Elastic (prod), Whoosh (léger/dev).
- Analyzer fr/en; champs “keyword” pour identifiants.
🧪 Fiche de test
- 10 requêtes “codes/ids” → comparer BM25 vs vecteur.
- Fusion α 0.6–0.8 ; vérifier top-1 exactitude.
- Garder “explain” pour audit (score, terme-match).
# fusion simple
score = α * score_vector + (1-α) * score_bm25Retrieval & fusion — top-k, filtres, α
🎛️ Paramètres
- k_vec=50, k_bm25=50 → merge → top_k=20.
- Filtres : lang, product, version, conf.
- α = 0.6–0.8 (normaliser scores par requête).
📦 Contrat de requête
{
"q":"erreur SSO AAD",
"filters":{"lang":"fr","product":"WebApp"},
"k":20, "hybrid":{"alpha":0.7, "k_vec":50, "k_bm25":50}
}Anti-patterns : fusion naïve sans normalisation; filtres appliqués à moitié; k trop faible.
Quantization & sizing — RAM, latence, coûts
🧮 Estimer la RAM
- RAM ≈ N × dim × 4 bytes (float32) + overhead index.
- Ex: 5M × 768 × 4 ≈ 14.3 Go (sans HNSW).
- Int8/PQ → ÷4 à ÷8 au prix d’un recall ↓.
⚙️ Options
- Scalar int8 (Qdrant) mémoire ÷4, bon pour QA.
- PQ (IVF-PQ, FAISS) si très grand N (≈100M+).
- Sharding par collection/tenant.
✅ KPI sizing
- Recall@k ≥ 0.9 vs brut; p95 < 100 ms à k=20.
- QPS soutenu, CPU/RAM dans budget.
- Coût / 1k requêtes suivi.
Lifecycle — rebuild, A/B & version d’index
🔁 Stratégie
- index_ver (v3, v4…) : écrire en parallèle, basculer trafic après validation.
- Snapshots/backup avant migration.
- Garder encoder_ver pour tracer les embeddings.
🧪 A/B de recherche
- 50/50 ou 90/10 sur utilisateurs internes.
- Comparer Hit@k, nDCG, latence, citation rate.
- Décision via seuils et durée mini (ex. 7 jours).
Anti-patterns : rebuild in-place, pas de rollback, mélange d’encodeurs.
KPIs d’audit & anti-patterns fréquents
📏 KPIs essentiels
- Hit@k / nDCG@k (golden set).
- p95 retrieve & rerank; QPS soutenu.
- Taux de “citation exacte”.
⛔ Anti-patterns
- Index vecteur seul (pas d’hybride).
- Embeddings multiples non versionnés.
- Filtres sur champs non indexés / non normalisés.
- Pas d’invalidation du cache après rebuild.
✅ Quick wins entretien
- Ajouter BM25 + fusion α.
- Augmenter ef_search à 96–128 (Qdrant) / probes pour IVFFlat.
- Dédupliquer/normaliser champs (lang, product, version, conf).
