"Prompt caching maliyeti düşürür" diye duydunuz. Peki tam olarak ne cache'leniyor? Bu yazıda prompt caching'in tam olarak ne olduğunu, neyin cache'lenip neyin cache'lenmediğini, üç büyük model üreticisinin kurallarını ve LangChain ile yazılmış bir ajanda bunu nasıl açıp kullanabileceğimizi öğreneceğiz.
1. Prompt Caching Ne Değildir?
En iyisi tersinden başlayalım, çünkü kafa karışıklığının kaynağı burada. Klasik cache'lemeyi hepimiz biliyoruz: bir SQL sorgusu veritabanına gider, sonuç döner, o sonucu bir cache'e koyarsınız. Aynı sorgu kısa süre sonra tekrar gelirse veritabanına hiç gitmez, cache'teki sonucu verirsiniz. Cache'lenen şey çıktıdır.
Bunu LLM'e uygulayınca akla gelen ilk şey de bu oluyor: kullanıcı bir prompt gönderdi, model bir cevap üretti; aynı prompt tekrar gelirse modeli hiç çağırmayıp cevabı cache'ten veririm. Bu yapılabilir bir şey — LangChain'de set_llm_cache ile InMemoryCache ya da SQLiteCache kurduğunuzda tam olarak bunu yaparsınız: prompt ve model parametreleri anahtar, modelin ürettiği cevabın tamamı değerdir[12]. Ama bunun adı output caching (çıktı cache'leme), prompt caching değil.
Prompt caching yalnızca girdiyi cache'ler. Model yine çalışır, yine yeni bir cevap üretir; kazanç, prompt'un daha önce gördüğü kısmını ikinci kez baştan işlememesinden gelir. Bu ayrımı oturtmadan gerisi anlaşılmıyor, o yüzden şemaya bir bakalım.
set_llm_cache'i soldaki, sağlayıcıların "prompt caching" dediği şey sağdaki [1][12].2. Prefill ve KV Cache
Prompt caching'in neyi cache'lediğini anlamak için modelin bir prompt'u aldığında ne yaptığına bakmamız gerekiyor. Bir istek geldiğinde model, ilk token'ı üretmeden önce prompt'taki her token için, her transformer katmanında key ve value vektörleri hesaplar. Bunlara topluca KV cache deniyor. Bu vektörleri modelin prompt'a dair iç anlayışı gibi düşünebilirsiniz: hangi kelime hangisiyle ilişkili, hangi bağlam önemli, dikkat nereye verilecek[16].
Bu hesaba prefill (ön doldurma) aşaması deniyor ve pahalı olan kısım bu. Prefill, prompt'un uzunluğuyla üstel büyüyor: attention'ın maliyeti karesel olduğu için prompt'u iki katına çıkardığınızda attention işi kabaca dört katına çıkıyor. Redis'in Llama 3.1 70B ile yaptığı ölçümde 32 bin token'lık bir prompt için ilk token'a kadar geçen süre (time to first token, TTFT) 472 milisaniye, 123 bin token için yaklaşık 2,2 saniye[16]. Yani kullanıcının ekranda ilk harfi görene kadar beklediği sürenin büyük kısmı prefill.
Prompt caching işte tam bu noktada devreye giriyor: hesaplanmış KV vektörleri saklanıyor. "Fransa'nın başkenti neresi?" gibi kısa bir prompt için bunun anlamı yok; birkaç token'ı işlemek zaten ucuz. Ama prompt'a 50 sayfalık bir doküman koyup "bunu özetle" dediğinizde model binlerce token için onlarca katman boyunca milyonlarca işlem yapıyor. Aynı dokümanı beş dakika sonra bu kez "garanti şartları ne?" sorusuyla tekrar gönderdiğinizde modeli sunan katman (model server) prompt'un başındaki token dizisinin hash'ini daha önce gördüğünü fark ediyor, o token'lara ait KV vektörlerini GPU belleğinden okuyor ve yalnızca sondaki yeni soruyu işliyor. Buradaki "tanıma" modelin hafızası değil: model her istekte aynı hesabı yapar; hatırlayan şey, etrafındaki serving yazılımının token içeriği üzerinden tuttuğu bir cache tablosu. Bu yüzden cache sizin sohbetinize değil içeriğe bağlıdır — aynı dokümanı aynı sırayla gönderen başka bir kullanıcı da (aynı model server'a düşerse) aynı bloklara isabet eder. Mekanizmanın ayrıntısına 7. bölümde gireceğiz.
3. Ne Cache'lenir?
Peki prompt'un içine koyduğumuz nelerden cache faydası görürüz? Sağlayıcı dokümanlarından derlediğimiz liste şöyle[1][3]:
| Cache'lenen içerik | Neden işe yarıyor? |
|---|---|
| System prompt | En yaygın durum. Her chatbot'un kişiliğini, kurallarını, davranışını tanımlayan ve her istekte aynen giden metin. |
| Büyük dokümanlar | Ürün kılavuzu, araştırma makalesi, sözleşme — üzerine birden fazla soru soracağınız her şey. |
| Few-shot örnekler | Modele "cevabı böyle formatla" demek için gösterdiğiniz örnekler; genelde uzun ve hep aynı. |
| Tool / fonksiyon tanımları | Ajanın elindeki 20–30 tool'un JSON şeması binlerce token tutar ve istekten isteğe değişmez. |
| Konuşma geçmişi | Çok turlu sohbette önceki turlar yeni turda da aynen gönderilir; geçmişin tamamı prefix'tir. |
Anthropic'in dokümanı buna görselleri, PDF'leri, tool sonuçlarını ve önceki turlardaki thinking bloklarını da ekliyor; cache'lenemeyen şeyler ise boş metin blokları ve alıntı (citation) gibi alt bloklar[1]. OpenAI tarafında da liste aynı mantıkta: system ve developer mesajları, tool tanımları, konuşma geçmişi, metin, görsel, doküman ve ses[3].
Ajan tarafında bunun neden bu kadar önemli olduğunu Manus ekibi bir rakamla anlatıyor: ajan döngüsünde her adımda context büyür ama çıktı kısa bir tool çağrısıdır; ortalama girdi/çıktı token oranı 100'e 1. Bu yüzden ekip "prod'daki bir ajan için KV cache isabet oranı (hit rate) en önemli tek metriktir" diyor[17]. Faturanın neredeyse tamamı girdiyse, girdinin %90 indirimli olup olmaması her şeyi belirliyor.
4. Sistem Neyin Cache'leneceğini Nasıl Biliyor? Prefix Matching
Buraya kadar "değişmeyen kısım cache'lenir" dedik. Peki sistem neyin değişmediğine nasıl karar veriyor? Cevap prefix matching (ön ek eşleştirme): cache sistemi prompt'u en baştan itibaren token token karşılaştırır. Cache'tekinden farklı ilk token'a rastladığı anda eşleştirme durur ve oradan itibaren normal işleme geçilir[1][3].
Bu tek cümle, prompt'unuzun sırasını bir mühendislik kararı hâline getiriyor. Şu yapıyı düşünün: önce system talimatları, sonra 20 sayfalık kılavuz, sonra few-shot örnekler, en sonda kullanıcının sorusu. İlk kullanıcı "garanti şartları ne?" diye sordu, ikincisi "iade politikası ne?" diye. İkinci istekte sistem baştan itibaren karşılaştırıyor: talimatlar aynı, kılavuz aynı, örnekler aynı — hepsi cache'ten geliyor; yalnızca sondaki soru işleniyor.
Şimdi aynı içeriği ters sırayla düşünün: kullanıcının sorusu en başta. İkinci istekte daha ilk token'da fark var, eşleştirme sıfırıncı token'da durur, kılavuzun tamamı yeniden işlenir. Aynı içerik, aynı model, aynı sağlayıcı; tek fark sıra — ve cache oranı %95'ten %0'a düşer.
Anthropic bu sırayı API seviyesinde de sabitlemiş: cache hiyerarşisi tools → system → messages şeklinde ve bir katmandaki değişiklik o katmanı ve altındaki her şeyi geçersiz kılıyor. Yani tool tanımlarını değiştirdiğinizde system prompt'unuz aynen dursa bile cache'i kaybediyorsunuz[1].
5. Üç Büyükler ve Üç Farklı Kural Seti
İnternette sık rastlayacağınız "en az 1024 token, 5–10 dakika ömür" genellemesi 2024'te doğruydu; bugün sağlayıcıya ve modele göre ciddi farklar var. En büyük ayrım şu: bazı sağlayıcılar cache'i otomatik yapıyor (siz hiçbir şey işaretlemiyorsunuz, prefix eşleşirse indirim geliyor), bazıları ise explicit (açıkça) işaretlemenizi istiyor.
| Anthropic (Claude) | OpenAI | Google (Gemini) | |
|---|---|---|---|
| Nasıl açılır? | Explicit: cache_control ile breakpoint (en fazla 4); istek seviyesinde otomatik seçenek de var |
Otomatik; GPT-5.6+ için isteğe bağlı explicit breakpoint | Implicit (otomatik), Gemini 2.5 ve sonrası için varsayılan olarak açık; ayrıca explicit cache nesneleri |
| Minimum prefix | Modele göre 512 – 4.096 token | 1.024 token | 2.5 için 2.048, 3.x için 4.096 token |
| Cache okuma fiyatı | Normal girdinin %10'u (bazı modellerde %2,5'i) | Normal girdinin %10'u (2024'te %50'ydi[4]) | Normal girdinin %10'u |
| Cache yazma fiyatı | 5 dk için 1,25×, 1 saat için 2× | GPT-5.6+ için 1,25×; eski modellerde ek ücret yok | Explicit cache'te saatlik saklama ücreti ($4,50 / milyon token / saat) |
| Ömür (TTL) | 5 dk (varsayılan) ya da 1 saat | GPT-5.6+ için en az 30 dk; eski modellerde 5–10 dk, en fazla 1 saat; 24 saate uzatılabilir | Implicit için kısa süre; explicit için sizin belirlediğiniz TTL |
Tablo, Eylül 2026 itibarıyla sağlayıcıların kendi dokümanlarından derlendi[1][3][5][6]. Rakamlar sık değişiyor; yazıyı okuduğunuz tarihte dokümanı bir daha açmakta fayda var. Değişmeyen kısım ise mantık: okuma ucuz, yazma biraz pahalı, ömür kısa.
Bu "yazma pahalı" kısmına dikkat. Anthropic'te 5 dakikalık cache'e yazmak normal girdinin 1,25 katı. Yani bir prefix'i cache'e yazıp bir daha hiç okumazsanız zarar edersiniz. Cache yalnızca aynı prefix'in TTL içinde tekrar geleceği durumlarda kazandırır: çok turlu sohbet, aynı dokümana art arda sorular, aynı system prompt'la binlerce istek. Tek seferlik işlerde açmanın anlamı yok.
Bir de zamanlama inceliği var: Anthropic'te TTL, isteğin başladığı andan itibaren sayılıyor, cevabın bittiği andan değil. Cevabın üretilmesi 4 dakika sürdüyse, aynı prefix'i kullanacak bir sonraki isteğin yaklaşık 1 dakika içinde başlaması gerekiyor[1].
6. LangChain'de Prompt Caching
Şimdi asıl meseleye gelelim: bir ajanı LangChain ile yazdınız, LangGraph üzerinde koşuyor, her turda system prompt, 25 tool tanımı ve büyüyen bir mesaj listesi gönderiyorsunuz. Cache'i nasıl açacaksınız?
LangChain dokümanı üç seviye tanımlıyor[11]. Birincisi, implicit sağlayıcı cache'i: OpenAI ya da Gemini kullanıyorsanız hiçbir şey yapmanıza gerek yok; prefix eşleşirse indirim usage_metadata'da kendiliğinden görünür. İkincisi, sağlayıcı seviyesinde explicit kontrol: ChatAnthropic'te bir system mesajının ya da herhangi bir içerik bloğunun sonuna cache_control: {"type": "ephemeral"} ekleyerek breakpoint koyarsınız; tool'lar için de @tool dekoratörünün extras parametresine aynı sözlüğü verirsiniz[10]. OpenAI'da ise prompt_cache_key ile istekleri aynı cache'e yönlendirirsiniz. Üçüncüsü, ajanlar için middleware: işin çoğunu sizin yerinize yapan katman.
Bu üçüncü yol, LangChain v1'in create_agent API'siyle gelen AnthropicPromptCachingMiddleware. Kaynak koduna baktığınızda üç şey yaptığını görüyorsunuz: istek seviyesinde model_settings'e cache_control ekliyor, system mesajının son içerik bloğunu işaretliyor ve tool tanımları tek parça gönderildiği için son tool tanımını işaretliyor[9]. Yani Anthropic'in tools → system → messages hiyerarşisinde en değerli iki katmanı tek satırla cache'e alıyor:
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain_anthropic.middleware import AnthropicPromptCachingMiddleware
model = init_chat_model("anthropic:claude-sonnet-4-6")
agent = create_agent(
model=model,
system_prompt=LONG_SYSTEM_PROMPT, # değişmeyen kısım
tools=[search_docs, create_ticket, ...], # değişmeyen kısım
middleware=[AnthropicPromptCachingMiddleware(ttl="5m")],
)
Parametreler az: ttl için "5m" ya da "1h"; min_messages_to_cache ile "konuşma şu kadar mesaja ulaşmadan cache'e yazma" diyebiliyorsunuz (kısa konuşmalarda yazma maliyetinden kaçınmak için); unsupported_model_behavior ise Anthropic dışı bir modelle karşılaşınca uyarsın mı, sussun mu, hata mı versin diye belirliyor[8]. Bu son parametre önemsiz görünüyor ama gerçek bir tuzağı işaret ediyor: middleware'i bir fallback zinciriyle (Anthropic düşerse OpenAI'a geç) birlikte kullananlar, cache_control parametresinin OpenAI'a gidip hata verdiğini bildirdi[19]. Fallback'iniz varsa "ignore" seçin.
İki şeyi netleştirelim. Birincisi, bu middleware yalnızca create_agent ile çalışıyor; bind_tools ile elle kurduğunuz zincirde içerik bloklarını kendiniz işaretlemeniz gerekiyor[7]. İkincisi, middleware konuşma hafızası sağlamıyor: turlar arasında mesajları taşıyan şey yine LangGraph'ın checkpointer'ı (MemorySaver vb.); middleware yalnızca o mesajların Anthropic'e giderken doğru işaretlenmesini sağlıyor[7].
Çalışıyor mu? usage_metadata'ya bakın
Cache'i açtım demek yetmiyor; ölçmeniz lazım. LangChain'de her AIMessage'ın usage_metadata["input_token_details"] alanında cache_read ve cache_creation sayıları geliyor[10]. İlk istekte cache_creation büyük, cache_read sıfır olmalı; ikinci istekte tam tersi. Eğer her istekte cache_creation büyük ve cache_read sıfırsa prefix'iniz istekten isteğe değişiyor demektir — ve her seferinde 1,25× ödüyorsunuz. Bu iki sayıyı LangSmith trace'lerinize ya da kendi metriklerinize koymadan "cache açık" demeyin.
Ajan döngüsünde cache'i sessizce öldüren beş şey
Middleware'i eklediniz, ama cache_read hâlâ sıfır. Sahada en çok görülen sebepler şunlar[17][18]:
| Hata | Neden cache'i bozar? | Doğrusu |
|---|---|---|
| System prompt'a zaman damgası koymak ("Bugün 13 Eylül 2026 14:32:07") | Prefix her saniye değişir; Manus ekibi "saniye hassasiyetli timestamp cache oranınızı öldürür" diyor | Tarihi en sona, kullanıcı mesajının yanına koyun ya da saat hassasiyetini düşürün |
| Tool listesini dinamik değiştirmek (son kullanılana göre sıralamak, kullanılmayanı çıkarmak) | Tools hiyerarşinin en üstünde; oradaki değişiklik her şeyi geçersiz kılar | Tool'ları sabit sırada tutun; kapatmak gerekiyorsa çıkarmak yerine maskeleyin |
| Geçmiş mesajları yerinde değiştirmek (özetleyip yerine koymak, tool sonucunu kırpmak) | Ortadan değişen tek token, ondan sonraki her şeyin cache'ini bozar | Context'i yalnızca sona ekleyerek (append-only) büyütün; özetleyecekseniz bunu bilinçli bir "cache sıfırlama" anı olarak planlayın |
| JSON serileştirmede anahtar sırasının değişmesi | Aynı sözlük iki istekte farklı sırada yazılınca token'lar farklı çıkar | Deterministik serileştirme (sort_keys=True) |
Her isteğe request_id, kullanıcı profili gibi değişken veriyi başa koymak |
Şekil 3'teki "soru önde" durumunun aynısı | Değişken her şey prefix'in arkasına |
Focused Labs ekibinin özeti güzel: bu değişikliklerin her biri tek başına savunulabilir, ama toplamda cache'i "söylentiye" çevirir — bazen var, bazen yok, kimse neden bilmiyor[18]. Aynı ekip, sağlayıcıya duyarlı cache'i açtıktan sonra Deep Agents ölçümlerinde token maliyetinde %49–80 düşüş bildiriyor.
7. Kendi Model Server'ınızda: vLLM ve SGLang
Modeli API'den değil kendi GPU'larınızdan sunuyorsanız aynı mekanizma sizin elinizde. vLLM'in automatic prefix caching'i KV cache'i 16 token'lık bloklara bölüyor, her bloğu "bir önceki bloğun hash'i + bu bloktaki token'lar" ile hash'liyor ve yeni istek geldiğinde blok blok eşleştiriyor; yer kalmayınca en uzun süredir kullanılmayan bloğu (LRU) atıyor. Yalnızca dolu bloklar cache'lendiği için sondaki yarım blok her zaman yeniden hesaplanıyor[13]. SGLang ise aynı işi radix ağacıyla yapıyor (RadixAttention) ve cache isabetini artırmak için istekleri ağaçtaki yakınlığa göre sıralıyor[15].
Rakam olarak: llm-d ekibinin tek vLLM instance'ında Qwen3-32B ile yaptığı ölçümde 10 bin token'lık aynı prompt'un TTFT'si 4,3 saniyeden 0,6 saniyeye iniyor. Çok replikalı kurulumda ise mesele değişiyor: prefix'in KV cache'i hangi pod'daysa isteğin oraya gitmesi gerekiyor. 8 pod'luk kurulumda cache'ten habersiz yönlendirmeyle P90 TTFT 95 saniye, cache'in nerede olduğunu bilen yönlendirmeyle 0,5 saniye[14]. Manus'un tavsiyesi de bu: vLLM'de prefix caching'i açın ve aynı oturumun isteklerini session ID ile aynı replikaya gönderin[17].
8. Kapanış
Toparlayalım. Prompt caching, modelin cevabını değil, prompt'un değişmeyen başı için hesapladığı KV vektörlerini saklamaktır. Kazanç prefill'den gelir; bu yüzden ilk token'a kadar geçen sürede ve girdi faturasında görünür. Sağlayıcılar okumayı normal fiyatın onda birine veriyor ama yazmayı biraz pahalı tutuyor, yani cache yalnızca aynı prefix'in kısa sürede tekrar geleceği işlerde kazandırıyor.
Yapmanız gereken üç şey: değişmeyeni öne, değişeni sona koyun; LangChain'de AnthropicPromptCachingMiddleware'i (ya da OpenAI/Gemini'de implicit cache'i) açın; ve cache_read sayısını izleyin. Üçüncüsünü atlamayın — cache'in açık olduğunu sanıp aylarca 1,25× ödeyen ekipler var.
Şimdi sıra sizde: ajanınızın son on isteğinin usage_metadata'sını çekin ve cache_read kolonuna bakın. Sıfırsa, yukarıdaki beş maddelik tabloyu baştan gezin; kırık olan şey büyük ihtimalle bir zaman damgası ya da yer değiştiren bir tool tanımı.
Kaynaklar
- Claude Platform Docs — Prompt Caching
- Claude Blog — Prompt Caching with Claude
- OpenAI Developers — Prompt Caching Guide
- OpenAI — Prompt Caching in the API (Ekim 2024)
- Gemini API Docs — Context Caching
- Gemini API Docs — Pricing
- LangChain Docs — Anthropic Middleware Integration
- LangChain Reference — AnthropicPromptCachingMiddleware
- GitHub — langchain_anthropic/middleware/prompt_caching.py
- LangChain Docs — ChatAnthropic (Prompt Caching bölümü)
- LangChain Docs — Models (Prompt Caching seviyeleri)
- LangChain Reference — Model Response Caches (InMemoryCache, SQLiteCache)
- vLLM Docs — Automatic Prefix Caching
- llm-d Blog — KV-Cache Wins You Can See
- LMSYS — Fast and Expressive LLM Inference with RadixAttention and SGLang
- Redis Blog — Prefill vs Decode: LLM Inference Phases Explained
- Manus — Context Engineering for AI Agents: Lessons from Building Manus
- Focused Labs — Agent Prompt Caches Are a Runtime Boundary
- GitHub Issue #33709 — AnthropicPromptCachingMiddleware breaks model fallback
- Kapak görseli: Photo by Sara Kurfeß on Unsplash




















