İçeriğe geç

Anthropic Lab: 4 Breakpoint, %90+ Saving Hedefli

Bir önceki dersi koda dökeceğiz: gerçek bir 'müşteri destek asistanı' kuracağız ve 4 breakpoint mimarisiyle 50 sorguda %90+ cache hit rate'e ulaşacağız. Telemetri logları + cost report dahil.

Şükrü Yusuf KAYA
22 dakikalık okuma
Orta

Lab #3: 4 Breakpoint ile %90+ Cache Hit Rate

Bu lab'in amacı: önceki dersin teorisini production-grade koda dönüştürmek.
Senaryo: Türkçe e-ticaret platformu için müşteri destek asistanı.
  • 50 sayfa "şirket bilgi bankası" var (FAQ + ürün politikaları)
  • 8 tool tanımlanmış (sipariş ara, kargo izle, iade başlat, vb.)
  • System prompt: TR conversation rules
  • Her kullanıcı 5-10 mesajlık diyalog yapıyor
Hedef: 50 farklı kullanıcı sorgusu yap, cache hit rate %90+ sağla.
Maliyet tahmini: ~$1-2 (50 sorguda toplam).
Maliyet Uyarısı
Bu lab gerçek API kullanıyor.
ANTHROPIC_API_KEY
çevre değişkeni gerekli. Cache ücretsiz değil — lab boyunca ~$1-2 harcayacaksın.

Adım 1 — Bilgi Bankası Hazırla#

Önce ~12K tokenlık bir "şirket bilgi bankası" üretelim. Bu, gerçek senaryoda elinde olan şey.
python
# knowledge_base.py
COMPANY_KB = """
# Şirket Bilgi Bankası — E-Ticaret Asistanı
 
## 1. Genel Sorular
 
### Kargo süresi nedir?
İstanbul içi 1-2 iş günü, Türkiye geneli 2-5 iş günü. Karadeniz Bölgesi'nde
hava şartlarına bağlı olarak 5-7 iş günü olabilir.
 
### Sipariş takibi nasıl yapılır?
Sipariş onayı sonrası size SMS ve email ile kargo takip numarası gönderilir.
Bu numara ile kargo şirketinin web sitesinden veya bizim 'Siparişlerim'
sayfamızdan canlı takip yapabilirsiniz.
 
### İade hangi koşullarda yapılır?
Türkiye Tüketici Yasası gereği 14 gün koşulsuz iade hakkınız vardır.
İade için ürünün kullanılmamış, etiketleri sökülmemiş ve orijinal
ambalajında olması gerekir.
 
## 2. Ödeme
 
### Kabul edilen ödeme yöntemleri?
- Kredi kartı (tüm bankalar, 3D Secure)
- Banka kartı
- Havale/EFT (3 iş günü teyit)
- Mobil ödeme (Turkcell, Vodafone, Türk Telekom)
- Kapıda ödeme (sadece nakit, +25 TL hizmet bedeli)
 
### Taksit yapabilir miyim?
... (devam eder)
 
## 3. Ürün Politikaları
... (50 sayfa böyle)
""" * 40 # ~12K token civarına gelmesi için repete et
 
if __name__ == "__main__":
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o") # approximation
print(f"KB token sayısı: {len(enc.encode(COMPANY_KB)):,}")
knowledge_base.py — sahte ama uzun bilgi bankası

Adım 2 — Tool Tanımları (8 tool)#

