İçeriğe Atla
Mustafa Erbay
Rehberler · 10 dk okuma · görüntülenme Read in English

RAG Retrieval Kalitesi: Büyük Dil Modelleri Her Zaman Gerekli mi?

RAG sistemlerinde pahalı LLM'ler yerine hafif re-ranker, BM25 ve PostgreSQL ile yüksek performanslı, düşük maliyetli arama altyapısı kurma rehberi.

100%

RAG (Retrieval-Augmented Generation) mimarisi kurarken gördüğüm en büyük hata, her probleme en büyük, en pahalı dil modelini (LLM) ve devasa vektör veritabanlarını fırlatmak oluyor. Geçen sene üzerinde çalıştığım bir üretim ERP’sinin tedarik zinciri arama motorunda, ilk başta her şeyi vektörlere çevirip bir bulut LLM’ine gönderen bir boru hattı (pipeline) tasarlamıştık. Sonuç? Kullanıcı başına saniyelerle ölçülen bir gecikme (latency) ve kabarık bir API faturası oldu. Sonra durup sistemi sadeleştirdim; LLM’i sadece son sentez aşamasına sıkıştırıp, retrieval (geri çağırma) aşamasını tamamen yerel, hafif araçlarla çözdüm. Arama gecikmesi belirgin biçimde düştü, maliyet ise neredeyse sıfırlandı.

Bu yazıda, RAG altyapılarında retrieval kalitesini artırırken büyük dil modellerine olan bağımlılığı nasıl azaltabileceğimizi, hibrit arama tekniklerini ve production ortamında bu işi nasıl ucuza kapatabileceğimizi kendi tecrübelerim üzerinden anlatacağım.

Vektör Veritabanı ve Dense Retrieval Çılgınlığı

Sektörde nereye baksam herkes bir vektör veritabanı kurma yarışı içinde. OpenAI’ın text-embedding-3-small gibi modelleriyle 1536 boyutta vektör alıp, bunları Cosine Similarity ile aratmanın her derde deva olduğu sanılıyor. Gerçekte ise durum hiç öyle değil. Dense retrieval (yoğun geri çağırma) dediğimiz bu yöntem, anlamsal ilişkileri yakalamakta çok başarılı olsa da spesifik anahtar kelimelerde, parça kodlarında veya seri numaralarında tamamen çuvallıyor.

Örneğin, kullanıcı “SKU-4912-A” kodlu parçayı aradığında, vektör arama motoru gidip benzer açıklamaya sahip “SKU-4912-B” parçasını getirebiliyor. Çünkü vektör uzayında bu iki metnin semantik farkı devede kulak kalıyor. Aşağıdaki tabloda, dense retrieval ile klasik yöntemlerin kaynak ve doğruluk karşılaştırmasını kabaca büyüklük mertebeleriyle görebilirsiniz (kesin değerler veri setine ve donanıma göre değişir):

Arama Tipi Latency RAM Tüketimi SKU/Kod Doğruluğu Semantik Başarı
Dense Vector (HNSW) Orta Yüksek Zayıf Çok iyi
Sparse (BM25 / FTS) Çok düşük Düşük Çok iyi Zayıf
Hybrid (RRF ile Birleşik) Düşük Orta Çok iyi Çok iyi

Gördüğünüz gibi, sadece vektör aramaya güvenmek bizi üretim ortamında hem yüksek RAM maliyetleriyle baş başa bırakıyor hem de teknik verilerde hata yapma payımızı inanılmaz artırıyor. Aynı trade-off, büyük verileri yerel sunucuda indekslerken de karşımıza çıkar; bellek optimizasyonu yapmazsanız sınırlı kaynaklı bir sunucu er ya da geç tıkanır.

BM25 ve Klasik Ters Dizin (Inverted Index) Gücü

Eğer elinizde teknik dökümanlar, ERP verileri veya yapılandırılmış loglar varsa, ilk başvurmanız gereken yer klasik ters dizin tabanlı arama motorlarıdır. PostgreSQL 14+ sürümünde gelen tsvector ve tsquery özellikleri, harici bir Elasticsearch kümesi kurup yönetme zahmetine girmeden işinizi fazlasıyla görür. BM25 algoritması, bir kelimenin döküman içindeki sıklığını (TF) ve tüm dökümanlar arasındaki nadirliğini (IDF) hesaplayarak bize matematiksel olarak çok tutarlı bir skor üretir.

