Aller au contenu
Développement medium

Qdrant : la base vectorielle serveur pour le RAG

10 min de lecture

logo python

FAISS et Chroma tournent dans votre processus Python : parfait pour prototyper, insuffisant dès qu'un RAG doit être partagé entre applications, monter en charge ou filtrer finement. Qdrant est la réponse : une base vectorielle serveur, écrite en Rust, qui s'exécute comme un service auquel le code se connecte en réseau. Ce guide montre comment lancer Qdrant, créer une collection, y indexer des points avec leur payload, faire une recherche sémantique filtrée, et préparer le passage en production. C'est la base vectorielle de référence du parcours RAG. Public visé : développeur dont le RAG dépasse le stade du prototype.

  • Pourquoi une base vectorielle serveur plutôt qu'embarquée.
  • Lancer Qdrant avec Docker.
  • Créer une collection typée et y indexer des points.
  • Attacher un payload à chaque vecteur.
  • Rechercher et filtrer par payload — la base du multi-tenant.
  • Python 3.10+ et Docker.
  • Une instance Ollama avec le modèle d'embedding nomic-embed-text.
  • Comprendre les embeddings et avoir vu Chroma.

FAISS et Chroma ont un point commun : elles vivent dans le processus Python qui les utilise. C'est leur force pour prototyper — rien à déployer — et leur limite dès qu'on industrialise.

Un RAG en production a d'autres besoins. Plusieurs applications — une API, un chatbot, un travail d'indexation — doivent interroger le même index en même temps. L'index doit survivre au redémarrage de n'importe lequel de ces clients. Et il doit tenir la charge de requêtes concurrentes. Une base embarquée ne fait rien de tout cela.

Qdrant est une base vectorielle serveur. Elle s'exécute comme un service indépendant ; les applications s'y connectent par le réseau. L'index est centralisé, partagé, persistant. Et Qdrant ajoute un filtrage par payload de premier ordre — la capacité qui rend possible le multi-tenant. C'est pourquoi le parcours RAG l'utilise comme base de référence pour tout ce qui dépasse le prototype.

Qdrant se lance en une commande Docker. L'image officielle expose son API sur le port 6333.

Fenêtre de terminal
docker run -d --name qdrant -p 6333:6333 qdrant/qdrant

On vérifie qu'il répond :

Fenêtre de terminal
curl http://localhost:6333/
# Doit renvoyer un JSON avec "title": "qdrant - vector search engine".

Côté Python, on installe le client officiel :

Fenêtre de terminal
pip install qdrant-client==1.18.0

Une collection est l'unité de stockage de Qdrant — l'équivalent d'une table. Elle est typée à sa création par deux paramètres : la dimension des vecteurs et la distance de comparaison.

from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams
DIMENSION = 768 # taille des vecteurs de nomic-embed-text
COLLECTION = "documentation"
client = QdrantClient(host="localhost", port=6333)
if client.collection_exists(COLLECTION):
client.delete_collection(COLLECTION)
client.create_collection(
COLLECTION,
vectors_config=VectorParams(size=DIMENSION, distance=Distance.COSINE),
)

Les deux paramètres de VectorParams sont structurants. La size doit correspondre exactement à la dimension du modèle d'embedding — 768 pour nomic-embed-text ; un vecteur d'une autre taille sera rejeté. La distance fixe la mesure de proximité — COSINE est le choix standard pour des vecteurs de texte, le même que la similarité cosinus du guide sur les embeddings.

Dans Qdrant, l'unité indexée est le point. Un point réunit trois choses : un identifiant, un vecteur, et un payload.

from qdrant_client.models import PointStruct
CORPUS = [
{"texte": "Un volume Docker conserve les données du conteneur.",
"sujet": "docker", "annee": 2026},
{"texte": "Terraform décrit une infrastructure en code déclaratif.",
"sujet": "terraform", "annee": 2024},
]
vecteurs = vectoriser([d["texte"] for d in CORPUS])
points = [
PointStruct(id=i, vector=vecteurs[i], payload=CORPUS[i])
for i in range(len(CORPUS))
]
client.upsert(COLLECTION, points=points)

