Skip to content

Cache Miss Anatomisi: Tek Karakter Bile Cache'i Kırar

Cache'in en hassas tarafı: exact-prefix kuralı. Bir trailing whitespace bile cache'i sıfırlar. Bu derste cache miss'in tüm anatomisini, en sık karşılaşılan tuzakları ve nasıl debug edileceğini öğreneceksin.

Şükrü Yusuf KAYA
14 min read
Intermediate

Cache Miss Anatomisi: Hassasiyet Dersi

Modül 1-3'te caching'i öğrendik. Modül 4'te artık %95+ cache hit rate sağlamayı öğreniyoruz. Bunun için önce neden miss olduğunu bilmek lazım.
İlk gerçek: cache, exact prefix matching kullanır. Tek karakter farkı = miss.
Erken Uyarı
Bu dersi okuduktan sonra production kodunda özellikle dinamik veriyi prompt'un başına koyma. Bu hatayı yaparsan cache hit rate %95'ten %5'e iner ve farkına varmayabilirsin.

Exact Prefix Matching Kuralı#

Caching mekanizmasının temel kuralı:
İki istekteki ilk N karakter (token bazında) birebir aynıysa cache hit. Bir karakter farkı varsa: o noktadan itibaren cache miss + yeniden write.
Pratik anlamı: prompt'un başındaki tek değişiklik tüm cache'i geçersiz kılar. Sonundaki değişiklikler önemli değil.

En Sık 8 Tuzak#

Production'da gözlemlediğim cache miss sebepleri:

Tuzak 1: System Prompt'a Tarih/Zaman Koymak#

# ❌ KÖTÜ system = f"Bugün {datetime.now()}. Şirket bilgisi: ..." # Her saniye değişir → her istek cache miss # ✅ İYİ system = "Şirket bilgisi: ..." messages = [ {"role": "user", "content": f"Bugün {datetime.now()}. Soru: ..."} ] # Tarihi user mesajında tut, system stabil kalır

Tuzak 2: Random ID veya UUID#

# ❌ KÖTÜ system = f"Session ID: {uuid.uuid4()}\n..." # Her sessions için farklı → cache miss # ✅ İYİ system = "Session ID'ler aşağıda tracking için." # Session ID'yi user mesajına veya conversation history'ye koy

Tuzak 3: Locale veya Personalization#

# ❌ KÖTÜ def get_system(user): return f"Hello {user.name}. You are using {user.locale}..." # Her kullanıcı ayrı cache → 1000 user = 1000 ayrı cache # ✅ İYİ def get_system(): return "Asistan, kullanıcı bilgilerini user message'da görecek." def make_request(user, query): return client.messages.create( system=[{"text": get_system(), "cache_control": ...}], messages=[ {"role": "user", "content": f"User: {user.name} ({user.locale})\nSoru: {query}"} ] ) # Tüm kullanıcılar aynı system → shared cache

Tuzak 4: Trailing Whitespace / Encoding#

# ❌ KÖTÜ SYSTEM = open("system.txt").read() # Windows: CRLF, Linux: LF # Aynı içerik farklı OS'lerde farklı bytes → cache miss # ✅ İYİ SYSTEM = open("system.txt").read().strip().replace("\r\n", "\n") # Whitespace ve line ending normalize

Tuzak 5: Dynamic JSON Field'lar#

# ❌ KÖTÜ tools = [ {"name": "search", "description": f"Created: {creation_ts}, search..."}, ] # Tool description'da timestamp → cache miss # ✅ İYİ tools = [ {"name": "search", "description": "Search products in catalog"}, ] # Stable, deterministik description

Tuzak 6: Dict/JSON Sırası#

# ❌ KÖTÜ # Python dict iteration order is insertion order, but if you build it # from two merges in random order... system_data = {**stable_dict, **dynamic_dict} system = json.dumps(system_data) # field sırası değişebilir # Aynı veri, farklı JSON çıktısı → cache miss # ✅ İYİ system = json.dumps(system_data, sort_keys=True) # Always sorted, deterministik

Tuzak 7: Conversation History'de Eski Mesajları Truncate Etmek#

# ❌ KÖTÜ # Conversation 50 turn'e çıktı, ilk 10'u sil messages = messages[10:] # Cache prefix değişti → tüm history yeniden write # ✅ İYİ # History'yi summarize et, en başta sabit özet tut summary = summarize_old_messages(messages[:10]) new_history = [ {"role": "user", "content": f"Önceki konuşma özeti: {summary}"}, *messages[10:], ] # Summary stabil, cache friendly

Tuzak 8: Tool Listesini Dinamik Üretmek#

# ❌ KÖTÜ def get_tools(user): if user.is_admin: return ADMIN_TOOLS + USER_TOOLS else: return USER_TOOLS # Admin'ler ve user'lar farklı cache # ✅ İYİ (Anthropic için) TOOLS = ADMIN_TOOLS + USER_TOOLS # hep aynı liste # Authorization'ı tool'un INSIDE'ında yap (admin değilse hata dön)

Debug Etme: Cache Miss'i Nasıl Bulursun?#

Bir cache hit kaybettin, neden? İşte mantıklı debug akışı:
python
def diff_cache_keys(prompt_a: str, prompt_b: str) -> int:
"""İki prompt'un ilk farklı karakter pozisyonunu döndür."""
min_len = min(len(prompt_a), len(prompt_b))
for i in range(min_len):
if prompt_a[i] != prompt_b[i]:
return i
return min_len if len(prompt_a) != len(prompt_b) else -1
 
# Önceki ve şu anki prompt'u logla
prev_system = open(".cache/last_system.txt").read()
curr_system = build_system()
 
diff_pos = diff_cache_keys(prev_system, curr_system)
if diff_pos >= 0:
print(f"⚠️ Cache kırıldı pozisyon {diff_pos}'te")
print(f" Önceki: ...{prev_system[diff_pos-20:diff_pos+20]}...")
print(f" Şimdi: ...{curr_system[diff_pos-20:diff_pos+20]}...")
 
# Şu anki prompt'u kaydet
with open(".cache/last_system.txt", "w") as f:
f.write(curr_system)
Cache kırıldıysa neresinde olduğunu bul

Pratik Tavsiyeler#

YapmaYap
Dynamic veriyi system'a koyUser message'a koy
Tool description'da timestampStatic description
Conversation history'yi truncateSummarize + sabit özet
Tool listesini user'a göre değiştirTek liste, authorization tool içinde
os.read() ile dosya okunormalize edip oku
Random seed prompt'a yazSeed'i logla, prompta yazma

✓ Pekiştir#

Bir Sonraki Derste#

Sahip olduğun bilgi: statik üstte, dinamik altta (golden rule). Şimdi bunu sistematik prompt yapılandırmasına dökeceğiz.

Frequently Asked Questions

Evet, telemetry açısından aynı: cache_creation_input_tokens > 0, cache_read = 0. Aradaki fark: prompt değişmediği halde miss olduysa TTL süresi geçmiş demektir. Trafik yoğun bir saatte normalde olmaz; trafik az saatlerde sık görülür.

Yorumlar & Soru-Cevap

(0)
Yorum yazmak için giriş yap.
Yorumlar yükleniyor...

Related Content