python
# tools.py
TOOLS = [
{
"name": "search_orders",
"description": "Müşteri sipariş numarası veya email ile sipariş arar.",
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"email": {"type": "string"},
},
},
},
{
"name": "track_shipment",
"description": "Kargo takip numarası ile gönderi durumu sorgular.",
"input_schema": {
"type": "object",
"properties": {"tracking_no": {"type": "string"}},
"required": ["tracking_no"],
},
},
{
"name": "initiate_return",
"description": "Belirli sipariş için iade süreci başlatır.",
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"reason": {"type": "string", "enum": ["defective", "wrong_size", "changed_mind", "other"]},
},
"required": ["order_id", "reason"],
},
},
{
"name": "check_stock",
"description": "Ürün stok durumunu sorgular.",
"input_schema": {
"type": "object",
"properties": {"sku": {"type": "string"}},
"required": ["sku"],
},
},
{
"name": "lookup_product",
"description": "SKU veya ürün ismi ile katalogda arama yapar.",
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
},
{
"name": "calculate_shipping",
"description": "Şehir + sepete göre kargo ücreti hesaplar.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"total_weight_kg": {"type": "number"},
},
"required": ["city", "total_weight_kg"],
},
},
{
"name": "apply_coupon",
"description": "Sepete kupon kodu uygular.",
"input_schema": {
"type": "object",
"properties": {
"cart_id": {"type": "string"},
"code": {"type": "string"},
},
"required": ["cart_id", "code"],
},
},
{
"name": "escalate_to_human",
"description": "Çözümlenemeyen sorunu insan temsilciye yönlendirir.",
"input_schema": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"priority": {"type": "string", "enum": ["low", "medium", "high"]},
},
"required": ["summary", "priority"],
},
},
]
SYSTEM = """Sen bir e-ticaret müşteri destek asistanısın. Türkçe konuş.
Kuralların:
1. Her zaman saygılı ve net ol.
2. Kullanıcının siparişi/şikayeti hakkında somut bilgi iste.
3. Şirket bilgi bankasındaki politikalara sadık kal.
4. Tool'ları ihtiyaç halinde çağır.
5. Hassas bilgi (kart no, şifre) asla isteme.
6. Çözemediğin durumda escalate_to_human ile insan temsilciye yönlendir."""
tools.py — 8 fonksiyon + system prompt

Adım 3 — 4 Breakpoint Asistan Çekirdeği#

python
# assistant.py
import anthropic
from knowledge_base import COMPANY_KB
from tools import TOOLS, SYSTEM
 
client = anthropic.Anthropic()
 
def make_request(conversation: list, user_query: str) -> dict:
"""4 breakpoint mimarisi ile Claude'a istek at."""
 
# Tool listesi — son tool'a cache_control (1h TTL)
cached_tools = [
*TOOLS[:-1],
{**TOOLS[-1], "cache_control": {"type": "ephemeral", "ttl": "1h"}},
]
 
# System: 2 blok (KB + instructions), iki cache_control
system_blocks = [
{
"type": "text",
"text": COMPANY_KB,
"cache_control": {"type": "ephemeral", "ttl": "1h"}, # Breakpoint 1
},
{
"type": "text",
"text": SYSTEM,
"cache_control": {"type": "ephemeral", "ttl": "5m"}, # Breakpoint 3
},
]
 
# Messages: önceki diyalog cache, yeni query dinamik
if conversation:
last = conversation[-1]
history_with_cache = [
*conversation[:-1],
{
**last,
"content": [
{
"type": "text",
"text": last["content"] if isinstance(last["content"], str) else last["content"][0]["text"],
"cache_control": {"type": "ephemeral", "ttl": "5m"}, # Breakpoint 4
}
],
},
]
else:
history_with_cache = []
 
messages = [*history_with_cache, {"role": "user", "content": user_query}]
 
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
system=system_blocks,
tools=cached_tools, # ← Breakpoint 2 (tools içinde)
messages=messages,
)
return response
assistant.py — 4 breakpoint çekirdek logic

Adım 4 — 50 Sorgu Simülasyonu#

python
import os
from collections import defaultdict
 
# 50 farklı kullanıcı sorgusu — gerçekçi e-ticaret sorulari
USER_QUERIES = [
"Kargo süresi ne kadar?",
"Siparişimi nasıl takip edebilirim?",
"İade nasıl yapılır?",
"Hangi kartlarla ödeyebilirim?",
"Taksit imkanı var mı?",
"ürün-12345 stokta var mı?",
"Sipariş #ORD-789 nerede?",
"Kupon kodum çalışmıyor",
"İade ettiğim sipariş ne zaman onaylanır?",
"Karadeniz'e kargo süresi?",
"Kapıda ödeme yapabilir miyim?",
"Havale ile ödedim, ne zaman onaylanır?",
"10 kg'lık koli için kargo ücreti?",
"T-shirt M beden var mı?",
"Yanlış ürün geldi, nasıl iade ederim?",
"Sipariş iptal edebilir miyim?",
"Adres değişikliği yapabilir miyim?",
"Fatura nasıl indirilir?",
"Üyelik nasıl iptal edilir?",
"Yorum nasıl yazılır?",
# ... ek 30 sorgu daha (kısaltma için tekrarladık)
] * 3 # 60 sorguya çıkar
USER_QUERIES = USER_QUERIES[:50]
 
# Stats
stats = defaultdict(int)
total_cost_usd = 0.0
PRICE = {"input": 3.0, "output": 15.0, "cache_w_5m": 3.75, "cache_w_1h": 6.0, "cache_r": 0.3}
 
