Bugün Passage, herhangi bir web sitesini tarayarak ya da eklenen metinlere dayanarak soruları kaynak göstererek yanıtlıyor ve kendi test setimde %98 doğru cevap oranına ulaşıyor. Bitirme projesi olarak aldığımda RAG konusunda hiç deneyimim yoktu; sıfırdan araştırıp öğrendim ve bugünkü hâline getirdim.
Genel amaçlı chatbot'lar güven vermiyor
Bir ziyaretçi bir web sitesine soru sorduğunda iki şeyden emin olmak ister: cevabın doğru olduğundan ve nereden geldiğinden. Genel amaçlı chatbot entegrasyonları çoğunlukla ikincisini atlıyor — kaynak göstermeden cevap veriyor, bu da cevabı denetlemeyi imkânsız hale getiriyor. Daha kötüsü, sistemin bilmediği bir konuda soru geldiğinde çoğu model "bilmiyorum" demek yerine inandırıcı ama uydurma bir cevap üretiyor.
Buna bir problem daha ekleniyor: kullanıcılar aynı bilgiyi farklı şekillerde arıyor. Kimi "İade süreci nasıl işliyor?" gibi doğal bir cümle yazıyor, kimi "iade süresi" gibi arama motoruna alışmış kısa bir sorgu giriyor. Tek bir arama yöntemi bu iki alışkanlığı aynı anda karşılayamıyor.
Kaynağını gösteren, uydurmayan bir asistan kurmak
Hedefim; herhangi bir web sitesinin içeriğini otomatik tarayıp güncel tutan, sorulara yalnızca bu kaynaklara dayanarak ve kaynak göstererek cevap veren, kapsam dışı bir soru geldiğinde bunu açıkça belirten ve hem doğal cümlelerde hem kısa sorgularda tutarlı doğrulukta çalışan bir sistem kurmaktı.
Sorudan kaynaklı cevaba
Sistem iki ayrı akışla çalışıyor. Ziyaretçi tarafında: soru sorulur → hybrid arama (semantic + anahtar kelime) ilgili metin parçalarını bulur → sonuçlar Reciprocal Rank Fusion ile birleştirilir → bir reranker en alakalı adayları öne çıkarır → model, yalnızca bu kaynaklara dayanarak kaynak göstererek cevap üretir. İçerik yöneticisi tarafında ise: URL eklenir ya da site taranır → sayfa chunk'lara bölünüp embedding'i çıkarılır → içerik değişmediyse bu adım atlanır → sistem kendini periyodik olarak yeniden tarar.
Tahmin etmeden, ölçerek ilerledim
Bu projede takip ettiğim yaklaşım hep aynıydı: bir özelliği sırf daha gelişmiş göründüğü için sisteme eklemedim. Hybrid arama, reranking, otomatik yenileme gibi her yeni katmanı önce gerçekten bir ihtiyacı karşılayıp karşılamadığını sorgulayarak, sonra ölçerek ekledim. Karmaşıklığın kendisi hedef değildi — hedef doğru soruyu doğru şekilde cevaplamaktı.
Katmanlı ve izole
Backend route → servis → repository → veritabanı sırasıyla katmanlı; hiçbir route doğrudan veritabanına konuşmuyor. Ingest, retrieval, reranking, LLM çağrısı gibi her adım kendi servis dosyasında izole; hybrid arama ve reranking birer özellik anahtarı, yani sistemi semantic-only'den hybrid+rerank'a kademeli olarak açabiliyorum.
Bu kademeli açılış rastgele değildi: önce sade semantic aramayla çalıştırdım, sonra hybrid'i ekledim, en son rerank'ı kattım. Her katmanı tek başına test edip öyle üstüne ekledim, hepsini birden yazıp umut etmedim.
Neden hybrid arama, reranking ve PostgreSQL?
Neden hybrid arama? Doğal cümlelerle sorulan sorularda semantic arama zaten oldukça başarılıydı, ama kullanıcılar arama motoru alışkanlığıyla kısa sorgular yazdığında ("iade süresi" gibi) tek başına yetersiz kalıyordu. Anahtar kelime araması bu kısa sorgularda daha güvenilirdi, bu yüzden ikisini Reciprocal Rank Fusion ile birleştirdim — sadece daha gelişmiş göründüğü için değil, ölçtüğümde gerçekten fark yarattığı için.
Neden reranking? İki aramanın sonuçlarını birleştirmek tek başına yeterli değildi; modelin önüne çok fazla aday koyduğumda gerçekten alakalı bilgi arada kaybolabiliyordu. Reranker, her adayı soruyla yeniden karşılaştırıp en alakalı olanları öne çıkarıyor.
Neden PostgreSQL + pgvector? Embedding'leri (pgvector) ve klasik anahtar kelime aramasını (full-text search) ayrı bir vektör veritabanı eklemeden, tek bir PostgreSQL üzerinde aynı sorguda birleştirebiliyorum — gereksiz bir sistem katmanı eklemiş olmuyorum.
Neden idempotent içerik ekleme? Bir sayfa aynı içerikle tekrar taranırsa embedding'i yeniden üretmek gereksiz maliyet ve gecikme demek; bu yüzden içerik değişmediyse embedding adımını atlıyorum.
Bulguları kafamdan atmadım, test ettim
Kendi kalite kontrol panelimi kurdum çünkü retrieval'ın gerçekte ne yaptığını görmeden ayar değiştirmek bana mantıklı gelmedi. Panel her sorgu için hangi chunk'ın hangi arama yolundan (semantic, keyword, ikisi birden ya da komşu) geldiğini, benzerlik skorunu ve modele giden nihai bağlamı gösteriyor.
Bunu kullanarak 25 sorudan oluşan bir test seti hazırladım: doğal cümle soruları, arama çubuğu tarzı kısa sorgular, çoklu kaynak gerektiren karşılaştırmalar, kapsam dışı sorular ve bir prompt-injection denemesi. Sonuç öğreticiydi: doğal cümlelerde anahtar kelime araması neredeyse hiç devreye girmedi (10 sorudan 1'i), ama arama motoru gibi yazılan kısa sorgularda getirilen sonuçların tamamı anahtar kelime eşleşmesinden de geçti. Yani hybrid arama her yerde değil, tam olarak beklediğim yerde işe yarıyor.
Panel, retrieval'ı mümkün kılmadı. Hızlı ve tekrarlanabilir kıldı.
Bugün canlıda çalışan, ölçülmüş bir ürün
İlk sürümdeki terminal tabanlı, LM Studio üzerinden çalışan kurulumu tamamen bıraktım — kaynak yetersizliği ciddi bir tıkanıklıktı ve arayüzü olmayan bir sistemi kimseye gösteremezdim. Bugün Passage; sohbet, içerik yönetimi ve kalite kontrolü olmak üzere üç ayrı panel ve bir landing page üzerinden çalışan, canlıda kullanılan bir ürün. Kendi test setimde ulaştığım %98 doğru cevap ve %96 halüsinasyonsuz cevap oranı, bu retrieval hattının rastgele değil, ölçülerek kurulduğunun bir kanıtı.
Bir şeyi bilmiyor olmak, o işe başlayamayacağın anlamına gelmiyor. Bazen en iyi öğrenme yöntemi, doğrudan problemin içine girmek.