Une recherche sémantique compare le sens d’une requête à celui des documents. Le modèle d’embeddings transforme chaque texte en vecteur ; Qdrant stocke ces vecteurs et retrouve les plus proches.
Démarrer Qdrant
~~~bash docker run --name qdrant -p 6333:6333 -p 6334:6334 \ -v "$(pwd)/qdrant_storage:/qdrant/storage:z" qdrant/qdrant ~~~
Le tableau de bord local est disponible sur "http://localhost:6333/dashboard". Cette configuration n’active pas l’authentification : ne l’exposez pas sur Internet.
Installez le client avec FastEmbed :
~~~bash python -m venv .venv source .venv/bin/activate pip install -U 'qdrant-client[fastembed]' ~~~
Indexer les documents
~~~python from qdrant_client import QdrantClient, models
COLLECTION = "articles" MODEL = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
client = QdrantClient(url="http://localhost:6333") client.set_model(MODEL)
documents = [ "FastAPI permet de créer des API Python asynchrones.", "Qdrant stocke et interroge des vecteurs.", "PostgreSQL est une base de données relationnelle.", ]
client.add( collection_name=COLLECTION, documents=documents, metadata=[ {"topic": "python", "lang": "fr"}, {"topic": "vectordb", "lang": "fr"}, {"topic": "database", "lang": "fr"}, ], ids=[1, 2, 3], ) ~~~
Le mode FastEmbed simplifie le tutoriel. Pour un pipeline contrôlé, calculez vous-même les embeddings, créez la collection avec la dimension exacte du modèle, puis envoyez des "PointStruct". Le modèle utilisé à la requête doit être identique à celui de l’indexation.
Interroger l’index
~~~python results = client.query( collection_name=COLLECTION, query_text="Comment retrouver des textes proches par leur sens ?", limit=3, )
for result in results: print(round(result.score, 3), result.document, result.metadata) ~~~
Selon la version du client, l’API bas niveau moderne est "query_points". Elle accepte un vecteur de requête et retourne les points triés par similarité.
Filtrer par métadonnées
~~~python from fastembed import TextEmbedding from qdrant_client.models import FieldCondition, Filter, MatchValue
embedding_model = TextEmbedding(MODEL) query_vector = next( embedding_model.query_embed("base vectorielle pour recherche sémantique") ).tolist()
points = client.query_points( collection_name=COLLECTION, query=query_vector, query_filter=Filter( must=[FieldCondition(key="lang", match=MatchValue(value="fr"))] ), with_payload=True, limit=5, ).points ~~~
Le vecteur de requête utilise le même encodeur que l’index. Créez des index de payload pour les champs souvent filtrés ; sinon le filtrage peut devenir coûteux à grande échelle.
Évaluer la recherche
Préparez des requêtes avec leurs documents attendus et mesurez recall@k ou MRR. Analysez séparément les requêtes sans réponse, les textes trop courts et les doublons. Le score Qdrant n’est pas une probabilité universelle : ne fixez pas un seuil sans données d’évaluation.
Production
Versionnez le modèle et la collection, utilisez des identifiants stables, conservez le texte source dans le payload ou un stockage associé, et réindexez vers une nouvelle collection lors d’un changement d’embedding. Activez TLS et clé API, sauvegardez les données et surveillez mémoire, latence, taille des segments et profondeur de réplication.
FAQ
Faut-il normaliser les vecteurs ?
Cela dépend du modèle et de la distance. Suivez la recommandation du modèle d’embeddings et conservez le même traitement à l’indexation et à la requête.
Pourquoi les résultats sont-ils hors sujet ?
Vérifiez d’abord le modèle, la langue, le découpage et l’absence de mélange entre deux versions d’embeddings. Un reranker peut ensuite améliorer l’ordre des premiers candidats.