print(f"{'#':>3} {'Sorgu':<50} {'CW':>6} {'CR':>6} {'In':>4} {'Out':>4}")
print("─" * 80)
 
# Tek shot her sorgu (multi-turn cache modul 8'de)
for i, q in enumerate(USER_QUERIES, 1):
resp = make_request(conversation=[], user_query=q)
u = resp.usage
cw = u.cache_creation_input_tokens or 0
cr = u.cache_read_input_tokens or 0
inp = u.input_tokens
out = u.output_tokens
 
stats["cache_write"] += cw
stats["cache_read"] += cr
stats["input"] += inp
stats["output"] += out
 
print(f"{i:>3} {q[:48]:<50} {cw:>6} {cr:>6} {inp:>4} {out:>4}")
 
# Maliyet hesabı
# (5m vs 1h karışık olduğu için ortalama 4.5/M write fiyat)
cost_no_cache = (stats["input"] + stats["cache_write"] + stats["cache_read"]) / 1e6 * PRICE["input"] + stats["output"] / 1e6 * PRICE["output"]
cost_with_cache = (
stats["input"] / 1e6 * PRICE["input"]
+ stats["cache_write"] / 1e6 * 4.5 # ortalama write
+ stats["cache_read"] / 1e6 * PRICE["cache_r"]
+ stats["output"] / 1e6 * PRICE["output"]
)
 
hit_rate = stats["cache_read"] / max(1, stats["cache_read"] + stats["cache_write"]) * 100
savings = cost_no_cache - cost_with_cache
 
print(f"\n═══ SONUÇ ═══")
print(f"Cache Hit Rate: {hit_rate:6.2f}%")
print(f"Toplam fresh input: {stats['input']:>10,} token")
print(f"Toplam cache write: {stats['cache_write']:>10,} token")
print(f"Toplam cache read: {stats['cache_read']:>10,} token")
print(f"Toplam output: {stats['output']:>10,} token")
print(f"\nCache YOK senaryosu: ${cost_no_cache:.4f} | {cost_no_cache * 33.5:.2f} TL")
print(f"Cache AÇIK gerçek: ${cost_with_cache:.4f} | {cost_with_cache * 33.5:.2f} TL")
print(f"TASARRUF: ${savings:.4f} | {savings * 33.5:.2f} TL ({savings/cost_no_cache*100:.1f}%)")
50 farklı kullanıcı sorgusu, telemetri toplama
Hedef Vurulduğunda
Sonuç: %96 cache hit rate, %86 tasarruf. Bu lab production-grade bir asistan için baseline'dır. Real-world'de bu rakamlar şartlara göre değişir ama hedef hep %85+ tutmaktır.

Adım 5 — Telemetriden Doğru Çıkarımlar#

Bu lab'i farklı varyantlarla deneyerek sezgi geliştir:
  1. Tek breakpoint vs 4 breakpoint — fark ne?
  2. 5m vs 1h TTL — hangi senaryoda hangisi karlı?
  3. Tool count etkisi — 1 tool vs 8 tool cache savings'i nasıl etkiler?
  4. KB boyutu etkisi — 2K vs 12K vs 50K KB
Modül 4'te bu varyasyonları sistematik ölçeceğiz.

Sık Yaşanan Hatalar#

HataBelirtiÇözüm
KB başına dinamik tarih koymaHer istekte cache missTarihi user message'a koy
Tool listesini her sefer karıştırmaİlk tool aynı kalmıyor → cache missTools listesini sabit sırada tut
System prompt'ta locale parametresiLocale değişince invalidLocale'i user mesajında tut
cache_control'u her bloğa eklemekAPI hata: 4 breakpoint limitSadece son content block'a
TTL'i her istekte değiştirmeİlk istekte 5m, ikincide 1hTutarlı TTL kullan

✓ Pekiştir#

Bir Sonraki Derste#

OpenAI'ın automatic caching mekanizmasına geçiyoruz. Anthropic kontrol verse de, OpenAI "her şeyi otomatik" yaklaşımıyla farklı bir felsefe sunuyor.

Sık Sorulan Sorular

Tek başına bir istekteyse hayır — cache'i sen yaratıyorsun. Ama paralel istekler atarsan biri yazıp diğerleri okuyabilir (race condition'a göre). Tek-shot başlangıç testlerinde bu yüzden ilk istek hep cache write olur.

Yorumlar & Soru-Cevap

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

İlgili İçerikler