RAG Hattı Kurma Rehberi: Adım Adım

## RAG'e Neden İhtiyaç Duyulur? Bir dil modeli, eğitim verisiyle sınırlıdır. Kuruma özel dokümanlar, güncel fiyat listeleri veya iç politika belgeler...

RAG Hattı Kurma Rehberi: Adım Adım

RAG'e Neden İhtiyaç Duyulur?

Bir dil modeli, eğitim verisiyle sınırlıdır. Kuruma özel dokümanlar, güncel fiyat listeleri veya iç politika belgeleri modelin parametrelerinde yer almaz. İnce ayar (fine-tuning) bir seçenek olsa da her bilgi güncellemesinde yeniden eğitim gerektirir; maliyetli ve yavaştır.

Retrieval-Augmented Generation (RAG), bu boşluğu modeli yeniden eğitmeden kapatır: kullanıcı sorgusuna en yakın belge parçalarını bir vektör deposundan çekip, bu parçaları bağlam olarak modele verirsiniz. Model, kendi bilgisinden değil, size ait veriden yanıt üretir.

Bu rehber, orta seviye bir geliştiricinin uçtan uca çalışan bir RAG hattı kurması için gereken adımları sıralar. Örnekler Python ekosistemine dayanır; kavramlar dil ve framework bağımsızdır.

Önkoşullar

  • Python 3.10 veya üzeri
  • Temel HTTP ve JSON bilgisi
  • Bir LLM API'sine erişim (OpenAI, Anthropic veya yerel bir model sunucusu)
  • Vektör benzerliği ve kosinüs mesafesi kavramlarına aşinalık

Sürüm numaraları hızla değişir. Aşağıdaki örneklerde belirli bir kütüphane sürümüne bağlı kalmayan, kararlı API yüzeylerini kullanmaya özen gösterdim. Kurulum yaparken pip install ile gelen sürümü sabitleyip (requirements.txt veya pyproject.toml içinde) ortamınızı tekrarlanabilir tutun.

RAG Hattının Bileşenleri

Tipik bir RAG akışı iki aşamaya ayrılır.

İndeksleme (offline):

  1. Belge yükleme
  2. Parçalama (chunking)
  3. Gömme (embedding) üretimi
  4. Vektör deposuna yazma

Sorgulama (online):

  1. Kullanıcı sorgusunu gömme vektörüne çevirme
  2. Benzer parçaları getirme
  3. Bağlamı ve soruyu istem (prompt) olarak modele gönderme
  4. Yanıtı döndürme

Her adımı ayrı ayrı ele alalım.

1. Belge Yükleme ve Parçalama

Parçalama, RAG kalitesini en çok etkileyen adımdır. Çok küçük parçalar bağlamı koparır; çok büyük parçalar gereksiz gürültü taşır ve token maliyetini artırır.

Pratik bir yaklaşım: paragrafları temel al, ardından belirli bir token veya karakter sınırına göre böl, komşu parçalar arasında küçük bir örtüşme (overlap) bırak. Örtüşme, cümle ortasında kesilen bilginin kaybolmasını engeller.

def chunk_text(text: str, max_chars: int = 1000, overlap: int = 150) -> list[str]:
    """Metni örtüşmeli parçalara böler."""
    if max_chars <= overlap:
        raise ValueError("max_chars, overlap'ten büyük olmalı")

    chunks: list[str] = []
    start = 0
    while start < len(text):
        end = start + max_chars
        chunks.append(text[start:end])
        start = end - overlap
    return chunks

Bu fonksiyon karakter bazlı çalışır. Üretimde token bazlı bölme daha doğru sonuç verir; çünkü model ve gömme modeli token sınırlarıyla çalışır. İlgili kütüphanenin tokenizer'ını kullanarak karakter yerine token sayısını sınırlayabilirsiniz.

Beklenen davranış: chunk_text("a" * 2500, max_chars=1000, overlap=150) çağrısı, her biri en fazla 1000 karakter olan ve bir öncekiyle 150 karakter örtüşen parçalar döndürür.

2. Gömme Üretimi

Her parçayı bir gömme modeliyle vektöre çevirin. Aynı modeli hem indeksleme hem sorgulama için kullanmanız zorunludur; farklı modeller farklı vektör uzayları üretir ve benzerlik hesabı anlamsızlaşır.

from openai import OpenAI

client = OpenAI()  # API anahtarını ortam değişkeninden okur

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]

text-embedding-3-small gibi model adları sağlayıcı tarafından değiştirilebilir. Kullandığınız sağlayıcının güncel dokümantasyonundan geçerli model adını doğrulayın; bu rehber belirli bir model sürümünü garanti etmez.

3. Vektör Deposu

Küçük ölçekli işler için bellekte tutulan bir liste yeterlidir. Üretimde ise kalıcı, indeksli bir vektör veritabanı gerekir. Seçim yaparken şu kriterlere bakın:

  • Ölçek: Kaç vektör saklayacaksınız?
  • Filtreleme: Meta veriye göre ön filtreleme gerekiyor mu (örneğin yalnızca belirli bir departmanın belgeleri)?
  • Barındırma: Yönetilen bir servis mi, kendi sunucunuzda mı çalışacak?