Aşağıdaki SQL örneğinde, bir parçalar tablosundaki ürün adı ve açıklamalarını nasıl indeksleyip arattığımı görebilirsiniz. Burada GIN (Generalized Inverted Index) kullanarak sorgu süresini mikrosaniyeler seviyesine indirdim:

-- Arama vektörlerini saklayacağımız generate edilmiş bir kolon ekliyoruz
ALTER TABLE parts_catalog 
ADD COLUMN search_vector tsvector 
GENERATED ALWAYS AS (
  to_tsvector('english', coalesce(part_name, '') || ' ' || coalesce(description, ''))
) STORED;

-- GIN indeksi oluşturuyoruz
CREATE INDEX idx_parts_search_gin ON parts_catalog USING gin(search_vector);

-- Arama sorgusu ve execution plan analizi
EXPLAIN ANALYZE
SELECT id, part_name, ts_rank(search_vector, query) as rank
FROM parts_catalog, to_tsquery('english', 'SKU-4912-A | copper') query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 5;

Bu sorgunun EXPLAIN ANALYZE çıktısına baktığımda, yüz binlerce satırlık tabloda diskten okuma yapmadan, sadece Bitmap Index Scan kullanarak sonucu milisaniyenin altında döndürdüğünü gördüm. Aynı aramayı vektör veritabanında yapmaya çalışsaydık, hem daha yavaş olacaktı hem de “copper” (bakır) kelimesinin geçtiği ama alakasız başka parçaları önümüze serecekti.

Hibrit Arama (Hybrid Search) Mimarisi ve Karar Matrisi

Peki hem semantik anlamı kaçırmak istemiyor hem de anahtar kelime hassasiyetini korumak istiyorsak ne yapacağız? Çözüm: Hibrit Arama. Burada devreye Reciprocal Rank Fusion (RRF) algoritması giriyor. RRF, iki farklı arama motorundan gelen sıralamaları (rank) alır ve bunları ortak bir formülle yeniden puanlar. Pahalı orkestrasyon kütüphanelerine hiç gerek yok; bunu kendi backend servisinizde birkaç satır kodla halledebilirsiniz.

Aşağıda, FastAPI ve PostgreSQL kullanarak yazdığım, hem vektör arama (pgvector) hem de Full-Text Search (FTS) sonuçlarını RRF ile birleştiren fonksiyonun sadeleştirilmiş halini görebilirsiniz:

from typing import List, Dict

def reciprocal_rank_fusion(vector_results: List[Dict], fts_results: List[Dict], k: int = 60) -> List[Dict]:
    """
    RRF algoritması ile iki farklı sonuç kümesini birleştirir.
    k: Sabit penalizasyon parametresi (genelde 60 seçilir)
    """
    scores = {}
    
    # Vektör sonuçlarını işle
    for rank, doc in enumerate(vector_results):
        doc_id = doc["id"]
        scores[doc_id] = scores.get(doc_id, 0.0) + (1.0 / (k + rank + 1))
        
    # FTS sonuçlarını işle
    for rank, doc in enumerate(fts_results):
        doc_id = doc["id"]
        # Eğer döküman her iki aramada da varsa skoru katlanır
        scores[doc_id] = scores.get(doc_id, 0.0) + (1.0 / (k + rank + 1))
        
    # Skorlara göre sırala
    sorted_docs = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    return sorted_docs

Bu mimarinin en güzel yanı, her iki dünyanın da en iyi özelliklerini bir araya getirmesidir. Kullanıcı “red actuator valve” yazdığında, FTS “actuator” ve “valve” kelimelerini tam eşleştirirken; vektör arama ise “red” (kırmızı) rengine benzer tonlardaki dökümanları yakalar. RRF ise bu iki sinyali birleştirip en doğru dökümanı en üste taşır.

Büyük Dil Modelleri Yerine Küçük Modeller (Cross-Encoders) Kullanımı

Retrieval kalitesini artırmak için milyarlarca parametreli LLM’leri (örneğin GPT-4 veya Claude) her sorguda tetiklemek hem finansal bir intihardır hem de ağ gecikmesini (network overhead) tavan yaptırır. Bunun yerine, ilk aşamada (retrieval) hızlıca 50 döküman çekip, bunları yerel olarak koşan bir Cross-Encoder (Re-ranker) modelinden geçirmek çok daha mantıklıdır.

