Guide
Recherche sémantique sans RAG : un moteur interne
Faire retrouver vos documents ou vos pages par le sens de la question, sans modèle qui rédige : architecture, code minimal, pièges et mesure.
Mis à jour le
Un moteur de recherche sémantique retrouve des documents par le sens de la question, même quand ils n'emploient pas les mêmes mots. Il suffit d'un modèle de plongements et d'un index de vecteurs : pas de modèle de langage, pas de réponse rédigée, seulement une liste de résultats, comme un moteur classique. C'est souvent la bonne première étape avant un RAG (génération augmentée par récupération, où un modèle rédige la réponse à partir des documents trouvés), et parfois tout ce dont on a besoin.
Recherche sémantique ou RAG ?
| Recherche sémantique | RAG | |
|---|---|---|
| Ce que voit l'utilisateur | une liste de documents ou de passages | une réponse rédigée, avec ses sources |
| Modèle de langage | aucun | oui, à chaque question |
| Risque d'invention | aucun dans la liste elle-même | possible, à surveiller |
| Coût par question | le plongement de la question | plongement + génération |
| Bon choix pour | intranet, base documentaire, catalogue, aide en ligne | assistant qui répond en langage courant |
Si vos utilisateurs veulent « le bon document » plutôt qu'« une réponse », la recherche sémantique suffit. Pour la version avec réponse rédigée, voir Mettre en place un RAG. Pour une explication non technique de la différence entre mots-clés et sens, voir Mots-clés ou question.
Recherche symétrique ou asymétrique
La documentation de Sentence Transformers distingue deux cas, et insiste : il est essentiel de choisir le modèle adapté à son type de tâche.
- Symétrique : la question et les entrées ont la même forme. Exemple : retrouver une question déjà posée dans une foire aux questions.
- Asymétrique : une question courte, des passages longs. Exemple : chercher « délai de préavis » dans des contrats. C'est le cas le plus fréquent dans un moteur interne.
En asymétrique, la question et les documents ne s'encodent pas de la même façon : encode_query et encode_document dans Sentence Transformers, préfixes query: et passage: pour multilingual-e5.
Le code minimal
Pour un petit corpus, tout tient en mémoire (méthodes documentées par Sentence Transformers) :
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer("intfloat/multilingual-e5-large")
corpus = ["passage: Procédure de remboursement des notes de frais.",
"passage: Règlement intérieur : horaires et congés."]
corpus_emb = model.encode(corpus, normalize_embeddings=True)
q = model.encode(["query: comment me faire rembourser un repas ?"], normalize_embeddings=True)
hits = util.semantic_search(q, corpus_emb, top_k=5, score_function=util.dot_score)
print(hits[0]) # [{'corpus_id': ..., 'score': ...}, ...]
Les vecteurs étant normalisés, le produit scalaire (dot_score) remplace le cosinus, comme le conseille la documentation pour gagner du temps. Les textes de l'exemple sont illustratifs.
Passer à l'échelle
La même documentation situe la recherche exacte faite à la main jusqu'à environ un million d'entrées. Au-delà, ou si vous voulez une base persistante partagée par plusieurs applications :
- une base avec index approximatif : pgvector dans PostgreSQL, ou une base dédiée (voir le comparatif) ;
- une bibliothèque de recherche approximative citée par Sentence Transformers : Annoy, FAISS, hnswlib ;
- un moteur de recherche existant : la documentation rappelle qu'Elasticsearch indexe des vecteurs denses avec un index HNSW, et qu'OpenSearch combine vecteurs et recherche par mots-clés BM25.
Les cinq pièges d'un moteur interne
- Les références exactes. Un numéro de commande ou un code article se retrouve mal par le sens. Combinez avec une recherche par mots-clés : voir Recherche hybride. BGE-M3 fournit d'ailleurs, d'après sa fiche, une sortie lexicale en plus du vecteur dense.
- Les documents trop longs. Un document de 40 pages résumé en un seul vecteur perd ses détails. Indexez des passages, et affichez le document parent dans les résultats.
- Les droits. Le moteur ne doit pas montrer un document à qui n'a pas le droit de le lire. Filtrez pendant la recherche (avec pgvector, une clause
WHERE). - L'absence de « rien trouvé ». Une recherche par vecteurs renvoie toujours les plus proches, même s'ils sont sans rapport. Fixez un seuil de score après essais, ou affichez le score.
- Le classement fin. Pour des recherches complexes, la documentation conseille deux étapes : une recherche large, puis un retri des meilleurs candidats par un modèle plus précis, dit de retri (reranking).
Mesurer avant d'ouvrir
Préparez une liste de recherches réelles (tirées des journaux de votre moteur actuel si vous en avez un) avec le document attendu pour chacune. Comparez l'ancien moteur et le nouveau : le document attendu apparaît-il dans les cinq premiers résultats ? C'est la même logique que la note de recherche du classement MTEB, appliquée à vos propres données.
Questions fréquentes
Faut-il un modèle de langage (LLM) pour faire de la recherche sémantique ?
Non. Un modèle de plongements suffit : il transforme documents et questions en vecteurs, et la recherche renvoie les documents les plus proches. Aucun texte n'est rédigé, ce qui évite les erreurs de rédaction et réduit le coût.
Jusqu'à quel volume peut-on chercher sans index ?
La documentation de Sentence Transformers indique qu'une recherche exacte faite à la main convient jusqu'à environ un million d'entrées ; au-delà, elle oriente vers des index approximatifs.
Sources
Pages consultées pour écrire cet article :