Retrieval Neden Bu Kadar Kritik?
RAG, LLM'leri gerçek ve alana özel bilgiyle beslemek için en pratik yöntemlerden biri. Model eğitim sırasında ne ezberlediyse ona güvenmek yerine, sorgu anında ilgili dokümanları çekip prompt'a ekliyorsunuz. Sonuç olarak daha az halüsinasyon, güncel cevaplar ve kaynak gösterebilme imkanı elde ediyorsunuz — production için vazgeçilmez şeyler.
Ama şöyle bir gerçek var: Jupyter notebook'taki RAG prototipini ölçeklenebilir bir production sistemine taşımak bambaşka bir iş. Retrieval kalitesi her şeyi belirliyor. Chunking, embedding veya ranking'deki küçük hatalar bile tüm pipeline'ı sessizce bozabiliyor. Bu yazıda, demo ile production-grade sistem arasındaki farkı yaratan kararları ve kalıpları paylaşacağım.
RAG sisteminiz ancak retrieval'ı kadar iyi olabilir. Doğru bağlam LLM'e ulaşmıyorsa, hiçbir prompt engineering sizi kurtaramaz.
Chunking Stratejileri: Fixed, Semantic ve Recursive
RAG pipeline'ındaki ilk büyük karar dokümanları nasıl parçalara böleceğiniz. Bunu yanlış yaparsanız gerisinin pek önemi kalmıyor — retrieval hassasiyeti düşer, LLM'e gürültülü veya eksik bağlam gider. Üç ana yaklaşım var ve her birinin kendine göre artıları-eksileri mevcut.
Sabit Boyutlu Chunking
En basit yaklaşım: metni belirli bir token veya karakter sayısına göre parçalara ayırıyorsunuz, sınırlarda bağlam kaybını önlemek için genelde biraz örtüşme ekliyorsunuz. Hızlı, öngörülebilir ve kolay. Ama anlam konusunda tamamen kör — bir chunk sınırı cümlenin tam ortasına denk gelebilir ya da önemli bir kavramı ikiye bölebilir. Bu da retrieval kalitesini düşürür.
Semantic Chunking
Semantic chunking daha akıllı bir yol izliyor. Ardışık cümleler arasındaki embedding benzerliğini kullanarak doğal kırılma noktalarını buluyor. Komşu cümlelerin embedding'leri arasındaki kosinüs mesafesi bir eşiği geçtiğinde yeni chunk başlıyor. Konu bütünlüğünü chunk içinde koruyor ama ekstra hesaplama maliyeti getiriyor. Ayrıca benzerlik eşiğini corpus'unuza göre ayarlamanız gerekiyor.
Recursive Chunking
LangChain'in RecursiveCharacterTextSplitter'ıyla popülerleşen recursive chunking, bir ayırıcı hiyerarşisi deniyor — çift satır sonu, tek satır sonu, cümle, kelime — ve ancak chunk hedef boyutu aştığında bir sonraki seviyeye geçiyor. Yapı farkındalığı ile öngörülebilirlik arasında güzel bir denge kuruyor. Pratikte 512 token hedefli ve %10-15 örtüşmeli recursive chunking, çoğu production senaryosu için sağlam bir başlangıç noktası. Özel bir nedeniniz yoksa ben buradan başlamanızı öneririm.
from langchain.text_splitter import RecursiveCharacterTextSplitter
import tiktoken
def create_chunks(documents: list[str], chunk_size: int = 512, overlap: int = 64):
"""Split documents using recursive chunking with token-based sizing."""
tokenizer = tiktoken.encoding_for_model("gpt-4")
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=overlap,
length_function=lambda text: len(tokenizer.encode(text)),
separators=["\n\n", "\n", ". ", " ", ""],
)
chunks = []
for doc in documents:
splits = splitter.split_text(doc)
chunks.extend(splits)
return chunks
# Semantic chunking alternative using embedding distances
from sentence_transformers import SentenceTransformer
import numpy as np
def semantic_chunk(text: str, threshold: float = 0.3):
"""Split text at points where semantic similarity drops below threshold."""
model = SentenceTransformer("all-MiniLM-L6-v2")
sentences = text.split(". ")
embeddings = model.encode(sentences)
chunks, current_chunk = [], [sentences[0]]
for i in range(1, len(sentences)):
similarity = np.dot(embeddings[i], embeddings[i - 1]) / (
np.linalg.norm(embeddings[i]) * np.linalg.norm(embeddings[i - 1])
)
if similarity < threshold:
chunks.append(". ".join(current_chunk))
current_chunk = [sentences[i]]
else:
current_chunk.append(sentences[i])
chunks.append(". ".join(current_chunk))
return chunksRecursive ve semantic chunking — muhtemelen karşılaştıracağınız iki yaklaşım
Doğru Embedding Modelini Seçmek
Embedding modeliniz sistemdeki benzerliğin nasıl çalışacağını belirliyor. Yanlış model seçerseniz retrieval sessizce düşük performans gösterir — sorgular eşleşmesi gereken dokümanlarla eşleşmez. Seçim yaparken şunlara bakın.
- Boyut ve performans dengesi: Yüksek boyutlu embedding'ler (mesela OpenAI'ın text-embedding-3-large için 1536) daha fazla nüans yakalar ama depolama ve arama maliyetini artırır. all-MiniLM-L6-v2 (384 boyut) birçok iş yükü için güzel bir denge sunuyor.
- Alan uyumu: Genel amaçlı modeller özelleşmiş alanlarda (hukuk, tıp, finans) zorlanabiliyor. Birkaç bin alan-özel çift üzerinde fine-tuning yapmak bile recall'da ciddi artış sağlayabiliyor.
- Çok dilli ihtiyaçlar: Birden fazla dile hizmet ediyorsanız multilingual-e5-large gibi çok dilli modeller kullanın. Farklı dillerdeki anlamca eşdeğer sorgular aynı dokümanları getirmeli.
- Matryoshka temsilleri: Bazı yeni modeller inference sırasında embedding boyutlarını kesmenize izin veriyor — yeniden eğitim yapmadan kalite-hız dengesi kurabiliyorsunuz.
MTEB gibi benchmark'lar modelleri karşılaştırmak için güzel ama körü körüne güvenmeyin. Benim gördüğüm en iyi yöntem, en iyi adaylarınızı gerçek sorgular ve ilgililik değerlendirmeleriyle test etmek. Başta birkaç saat harcamak, sonradan aylarca debug yapmaktan kurtarır.
Hybrid Search ve Reranking
Embedding tabanlı arama tek başına production kalitesi için nadiren yeterli oluyor. İki teknik sonuç kalitesinde büyük fark yaratıyor: hybrid search ve reranking.
Hybrid Search
Hybrid search, dense vektör aramasını sparse lexical arama (genelde BM25) ile birleştiriyor. Dense retrieval anlam yakalamada çok iyi — "otomobil" ile "araba"nın ilişkili olduğunu anlıyor. Sparse retrieval ise tam kelime eşleşmelerini, kısaltmaları ve embedding'lerin kaçırabileceği nadir alan terimlerini yakalıyor. İkisini Reciprocal Rank Fusion (RRF) ile birleştirdiğinizde, tek başlarına olduklarından tutarlı şekilde daha iyi sonuç alıyorsunuz. Weaviate, Qdrant, Pinecone gibi modern vektör veritabanları bunu native olarak destekliyor.
Cross-Encoder Reranking
İlk retrieval aşaması bir aday seti döndürdükten sonra (genelde 20-100 doküman), cross-encoder reranker her adayı orijinal sorguya karşı puanlıyor. Sorgu ve dokümanları ayrı ayrı encode eden bi-encoder'lardan farklı olarak, cross-encoder'lar çifti birlikte işliyor — çok daha zengin eşleştirme sağlıyor. Hızlı yaklaşık retrieval + hassas reranking kalıbı, kaliteli RAG sistemlerinin endüstri standardı haline geldi. Sebepsiz değil.
from sentence_transformers import CrossEncoder
import numpy as np
def retrieve_and_rerank(
query: str,
vector_store,
reranker_model: str = "cross-encoder/ms-marco-MiniLM-L-12-v2",
top_k_retrieval: int = 50,
top_k_final: int = 5,
):
"""Two-stage retrieval: vector search followed by cross-encoder reranking."""
# Stage 1: Fast approximate retrieval via vector similarity
candidates = vector_store.similarity_search(query, k=top_k_retrieval)
# Stage 2: Precise reranking with a cross-encoder
reranker = CrossEncoder(reranker_model)
pairs = [(query, doc.page_content) for doc in candidates]
scores = reranker.predict(pairs)
# Sort by reranker score and return top results
ranked_indices = np.argsort(scores)[::-1][:top_k_final]
return [candidates[i] for i in ranked_indices]İki aşamalı retrieval: hızlı vektör araması, ardından hassas reranking
Tip
Reranking gecikme ekliyor — 50 aday için yaklaşık 50-200ms. Aday setini 50 dokümanla sınırlayın ve hafif bir reranker kullanın. Gecikmenin kritik olduğu durumlarda TinyBERT tabanlı distilled modeller güzel bir hız-doğruluk dengesi sunuyor.
Halüsinasyonları Kontrol Altına Almak
Retrieval ne kadar iyi olursa olsun, LLM'ler hâlâ bağlamda desteklenmeyen iddialar üretebiliyor. Production'da — özellikle sağlık, finans veya hukuk alanlarında — halüsinasyonlar sadece sinir bozucu değil, tehlikeli. İşe yarayan stratejileri paylaşayım.
- Alıntıları zorunlu kılın: Modelden her iddiayı belirli bir chunk'a dayandırmasını ve satır içi kaynak göstermesini (örn. [Kaynak 1]) isteyin. Bu hem doğrulamayı kolaylaştırır hem modeli daha disiplinli üretim yapmaya zorlar.
- Güven eşikleri koyun: Hiçbir doküman minimum ilgililik puanını geçmiyorsa, sistem cevap uydurmak yerine "Bu soruyu yanıtlamak için yeterli bilgim yok" desin. Dürüst bir "bilmiyorum" her zaman kendinden emin bir halüsinasyondan iyidir.
- Öz-tutarlılık kontrolü yapın: Birden fazla cevap üretip aralarındaki uyumu kontrol edin. Üç bağımsız üretim birbiriyle çelişiyorsa, cevap güvenilir değildir — insan incelemesine yönlendirin.
- Sadakat değerlendirmesi çalıştırın: NLI modelleri üretilen her cümleyi bağlama göre desteklenen, çelişen veya nötr olarak sınıflandırır. Çelişen cümleler güçlü halüsinasyon sinyalleridir.
- Bağlam penceresini iyi yönetin: Marjinal olarak ilgili chunk'larla bağlamı şişirmeyin. Özenle seçilmiş 3-5 yüksek ilgili chunk, 20 tane gevşek ilgili pasajı yığmaktan neredeyse her zaman daha iyi sonuç verir.
En etkili yaklaşım tek bir teknik değil, hepsini katmanlı kullanmak: güçlü retrieval, hassas reranking, kısıtlanmış prompting ve üretim sonrası doğrulama birlikte çalışmalı.
İzleme ve Değerlendirme
Ölçemediğiniz şeyi iyileştiremezsiniz. Production'daki bir RAG sistemi hem retrieval hem generation tarafında sürekli izleme gerektirir. Bu olmadan kalite haftalarca sessizce düşebilir ve kimse fark etmez.
Retrieval Metrikleri
Standart IR metriklerini — Recall@k, MRR ve NDCG — sorgu-ilgililik çiftlerinden oluşan bir eval seti üzerinden takip edin. Corpus güncellemesinden sonra Recall@10 0.85'ten 0.72'ye düşüyorsa, chunking veya embedding pipeline'ında bir şeylerin bozulduğunun net işareti. Bu gerilemeleri hızlı yakalamanız gerekiyor.
Üretim Kalitesi Metrikleri
Generation tarafında sadakat (cevap bağlamda mı temellendirilmiş?), ilgililik (soruyu gerçekten cevaplıyor mu?) ve tamlık (tüm yönleri kapsıyor mu?) metriklerine bakıyorsunuz. RAGAS ve DeepEval gibi araçlar LLM-as-judge teknikleriyle bu değerlendirmeleri otomatize ediyor. Manuel etiketleme yapmadan sürekli regresyon testi çalıştırabiliyorsunuz.
- Her sorguyu, alınan bağlamı ve üretilen cevabı offline analiz için kaydedin.
- Retrieval skor dağılımlarına alert kurun — ani kaymalar genelde corpus veya embedding sorunlarına işaret eder.
- Kullanıcı geri bildirimini (beğen/beğenme, sorgu yeniden formüle etme) örtük kalite sinyali olarak takip edin.
- Kademeli sapmaları yakalamak için haftalık otomatik eval'leri golden dataset üzerinde çalıştırın.
Önemli Çıkarımlar
Production-ready bir RAG sistemi kurmak bir mühendislik disiplini — tek seferlik bir deployment değil. En çok fark yaratan kararlar chunking stratejisi, embedding modeli seçimi ve hybrid search + reranking uygulaması. Bunları doğru yaparsanız downstream'deki her şey bundan faydalanır.
- Recursive chunking ile başlayın — 512 token hedefli, örtüşmeli. Çoğu doküman türünde iyi çalışan sağlam bir varsayılan.
- Embedding modellerini kendi alanınızdaki gerçek sorgularla değerlendirin, sadece public benchmark'larla değil. Başta birkaç saat test etmek, sonradan aylarca debug yapmaktan kurtarır.
- Hybrid search (dense + sparse) ve cross-encoder reranking kullanın. İki aşamalı kalıp eklediği karmaşıklığa değiyor — ciddi her RAG deployment'ında bunu görürsünüz.
- Halüsinasyon savunmalarınızı katmanlayın: kısıtlanmış prompting, güven eşikleri ve üretim sonrası sadakat kontrolleri birlikte çalışmalı.
- Değerlendirme altyapısını erken kurun. Otomatik retrieval ve generation metrikleri + özenle hazırlanmış golden dataset, zamanla kaliteyi korumak için şart.
- Her şeyi izleyin. Sorgu logları, retrieval skorları, generation kalitesi, kullanıcı geri bildirimi — bunlar veri ve kullanım kalıpları değiştikçe sistemi sağlıklı tutan sürekli iyileştirme döngüsünü oluşturur.
RAG çözülmüş bir problem değil. Alan hızla ilerliyor — agentic retrieval, multi-hop reasoning, knowledge graph entegrasyonu gibi yenilikler sınırları zorluyor. Ama burada anlattığım temeller, güvenilir, gözlemlenebilir ve gerçek trafiğe hazır sistemler kurmak için sağlam bir zemin sağlıyor.