Le payload est l'atout de Qdrant. C'est un dictionnaire libre attaché au point : le texte d'origine, mais aussi toute métadonnée utile — un sujet, une année, une équipe propriétaire. Ce payload voyage avec le vecteur, et surtout il devient filtrable. La méthode upsert insère les points — ou les met à jour s'ils existent déjà, d'où son nom.

La recherche se fait avec query_points : on fournit le vecteur de la question et le nombre limit de résultats voulus.

def rechercher(client, question, k=3, filtre=None):
"""Recherche les k documents les plus proches, avec filtre optionnel."""
resultat = client.query_points(
COLLECTION,
query=vectoriser([question])[0],
limit=k,
query_filter=filtre,
with_payload=True,
)
return [
{"texte": p.payload["texte"], "sujet": p.payload["sujet"],
"score": p.score}
for p in resultat.points
]

query_points renvoie les points classés par similarité décroissante, chacun avec son score et son payload. L'option with_payload=True est ce qui ramène le texte et les métadonnées : sans elle, on n'obtiendrait que des identifiants et des scores, sans contenu.

C'est la capacité qui distingue vraiment Qdrant. Le filtre restreint la recherche aux points dont le payload satisfait une condition — la recherche sémantique ne s'applique qu'à ce sous-ensemble.

from qdrant_client.models import FieldCondition, Filter, MatchValue
def filtre_sujet_annee(sujet, annee):
"""Construit un filtre Qdrant : payload sujet ET année."""
return Filter(
must=[
FieldCondition(key="sujet", match=MatchValue(value=sujet)),
FieldCondition(key="annee", match=MatchValue(value=annee)),
]
)
# Chercher uniquement dans les documents Docker de 2026.
resultats = rechercher(client, "conteneur", k=5,
filtre=filtre_sujet_annee("docker", 2026))

Un Filter se compose de FieldCondition — une condition sur une clé du payload. La clause must exige que toutes les conditions soient vraies ; il existe aussi should (au moins une) et must_not (aucune). Le filtre s'applique avant la recherche sémantique : un point hors filtre n'est jamais examiné, donc jamais renvoyé.

C'est le mécanisme du multi-tenant : plusieurs équipes ou clients partagent une seule collection, mais chaque recherche est cloisonnée à un périmètre par son filtre. Le guide sur le RAG en production en fait la pierre angulaire de la sécurité.

Qdrant tient sa place du prototype à la production, mais l'usage réel demande quelques précautions.

La persistance d'abord : un volume monté, comme rappelé plus haut, pour que l'index survive aux conteneurs. Le déploiement ensuite : un docker compose pour un service simple, un déploiement Kubernetes pour de la haute disponibilité — Qdrant fournit les manifestes. La sécurité enfin : l'API se protège par une clé, et le trafic se chiffre en TLS — on n'expose jamais un Qdrant nu sur un réseau ouvert.

Étape du projetBase vectorielle
Prototype jetableFAISS
Développement, métadonnéesChroma
Production, service partagé, filtrageQdrant

Le passage de l'une à l'autre ne remet jamais en cause le principe — vectoriser, indexer, rechercher. Seule la robustesse du stockage évolue. Apprendre Qdrant, c'est se donner la base qui tiendra du premier index au service en production.

SymptômeCause probableSolution
Connection refused sur le port 6333Qdrant non démarréLancer le conteneur qdrant/qdrant
Erreur de dimension à l'upsertsize de la collection ≠ modèle d'embeddingRecréer la collection à la bonne dimension
Résultats sans textePayload non demandéPasser with_payload=True à query_points
Le filtre ne restreint rienClé de payload ou valeur incorrecteVérifier les noms et types dans FieldCondition
Index perdu au redémarrageDonnées dans le conteneurMonter un volume sur /qdrant/storage
  • Une base vectorielle serveur se partage entre applications, persiste et tient la charge — pas une base embarquée.
  • Qdrant se lance en un conteneur Docker et s'interroge via son client Python.
  • Une collection est typée par la dimension des vecteurs et la distance.
  • Un point réunit un identifiant, un vecteur et un payload de métadonnées libres.
  • Le filtre restreint la recherche par payload avant la similarité — la base du multi-tenant.
  • Qdrant accompagne le projet du développement à la production, sans changer le principe.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn