Ayarlar

Dil

Semantic Cache'iniz Neden Yanlış Cevaplar Döndürüyor

L
LemonData
·5 Mart 2026·816 görüntüleme
Semantic Cache'iniz Neden Yanlış Cevaplar Döndürüyor

Bir kullanıcı, çeviri eklentimizin girdiden bağımsız olarak her istek için aynı önbelleğe alınmış sonucu döndürdüğünü bildirdi. İnceleme yaptık ve daha kötü bir durumla karşılaştık: Platformumuzdaki tüm semantik önbellek eşleşmelerinin (semantic cache hits) %95'i hatalı eşleşmeydi (false positive). 199 farklı çeviri isteği, 198 benzersiz istek gövdesi ve hepsine sunulan tek bir önbelleğe alınmış yanıt.

Uzun ömürlü ajan durumu ve üretim ortamı istek yönetimiyle ilgileniyorsanız, bu yazı AI Ajanınız Neden Hafızasını Kaybedip Duruyor, tek anahtarlı chatbot rehberi ve AI API hız sınırlama rehberi ile iyi bir bütünlük oluşturacaktır.

Hata Raporu

Hata raporu basitti: "Semantik önbelleği devre dışı bıraktım ama her çeviri aynı sonucu döndürüyor."

Üç istek ID'si, üç farklı çeviri segmenti, birbirinin aynısı önbelleğe alınmış yanıtlar. İstek gövdeleri 1.564 ile 8.676 byte arasındaydı. Önbelleğe alınan yanıt ID'si hepsinde aynıydı: chatcmpl-DG6J03nhdvcF7Ek0C8rJkjh7lN9pF.

İlk şüphe: Kullanıcının önbellek ayarlarının uygulanmadığıydı. Bunun ayrı bir veri kaynağı senkronizasyon hatası olduğu ortaya çıktı (admin paneli bir tabloya yazıyor, API gateway diğerinden okuyordu). Ancak bunu düzeltmek sorunun sadece yarısını çözdü. Önbellek etkinleştirildiğinde ve doğru çalıştığında bile, semantik önbellek asla eşleşmemesi gereken istekleri eşleştiriyordu.

Üretim Verileri

ClickHouse'dan 24 saatlik önbellek eşleşme verilerini çektik. Rakamlar kötüydü.

Model Toplam İstek Önbellek Eşleşmesi Benzersiz İstek Benzersiz Yanıt Eşleşme Oranı
gpt-4.1-nano 200 199 198 1 99.5%
glm-4.6-thinking 100 38 13 1 38%
gpt-5-nano 31 29 28 2 93.5%
gpt-oss-120b 18 17 17 1 94.4%
qwen3-vl-flash 17 16 16 1 94.1%

198 benzersiz çeviri isteği, hepsi aynı tek önbelleğe alınmış yanıtı döndürüyor. Bu bir önbellek değil. Bu, sabit bir değer döndüren bozuk bir fonksiyondur.

Etkilenen her model iki ortak özelliğe sahipti: Tüm istekler tek bir kullanıcıdan geliyordu ve hepsi değişen kullanıcı içeriğine sahip sabit bir sistem istemi (system prompt) şablonu kullanıyordu.

Embedding'ler Yapılandırılmış Girdilerde Neden Başarısız Olur?

Çeviri eklentisi şu şekilde istekler gönderiyor:

System: "Act as a translation API. Output a single raw JSON object only.
         Input: {"targetLanguage":"<lang>","title":"...","segments":[...]}"

User:   {"targetLanguage":"zh","title":"Product Page",
         "description":"Translate product descriptions",
         "tone":"formal",
         "segments":[{"text":"actual varying content here"}]}

Sistem istemi tüm isteklerde aynıdır. Kullanıcı mesajı; targetLanguage, title, description ve tone alanlarının sabit olduğu bir JSON nesnesidir. Sadece segments[].text değişir.

Semantik önbelleğimiz embedding için metin çıkardığında, sistem istemini ve kullanıcı mesajını birleştirir. Sabit şablon, metnin yaklaşık %80'ini oluşturur. Embedding modeli (all-mpnet-base-v2, 768 boyutlu), bunu şablon yapısının baskın olduğu bir vektöre sıkıştırır. Gerçek çeviri içeriği ibreyi neredeyse hiç oynatmaz.

Sonuç: "translate 'Hello world'" ile "translate 'The quarterly financial report shows a 15% increase in revenue'" arasındaki kosinüs benzerliği (cosine similarity) 0.95'i aşıyor. Bizim eşik değerimiz 0.95. Her çeviri isteği, önbelleğe alınan ilk girdiyle eşleşiyor.

Günlükleri incelediğimizde, bu durumun üç farklı şekilde bozulduğunu gördük:

Çeviri eklentisi en kötü örnekti. Sabit JSON anahtarları ve değerleri, gerçek çeviri segmentlerini bastırıyordu. Hem gpt-4.1-nano hem de gpt-5-nano bu soruna takıldı.

