Recherche, reranking et citations
Recherche vectorielle, BM25 et fusion RRF ; pré-filtrage par permissions et ses effets sur HNSW ; rerankers ; construction du contexte ; restitution des citations page par page ; garde-fous contre les hallucinations.
L'essentiel
- La recherche combine deux signaux : la similarité vectorielle (sens) et BM25 (mots exacts : numéros de pièce, noms propres, références d'articles). En droit, le second est indispensable.
- La fusion se fait par RRF (k = 60, Cormack et al. 2009) : robuste, sans réglage, disponible nativement dans Qdrant, OpenSearch, Weaviate, LanceDB, à coder en SQL avec pgvector.
- Le filtre de permissions s'applique dans la requête (pré-filtrage). Avec HNSW, un post-filtrage renvoie trop peu de résultats ; pgvector documente le problème et son remède (
iterative_scan, 0.8.0) ; Qdrant intègre le filtre au graphe. - Un reranker (bge-reranker-v2-m3 ou Qwen3-Reranker-0.6B, Apache 2.0) reclasse 20 à 50 candidats et en garde 5 à 10 pour le LLM.
- Chaque réponse cite dossier, document, page, extrait et lien. Une citation n'est pas une preuve de fidélité (« up to 57% of citations being post-rationalized ») : d'où des garde-fous et une évaluation.
Recherche vectorielle
La question est vectorisée avec le même modèle que les chunks (embeddings), puis la base renvoie les k vecteurs les plus proches (cosinus ou produit scalaire). L'index HNSW rend la recherche approximative mais rapide ; ses paramètres (m, ef_construction, ef_search) arbitrent rappel contre latence et mémoire. Sur quelques centaines de milliers de chunks, une recherche exacte (scan complet) reste envisageable en secours pour vérifier le rappel de l'index (estimation).
Ce que la recherche vectorielle rate : un numéro de pièce, une référence « L. 1235-3 », un nom de partie mal orthographié, une date précise. Ce sont pourtant les requêtes les plus fréquentes d'un avocat.
BM25 et recherche hybride
BM25 indexe les termes et pondère par fréquence et rareté. Il retrouve les identifiants exacts et les termes rares. La recherche hybride lance les deux recherches, sur le même filtre de permissions, puis fusionne.
| Moteur | Hybride natif | Fusion | Source |
|---|---|---|---|
| Qdrant | vecteurs sparse + dense dans une même requête | RRF natif | 4 |
| pgvector + PostgreSQL | tsvector (ou ParadeDB, non vérifié) + pgvector | RRF à écrire en SQL (CTE) | 2 |
| Weaviate | oui, paramètre alpha (0 = BM25, 1 = vecteur) | Relative Score Fusion par défaut depuis 1.24 ; filtres where appliqués post-fusion | 5 |
| LanceDB | oui | RRFReranker() par défaut ; préfiltrage par défaut | 6 |
| OpenSearch | oui | normalisation arithmétique, harmonique, géométrique ou RRF | 7 |
| Open WebUI (intégré) | ENABLE_RAG_HYBRID_SEARCH (défaut False), RAG_HYBRID_BM25_WEIGHT 0,5 | pondération linéaire | 12 |
Le français exige un analyseur adapté pour BM25 : lemmatisation ou au minimum stemming (french dans PostgreSQL), suppression des mots vides, gestion des apostrophes et des accents. Sans cela, « contrats » et « contrat » sont deux termes différents.
Reciprocal Rank Fusion
Score d'un document = somme, sur chaque liste de résultats, de 1 / (k + rang). Introduit par Cormack, Clarke et Büttcher (SIGIR 2009) ; k = 60 est la valeur empirique de l'article et k entre 40 et 80 donne des résultats équivalents ; la méthode ignore les scores bruts, donc s'accommode d'un score BM25 non borné et d'un cosinus entre 0 et 1 1. C'est le défaut d'OpenSearch, Elasticsearch, Azure AI Search, Weaviate (avant 1.24) et LanceDB. Pour la Box : RRF avec k = 60, 50 candidats par liste, sans pondération au départ ; la pondération éventuelle sera décidée par l'évaluation.
Pré-filtrage et post-filtrage
Le pré-filtrage a un coût que chaque moteur gère différemment :
- pgvector : « With approximate indexes, filtering is applied after the index is scanned. If a condition matches 10% of rows, with HNSW and the default hnsw.ef_search of 40, only 4 rows will match on average » 2. Autrement dit, sans précaution, un avocat qui n'a accès qu'à 10 % des dossiers obtient 4 résultats au lieu de 40, parfois zéro. Remèdes documentés : augmenter
ef_search, créer des index partiels par dossier, et depuis la 0.8.0 activerhnsw.iterative_scan = relaxed_order(oustrict_order) qui « will automatically scan more of the index until enough results are found », borné parhnsw.max_scan_tuples(défaut 20 000) ethnsw.scan_mem_multiplier. Ce n'est pas une fuite mais une perte de rappel silencieuse, exactement ce qu'il faut mesurer. - Qdrant : le filtrage sur le payload est intégré à la traversée HNSW ; un index payload marqué
is_tenant: trueregroupe physiquement les vecteurs d'un même dossier et rend la recherche filtrée efficace 3. - Weaviate : en hybride, la doc indique d'appliquer les clauses
where« post-fusion », ce qui demande vérification avant tout usage ACL 5. - LanceDB : préfiltrage par défaut 6.
Azure justifie aussi le pré-filtrage par la performance : « Filtering inside the search pipeline is faster than loading larger result sets into your application and trimming there » 14.
Reranking
Le bi-encodeur (embeddings) compare la question et le chunk séparément ; le cross-encodeur (reranker) lit les deux ensemble et produit un score bien plus fin, au prix d'un passage par paire. On l'applique donc à une liste courte.
| Modèle | Licence | Taille | Langues | Remarques | Source |
|---|---|---|---|---|---|
| BAAI/bge-reranker-v2-m3 | Apache 2.0 | environ 0,6 Md | multilingue | base bge-m3 ; référence courante ; fiche bge-reranker-v2-m3 | 8 |
| Qwen3-Reranker 0.6B / 4B / 8B | Apache 2.0 | 0,6 à 8 Md | plus de 100 | contexte 32k, instruction-aware ; MTEB-R 65,80 pour le 0.6B (chiffres éditeur) ; fiche qwen3-reranker-8b | 9 |
| Jina-ColBERT-v2 (late interaction) | non trouvée (CC BY-NC pour les versions précédentes) | — | 89 | un vecteur par token, stockage lourd ; à vérifier avant tout usage | 10 |
| CrossEncoder Open WebUI | selon modèle | — | — | RAG_RERANKING_MODEL, vide par défaut ; RAG_TOP_K_RERANKER 3 | 12 |
Hypothèse de départ : bge-reranker-v2-m3 sur GPU (ou CPU si la mémoire GPU est réservée au LLM), 40 candidats en entrée, 8 en sortie, seuil de score minimal pour rejeter les passages hors sujet plutôt que de les donner au modèle. À comparer avec Qwen3-Reranker-0.6B sur le corpus fictif.
Top-k et fenêtre de contexte
Le nombre de passages transmis au LLM est un compromis : trop peu, la réponse rate une pièce ; trop, le modèle se disperse et la fenêtre de contexte se remplit, ce qui allonge le TTFT et consomme du KV cache partagé entre utilisateurs (multi-utilisateurs).
Ordre de grandeur (hypothèse) : 8 passages de 600 tokens = environ 5 000 tokens de contexte documentaire, plus la question, l'historique et les consignes : 6 000 à 8 000 tokens par requête. Avec un modèle à 128k de contexte, la limite n'est pas la fenêtre mais le temps de prompt processing sur la machine candidate, à mesurer dans les benchmarks. Open WebUI ne transmet que 3 passages par défaut (RAG_TOP_K 3) 12, ce qui est faible pour du juridique.
Le prompt de génération contient : la consigne de répondre uniquement à partir des passages, de citer leurs numéros, et de dire quand l'information n'y est pas ; puis les passages numérotés avec leur en-tête (dossier, pièce, page) ; puis la question. Rappel : ces consignes servent la qualité, pas la sécurité.
Citations : restituer page, passage et lien
Chaque passage envoyé au modèle porte un numéro et ses métadonnées. Le modèle référence les numéros ; l'interface les remplace par des cartes :
| Élément affiché | Métadonnée source | Origine |
|---|---|---|
| Dossier | dossier_id, nom | table des dossiers |
| Document | nom_fichier, type | ingestion |
| Page ou section | page_debut, section | Docling |
| Extrait | texte du chunk, surligné | base |
| Lien | chemin transformé en URL (visionneuse interne, lien SMB file://, lien SharePoint) ouvrant à la page, avec la zone bbox surlignée quand la visionneuse le permet | ingestion + interface |
RAGFlow annonce des « grounded citations » ancrées dans le document 13 ; c'est le niveau visé : cliquer sur une citation ouvre la pièce à la bonne page, passage surligné. Cela suppose que le lien respecte lui aussi les permissions : la visionneuse vérifie la portée avant de servir le fichier (une seconde fois, indépendamment de la recherche).
Deux compléments utiles : afficher les passages récupérés non utilisés dans la réponse (l'avocat voit ce que le système a lu) et un bouton « signaler une citation fausse » qui alimente le jeu d'évaluation.
Hallucinations et garde-fous
Une réponse est fondée « when it correctly answers a question using only the information in the documents, and the response can be inferred from the inline citations » ; or une étude mesure « up to 57% of citations being post-rationalized » : le modèle cite après avoir répondu, pas parce qu'il a lu 11. Les hallucinations en RAG prennent trois formes : réponse hors des passages, citation qui ne soutient pas la phrase, mélange de deux dossiers.
Garde-fous, du plus simple au plus coûteux :
- Seuil de pertinence au reranking : sous le seuil, la Box répond « rien dans les documents accessibles » au lieu d'inventer.
- Consigne de refus et exemples dans le prompt ; température basse.
- Vérification des citations : après génération, vérifier que chaque numéro cité existe dans la liste fournie et que la phrase citée a un recouvrement lexical minimal avec le passage (règle simple, peu coûteuse).
- Second passage de vérification par le LLM (« cette phrase est-elle soutenue par ce passage ? ») sur les réponses longues : coûteux, à réserver aux usages sensibles.
- Évaluation hors ligne de la fidélité (Ragas, DeepEval) sur le jeu de test (évaluation).
- Isolation par dossier dans le prompt : quand la portée couvre plusieurs dossiers, grouper les passages par dossier et le dire au modèle, pour limiter les mélanges.
Questions ouvertes
Ce qu'il reste à tester
Sources
- 1Reciprocal Rank Fusion — explications (ParadeDB, BigData Boutique)Article techniqueFiabilité moyenneParadeDB / BigData Boutique · publié lu le 16/09/2026 · consulté le 16 sept. 2026 · fiche source
- 2pgvector — README (filtrage, HNSW, iterative scan 0.8.0)GitHubFiabilité hautepgvector · publié v0.8.6 (README) ; 0.8.0 annoncé sur postgresql.org · consulté le 16 sept. 2026 · fiche source
- 3Qdrant — Multitenancy (payload partitioning, is_tenant)Documentation officielleFiabilité hauteQdrant · publié lu le 16/09/2026 · consulté le 16 sept. 2026 · fiche source
- 4Qdrant — README et releases (Apache 2.0, v1.19.1)GitHubFiabilité hauteQdrant · publié v1.19.1, 04/09/2026 · consulté le 16 sept. 2026 · fiche source
- 5Weaviate — multi-tenancy native et recherche hybrideDocumentation officielleFiabilité hauteWeaviate · publié lu le 16/09/2026 · consulté le 16 sept. 2026 · fiche source
- 6LanceDB — Hybrid search et préfiltrageDocumentation officielleFiabilité hauteLanceDB · publié lu le 16/09/2026 · consulté le 16 sept. 2026 · fiche source
- 7OpenSearch — recherche hybride et sécuritéDocumentation officielleFiabilité hauteOpenSearch · publié lu le 16/09/2026 · consulté le 16 sept. 2026 · fiche source
- 8BAAI/bge-reranker-v2-m3 — model cardDocumentation officielleFiabilité hauteBAAI · publié 02/2024 · consulté le 16 sept. 2026 · fiche source
- 9Qwen/Qwen3-Reranker — model card (0.6B / 4B / 8B)Documentation officielleFiabilité hauteAlibaba Qwen · publié 05/06/2025 · consulté le 16 sept. 2026 · fiche source
- 10Jina-ColBERT-v2 — late interaction multilingueArticle techniqueFiabilité moyenneJina AI · publié 2024 (arXiv 2408.16672) · consulté le 16 sept. 2026 · fiche source
- 11Citations et fidélité en RAG (arXiv 2412.18004, 2409.11242)ÉtudeFiabilité moyennearXiv · publié 2024 · consulté le 16 sept. 2026 · fiche source
- 12Open WebUI — variables d'environnement (docs + config.py)Documentation officielleFiabilité hauteOpen WebUI · publié lu le 16/09/2026 (main) · consulté le 16 sept. 2026 · fiche source
- 13RAGFlow — README (Apache 2.0, DeepDoc, prérequis)GitHubFiabilité hauteInfiniFlow · publié v0.27.2, lu le 16/09/2026 · consulté le 16 sept. 2026 · fiche source
- 14Azure AI Search — Document-Level Access ControlDocumentation officielleFiabilité hauteMicrosoft · publié 08/08/2026 (mise à jour 31/08/2026) · consulté le 16 sept. 2026 · fiche source