Bases de données vectorielles (Pinecone, Qdrant, Weaviate)
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 pour quelques milliers de documents. Au-delà, vous avez besoin de :
- Recherche ANN (Approximate Nearest Neighbors) pour des réponses en millisecondes sur des millions de vecteurs
- Filtrage par métadonnées combiné à la recherche vectorielle
- Persistance et mise à jour en temps réel
- Scaling horizontal pour la production
Pinecone
Service managé, aucune infrastructure à gérer :
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']}")
Qdrant
Open source, auto-hébergeable, excellentes performances :
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
Base vectorielle avec recherche hybride native :
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 — idéal pour le prototypage
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 — si vous avez déjà PostgreSQL
-- 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é | ✅✅ | ✅ | ✅ | ✅✅ | ✅ |
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