Context Engineering — Section 3 : Retrieval & Ranking
Embeddings, BM25, filtres, récence, hybrid search, RRF & re-ranking — du signal plutôt que du bruit.
3. Retrieval & Ranking
Concevoir une recherche hybride robuste, explicable et économe pour alimenter le contexte.
- A. Panorama
- B. Lexical (BM25)
- C. Vectoriel (Embeddings)
- D. Hybride & RRF
- E. Re-ranking
- F. Stratégies de requêtes
- G. Filtres & Récence
- H. Métriques
- I. Snippets
- J. Schémas JSON
- K. Anti-patterns
- L. Playbook
Le retrieval sélectionne des passages pertinents ; le ranking les ordonne pour maximiser la qualité du contexte utile. Une bonne pile combine lexical (BM25), vecteur (embeddings), une fusion (RRF) et parfois un re-ranking (cross-encoder) sur le top-N.
Objectifs
Pertinence, couverture, fraîcheur, explicabilité (citations), coût stable, latence maîtrisée.
Entrées
Question normalisée (+ variantes), filtres (tenant/role/produit/langue), fenêtre budget tokens.
Sorties
Liste ordonnée de passages avec doc_id, offset, score, permalink, et métadonnées.
Utilisateur/Agent ──► [ Générateur de requêtes ] ─► [ BM25 ] ┐
├─► [ Fusion RRF ] ─► [ Re-rank (optionnel) ] ─► Top-k passages → Contexte
└──────────────► [ Vector Search ] ┘
Toujours loguer queries, filtres, scores, et la liste des sources pour l’audit et l’amélioration continue.
BM25 — solides bases lexicales
- Excellent sur mots clés, noms propres, références d’articles, codes d’erreur.
- Nécessite une tokenisation/stemming par langue (fr/en/…)
- Sensible à la qualité des titres/résumés (penser “boosts”).
Créer des champs dédiés : title_tsv, summary_tsv, body_tsv et booster les premiers.
Embeddings — sens & paraphrases
- Robuste aux reformulations ; utile pour “qu’est-ce que / comment / pourquoi”.
- Qualité dépendante du modèle d’embeddings et du chunking.
- Bien choisir la dimension & la normalisation (cosine/dot) selon le moteur.
Ne pas mélanger des embeddings de versions différentes sans namespace/versionning explicite.
Fusion Hybride (RRF)
La Reciprocal Rank Fusion combine des classements hétérogènes (BM25, vecteur, règles) sans paramétrage lourd.
scores_RRF(d) = Σ_m 1 / (k + rank_m(d)) (k≈60)
- Simple, robuste, améliore le rappel sans trop nuire à la précision.
- Permet d’ajouter une 3ᵉ source de score (ex : popularité, fraicheur).
A/B tester k et la taille des listes initiales (ex. 50+50 → rerank 20 → top-k 8).
Re-ranking (cross-encoder)
Un modèle bi-encodeur (embeddings) sélectionne rapidement ; un cross-encoder re-score finement un top-N réduit (ex. 20→10).
- Coût/latence plus élevés → à réserver aux tâches critiques (juridique, médical, incident).
- Utile pour distinguer des nuances sémantiques proches.
Mesurer l’apport réel via relevance@k et “win rate” avant de l’activer en prod.
Stratégies de requêtes
- Multi-query : générer 3–6 variantes (définition, procédure, exemple, contrainte, anti-cas).
- Expansion contrôlée : ajouter synonymes/domain terms, pas du “keyword stuffing”.
- Query routing : router vers index thématiques (juridique, code, SRE…).
- Ask-to-search (agents) : si rappel faible, poser une question clarifiante rapide.
User Q ─► [Normalizer] ─► {q0 (brut), q1..q4 (variantes)} ─► Retrieval Hybride ─► Fusion ─► (Re-rank) ─► k
Filtres & Boosting
- RBAC : tenant/role/équipe/produit/version/langue.
- Fraîcheur : “recency boost” (ex. exponentiel sur l’âge du doc).
- Type de section : booster “Résumé”, “Procédure”, “Caveats”.
- Qualité : pénaliser items incomplets (métadonnées manquantes).
score_final = α·rrf + β·recency + γ·section_boost − δ·penalty_incomplet
Métriques d’évaluation
- Relevance@k (top-k couvre la réponse attendue), MRR, nDCG.
- Coverage des domaines, Freshness, Faithfulness (avec citations côté LLM).
- Coût & latence (p50/p95) + hit-rate du context cache.
Tenir un “gold set” versionné (100–300 Q/R) par domaine ; exécuter des tests nocturnes.
Python — Hybride + RRF + re-ranking
def hybrid_search(question: str, k=8, rerank=True, filters=None):
qs = generate_queries(question) # multi-query
bm = bm25_search(qs, top=50, filters=filters)
ve = vector_search(qs, top=50, filters=filters)
fused = rrf_fusion([bm, ve], k_param=60)
if rerank:
fused = cross_encoder_rerank(question, fused, top=20)
return fused[:k]SQL — vue matérialisée (BM25 FR)
CREATE MATERIALIZED VIEW rr_docs AS
SELECT
doc_id,
title,
to_tsvector('french', coalesce(title,'') || ' ' || coalesce(summary,'') || ' ' || coalesce(content,'')) AS tsv,
metadata->>'lang' AS lang,
metadata->>'tenant' AS tenant,
metadata->>'section' AS section,
metadata->>'permalink' AS permalink
FROM chunks;Python — Recency boost
def recency_boost(age_days: int, half_life=180) -> float:
# score ∈ (0,1] ; 1 pour document du jour
import math
return math.exp(-math.log(2)*age_days/half_life)Schéma — Passage récupéré
{
"doc_id": "kb-2025-00142#p17",
"permalink": "https://kb/acme/kb-2025-00142#p17",
"offset": {"start": 14832, "end": 15497},
"section": "Procédure",
"score": {"rrf": 0.71, "recency": 0.92, "final": 0.79},
"snippet": "Étapes N2 …",
"metadata": {"lang":"fr","tenant":"acme-eu","version":"v2025.3"}
}Inclure doc_id + offset pour ouvrir la source précisément (UX de preuve).
Anti-patterns fréquents
- Coller un document entier au lieu de passages titrés.
- Mélanger embeddings de versions différentes sans namespace.
- Ignorer les filtres d’accès (RBAC) au retrieval.
- Activer le re-ranking sans mesurer le gain réel (coût/latence).
- Ne pas logger queries/scores → impossible d’améliorer.
Eviter les heuristiques “magiques” non mesurées ; tout changement doit passer par A/B tests.
Playbook de déploiement
- Définir gold set & métriques (relevance@k, MRR, nDCG, latence, coûts).
- Mettre en place BM25 FR/EN + index vecteur versionné.
- Activer la fusion RRF ; instrumenter les logs de scores.
- Ajouter re-ranking sur top-20 si gain démontré & SLA OK.
- Régler filtres/boosting (récence, section, qualité).
- Automatiser tests nocturnes et tableau de bord.
Commencer simple (BM25+vecteur+RRF) puis n’ajouter le re-ranking qu’après preuve de valeur.
Context Engineering · Retrieval & Ranking
