Kaan Gümele

yazılar

RAG Aslında Ne? Doğru Tabloyu Bulmak

RAG · Vektör DB · ChromaDB · Text-to-SQL · Embedding
Sunum slaytları →

Seri: Doğal Dille Veritabanına Soru Sormak — Sıfırdan Text-to-SQL Asistanı. Önceki yazı: İlk SQL ve Halüsinasyon

Eğitim slaytlarıDers 3 — Schema RAG

Geçen yazıda küçük bir sorun bıraktık: model, şemada olmayan bir suppliers tablosunu uydurdu. Şemamız 4 tablodan oluştuğu için hepsini prompt’a sığdırabildik. Peki ya veritabanında 100 tablo, 1000 kolon varsa?

Hepsini prompt’a tıkıştıramayız — hem token limiti dolar (para yanar), hem de model alakasız onlarca tablo arasında boğulup daha çok hata yapar. Çözüm: soruya ilgili olan tabloları seçip sadece onları vermek. İşte RAG budur.

RAG’i Somutlaştıralım

RAG = Retrieval-Augmented Generation. Kulağa havalı geliyor ama fikir çok basit:

Modele cevap ürettirmeden önce, elindeki bilgi yığınından soruyla ilgili parçaları bul (retrieval), sadece onları prompt’a ekle (augment), sonra ürettir (generation).

Bizim senaryomuzda “bilgi yığını” = veritabanının tablo tanımları. “İlgili parçaları bulmak” = soruya en yakın tabloları seçmek. Buna literatürde schema retrieval ya da schema linking deniyor. Yani bizim RAG’imiz, aslında doğru tabloyu bulma işi.

Peki “ilgili” olanı nasıl buluruz? Cevap ilk yazıda: embedding. Soruyu ve her tablo tanımını embedding’e çeviririz, sonra soruya anlamca en yakın tabloları seçeriz.

Vektör Veritabanı: ChromaDB

Her tablonun embedding’ini alıp bir yerde saklamamız ve “şu soruya en yakın 3 tanesini getir” diye sorgulayabilmemiz gerekiyor. Bu iş için tasarlanmış araçlara vektör veritabanı deniyor. Biz burada ChromaDB kullanıyoruz — kurulumu kolay, diske kaydeden (persistent), yerel çalışan bir vektör DB.

Önce tablo tanımlarımızı hazırlıyoruz. Gerçekçi olması için 4 gerçek tablonun yanına 6 gürültü tablosu da ekliyoruz (shipping_logs, employees, suppliers, payments, product_reviews, warehouses) — tıpkı büyük bir veritabanındaki gibi. RAG’in görevi, bu 10 tablodan doğru olanları ayıklamak.

import chromadb

SCHEMA_DOCS = {
    "customers":     "customers(id, name, city, registered_at) — müşteri bilgileri",
    "products":      "products(id, name, category, price) — ürün kataloğu",
    "orders":        "orders(id, customer_id, order_date) — siparişler",
    "order_items":   "order_items(id, order_id, product_id, quantity) — sipariş kalemleri",
    "shipping_logs": "shipping_logs(...) — kargo hareketleri",
    "employees":     "employees(...) — çalışan kayıtları",
    "suppliers":     "suppliers(...) — tedarikçi firmalar",
    # ... payments, product_reviews, warehouses (gürültü)
}

client = chromadb.PersistentClient(path="chroma_db")
collection = client.get_or_create_collection(
    "schema_catalog",
    metadata={"hnsw:space": "cosine"},   # yakınlığı kosinüsle ölç
)

Şimdi her tablo tanımını embedding’e çevirip koleksiyona ekliyoruz (bu işleme indexleme deniyor). Bir kez yapılır, diske yazılır:

from sentence_transformers import SentenceTransformer

embedder = SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2")

def index_schema():
    ids   = list(SCHEMA_DOCS.keys())
    docs  = list(SCHEMA_DOCS.values())
    vecs  = embedder.encode(docs).tolist()
    collection.upsert(ids=ids, documents=docs, embeddings=vecs)  # idempotent

Retrieval: Doğru Tabloyu Getirmek

