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/)