Bases de données vectorielles (Pinecone, Qdrant, Weaviate)
Mis à jour le 29 juillet 2026
Objectifs
- Comprendre pourquoi les bases vectorielles sont nécessaires
- Comparer Pinecone, Qdrant, Weaviate, Chroma et pgvector
- Implémenter une recherche avec chacune d’entre elles
Pourquoi une base vectorielle ?
L’index NumPy de la leçon précédente fonctionne parfaitement pour quelques milliers de documents, parce qu’un balayage exhaustif de dix mille vecteurs reste imperceptible. À un million, le même calcul devient une seconde de latence par requête, et la matrice ne tient plus confortablement en mémoire.
Quatre besoins apparaissent alors ensemble. La recherche ANN (Approximate Nearest Neighbors) abandonne la garantie du résultat exact au profit d’une réponse en millisecondes sur des millions de vecteurs — un compromis presque toujours acceptable, puisque le cinquième meilleur document au lieu du quatrième ne change rien à l’expérience. Le filtrage par métadonnées combiné à la recherche vectorielle permet de restreindre la requête à une catégorie, une langue ou une plage de dates sans post-traitement. La persistance et la mise à jour en temps réel évitent de reconstruire un fichier entier pour ajouter un document. Le scaling horizontal, enfin, est ce qui distingue un prototype d’un service de production.
Pinecone
Pinecone est un service managé : vous ne provisionnez aucun serveur, vous créez un index en indiquant sa dimension et sa métrique, et le reste est pris en charge. Remarquez le filtre categorie passé directement à query — la recherche vectorielle et le filtre s’exécutent ensemble, pas l’un après l’autre.
from pinecone import Pinecone
from openai import OpenAI
pc = Pinecone(api_key="votre-cle-pinecone")
openai_client = OpenAI()
# Créer un index
pc.create_index(
name="mon-corpus",
dimension=3072,
metric="cosine",
spec={"serverless": {"cloud": "aws", "region": "eu-west-1"}}
)
index = pc.Index("mon-corpus")
# Indexer des documents
documents = [
{"id": "doc1", "texte": "Guide d'installation Python",
"categorie": "tutoriel"},
{"id": "doc2", "texte": "Déployer une application Flask",
"categorie": "tutoriel"},
{"id": "doc3", "texte": "Rapport trimestriel Q1 2026",
"categorie": "rapport"},
]
# Générer les embeddings
textes = [d["texte"] for d in documents]
response = openai_client.embeddings.create(
input=textes,
model="text-embedding-3-large"
)
# Upserter dans Pinecone
vecteurs = [
{
"id": doc["id"],
"values": emb.embedding,
"metadata": {"texte": doc["texte"], "categorie": doc["categorie"]}
}
for doc, emb in zip(documents, response.data)
]
index.upsert(vectors=vecteurs)
# Rechercher
query_emb = openai_client.embeddings.create(
input="comment installer Python",
model="text-embedding-3-large"
).data[0].embedding
resultats = index.query(
vector=query_emb,
top_k=3,
include_metadata=True,
filter={"categorie": {"$eq": "tutoriel"}}
)
for match in resultats.matches:
print(f"[{match.score:.3f}] {match.metadata['texte']}")
La dimension déclarée à la création est définitive : passer de text-embedding-3-large à text-embedding-3-small impose de recréer l’index. Décidez donc du modèle avant, pas après.
Qdrant
Qdrant est open source, s’auto-héberge et offre d’excellentes performances. Le vocabulaire diffère — on parle de collection, de points et de payload là où Pinecone parlait d’index, de vecteurs et de métadonnées — mais le schéma reste identique. Notez que l’exemple appelle l’API d’embedding document par document dans la boucle : c’est acceptable sur trois documents, mais pensez à repasser en lots dès que le corpus grandit.
from qdrant_client import QdrantClient
from qdrant_client.models import (
Distance, VectorParams, PointStruct, Filter,
FieldCondition, MatchValue
)
from openai import OpenAI
qclient = QdrantClient(url="http://localhost:6333")
openai_client = OpenAI()
# Créer une collection
qclient.create_collection(
collection_name="mon-corpus",
vectors_config=VectorParams(
size=3072,
distance=Distance.COSINE
)
)
# Indexer des documents
points = []
for i, doc in enumerate(documents):
emb = openai_client.embeddings.create(
input=doc["texte"],
model="text-embedding-3-large"
).data[0].embedding
points.append(PointStruct(
id=i,
vector=emb,
payload={"texte": doc["texte"], "categorie": doc["categorie"]}
))
qclient.upsert(collection_name="mon-corpus", points=points)
# Rechercher avec filtre
query_emb = openai_client.embeddings.create(
input="déployer une application web",
model="text-embedding-3-large"
).data[0].embedding
resultats = qclient.search(
collection_name="mon-corpus",
query_vector=query_emb,
limit=3,
query_filter=Filter(
must=[FieldCondition(
key="categorie",
match=MatchValue(value="tutoriel")
)]
)
)
for r in resultats:
print(f"[{r.score:.3f}] {r.payload['texte']}")
Weaviate
Weaviate se distingue par sa recherche hybride native, sur laquelle nous reviendrons en leçon 9. Son modèle de données est plus structuré que celui de ses concurrents : on déclare explicitement les propriétés et leurs types avant d’insérer quoi que ce soit, ce qui rapproche l’expérience d’une base classique.
import weaviate
from openai import OpenAI
wclient = weaviate.connect_to_local()
openai_client = OpenAI()
# Créer une collection
from weaviate.classes.config import Property, DataType
collection = wclient.collections.create(
name="Document",
properties=[
Property(name="texte", data_type=DataType.TEXT),
Property(name="categorie", data_type=DataType.TEXT),
]
)
# Indexer
for doc in documents:
emb = openai_client.embeddings.create(
input=doc["texte"],
model="text-embedding-3-large"
).data[0].embedding
collection.data.insert(
properties={"texte": doc["texte"], "categorie": doc["categorie"]},
vector=emb
)
# Rechercher
query_emb = openai_client.embeddings.create(
input="installer Python",
model="text-embedding-3-large"
).data[0].embedding
resultats = collection.query.near_vector(
near_vector=query_emb,
limit=3
)
Chroma et pgvector
Chroma vise le prototypage : un client persistant pointé sur un dossier local, une collection créée à la volée, aucune infrastructure. C’est l’option qui vous fait passer de l’index NumPy à une vraie base sans changer d’environnement de travail.
import chromadb
from openai import OpenAI
chroma = chromadb.PersistentClient(path="./chroma_db")
openai_client = OpenAI()
collection = chroma.get_or_create_collection("mon-corpus")
# Indexer
for doc in documents:
emb = openai_client.embeddings.create(
input=doc["texte"],
model="text-embedding-3-large"
).data[0].embedding
collection.add(
ids=[doc["id"]],
embeddings=[emb],
documents=[doc["texte"]],
metadatas=[{"categorie": doc["categorie"]}]
)
# Rechercher
query_emb = openai_client.embeddings.create(
input="comment installer Python",
model="text-embedding-3-large"
).data[0].embedding
resultats = collection.query(query_embeddings=[query_emb], n_results=3)
pgvector répond à une situation très différente : vos documents sont déjà dans PostgreSQL, avec leurs jointures, leurs droits d’accès et leurs sauvegardes. Plutôt que d’introduire un second système à synchroniser, l’extension ajoute un type vector et des opérateurs de distance à la base existante. L’opérateur <=> calcule la distance cosinus, et l’index HNSW est ce qui rend la requête utilisable au-delà de quelques milliers de lignes.
-- Extension pgvector
CREATE EXTENSION vector;
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
texte TEXT,
categorie TEXT,
embedding vector(3072)
);
-- Index HNSW pour la performance
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops);
-- Recherche des 5 plus proches
SELECT id, texte, 1 - (embedding <=> $1::vector) AS score
FROM documents
ORDER BY embedding <=> $1::vector
LIMIT 5;
Comparatif
| Critère | Pinecone | Qdrant | Weaviate | Chroma | pgvector |
|---|---|---|---|---|---|
| Type | Managé | Open source | Open source | Open source | Extension |
| Hébergement | Cloud | Self-hosted/Cloud | Self-hosted/Cloud | Local/Embed | PostgreSQL |
| Recherche hybride | ✅ | ✅ | ✅ Native | ❌ | Via SQL |
| Production | ✅ | ✅ | ✅ | ⚠️ Prototypage | ✅ |
| Facilité | ✅✅ | ✅ | ✅ | ✅✅ | ✅ |
Le choix se décide moins sur les performances brutes, très proches en pratique, que sur votre contexte : l’équipe qui exploite déjà PostgreSQL a tout intérêt à rester dessus, celle qui n’a pas d’ops choisira le managé, et celle qui doit héberger en Europe pour des raisons réglementaires se tournera vers l’auto-hébergé.
Résumé
- Chroma pour le prototypage rapide
- pgvector si vous avez déjà PostgreSQL
- Pinecone pour du managé sans infrastructure
- Qdrant pour l’auto-hébergement performant
- Weaviate pour la recherche hybride native