ADR-005 : Fusion RRF v2 (pondération question×2 + blending position-aware)
ADR-005 : Fusion RRF v2 (pondération question×2 + blending position-aware)
Date : 2026-Q1 — Statut : accepté
Contexte
Le retrieval combine plusieurs sources :
- Vecteur dense de la question brute (TEI bge-m3)
- Vecteur dense du passage HyDE (Haiku-généré)
- Sparse BM25 sur la question brute
- (Selon intent) chunks META, chunks synergy
Toutes les sources retournent un top-K avec rank et score. Il faut les fusionner en une seule liste classée.
Premier essai (RRF v1, classique) : score = Σ 1/(60 + rank) pour chaque chunk présent dans une passe. Inspiré de la littérature standard.
Problèmes constatés :
- HyDE dilue la précision : les exact-matches de la question brute sont noyés par les paraphrases HyDE. Si rank 1 dans question + rank 50 dans HyDE → score combiné moyen, pas top-1.
- Reranker peu confiant sur du contenu technique abstrait (notes de mécaniques, conditions de victoire) → score reranker ~0 → enterre des bons chunks.
Décision
Fusion RRF v2 avec deux modifications :
1. Pondération question × 2
score = 2 × (1 / (60 + rank_question + 1))
+ 1 × (1 / (60 + rank_hyde + 1))
+ 1 × (1 / (60 + rank_bm25 + 1))
La question brute pèse double — sa précision compte plus que celle du HyDE qui est une paraphrase.
2. Top-rank bonus
if (rank === 0) score += 0.05;
else if (rank === 1 || rank === 2) score += 0.02;
Préserve les exact-matches contre la dilution.
3. Blending position-aware (post-rerank)
Au lieu d'écraser le score RRF avec le score reranker :
const rrf_position = candidate.rrf_rank;
let weight_rrf, weight_rerank;
if (rrf_position <= 2) { weight_rrf = 0.75; weight_rerank = 0.25; }
else if (rrf_position <= 9) { weight_rrf = 0.60; weight_rerank = 0.40; }
else { weight_rrf = 0.40; weight_rerank = 0.60; }
final_score = weight_rrf * rrf_score + weight_rerank * rerank_score;
Si la fusion RRF a déjà classé un chunk dans le top, on garde son signal. Si le reranker est confiant et qu'il détrône un mauvais top, son signal compte plus.
Activable / désactivable via RAG_FUSION_V2_ENABLED (défaut true).
Conséquences
Bonnes
- Les exact-matches de la question survivent au passage HyDE
- Le reranker peu confiant sur du contenu abstrait n'enterre plus systématiquement les bons chunks
- Kill-switch via env var pour A/B test si nécessaire
- Inspiré de tobi/qmd, pattern documenté
Mauvaises
- Magic numbers : 0.05, 0.02, 75/25, 60/40, 40/60. Les ratios ont été tunés empiriquement, pas théoriquement.
- Plus de paramètres à comprendre pour un dev qui débarque
- Toute modification doit être validée sur le banc d'éval RAG (sinon régression silencieuse)
Tuning
Si tu veux ajuster :
- Ratios blending : monter à 85/15 sur top-3 si tu veux encore plus protéger le signal RRF
- Top-rank bonus : monter à +0.10 sur rank=0 si exact-matches cruciaux
Toujours npm run test:rag avant et après pour comparer la qualité.
Source d'inspiration
- RRF original paper (Cormack 2009)
- tobi/qmd — implémentation référence pour les ratios pondérés
- Banc d'éval RAG interne (boardgame-referee
tests/rag/)
No comments to display
No comments to display