Aşağıda, harici bir servise bağlı kalmadan benzerliği hesaplayan minimal bir örnek var. Bu, kavramı göstermek içindir; milyonlarca vektörde kaba kuvvet arama yavaşlar.

import math

def cosine_similarity(a: list[float], b: list[float]) -> float:
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = math.sqrt(sum(x * x for x in a))
    norm_b = math.sqrt(sum(y * y for y in b))
    if norm_a == 0 or norm_b == 0:
        return 0.0
    return dot / (norm_a * norm_b)

Beklenen davranış: Aynı vektörün kendisiyle benzerliği 1.0 döner; dik (ilişkisiz) iki vektörde sonuç 0'a yaklaşır.

4. Getirme (Retrieval)

Sorgu gömme vektörünü üretip, depodaki tüm vektörlerle benzerliği hesaplayıp en yüksek skorlu k parçayı seçin.

def retrieve(query: str, store: list[dict], k: int = 4) -> list[dict]:
    query_vec = embed([query])[0]
    scored = [
        {**item, "score": cosine_similarity(query_vec, item["embedding"])}
        for item in store
    ]
    scored.sort(key=lambda x: x["score"], reverse=True)
    return scored[:k]

store yapısı, her öğede embedding ve text alanlarını taşıyan bir liste olarak düşünülebilir.

Yaygın hata: k değerini sabit ve büyük tutmak. Çok fazla parça getirmek, modelin bağlam penceresini doldurur, alakasız bilgiyi karıştırır ve maliyeti artırır. k değerini ölçerek ayarlayın.

5. Üretim (Generation)

Getirilen parçaları tek bir bağlam bloğunda birleştirip modele verin. İstemde modelin yalnızca verilen bağlama dayanmasını açıkça isteyin.

def build_prompt(question: str, contexts: list[str]) -> str:
    joined = "\n\n---\n\n".join(contexts)
    return (
        "Aşağıdaki bağlamı kullanarak soruyu yanıtla. "
        "Yanıt bağlamda yoksa 'Bu bilgi verilen kaynaklarda yok' yaz. "
        "Kaynak uydurma.\n\n"
        f"Bağlam:\n{joined}\n\n"
        f"Soru: {question}"
    )

Bu istem, modelin halüsinasyon eğilimini azaltmaya yardımcı olur ancak ortadan kaldırmaz. Yanıtı üretim ortamında her zaman doğrulama katmanından geçirin.

Yaygın Hatalar

  • Farklı gömme modelleri kullanmak: İndeksleme ve sorgulama aynı modeli kullanmalıdır.
  • Örtüşmesiz parçalama: Cümle ortasında kesilen bilgi kaybolur.
  • Bağlamı sıralamadan birleştirmek: En alakalı parçayı bağlamın başına veya sonuna koymak, modelin dikkatini artırır.
  • Sorguyu olduğu gibi göndermek: Kısa veya belirsiz sorgular zayıf getirme sonucu verir. Sorgu genişletme (query expansion) veya yeniden yazma (query rewriting) düşünün.
  • Getirilen her şeyi bağlama doldurmak: Token bütçesini aşar ve kaliteyi düşürür.

Güvenlik Etkileri

  • İstem enjeksiyonu: Getirilen belgeler, modelin talimatlarını geçersiz kılmaya çalışan metin içerebilir. Bağlamı güvenilmeyen veri olarak ele alın; kullanıcı talimatlarını bağlamdan ayırın.
  • Veri sızıntısı: Getirme katmanı, kullanıcının yetkisi olmayan belgeleri döndürebilir. Erişim kontrolünü vektör deposu sorgusuna meta veri filtresi olarak ekleyin; yalnızca kullanıcının erişebildiği parçaları getirin.
  • PII ve gizli veri: Gömme vektörleri geri döndürülemez görünse de ilişkili metinle birlikte saklanır. Hassas veriyi indekslemeden önce maskeleyin veya hariç tutun.
  • Maliyet ve kötüye kullanım: Getirme ve üretim adımları API çağrısı yapar. Hız sınırı ve istek başına token sınırı uygulayın.

Doğrulama ve Değerlendirme

RAG hattını "çalışıyor" demekle bırakmayın. En azından şu iki soruyu ölçün:

  1. Getirme doğruluğu: Doğru yanıtı içeren parça, ilk k sonuç arasında geliyor mu?
  2. Yanıt sadakati: Model, yalnızca getirilen bağlama mı dayanıyor, yoksa dışarıdan bilgi mi katıyor?

Bunun için elinizde yanıtı bilinen bir dizi test sorusu tutun. Her değişiklikte (parçalama boyutu, k, istem) bu seti yeniden çalıştırın. Ölçüm olmadan yapılan ayarlar tahminden ibarettir.

Sonraki Adımlar

Temel hat çalıştığında şu iyileştirmeler gündeme gelir: hibrit arama (anahtar kelime + vektör), yeniden sıralama (reranking), sorgu genişletme ve önbellekleme. Bunların her biri kendi başına bir konu; önce temel hattı ölçülebilir hale getirin, sonra ekleyin.

İLGİLİ İÇERİKLER

Bunları da Okumak İsteyebilirsiniz