Vektör Veritabanları ve Semantik Arama: Pinecone ve Milvus ile Uygulamalı Rehber
Semantik arama, anahtar kelime eşleşmesi yerine anlam yakınlığına göre sonuç döndürür. Bunun altyapısını embedding adı verilen yoğun vektörler ve bu vektörleri hızlı biçimde sorgulayan vektör veritabanları oluşturur. Bu rehberde önce ortak kavramları netleştirip ardından Pinecone (yönetilen) ve Milvus (self-hosted veya yönetilen) üzerinde çalışan örneklerle ilerleyeceğiz.
Önkoşullar ve Kapsam
- Orta seviye Python bilgisi ve
pipile paket kurulumu. - Temel HTTP/REST ve Docker kavramları (Milvus için).
- Bir embedding modeli. Örneklerde OpenAI uyumlu bir embedding uç noktası varsayıyoruz; farklı bir sağlayıcı kullanıyorsanız yalnızca vektör üretim kısmını değiştirmeniz yeterli.
- Sürüm kapsamı: Örnekler
pinecone-clientvepymilvuspaketlerinin güncel 3.x/2.x API yüzeyini hedefler. Kesin sürüm numarası vermiyoruz; kurulumdan önce ilgili paketin resmi dokümanından API uyumluluğunu doğrulayın. Milvus için Docker Compose tabanlı tek düğümlü kurulum temel alınmıştır.
Vektör Aramanın Temelleri
Bir embedding modeli, metni sabit boyutlu bir sayı dizisine (örneğin 768 veya 1536 boyutlu) dönüştürür. Anlamca yakın metinlerin vektörleri de uzayda yakın konumlanır. Arama, sorgu vektörüne en yakın komşuların bulunmasıdır.
Bilinmesi gereken üç kavram:
- Mesafe metriği:
cosine,dotproductveyaeuclidean. Çoğu metin embedding modeli normalize vektör ürettiği içincosinegüvenli bir varsayılandır. Metrik seçimi indeks oluşturulurken sabitlenir; sonradan değiştirmek indeksi yeniden kurmayı gerektirir. - Yaklaşık en yakın komşu (ANN): Tam arama (brute-force) doğru sonuç verir ama büyük veri kümelerinde yavaştır. HNSW, IVF gibi indeksler hız için küçük bir doğruluk kaybını kabul eder.
- Metadata filtreleme: Sonuçları kategori, tarih, kullanıcı gibi alanlara göre daraltmak. Filtre alanlarını baştan planlamak, sonradan şema değiştirmekten çok daha kolaydır.
Adım 1: Embedding Üretimi
Aşağıdaki yardımcı, metin listesini vektöre çevirir. Sağlayıcıya özgü istemci kurulumunu kendi ortamınıza göre uyarlayın:
from openai import OpenAI
client = OpenAI()
def embed(texts: list[str], model: str = "text-embedding-3-small") -> list[list[float]]:
response = client.embeddings.create(input=texts, model=model)
return [item.embedding for item in response.data]
Beklenen davranış: Fonksiyon, girdi listesiyle aynı sırada vektör listesi döndürür. Aynı model ve aynı metin her çağrıda aynı boyutu üretir; ancak farklı model sürümleri farklı vektör uzayları tanımlar. Bu yüzden indeks boyutu ile model boyutu eşleşmelidir.
Adım 2: Pinecone ile Yönetilen Vektör Arama
Pinecone, altyapı yönetimini üstlenir. İndeks oluşturma, yazma ve sorgulama adımları şöyledir:
import os
from pinecone import Pinecone, ServerlessSpec
pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
index_name = "docs-index"
if index_name not in pc.list_indexes().names():
pc.create_index(
name=index_name,
dimension=1536,
metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
)
index = pc.Index(index_name)
Veri yazma. Vektörler ID ve metadata ile birlikte gönderilir:
texts = [
"Vektör veritabanları benzerlik araması için kullanılır.",
"Pinecone yönetilen bir vektör veritabanıdır.",
]
vectors = embed(texts)
index.upsert(vectors=[
{"id": f"doc-{i}", "values": v, "metadata": {"text": t, "source": "intro"}}
for i, (t, v) in enumerate(zip(texts, vectors))
])
Sorgulama:
query = "yönetilen vektör servisi hangisi?"
query_vec = embed([query])[0]
results = index.query(
vector=query_vec,
top_k=3,
include_metadata=True,
filter={"source": {"$eq": "intro"}},
)
for match in results.matches:
print(match.score, match.metadata["text"])
Beklenen davranış: matches listesi skora göre azalan sırada döner. filter alanı metadata üzerinde çalışır ve top_k sonuç filtreden geçen adaylar arasından seçilir.
Adım 3: Milvus ile Self-Hosted Vektör Arama
Milvus'u Docker Compose ile ayağa kaldırdıktan sonra istemci bağlantısı kurulur. Koleksiyon şeması, indeks ve arama adımları:
from pymilvus import (
connections, FieldSchema, CollectionSchema,
DataType, Collection, utility,
)
connections.connect(alias="default", host="localhost", port="19530")
collection_name = "docs"
if utility.has_collection(collection_name):
utility.drop_collection(collection_name)
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536),
FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=2048),
FieldSchema(name="source", dtype=DataType.VARCHAR, max_length=64),
]
schema = CollectionSchema(fields=fields, description="Doküman embedding'leri")
collection = Collection(name=collection_name, schema=schema)
İndeks oluşturma ve veri ekleme:
collection.create_index(
field_name="embedding",
index_params={"index_type": "HNSW", "metric_type": "COSINE", "params": {"M": 16, "efConstruction": 200}},
)
collection.load()
vectors = embed(texts)
collection.insert([
vectors,
texts,
["intro"] * len(texts),
])
collection.flush()
Arama:
query_vec = embed([query])[0]
search_results = collection.search(
data=[query_vec],
anns_field="embedding",
param={"metric_type": "COSINE", "params": {"ef": 64}},
limit=3,
output_fields=["text", "source"],
expr='source == "intro"',
)
for hits in search_results:
for hit in hits:
print(hit.score, hit.entity.get("text"))
Beklenen davranış: search sonuçları mesafeye göre sıralı döner. expr ifadesi metadata filtresi işlevi görür; filtre alanlarının şemada tanımlı olması gerekir. load() çağrısı yapılmadan arama çalışmaz.
Pinecone ve Milvus Karşılaştırması
- İşletim yükü: Pinecone altyapıyı yönetir; Milvus'ta küme, depolama ve ölçeklendirme sizin sorumluluğunuzdadır (yönetilen Milvus seçenekleri de mevcuttur).
- Maliyet modeli: Pinecone kullanım/indeks bazlı, Milvus altyapı bazlı. Yoğun ve sabit yüklerde self-hosted maliyet avantajı sağlayabilir.
- Filtreleme: İki sistem de metadata filtresini destekler; sözdizimi ve desteklenen operatörler farklıdır.
- Taşınabilirlik: Milvus açık kaynak olduğu için ortam değişikliğinde daha esnektir; Pinecone API'si sağlayıcıya bağlıdır.
Yaygın Hatalar
- Boyut uyuşmazlığı: İndeks boyutu ile embedding modelinin çıktı boyutunu eşleştirmemek en sık hatadır. Model değiştirirken indeksi yeniden oluşturun.
- Metrik uyumsuzluğu: Normalize vektörlerle
euclideankullanmak sıralamayı bozar. Model dokümanının önerdiği metriği seçin. load()atlanması (Milvus): Koleksiyon yüklü değilse arama hata verir.- Filtre alanlarını sonradan eklemek: Şema değişikliği yeniden indeksleme gerektirebilir; filtre alanlarını baştan tanımlayın.
- Toplu yazımda tek tek çağrı: Vektörleri gruplar hâlinde göndermek, gecikmeyi ve maliyeti belirgin biçimde azaltır.
top_kile filtre etkileşimini yanlış anlamak: Bazı sistemlerde filtre,top_kuygulandıktan sonra çalışır ve beklediğinizden az sonuç dönebilir; davranışı sağlayıcı dokümanından doğrulayın.
Güvenlik ve Gizlilik
- API anahtarları: Pinecone anahtarı ve embedding sağlayıcısı anahtarı sunucu tarafında tutulmalı, istemciye sızdırılmamalıdır.
- Veri sızıntısı: Embedding'ler orijinal metni birebir içermez ama tersine mühendislikle kısmi bilgi çıkarımı mümkündür. Hassas veriyi indekslemeden önce maskeleme ve erişim politikası uygulayın.
- Çok kiracılı yapı: Aynı indekste farklı müşterilerin verisini tutuyorsanız metadata filtresini zorunlu kılın; aksi hâlde bir kiracı diğerinin kayıtlarını görebilir.
- Ağ maruziyeti: Self-hosted Milvus portlarını genel ağa açmayın; kimlik doğrulama ve TLS kullanın.
- Girdi doğrulama: Kullanıcıdan gelen sorgu metnini sınırlayın; çok uzun girdiler maliyeti ve gecikmeyi artırır.
Sonuç
Semantik aramanın kalitesi büyük ölçüde embedding modeli ile indeks yapılandırmasının uyumuna bağlıdır. Pinecone hızlı başlangıç ve düşük işletim yükü isteyen ekipler için uygundur; Milvus altyapı üzerinde tam kontrol ve taşınabilirlik gerektiğinde tercih edilir. Hangi sistemi seçerseniz seçin, indeks boyutu, metrik ve metadata şemasını baştan doğru planlamak sonradan yapılacak yeniden indeksleme maliyetinden çok daha ucuzdur.