Bi-Encoder modelleri (klasik embedding modelleri) sorgu ve dökümanı ayrı ayrı vektörleştirirken, Cross-Encoder modelleri sorgu ile dökümanı aynı anda işlemciye sokar ve aralarındaki ilişkiyi çok daha derin analiz eder. BAAI/bge-reranker-base gibi sadece 278 MB boyutunda bir model, CPU üzerinde bile harikalar yaratabilir.

from sentence_transformers import CrossEncoder

# Küçük ve efektif bir re-ranker modeli yüklüyoruz
model = CrossEncoder("BAAI/bge-reranker-base", max_length=512)

query = "How to adjust the pressure limit on model X12?"
documents = [
    "To adjust the pressure limit on model X12, turn the valve clockwise.",
    "Model X12 is a hydraulic actuator designed for high pressure systems.",
    "Standard pressure limits for older models are set at 150 PSI."
]

# Modeli çalıştırıp skorları alıyoruz
pairs = [[query, doc] for doc in documents]
scores = model.predict(pairs)

# Sonuçları ekrana yazdırıyoruz
for doc, score in zip(documents, scores):
    print(f"Score: {score:.4f} | Doc: {doc}")

Bu Python betiğini standart bir VPS CPU’sunda çalıştırdığımda, birkaç döküman için çıkarım süresi yalnızca birkaç milisaniye sürdü. Bu işlem sayesinde, LLM’e göndereceğimiz bağlamı (context) onlarca dökümandan en alakalı birkaç dökümana indirgemiş oluyoruz. Böylece hem LLM token maliyetimiz ciddi biçimde düşüyor hem de LLM’in “kaybolma” (lost in the middle) problemi ortadan kalkıyor.

Bellek ve CPU Yönetimi: Üretim Ortamında Kaynak Sınırlandırma

Yerel modelleri (re-ranker veya küçük embedding modelleri) production sunucularında koştururken en sık karşılaştığım sorun, PyTorch veya ONNX runtime kütüphanelerinin sunucudaki tüm CPU çekirdeklerini sömürmeye çalışmasıdır. Eğer önlem almazsanız, bir istek geldiğinde sistem yükü (load average) anında fırlar ve aynı sunucudaki PostgreSQL veya Nginx servisleriniz yanıt veremez hale gelir.

Bu sorunun önüne geçmek için Docker Compose veya SystemD düzeyinde kaynak limitleri tanımlamak şarttır. Ben genelde Python servislerimi Docker içinde sınırlandırarak koşturuyorum:

version: '3.8'

services:
  reranker-service:
    image: python-reranker:latest
    environment:
      - OMP_NUM_THREADS=2
      - MKL_NUM_THREADS=2
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 2G
        reservations:
          memory: 512M
    restart: always

Buradaki en kritik ayar OMP_NUM_THREADS=2 çevre değişkenidir. PyTorch, varsayılan olarak sunucudaki tüm çekirdekleri (örneğin 32 çekirdekli bir bare-metal ise tamamını) kullanmaya meyillidir. Bu limiti 2 veya 4 ile sınırlamak, paralel isteklerde işlemciyi boğmadan stabil bir throughput elde etmenizi sağlar.

Gerçek Dünya Senaryosu: ERP Stok ve Tedarik Zinciri RAG Altyapısı

Tüm bu anlattıklarımı bir araya getirdiğimizde nasıl bir canavar ortaya çıktığını somut bir örnekle göstereyim. Bir üretim ERP’sinde, geçmişe dönük 150.000 satırlık bakım-onarım dökümanları ve arıza kayıtları bulunuyordu. Bakım teknisyenleri, sahada bir arıza gördüklerinde tabletlerinden sisteme arıza açıklamasını yazıp geçmiş çözüm önerilerini RAG ile sorguluyorlardı.

İlk kurduğumuz sistemde akış şöyleydi:

  1. Kullanıcı sorgusu -> OpenAI Embedding API -> Pinecone Vektör DB -> GPT-4 API -> Sonuç.
  2. Sorun: İnternet kesildiğinde veya API limitlerine takılındığında sistem duruyordu. Ortalama cevap süresi saniyeler mertebesindeydi.

Yeniden tasarladığım yerel hibrit akış:

  1. Kullanıcı sorgusu -> Postgres FTS + pgvector (lokal) -> Top 30 aday.
  2. Top 30 aday -> Lokal Docker’da koşan BGE-Reranker-Base -> Top 3 aday.
  3. Top 3 aday -> Sunucuda koşan hafif bir LLM (ya da fallback olarak Gemini Flash API).
  4. Sonuç: İnternet bağımlılığı minimuma indi. Ortalama cevap süresi göz açıp kapayıncaya kadar inecek seviyeye düştü.
