🏠 Ana Sayfa 📖 Sözlük 💬 Doküman asistanı
Ana sayfaPlatform & AltyapıQdrant Vektör DB

🗄️Qdrant Vektör Veritabanı

📺 Daha Fazlası İçin İzleyin
✅ Komtaş Kurumsal Qdrant Ortamı

Komtaş, GCP europe-west3 bölgesinde şirket geneline hizmet veren kurumsal bir Qdrant ortamına sahiptir. Bu ortam; iç bilgi tabanları, doküman arşivleri ve yapay zekâ destekli arama senaryoları için aktif olarak kullanılmaktadır.

Kurumsal bilgilerin bu vektör veritabanına eklenmesi ve mevcut koleksiyonlara erişim için Komtaş TIO (Transformation and Innovation Office) ile iletişime geçiniz. TIO ekibi; koleksiyon tasarımı, embedding pipeline kurulumu ve kiracı yönetimi konularında destek sağlayacaktır.

Qdrant, Komtaş CoPilot'un semantik arama ve RAG altyapısının temelini oluşturmaktadır. europe-west3 bölgesinde çalışan self-hosted kurulum GDPR uyumluluğunu sağlamaktadır.

7.1 Koleksiyon Tasarımı ve HNSW Ayarları

from qdrant_client import QdrantClient
from qdrant_client.models import (
    Distance, VectorParams, HnswConfigDiff,
    QuantizationConfig, ScalarQuantizationConfig, ScalarType,
    OptimizersConfigDiff
)

client = QdrantClient(
    url="http://qdrant:6333",
    api_key=os.environ["QDRANT_API_KEY"],
    timeout=30
)

# Üretim kalitesi koleksiyon oluşturma
def create_production_collection(collection_name: str, vector_size: int = 1536):
    client.recreate_collection(
        collection_name=collection_name,
        vectors_config=VectorParams(
            size=vector_size,
            distance=Distance.COSINE,
            # HNSW parametreleri
            hnsw_config=HnswConfigDiff(
                m=16,             # Her node için bağlantı sayısı (8-64, varsayılan: 16)
                ef_construct=200, # Indeks oluşturma kalitesi (yüksek = yavaş build, iyi kalite)
                full_scan_threshold=10000  # Bu sayının altında brute-force
            )
        ),
        # Scalar quantization: vektör boyutunu 4x küçültür, %5-10 doğruluk kaybı
        quantization_config=QuantizationConfig(
            scalar=ScalarQuantizationConfig(
                type=ScalarType.INT8,
                quantile=0.99,
                always_ram=True  # Hız için RAM'de tut
            )
        ),
        # Optimizasyon ayarları
        optimizers_config=OptimizersConfigDiff(
            default_segment_number=4,
            max_segment_size=200000,
            memmap_threshold=50000,
            indexing_threshold=20000,
            flush_interval_sec=5
        ),
        # Varsayılan shard sayısı
        shard_number=2,
        replication_factor=2  # Yüksek erişilebilirlik
    )
    print(f"✅ Koleksiyon oluşturuldu: {collection_name}")
📌 Kod Notları
  • Koleksiyon stratejisi: Her tenant için ayrı koleksiyon vs. tek koleksiyon + payload filtresi seçimi kritiktir. Küçük tenant sayısında ayrı koleksiyon, büyük sayıda payload bazlı izolasyon tercih edilir.
  • Distance.COSINE: Normalize edilmiş embedding'ler için Cosine benzerliği en yaygın metriktir. Normalize edilmemiş vektörler için Dot Product daha hızlı olabilir. Metriği sonradan değiştiremezsiniz; koleksiyon yeniden oluşturulmalıdır.
  • HNSW index: Approximate Nearest Neighbor araması için kullanılır. m ve ef_construct parametreleri hız/doğruluk/bellek dengesini ayarlar.
  • on_disk_payload: Vektör boyutu büyük ya da koleksiyon sayısı yüksekse on_disk=True RAM kullanımını azaltır ancak I/O yavaşlar.

7.2 Çoklu Kiracılık (Multi-tenancy)

Yaklaşımİzolasyon SeviyesiÖlçekYönetim KarmaşıklığıKomtaş Kullanımı
Payload TabanlıOrta (filtre)Yüksek (tek koleksiyon)Düşükİç kullanıcılar, paylaşılan bilgi tabanı
Koleksiyon Başına TenantYüksek (tam izolasyon)Orta (N koleksiyon)OrtaPremium müşteri verileri
Hibrit (Tiered)DinamikYüksekYüksekKarma kullanım senaryoları
# Payload tabanlı multi-tenancy
from qdrant_client.models import Filter, FieldCondition, MatchValue

def search_with_tenant_isolation(
    collection: str,
    query_vector: list[float],
    tenant_id: str,
    limit: int = 10
):
    """Tenant izolasyonlu arama"""
    return client.search(
        collection_name=collection,
        query_vector=query_vector,
        limit=limit,
        query_filter=Filter(
            must=[
                FieldCondition(
                    key="tenant_id",
                    match=MatchValue(value=tenant_id)
                )
            ]
        ),
        with_payload=True,
        score_threshold=0.7
    )

# JWT tabanlı RBAC
def create_qdrant_jwt(user_id: str, tenant_id: str, permissions: list[str]) -> str:
    """Qdrant API key ve JWT ile erişim kontrolü"""
    payload = {
        "sub": user_id,
        "tenant_id": tenant_id,
        "permissions": permissions,  # ["read", "write"] veya ["read"]
        "exp": datetime.utcnow() + timedelta(hours=24)
    }
    return jwt.encode(payload, settings.JWT_SECRET, algorithm="HS256")
📌 Kod Notları
  • FieldCondition: Qdrant'ın vektör aramasını payload değerlerine göre filtreleyen yapısıdır. Her aramaya filter=Filter(must=[...]) eklemek tenant izolasyonunu garanti eder.
  • Tenant sızıntısı: Filtre eklemeyi unutmak kritik bir güvenlik açığıdır. Her arama fonksiyonunu tenant_id parametresiyle zorunlu yapın ve iç testlerle doğrulayın.
  • Performans: Payload filtresi eklemek büyük koleksiyonlarda aramayı yavaşlatabilir. Sık filtrelenen alanlar için create_payload_index() kullanın.

7.3 Hybrid Search ve Performans

from qdrant_client.models import SparseVector, SparseVectorParams, NamedSparseVector
from fastembed import SparseTextEmbedding

# Sparse embedding modeli (BM25 benzeri)
sparse_model = SparseTextEmbedding("Qdrant/bm25")

def hybrid_search(collection: str, query: str, limit: int = 10):
    """
    Dense (semantik) + Sparse (keyword) hibrit arama
    Reciprocal Rank Fusion ile birleştirme
    """
    # Dense vector (anlamsal)
    dense_vector = embed_dense(query)
    
    # Sparse vector (keyword)
    sparse_embedding = list(sparse_model.embed([query]))[0]
    sparse_vector = SparseVector(
        indices=sparse_embedding.indices.tolist(),
        values=sparse_embedding.values.tolist()
    )
    
    results = client.query_points(
        collection_name=collection,
        prefetch=[
            # Dense search
            {"query": dense_vector, "using": "dense", "limit": 20},
            # Sparse search
            {"query": sparse_vector, "using": "sparse", "limit": 20}
        ],
        # RRF (Reciprocal Rank Fusion) ile birleştir
        query={"fusion": "rrf"},
        limit=limit,
        with_payload=True
    )
    
    return results