Bir bağlam özetleme asistanı aynı sorunun farklı bir versiyonunu yaşıyordu. Sistem istemi o kadar uzundu ki, kullanıcı içeriği (5KB ile 47KB arası) embedding içinde zar zor fark ediliyordu. glm-4.6-thinking'in her konuşma için aynı özeti döndürmesinin sebebi buydu.

Üçüncü model daha sinsiydi. gpt-oss-120b ve qwen3-vl-flash için her isteğin ilk 500 karakteri byte'ı byte'ına aynıydı. Değişen içerik sonradan geliyordu ancak embedding çoktan paylaşılan önek (prefix) tarafından domine edilmişti.

Araştırmalar Ne Diyor?

Bu yeni bir sorun değil. Yakın zamandaki makaleler bunu nicelendirdi.

UC Berkeley'in vCache projesi, doğru ve yanlış önbellek eşleşmelerinin "yüksek oranda örtüşen benzerlik dağılımlarına" sahip olduğunu buldu. Optimal eşik değeri, farklı önbelleğe alınmış girdiler arasında 0.71 ile 1.0 arasında değişiyor. Tek bir rakam işe yaramıyor. Çözümleri: Önbellek girişi başına ayrı bir eşik değeri öğrenmek; bu, hata oranlarını 6 kat azaltırken eşleşme oranlarını iki katına çıkardı. (vCache, 2025)

Sorgu türlerini karıştırdığınızda durum daha da kötüleşiyor. Kategori duyarlı bir önbellekleme çalışması, 0.80 eşik değerinin kod sorgularında (sort_ascending vs sort_descending) %15 hatalı eşleşme ürettiğini, aynı eşik değerinin ise konuşma sorgularındaki geçerli başka sözcüklerle anlatımları (paraphrase) kaçırdığını gösterdi. Tek eşik, iki hata modu. (Category-Aware Semantic Caching, 2025)

Bankalar da bu sorunla karşılaşıyor. Bir InfoQ vaka çalışması, "Bu ay kredi ödememi atlayabilir miyim" sorusunun "Kredi ödemesini kaçırırsam ne olur" sorusuyla %88.7 benzerlikle eşleştiği bir RAG sistemini belgeledi. Farklı niyet, aynı önbelleğe alınmış yanıt. %99 hatalı eşleşme oranıyla başladılar ve bunu %3.8'e indirmek için dört tur optimizasyon yapmaları gerekti. (InfoQ Banking Case Study, 2025)

Daha derin bir sorun: Embedding'ler iki istemin semantik olarak benzer olup olmadığını ölçer, aynı yanıtın her ikisine de cevap verip veremeyeceğini değil. Hatalı önbellek eşleşmeleri bu boşlukta yaşar. (Efficient Prompt Caching via Embedding Similarity, 2024)

Bulduğumuz her makale bir konuda hemfikir: Tek başına embedding benzerliği yeterli değil. Bir doğrulama katmanına (verification layer) ihtiyacınız var.

İki Katmanlı Çözüm

İki savunma hattı oluşturduk. İlki, embedding öncesinde şablon gürültüsünü temizliyor. İkincisi, eşleşme sonrası isabetleri doğruluyor.

Katman 2: Embedding İçin İçerik Çıkarma

Bir embedding oluşturmadan önce, artık yapılandırılmış girdiyi (JSON) tespit ediyor ve yalnızca anlamlı, değişken içeriği çıkarıyoruz.