Asıl sihir burada. Kullanıcının sorusunu embedding’e çevirip, koleksiyondan en yakın k tabloyu istiyoruz:

def relevant_schema(question, k=3):
    q_vec = embedder.encode([question]).tolist()
    res = collection.query(query_embeddings=q_vec, n_results=k)
    return res["documents"][0]

Deneyelim:

for doc in relevant_schema("En çok satan 3 ürünü göster", k=3):
    print(doc)

Çıktı:

products(id, name, category, price) — ürün kataloğu
order_items(id, order_id, product_id, quantity) — sipariş kalemleri
orders(id, customer_id, order_date) — siparişler

Dikkat: soru “satan ürün” ile ilgili ve RAG tam da satış için gereken üç tabloyu getirdi — employees, shipping_logs, payments gibi alakasız gürültüyü elemiş. Üstelik “satmak” kelimesi hiçbir tablo tanımında geçmiyor; eşleşme anahtar kelimeyle değil, anlamla oldu. İlk yazıdaki embedding yakınlığının pratik faydası tam olarak bu.

Artık generate_sql’e tüm şemayı değil, sadece bu 3 satırı veriyoruz. Sonuç: daha kısa prompt, daha az maliyet, daha az halüsinasyon.

RAG’in Türleri (Kısa Harita)

Bizim yaptığımız temel bir schema-linking RAG’i. Ölçek ve ihtiyaç arttıkça çeşitleri var:

  • Naive RAG — sadece embedding yakınlığı (bizim başlangıç noktamız).
  • Hybrid (BM25 + vektör) — anahtar kelime araması ile anlam aramasını birleştirir; tablo/kolon adları gibi tam eşleşmelerde güçlü.
  • Reranking — önce çok aday getir, sonra ikinci bir model en iyileri yeniden sıralasın.
  • Agentic RAG — modelin kendisi “hangi tabloya bakayım?” diye bir araç çağırarak karar verir (Ders 4’te bu fikre değineceğiz).
  • GraphRAG — tablolar arası ilişkileri (foreign key’ler) bir graf olarak kullanır.

Vektör DB Manzarası ve AWS Tarafı

ChromaDB yerel geliştirme için ideal. Production’da tercihler değişir:

Araç Ne zaman?
FAISS Salt kütüphane, çok hızlı; kalıcılık/servisi sen yönetirsin
ChromaDB Yerel/küçük servis, kolay başlangıç
Qdrant Filtreleme + ölçek gerektiğinde, prod-hazır
pgvector Zaten Postgres kullanıyorsan (veri + vektör tek yerde)
OpenSearch Serverless AWS’de yönetilen, hibrit arama
Bedrock Knowledge Bases AWS’de “hazır RAG” — indexleme/retrieval’ı yönetir

AWS tarafında pratik bir bulgu: Bedrock’ta embedding için Cohere Embed v4, Titan’a göre Türkçe’de belirgin şekilde daha iyi sonuç veriyor. Çoğu senaryo için önerim, veri zaten ilişkisel veritabanındaysa pgvector (Aurora/RDS) ile başlamak — vektörü ayrı bir sisteme taşımadan, SQL’in yanında tutuyorsun.

Nereye Geldik?

  • Ders 1: Model metni token’a böler, embedding’le anlamı sayıya gömer.
  • Ders 2: Şemayı verince SQL üretiyor — ama olmayan tabloyu uydurabiliyor (halüsinasyon).
  • Ders 3 (bu yazı): Doğru tabloları vektör DB ile seçip veriyoruz — halüsinasyon zemini daralıyor.

Sıradaki büyük adım: asistanı pasif bir SQL yazarı olmaktan çıkarıp, sorguyu çalıştıran, hata alınca kendini düzelten bir agent’a dönüştürmek. Serinin ikinci yarısı orada başlıyor.


Seri:

  1. Token ve Embedding
  2. İlk SQL ve Halüsinasyon
  3. RAG ve Schema Retrieval (bu yazı)
  4. Agent: Tool Calling ve Self-Correction
  5. Güvenlik, Multi-Agent ve Eval
  6. Deploy: API, Docker ve AWS