Vektordatenbanken (Pinecone, Qdrant, Weaviate)
Auf dieser Seite
Lernziele
- Verstehen, warum Vektordatenbanken notwendig sind
- Vergleich von Pinecone, Qdrant, Weaviate, Chroma und pgvector
- Implementierung der Suche mit jedem dieser Systeme
Warum eine Vektordatenbank?
Der NumPy-Index aus der vorherigen Lektion funktioniert perfekt für einige tausend Dokumente, da eine vollständige Abfrage von zehntausend Vektoren für den Nutzer kaum wahrnehmbar bleibt. Bei einer Million Einträgen wird dieselbe Berechnung jedoch zu einer Latenz von einer Sekunde pro Abfrage, und die Matrix passt nicht mehr komfortabel in den Arbeitsspeicher.
Daraus ergeben sich vier zusammenhängende Anforderungen. ANN-Suche (Approximate Nearest Neighbors) verzichtet auf die Garantie eines exakten Ergebnisses zugunsten einer Antwort im Millisekundenbereich bei Millionen von Vektoren – ein Kompromiss, der fast immer akzeptabel ist, da die Rückgabe des fünftbesten Dokuments anstelle des vierten für die Benutzererfahrung keinen Unterschied macht. Metadatenfilterung in Kombination mit der Vektorsuche ermöglicht es, eine Abfrage auf eine Kategorie, eine Sprache oder einen Datumsbereich zu beschränken, ohne nachträgliche Verarbeitungsschritte. Persistenz und Echtzeit-Updates vermeiden den Wiederaufbau einer gesamten Datei, um lediglich ein Dokument hinzuzufügen. Schließlich ist das horizontale Skalieren das, was einen Prototypen von einem Produktionsservice unterscheidet.
Pinecone
Pinecone ist ein verwalteter Dienst: Sie provisionieren keine Server, erstellen einen Index, indem Sie dessen Dimension und Metrik angeben, und der Rest wird für Sie übernommen. Beachten Sie den categorie-Filter, der direkt an query übergeben wird – die Vektorsuche und die Filterung werden gemeinsam ausgeführt, nicht nacheinander.
from pinecone import Pinecone
from openai import OpenAI
pc = Pinecone(api_key="your-pinecone-key")
openai_client = OpenAI()
# Index erstellen
pc.create_index(
name="my-corpus",
dimension=3072,
metric="cosine",
spec={"serverless": {"cloud": "aws", "region": "eu-west-1"}}
)
index = pc.Index("my-corpus")
# Dokumente indizieren
documents = [
{"id": "doc1", "texte": "Python Installation Guide",
"categorie": "tutorial"},
{"id": "doc2", "texte": "Deploying a Flask Application",
"categorie": "tutorial"},
{"id": "doc3", "texte": "Q1 2026 Quarterly Report",
"categorie": "report"},
]
# Embeddings generieren
textes = [d["texte"] for d in documents]
response = openai_client.embeddings.create(
input=textes,
model="text-embedding-3-large"
)
# In Pinecone hochladen
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)
# Suche durchführen
query_emb = openai_client.embeddings.create(
input="how to install Python",
model="text-embedding-3-large"
).data[0].embedding
resultats = index.query(
vector=query_emb,
top_k=3,
include_metadata=True,
filter={"categorie": {"$eq": "tutorial"}}
)
for match in resultats.matches:
print(f"[{match.score:.3f}] {match.metadata['texte']}")
Die bei der Erstellung deklarierte Dimension ist endgültig: Ein Wechsel von text-embedding-3-large zu text-embedding-3-small erfordert die Neuerstellung des Indexes. Entscheiden Sie sich daher für das Modell vorab, nicht nachträglich.
Qdrant
Qdrant ist quelloffen, kann selbst gehostet werden und bietet eine hervorragende Leistung. Die Vokabel unterscheidet sich – wir sprechen von Collections, Points und Payloads, während Pinecone von Indexes, Vektoren und Metadaten spricht – das Schema bleibt jedoch identisch. Beachten Sie, dass das Beispiel die Embedding-API im Loop dokumentenweise aufruft: Dies ist für drei Dokumente akzeptabel, aber überlegen Sie, sobald der Korpus wächst, wieder auf Batches umzusteigen.
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()
# Collection erstellen
qclient.create_collection(
collection_name="my-corpus",
vectors_config=VectorParams(
size=3072,
distance=Distance.COSINE
)
)
# Dokumente indizieren
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="my-corpus", points=points)
# Suche mit Filter
query_emb = openai_client.embeddings.create(
input="deploy a web application",
model="text-embedding-3-large"
).data[0].embedding
resultats = qclient.search(
collection_name="my-corpus",
query_vector=query_emb,
limit=3,
query_filter=Filter(
must=[FieldCondition(
key="categorie",
match=MatchValue(value="tutorial")
)]
)
)
for r in resultats:
print(f"[{r.score:.3f}] {r.payload['texte']}")
Weaviate
Weaviate zeichnet sich durch seine native hybride Suche aus, die wir in Lektion 9 erneut behandeln werden. Sein Datenmodell ist strukturierter als das seiner Konkurrenten: Sie deklarieren Eigenschaften und deren Typen explizit, bevor Sie etwas einfügen, was die Erfahrung der klassischen Datenbank näher bringt.
import weaviate
from openai import OpenAI
wclient = weaviate.connect_to_local()
openai_client = OpenAI()
# Collection erstellen
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),
]
)
# Indizierung
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
)
# Suche
query_emb = openai_client.embeddings.create(
input="install Python",
model="text-embedding-3-large"
).data[0].embedding
resultats = collection.query.near_vector(
near_vector=query_emb,
limit=3
)
Chroma und pgvector
Chroma zielt auf das Prototyping ab: ein persistenter Client, der auf einen lokalen Ordner zeigt, eine Collection, die auf dem Fly erstellt wird, ohne Infrastruktur. Dies ist die Option, die Sie von einem NumPy-Index zu einer echten Datenbank führt, ohne Ihre Arbeitsumgebung zu ändern.
import chromadb
from openai import OpenAI
chroma = chromadb.PersistentClient(path="./chroma_db")
openai_client = OpenAI()
collection = chroma.get_or_create_collection("my-corpus")
# Indizierung
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"]}]
)
# Suche
query_emb = openai_client.embeddings.create(
input="how to install Python",
model="text-embedding-3-large"
).data[0].embedding
resultats = collection.query(query_embeddings=[query_emb], n_results=3)
pgvector adressiert eine sehr unterschiedliche Situation: Ihre Dokumente befinden sich bereits in PostgreSQL, inklusive ihrer Joins, Zugriffsrechte und Backups. Anstatt ein zweites System einzuführen, um zu synchronisieren, fügt die Erweiterung einen vector-Typ und Distanzoperatoren zur bestehenden Datenbank hinzu. Der <=>-Operator berechnet die kosinische Distanz, und der HNSW-Index macht die Abfrage erst jenseits von einigen tausend Zeilen nutzbar.
-- pgvector Erweiterung
CREATE EXTENSION vector;
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
texte TEXT,
categorie TEXT,
embedding vector(3072)
);
-- HNSW Index für Leistung
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops);
-- Suche nach den 5 nächsten Nachbarn
SELECT id, texte, 1 - (embedding <=> $1::vector) AS score
FROM documents
ORDER BY embedding <=> $1::vector
LIMIT 5;
Vergleich
| Kriterium | Pinecone | Qdrant | Weaviate | Chroma | pgvector |
|---|---|---|---|---|---|
| Typ | Verwaltet | Quelloffen | Quelloffen | Quelloffen | Erweiterung |
| Hosting | Cloud | Selbstgehostet/Cloud | Selbstgehostet/Cloud | Lokal/Eingebettet | PostgreSQL |
| Hybride Suche | ✅ | ✅ | ✅ Native | ❌ | Über SQL |
| Produktion | ✅ | ✅ | ✅ | ⚠️ Prototyping | ✅ |
| Einfachheit | ✅✅ | ✅ | ✅ | ✅✅ | ✅ |
Die Entscheidung hängt weniger von der rohen Leistung ab, die in der Praxis sehr ähnlich ist, als vielmehr von Ihrem Kontext: Ein Team, das bereits PostgreSQL betreibt, hat jedes Interesse daran, dabei zu bleiben; ein Team ohne Operations-Experten wird den verwalteten Dienst wählen, und ein Team, das aus regulatorischen Gründen in Europa hosten muss, wird sich für selbstgehostete Lösungen entscheiden.
Zusammenfassung
- Chroma für schnelles Prototyping
- pgvector, wenn Sie bereits PostgreSQL nutzen
- Pinecone für verwaltete Dienste ohne Infrastruktur
- Qdrant für hochperformantes Self-Hosting
- Weaviate für native hybride Suche