# Log çıktısından bir kesit (Yerel hibrit sistemin akış adımları)
# Süreler ortama ve donanıma göre değişir
INFO - Received query: "valve pressure drop during startup"
INFO - Postgres FTS & Vector Search completed (30 candidates found)
INFO - Re-ranking completed (Top 3 selected)
INFO - Local LLM response generated
INFO - Total turnaround completed

Görüldüğü üzere, büyük dil modellerini ve bulut servislerini sistem mimarisinin merkezinden çıkarıp sadece en son aşamada “metin birleştirici” olarak konumlandırdığımızda, sistemin kararlılığı ve hızı bambaşka bir boyuta taşındı.

Sonraki adımda, benzer bir yerel optimizasyon sürecini kendi veritabanı replikasyon mimarimizde nasıl kurguladığımızı ve WAL bloat sorunlarını nasıl çözdüğümüzü ele alacağım.

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

Sıkça Sorulanlar

Bu makale ile ilgili okurların sorduğu yaygın sorular.

RAG sistemlerinde büyük dil modelleri yerine hafif araçları kullanmanın avantajları nelerdir?
Benim deneyimime göre, büyük dil modelleri yerine hafif araçları kullanmak, arama gecikmesini azaltabilir ve maliyetleri düşürebilir. Örneğin, bir üretim ERP'sinin tedarik zinciri arama motorunda, LLM'i sadece son sentez aşamasına sıkıştırdım ve retrieval aşamasını tamamen yerel, hafif araçlarla çözdüm. Bu sayede arama gecikmesi belirgin biçimde düştü, maliyet ise neredeyse sıfırlandı.
Dense retrieval yönteminin sınırları nelerdir?
Dense retrieval yöntemi, anlamsal ilişkileri yakalamakta başarılı olsa da, spesifik anahtar kelimelerde, parça kodlarında veya seri numaralarında çuvallayabiliyor. Örneğin, bir müşteri projesinde, vektör arama motoru, benzer açıklamaya sahip yanlış parçayı getirebiliyor. Bu nedenle, dense retrieval yöntemini kullanırken, bu sınırları dikkate almak önemlidir.
Hibrit arama tekniklerini production ortamında nasıl uygulanabilir?
Benim deneyimime göre, hibrit arama tekniklerini production ortamında uygulamak, büyük dil modellerine olan bağımlılığı azaltabilir ve arama performansı artırabilir. Örneğin, LLM'i sadece son sentez aşamasına sıkıştırmak ve retrieval aşamasını tamamen yerel, hafif araçlarla çözmek, arama gecikmesini azaltabilir ve maliyetleri düşürebilir. Ayrıca, BM25 ve PostgreSQL gibi araçları kullanmak, arama performansını artırabilir.
RAG sistemlerinde vektör veritabanı kurmanın doğru bir yaklaşım mı?
Benim deneyimime göre, RAG sistemlerinde vektör veritabanı kurmak her zaman doğru bir yaklaşım değil. Vektör veritabanları, anlamsal ilişkileri yakalamakta başarılı olsa da, spesifik anahtar kelimelerde, parça kodlarında veya seri numaralarında çuvallayabiliyor. Bu nedenle, vektör veritabanı kurmadan önce, sistemlerinizi ve gereksinimlerinizi iyi analiz etmek önemlidir. Ayrıca, hibrit arama tekniklerini kullanmak, arama performansı ve maliyetleri açısından daha iyi bir seçenek olabilir.
ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Yorumlar

Sunucu Taraflı AI Moderasyon

Yorumlar sunucuda yapay zeka ile denetlenir ve kalıcı olarak saklanır.

?
0/2000

Sunucu taraflı AI denetim

✉️ Ücretsiz · Spam yok · İstediğin an çık

Yeni yazılardan haberdar olun

Yeni içerikler ve teknik notlar e-postanıza gelsin.

  • 📌
    Haftanın en iyisi Sadece okumaya değer tek yazı
  • 🔧
    Alet çantası Bu hafta kullandığım araçlar
  • 🧠
    Perde arkası Blog'a girmeyen notlar

Spam yapmıyoruz. İstediğiniz zaman ayrılabilirsiniz. · Sadece Umami (self-hosted, Google yok) ile takip.

Okuma İstatistikleriniz

0

Yazı Okundu

0dk

Okuma Süresi

0

Gün Serisi

-

Favori Kategori

İlgili Yazılar