Mantık:

  1. Mesaj içeriğinin { veya [ ile başlayıp başlamadığını kontrol et
  2. JSON olarak ayrıştırılabiliyorsa, tüm dize (string) yaprak değerlerini yinelemeli olarak topla
  3. Kısa değerleri (20 karakter veya daha az) filtrele; çünkü bunlar genellikle "zh", "formal" veya "Product Page" gibi yapılandırma alanlarıdır
  4. Çıkarılan metin çok kısaysa veya boşsa, orijinal metne geri dön
function extractContentForEmbedding(text: string): string {
  const extracted = tryExtractJsonContent(text);
  return extracted && extracted.length > 20 ? extracted : text;
}

Bu hem sistem istemi hem de kullanıcı mesajı için geçerlidir. Çeviri eklentisi için embedding artık 2KB'lık bir JSON bloğu yerine "Hello world"ü temsil ediyor. Özetleme asistanı için ise konuşmayı şablon sarmalayıcısından çıkarıyor.

20 karakterlik eşik deneysel olarak seçildi:

  • "zh" (2 karakter): filtrelendi. Yapılandırma değeri.
  • "formal" (6 karakter): filtrelendi. Yapılandırma değeri.
  • "Product Page" (12 karakter): filtrelendi. Şablon alanı.
  • "Translate product descriptions" (31 karakter): tutuldu. Anlamlı içerik.
  • "The quarterly financial report..." (40+ karakter): tutuldu. Gerçek çeviri içeriği.

Katman 3: Parmak İzi Doğrulaması

Bir semantik önbellek eşleşmesinden sonra, mevcut isteğin çıkarılan metninin hash değerini, önbelleğe alınan girişte saklanan hash ile karşılaştırıyoruz. Eğer eşleşmezlerse, isabet reddedilir.

// Önbelleğe yazarken
entry.metadata.textHash = fnv1aHash(extractedText);

// Önbellekten okurken, benzerlik eşleşmesi bulduktan sonra
if (entry.metadata.textHash !== undefined) {
  if (entry.metadata.textHash !== fnv1aHash(currentExtractedText)) {
    // Hatalı eşleşme: semantik olarak benzer ama içerik farklı
    metrics.recordFingerprintRejection();
    return null;
  }
}

Hash, ham girdi yerine çıkarılan metni (Katman 2 sonrası) kullanır. Farklı şablon sarmalayıcılarına sahip ancak gerçek içeriği aynı olan iki istek hala eşleşir. Farklı içerik, farklı hash, reddedildi.

textHash içermeyen eski önbellek girişleri doğrulamayı atlar (geriye dönük uyumlu). TTL aracılığıyla doğal olarak süreleri dolar.

Hash için FNV-1a (32-bit) kullanıyoruz. Hızlıdır, deterministiktir ve ~4 milyarda 1'lik çakışma oranı tek bir önbellek isabetini kontrol etmek için uygundur.

Neden Sadece Eşik Değerini Yükseltmiyoruz?

Eşik değerimiz zaten 0.95. Bunu yükseltmek yardımcı olmuyor. Sorun, yapısal olarak benzer girdilerin, gerçek içerik ne olursa olsun 0.95'in üzerinde benzerlik puanları üretmesidir.

vCache'in verileri bunu destekliyor: Doğru ve yanlış eşleşmelerin benzerlik dağılımları o kadar çok örtüşüyor ki, hiçbir tek kesme noktası onları ayıramıyor. Eşiği 0.99'a çekerseniz, şablon ağırlıklı isteklerden kaynaklanan hatalı eşleşmeleri ortadan kaldırmadan, başka sözcüklerle anlatılan meşru önbellek isabetlerini öldürürsünüz.

Girdiyi düzeltin, çıktıyı doğrulayın. Eşik değeriyle oynamayın.

Sonuçlar

Her iki katman da devreye alındığında:

Metrik Önce Sonra
gpt-4.1-nano hatalı eşleşmeleri 198/199 0
Tüm önbellek isabetleri içindeki hatalı eşleşme payı ~95% <5%
Meşru önbellek eşleşme oranı Değişmedi Değişmedi
İstek başına eklenen gecikme 0 <1ms (JSON ayrıştırma + FNV hash)

Sadece Katman 2 bile çeviri eklentisini düzeltirdi. Katman 3, JSON çıkarımının içeriği tam olarak ayıramadığı durumlar veya JSON olmayan yapılandırılmış girdiler için bir güvenlik ağıdır.

Önemli Çıkarımlar

Üretim ortamında bir semantik önbellek çalıştırıyorsanız:

  1. Yanıt çeşitliliğini izleyin. Eğer bir modelin %100 önbellek eşleşme oranı ve 1 benzersiz yanıtı varsa, hatalı eşleşme sorununuz var demektir. Sorgu: SELECT model, uniqExact(substring(response_body, 1, 200)) as unique_responses, count() as total FROM request_logs WHERE cache_hit = true GROUP BY model.

  2. Yapılandırılmış girdi, basit embedding'i bozar. Sabit bir şablona sahip her istek (JSON API'leri, sistem istemi sarmalayıcıları, form doldurma görevleri) yapay olarak yüksek benzerlik puanları üretecektir. Embedding öncesinde ön işlem yapın.

  3. Bir doğrulama katmanı isteğe bağlı değildir. Literatürdeki her üretim seviyesi semantik önbellek bir doğrulama katmanına sahiptir. Soru, hafif bir hash kontrolü mü, bir cross-encoder reranker mı yoksa tam bir LLM doğrulama çağrısı mı kullanacağınızdır. Gecikme bütçenize göre seçiminizi yapın.

  4. Küresel eşik değerleri bir çözüm değil, bir tavizdir. Farklı sorgu türleri farklı eşiklere ihtiyaç duyar. Kategori başına veya girdi başına eşik yapamıyorsanız, en azından kategoriler arasında embedding kalitesini normalize etmek için girdi ön işleme ekleyin.

Semantik önbellekleme, LLM API maliyetlerini %30-70 oranında azaltabilir. Ancak girdi ön işleme ve isabet doğrulaması olmadan, bayat yanıtlar sunuyor ve buna performans artışı diyorsunuz demektir.


LemonData, yerleşik önbellekleme, yönlendirme ve maliyet optimizasyonu ile 300'den fazla AI modeline birleşik erişim sağlar. 1$ kredi ile ücretsiz deneyin.

Paylaş: