Ana sayfa → Teknik
RAG: Arama Değil, Bağlam Kurmak
“Günlük çekim limiti kuralı ne?” Agent cevap verdi: net, gerekçeli, kaynaklı. Cevap yanlıştı — daha doğrusu 14 gün eskiydi. Limit politikası iki hafta önce güncellenmişti; doküman yenilenmiş, index yenilenmemişti. Model uydurmadı; ona eski kâğıdı biz verdik. RAG’in zor kısmı arama değil, modelin önüne hangi metnin konduğu.
- Önce karar: RAG mı, tool mu? Cevabı bir SQL sorgusu veriyorsa tool; cevabı bir insanın doküman okuyup yorumlaması gerekiyorsa RAG. İlk sürümümüzün en büyük hatası bu ayrımı yapmamaktı.
- Chunk sınırı dokümanın yapısıdır, sabit karakter sayısı değil. Bölüm başlığı chunk’a eklenmezse parça bağlamsız kalır.
- Tazelik bir özellik değil, bir taahhüttür. Index güncellemesi 14 gün gecikiyorsa sistemin cevabı da 14 gün eskidir; kullanıcı bunu göremiyorsa yanılır.
- Retrieval ayrı ölçülür, cevap ayrı. Doğru parça ilk 5’te mi? Bizde recall@5 %71’di; reranking ile %89 oldu.
- “Bilmiyorum” öğretilir: benzerlik eşiği + alıntı zorunluluğu. Kaynaksız cevap oranımız %14’ten %2’ye indi.
- Yetki index’te durur. Her chunk’ta kimin görebileceği yazılı; filtreleme arama anında yapılır, cevap üretildikten sonra değil.
Asıl karar: RAG mı, tool mu?
İlk sürümde bütün soruları aynı yoldan geçirdik: soruyu al, dokümanlarda ara, bulduğunu modele ver. “Bugün kaç dolar depozit oldu?” sorusu da o yoldan geçti ve dokümanlardan “depozit süreçleri” sayfasını getirdi. Cevap güzel bir paragraftı ve içinde rakam yoktu.
| Soru | Doğru yol | Neden |
|---|---|---|
| “Bugün kaç dolar depozit oldu?” | Tool | Cevap bir sorgudan gelir, kesindir |
| “Günlük çekim limiti kuralı ne?” | RAG | Cevap bir politika dokümanında, yorum gerektirir |
| “Bu müşteri limiti aşmış mı?” | İkisi | Kural RAG’den, rakam tool’dan; karşılaştırma kodda |
| “Geçen yıl bu kararı neden aldık?” | RAG | Karar kayıtları, toplantı notları |
| “KYC’de kaç başvuru bekliyor?” | Tool | Sayı; vektör araması burada yanlış araç |
Üçüncü satır en sık gelen tip ve en öğretici olanı: kuralı RAG getiriyor, rakamı tool getiriyor, karşılaştırmayı kod yapıyor. Modele “bu müşteri limiti aşmış mı” diye sorup hem kuralı hem rakamı onun kafasında birleştirmesini beklemek, üç hatanın kapısını birden açmak.
Chunk: sınırı doküman çizer
İlk denememizde 1.000 karakterde bölmüştük. Sonuç: bir limit kuralının başı bir chunk’ta, istisnası diğerinde. Model istisnayı hiç görmedi ve kuralı eksik anlattı.
{
"id": "politika-cekim-v7#gunluk-limit",
"baslik_yolu": "Cekim Politikasi > Limitler > Gunluk limit",
"metin": "Gunluk cekim limiti ... (bolumun tamami, 420 token)",
"kaynak": "politika/cekim.md",
"surum": 7,
"guncelleme": "2026-09-08",
"yetki": ["destek", "risk", "yonetim"],
"komsu": ["politika-cekim-v7#istisnalar"]
}
- Başlık yolu metne eklenir. Model “hangi politikanın hangi bölümü” olduğunu ancak böyle bilir; tek başına paragraf, bağlamsız bir cümle yığınıdır.
- Hedef 300–800 token. Kısa olursa kural parçalanır, uzun olursa alakasız metin pencereyi yer.
- Komşu bağlantısı. “İstisnalar” bölümü ayrı bir chunk ama komşu olarak işaretli; kural geldiğinde istisnası da geliyor. Bu tek ayar, eksik cevapların çoğunu bitirdi.
- Yetki etiketi. Arama sırasında filtreleniyor. Cevap üretildikten sonra filtrelemek, veriyi zaten modele göstermiş olmak demektir.
Tazelik: bizim 14 günümüz
Giriş paragrafındaki olayın sebebi basitti: index’i gecelik bir iş güncelliyordu ve o iş iki hafta önce sessizce ölmüştü. Kimse fark etmedi, çünkü sistem cevap vermeye devam ediyordu. Bozuk bir RAG sessizce çalışır; eski cevap, hata mesajı gibi görünmez.
Üç değişiklik yaptık:
- Doküman değişince index güncellenir (webhook), gecelik iş yedek olarak kaldı.
- Her cevabın altında kaynak ve tarih var: “Kaynak: Çekim Politikası v7, güncelleme 8 Eylül 2026”. Kullanıcı eski bir tarih görürse itiraz edebiliyor.
- Tazelik alarmı: index’teki en eski doküman ile kaynaktaki sürüm arasındaki fark 24 saati geçerse alarm. Bu, ölen işi ikinci kez saklamadı.
İsabeti ölçmek: iki ayrı sayı
“RAG iyi çalışıyor mu?” tek soru değil, iki soru: doğru parçayı getirdi mi, ve o parçadan doğru cevabı üretti mi? İkisi ayrı ölçülür, çünkü çözümleri farklı.
| Ölçüm | Nasıl | Bizde |
|---|---|---|
| recall@5 (retrieval) | 30 soru, her birinin doğru chunk’ı elle işaretli; ilk 5’te var mı? | %71 → %89 |
| Cevap doğruluğu | Aynı 30 soru, doğru chunk verilerek | %93 |
| Kaynaksız cevap | Alıntısı olmayan iddia sayısı | %14 → %2 |
| “Bilmiyorum” isabeti | Dokümanda olmayan 10 soru; kaçında doğru şekilde reddetti? | 6/10 → 10/10 |
İkinci satır önemli: doğru parça verildiğinde cevap doğruluğu zaten %93’tü. Yani sorunumuz model değil, retrieval’dı. Model değiştirmek bu tabloyu düzeltmezdi; iyileştirme gereken yeri ancak iki sayıyı ayırdığımızda gördük.
recall@5’i ne yükseltti?
- Reranking. Vektör araması 20 aday getiriyor, küçük bir sıralayıcı model bunları soruya göre yeniden sıralıyor, ilk 5 modele gidiyor. Tek başına en büyük katkı: %71 → %84.
- Anahtar kelime ile birleştirme. Sadece vektör araması “IBAN”, “MT103”, “v7” gibi tam eşleşmeleri kaçırıyor. Klasik metin araması ile birleştirince %84 → %89.
- Başlık yolunu metne katmak. Küçük ama bedava: aynı kelimenin hangi politikada geçtiğini ayırt ediyor.
“Bilmiyorum” demeyi öğretmek
En tehlikeli cevap yanlış cevap değil, kaynaksız ama emin cevap. İki kural koyduk:
# 1) Esik: getirilen parcalar yeterince benzemiyorsa modele hic sorma
if en_iyi_puan < ESIK:
return "Bu konuda dokumanim yok. Politika ekibine sorabilirim."
# 2) Alinti zorunlulugu: her iddia bir parcaya baglanir
# cevap uretildikten sonra kod kontrol eder:
for cumle in cevap.iddialar():
if not cumle.kaynak_id:
cevaptan_cikar(cumle) # kaynaksiz cumle cevapta kalmaz
İkinci kural cevabı bazen kısaltıyor ve bu iyi bir şey. Kullanıcıya giden metnin altında kaynak listesi var; tıklayıp dokümanın kendisini açabiliyor. Ölçtüğümüz en güzel yan etki: kullanıcılar artık cevabı doğrulayabildikleri için sisteme daha çok güveniyor — güven, emin görünmekten değil, gösterilebilir kaynaktan geliyor.
Sahadan: üç hafta, üç düzeltme
| Hafta | Sorun | Düzeltme | Sonuç |
|---|---|---|---|
| 1 | Rakam soruları dokümanlardan cevaplanıyor | Yönlendirici: rakam → tool, metin → RAG | 12 sorunun 12’si doğru yola gitti |
| 2 | Kural geliyor, istisnası gelmiyor | Komşu chunk bağlantısı | Eksik cevap 9 → 1 |
| 3 | 14 gün eski politika | Webhook + tazelik alarmı + cevapta tarih | Gecikme 14 gün → ~2 dakika |
- Soruyu önce yönlendir: tool mu, RAG mi?
- Chunk’ı başlık düzeyinde böl, başlık yolunu metne kat.
- Metadata tut: sürüm, tarih, yetki, komşu.
- Yetkiyi arama anında filtrele.
- recall ve cevap doğruluğunu ayrı ölç.
- Eşik + alıntı zorunluluğu; cevapta kaynak ve tarih göster.
- Rakam sorularını vektör aramasına sordurmak.
- Sabit karakter sayısıyla bölmek.
- Index güncellemesini gecelik bir işe bırakıp izlememek.
- “Model uyduruyor” deyip model değiştirmek — önce recall’a bak.
- Cevabı üretip sonra yetkiye göre gizlemek.
- Kaynağı kullanıcıdan saklamak.
Ne izlemeli?
- recall@5. Haftalık; 30 soruluk sabit set. Düşerse doküman yapısı değişmiştir.
- Index tazeliği. Kaynaktaki en son değişiklik ile index arasındaki gecikme, p95.
- “Dokümanım yok” oranı. Çok düşükse eşik gevşek (uyduruyor), çok yüksekse dar (işe yaramıyor).
- Kaynaksız iddia sayısı. Sıfıra yakın olmalı; kod kontrolü var.
- Chunk başına çağrılma sayısı. Hiç getirilmeyen dokümanlar: ya gereksiz ya da kötü bölünmüş.
- Yetki filtresi isabeti. Bir kullanıcının görmemesi gereken chunk, sonuçlara hiç girmemeli; test setiyle ölç.
Kontrol listesi
- Bu soru tipinin cevabı yapılandırılmış veride mi, metinde mi?
- Yönlendirici var mı: rakam sorusu tool’a gidiyor mu?
- Chunk sınırını ne belirliyor: başlık mı, karakter sayısı mı?
- Başlık yolu metne ekleniyor mu?
- Metadata’da sürüm, tarih ve yetki var mı?
- Index ne zaman güncelleniyor? Gecikme ölçülüyor mu?
- Silinen doküman index’ten çıkıyor mu?
- recall@k ölçülüyor mu? Kaç soruluk set?
- Reranking var mı? Anahtar kelime aramasıyla birleşiyor mu?
- Eşik altında ne oluyor: uyduruyor mu, “bilmiyorum” mu diyor?
- Cevapta kaynak ve tarih görünüyor mu?
- Yetki filtresi arama anında mı çalışıyor?
Sonuç
Agent o gün doğru cevabı verdi; sadece yanlış kâğıttan okudu. Sonraki iki hafta boyunca “model uyduruyor” diye konuştuk, oysa model önüne konanı düzgünce özetlemişti. İki sayıyı ayırıp retrieval’i ayrı ölçtüğümüzde iş on dakikada netleşti: cevap doğruluğu zaten %93’tü, doğru parçayı getirme oranı %71.
Bugün soru önce yönlendiriliyor, parçalar başlık düzeyinde bölünüyor, sonuçlar yeniden sıralanıyor, eşik altında “dokümanım yok” deniyor ve her cevabın altında kaynağın adı ile tarihi duruyor. Index gecikmesi 14 günden iki dakikaya indi — ama asıl kazanç, geciktiği gün bunu bilecek olmamız.
Akılda kalacak cümle: RAG’de model son adımdır; kalite ondan önceki adımlarda belirlenir. Yanlış kâğıdı iyi özetleyen bir sistem, güvenilir görünen bir yanlış üretir.