Mert Özen Her Satırda Daha İleri
Yapay Zeka Claude API ile Uygulama Geliştirme — #07

Claude'da Prompt Caching: Cache Breakpoint'leri, TTL ve Maliyeti %90 Düşürmek

Mert Özen 15 Kas 2026 13 dk 4 görüntülenme
Claude'da Prompt Caching: Cache Breakpoint'leri, TTL ve Maliyeti %90 Düşürmek

Claude'da prompt caching için sabit içeriği (tools, system) başa koyar, sonuna cache_control breakpoint'i ve konuşma için top-level otomatik caching eklersin; cache'in çalıştığını usage'daki cache_read_input_tokens ve cache diagnostics ile doğrularsın. 5 dakika ve 1 saat TTL fiyatları, 512 token alt sınırı, 1.000 isteklik maliyet hesabı, DateTime.Now gibi sessiz bozucular, compaction ve context editing.

Sürüm notu: Bu yazıdaki kodlar .NET 10, Anthropic C# SDK 13.8.0 ve Claude API version 2026-10-21 ile derlenmiş, birim testleri çalıştırılmıştır. Testler SDK'nın gerçek client'ını ağa çıkmayan sahte bir HttpMessageHandler ile kullanıyor; sahte cevaplar prompt caching ve cache diagnostics sayfalarındaki örneklerden türetilmiştir. Maliyet tablosu yazıdaki aracın fiyat listesiyle yaptığı hesaptır. Fiyatlar ve davranışlar 15 Kasım 2026'da platform.claude.com belgelerinden alınmıştır. Bu yazıda resmî belgelerden yararlanılmıştır.

Önce Tahmin Et

Sitedeki serilerin ortak örnek uygulaması Bookstore (bir online kitapçı) için ikinci yazıda yazdığımız destek botu artık yalnız beş satırlık bir talimatla çalışmıyor. Gül'ün ekibi botun her cevabı iade, kargo ve ödeme politikasına dayandırmasını istedi; Emre de politikayı ve kategori ağacını system prompt'a ekledi. Production'daki gerçek doküman on binlerce token. Selin ay sonunda faturaya bakıyor: input token'ları neredeyse tamamen bu aynı metinden geliyor. Her istekte aynı 40.000 token'ı yeniden gönderiyor ve yeniden ödüyoruz.

Emre prompt caching'i açıyor, testte cache_read_input_tokens dolu geliyor. Bir hafta sonra fatura yine yüksek. Tahminin ne? Cache neden çalışmayı bırakmış olabilir ve bunu hata almadan nasıl fark edersin? Maliyetin genel hesabı LLM Temelleri serisinin on altıncı yazısında, uzun konuşmalarda context yönetimi AI Agent'ları ve MCP (Model Context Protocol; modele tool ve veri kaynaklarını standart bir yolla bağlayan protokol) serisinin dördüncü yazısında anlatıldı. Bu yazıda Claude API'nin prompt caching mekanizmasına, C# SDK'daki karşılığına ve cache'in gerçekten çalıştığını doğrulamaya bakıyoruz.

Prompt Caching Nasıl Çalışıyor?

Prompt caching sayfasının anlattığı mekanizma bir prefix eşleşmesi. İstekte bir cache breakpoint varsa API, istek başından o breakpoint'e kadar olan kısmın yakın zamanda cache'e yazılıp yazılmadığına bakıyor. Varsa o kısmı yeniden işlemeden okuyor, yoksa bütün prompt'u işliyor ve cevap başladığında prefix'i cache'e yazıyor. Prefix'in sırası sabit: önce tools, sonra system, sonra messages. Bu yüzden bir tool tanımının değişmesi bütün cache'i, system prompt'un değişmesi system ve messages kısmını geçersiz kılıyor.

Üç kural davranışın tamamını açıklıyor. Birincisi, yazma yalnız breakpoint'te oluyor: breakpoint'in olduğu block'a kadar olan prefix'in hash'i tek bir kayıt olarak yazılıyor, daha önceki pozisyonlar için kayıt oluşmuyor. İkincisi, okuma geriye doğru arıyor: API breakpoint'teki hash'e uyan bir kayıt bulamazsa block block geri gidip önceki isteklerin yazdığı bir kayıt arıyor. Üçüncüsü, bu geriye bakış en fazla 20 block sürüyor. Konuşma bir breakpoint'i son yazılan kayıttan 20 block'tan fazla ileri iterse eşleşme bulunmuyor.

Cache'in ömrü varsayılan olarak 5 dakika ve her okumada ücretsiz olarak yenileniyor. Belge önemli bir ayrıntıyı vurguluyor: süre isteğin başından ölçülüyor, cevabın bitişinden değil. 4 dakika süren bir stream'den sonra aynı prefix'i kullanacak istek yaklaşık 1 dakika içinde başlamalı.

Bir de alt sınır var. Opus 5.5, Sonnet 5.5, Haiku 5.5 ve Fable 5.2'de cache'lenebilen en kısa prompt 512 token. Daha kısa bir prefix'e cache_control koymak hata vermiyor; istek cache'siz işleniyor ve usage'daki iki cache alanı da 0 geliyor. Destek botunun eski beş satırlık system prompt'u bu yüzden hiç cache'lenmezdi.

Bookstore Destek Botu: İki Breakpoint

Uzun ve sabit kısmı ayrı bir class'ta topluyoruz:

// Destek prompt'unun uzun ve sabit kısmı. Bir kez kurulur, her istekte byte byte aynı gider.
public sealed class SupportKnowledge(string policy, IEnumerable<string> categoryPaths)
{
    public string Text { get; } = policy.TrimEnd() + "\n\n## Category tree\n\n" + RenderCategoryTree(categoryPaths);

    // Aynı yollar hangi sırayla gelirse gelsin aynı metni verir; cache hit için prefix byte byte aynı olmalı.
    public static string RenderCategoryTree(IEnumerable<string> categoryPaths) =>
        string.Join('\n', categoryPaths
            .Select(path => path.Trim())
            .Where(path => path.Length > 0)
            .Distinct(StringComparer.Ordinal)
            .Order(StringComparer.Ordinal)

Politika metni ve kategori listesi assembly'ye embedded resource olarak giriyor ve uygulama açılırken bir kez okunuyor. Depodaki politika dosyası birkaç KB'lık bir örnek; production'daki metin çok daha uzun ve gerçek token sayısını ikinci yazıdaki count_tokens ile görebilirsin. RenderCategoryTree listeyi StringComparer.Ordinal ile sıralıyor. Kategoriler bugün bir dosyadan, yarın bir veritabanından gelebilir; sıra hangi kaynaktan nasıl gelirse gelsin metin aynı kalmalı. Cache hit için prefix'in byte byte aynı olması gerekiyor.

İsteği kuran method:

    public static MessageCreateParams BuildRequest(
        IReadOnlyList<MessageParam> history,
        SupportKnowledge knowledge,
        ClaudeOptions settings,
        string? previousMessageId = null)
    {
        MessageCreateParams request = new()
        {
            Model = settings.Model,
            MaxTokens = MaxTokens,
            System = new List<TextBlockParam>
            {
                new() { Text = SystemPrompt },
                // Breakpoint 1: sabit system prompt'un sonu; konuşma turları bu kaydı hiç değiştirmez.
                new() { Text = knowledge.Text, CacheControl = new CacheControlEphemeral() },
            },
            Messages = history,
            // Breakpoint 2 (otomatik caching): konuşma uzadıkça son block'a kayar.
            CacheControl = new CacheControlEphemeral(),
        };

        if (!settings.CacheDiagnostics)
        {
            // null atamak "diagnostics": null gönderir; bunun yerine property'yi hiç set etme.
            return request;
        }

        // İlk turda previous_message_id null: karşılaştıracak bir şey olmadan özelliği aç.
        return request with { Diagnostics = new DiagnosticsParam { PreviousMessageID = previousMessageId } };
    }

İki breakpoint kullanıyoruz. İlki, system'in ikinci block'unda, açık bir cache_control: politikanın bittiği yer. İkincisi isteğin üst seviyesindeki CacheControl; belgenin "automatic caching" dediği bu ayar breakpoint'i otomatik olarak son cache'lenebilir block'a koyuyor ve konuşma uzadıkça onu ileri taşıyor. Belgedeki çok turlu konuşma tablosuna göre her yeni istekte önceki tura kadar olan kısım cache'ten okunuyor, yalnız son assistant cevabı ve yeni user mesajı yazılıyor.

07-prompt-cache-tr.png

Neden tek otomatik breakpoint yetmiyor? Belgenin compaction sayfası bunun cevabını veriyor: eski turların yerine bir özet konduğunda ya da geçmiş kırpıldığında messages kısmı değişiyor. System'in sonunda ayrı bir breakpoint olursa politika kaydı bundan etkilenmiyor; yalnız konuşma kısmı yeniden yazılıyor. Belgeye göre bir istekte en fazla 4 breakpoint olabiliyor ve otomatik caching bunlardan birini kullanıyor. Breakpoint koymanın kendisi ücretsiz; ücret yalnız yazılan ve okunan token'lar için.

TTL, Fiyat ve 1.000 İstek

Fiyat çarpanları belgede açık. 5 dakikalık TTL (cache kaydının geçerli kalma süresi) ile yazma, base input fiyatının 1,25 katı; 1 saatlik TTL ile yazma 2 katı. Okuma normalde 0,1 katı; Opus 5.5 ve Sonnet 5.5'te 0,05, Fable 5.2'de 0,025 katı. Bu çarpanlar batch indirimi gibi diğer fiyat değişiklikleriyle birlikte uygulanıyor. Üçüncü yazıdaki ClaudePricing'e 1 saatlik yazma kolonunu ekledik ve bunun üzerine küçük bir hesap modeli kurduk:

namespace Bookstore.Api.Ai;

// Cache'lenebilir tek bir prefix'i paylaşan bir dizi istek. Yalnız fiyat hesabı; API çağrısı yok.
public sealed record CacheScenario(long PrefixTokens, long SuffixTokens, long OutputTokens, int Requests, int CacheWrites);

public static class PromptCacheCostModel
{
    public static decimal WithoutCache(string model, CacheScenario s) =>
        s.Requests * ClaudePricing.EstimateUsd(model,
            new TokenUsage(s.PrefixTokens + s.SuffixTokens, s.OutputTokens, CacheRead: 0, CacheWrite: 0, Thinking: 0));

    // CacheWrites kadar istek prefix'i yazar (ilki ya da kayıt süresi dolduktan sonraki); diğerleri okur.
    public static decimal WithCache(string model, CacheScenario s)
    {
        TokenUsage write = new(s.SuffixTokens, s.OutputTokens, CacheRead: 0, CacheWrite: s.PrefixTokens, Thinking: 0);
        TokenUsage read = new(s.SuffixTokens, s.OutputTokens, CacheRead: s.PrefixTokens, CacheWrite: 0, Thinking: 0);
        return s.CacheWrites * ClaudePricing.EstimateUsd(model, write)
               + (s.Requests - s.CacheWrites) * ClaudePricing.EstimateUsd(model, read);
    }
}

Değerlendirme aracına bir cache komutu ekledik. Varsayımlar: 40.000 token'lık sabit prefix, istek başına 600 token konuşma ve 250 token cevap, 1.000 istek. "1 write" sütunu prefix'in bir kez yazılıp sonra hep okunduğu, "every write" sütunu ise her isteğin cache'i yeniden yazdığı durum:

dotnet run --project tools/Bookstore.ClaudeEval -- cache 40000 600 250 1000
Assumed: prefix=40000 suffix=600 output=250 requests=1000
model                  no cache    1 write  every write   saving
claude-fable-5-2         334.80      23.19       414.80     93 %
claude-opus-5-5          167.40      15.59       207.40     91 %
claude-sonnet-5-5         83.70       7.80       103.70     91 %
claude-haiku-5-5           4.19       0.59         5.19     86 %

Bu tablo bir ölçüm değil, belgedeki fiyat listesiyle yapılmış bir hesap. Yine de iki şeyi açıkça gösteriyor. Sağlıklı bir cache, Sonnet 5.5'te bu senaryonun maliyetini yaklaşık %91 düşürüyor. Bozuk bir cache ise cache'siz çalışmaktan daha pahalı: her istek prefix'i 1,25 katına yeniden yazıyor. Önce Tahmin Et'teki faturanın açıklaması büyük ihtimalle bu son sütun.

1 saatlik TTL'yi ne zaman seçmeli? Belgeye göre bir prompt 5 dakikadan sık kullanılıyorsa 5 dakikalık cache her okumada ücretsiz yenilendiği için yeterli. 1 saat, 5 dakikadan seyrek ama saatte birden sık kullanılan prefix'ler için anlamlı: cevabını düşünen bir müşteri ya da 5 dakikadan uzun süren bir yan agent. Belge ayrıca cache hit'lerin rate limit'ten düşülmediğini not ediyor. SDK'da 1 saatlik TTL şöyle yazılıyor ve belgedeki JSON'u üretiyor:

    [Fact]
    public void OneHourTtl_IsSerializedAsDocumented()
    {
        CacheControlEphemeral oneHour = new() { Ttl = Ttl.Ttl1h };

        Assert.Equal("""{"type":"ephemeral","ttl":"1h"}""", JsonSerializer.Serialize(oneHour));
    }

İki TTL aynı istekte birlikte kullanılabiliyor ama 1 saatlik kayıtlar 5 dakikalıklardan önce gelmeli. Bizim düzenimizde bu doğal: politika bloğu 1 saate çıkarılırsa konuşmanın otomatik breakpoint'i ondan sonra geliyor.

Cache'in Çalıştığını Doğrulamak

İlk sinyal usage. Belgeye göre cache_read_input_tokens cache'ten okunan, cache_creation_input_tokens bu istekte cache'e yazılan, input_tokens ise son breakpoint'ten sonra gelen ve cache'e girmeyen token'lar. Toplam input bu üçünün toplamı; input_tokens'a bakıp "prompt'umuz küçükmüş" diye düşünmek yanlış. İkinci yazıdan beri kullandığımız ClaudeReplyReader iki cache alanını zaten okuyordu; destek servisinin log satırına ekledik.

İkinci sinyal cache diagnostics. Bu özellik 23 Eylül 2026'da beta'dan çıktı ve artık beta header istemiyor. İstekte bir diagnostics object'i gönderdiğinde API, isteğin parmak izini response id'siyle saklıyor; bir sonraki istekte bu id'yi previous_message_id olarak verirsen iki isteği karşılaştırıp prefix'in ilk nerede ayrıştığını söylüyor. Belgeye göre parmak izi yalnız hash'lerden ve token tahminlerinden oluşuyor, prompt içeriği saklanmıyor. Konuşma kaydına son response id'sini ekledik ve cevaptaki sonucu log'luyoruz:

    // Hiçbir şey ayrışmadıysa diagnostics null; karşılaştırma sürüyorsa cache_miss_reason null.
    private void LogCacheMiss(Guid conversationId, Diagnostics? diagnostics)
    {
        if (diagnostics?.CacheMissReason is not { } reason)
        {
            return;
        }

        logger.LogWarning(
            "Support {ConversationId}: prompt cache miss reason={Reason} missedTokens={MissedTokens}",
            conversationId, reason.Type.Raw(), reason.CacheMissedInputTokens);
    }

Cevapta diagnostics alanının üç hâli var: null (bir ayrışma yok ya da ilk tur), cache_miss_reason: null (karşılaştırma henüz bitmedi, sonraki tura bak) ve dolu bir cache_miss_reason. Sebep tipleri model_changed, system_changed, tools_changed, messages_changed, previous_message_not_found ve unavailable. Belgedeki örnek cevabı test fixture'ı olarak kullandık:

{"id":"msg_01Xyz","type":"message","role":"assistant","model":"claude-haiku-5-5",
 "content":[{"type":"text","text":"..."}],
 "stop_reason":"end_turn",
 "usage":{"input_tokens":42,"cache_read_input_tokens":0,"cache_creation_input_tokens":41850,"output_tokens":210},
 "diagnostics":{"cache_miss_reason":{"type":"system_changed","cache_missed_input_tokens":41850}}}

Bir SDK ayrıntısı burada işimize yaradı. İlk denemede Diagnostics = settings.CacheDiagnostics ? new() { ... } : null yazdık ve özellik kapalıyken test kırıldı. SDK, property'ye açıkça null atandığında alanı göndermemek yerine JSON'a null yazıyor:

MessageCreateParams a = new() { Model = "claude-haiku-5-5", MaxTokens = 10, Messages = [new UserMessageParam { Content = "x" }], Diagnostics = null };
MessageCreateParams b = new() { Model = "claude-haiku-5-5", MaxTokens = 10, Messages = [new UserMessageParam { Content = "x" }] };
Console.WriteLine(JsonSerializer.Serialize(a.RawBodyData));
Console.WriteLine(JsonSerializer.Serialize(b.RawBodyData));
{"model":"claude-haiku-5-5","max_tokens":10,"messages":[{"role":"user","content":"x"}],"diagnostics":null}
{"model":"claude-haiku-5-5","max_tokens":10,"messages":[{"role":"user","content":"x"}]}

API'nin "diagnostics": null'u nasıl karşıladığını belge söylemiyor; biz property'yi hiç set etmeyerek bu belirsizliği ortadan kaldırdık. Aynı ilke SDK'daki bütün opsiyonel alanlar için geçerli: göndermek istemediğin alana null atama, hiç dokunma.

Asıl test, iki turun aynı prefix'i göndermesi:

    [Fact]
    public async Task TwoTurns_SendTheSamePrefixAndChainDiagnostics()
    {
        (SupportChatService service, FakeClaudeHandler handler, _) = Create(FirstTurn, SecondTurnSystemChanged);
        Guid conversation = Guid.NewGuid();

        await service.SendAsync(conversation, "Can I return a book?", null, CancellationToken.None);
        await service.SendAsync(conversation, "What if it arrived damaged?", null, CancellationToken.None);

        JsonElement first = JsonDocument.Parse(handler.RequestBodies[0]).RootElement;
        JsonElement second = JsonDocument.Parse(handler.RequestBodies[1]).RootElement;

        // tools -> system -> messages: yeni turdan önceki her şey byte byte aynı olmalı.
        Assert.Equal(first.GetProperty("system").GetRawText(), second.GetProperty("system").GetRawText());
        Assert.Equal(first.GetProperty("messages")[0].GetRawText(), second.GetProperty("messages")[0].GetRawText());
        Assert.Equal(3, second.GetProperty("messages").GetArrayLength());

        Assert.Equal("""{"previous_message_id":null}""", first.GetProperty("diagnostics").GetRawText());
        Assert.Equal("msg_turn1", second.GetProperty("diagnostics").GetProperty("previous_message_id").GetString());
    }
Passed!  - Failed:     0, Passed:    35, Skipped:     0, Total:    35, Duration: 58 ms - Bookstore.Api.Tests.dll (net10.0)

Sessiz Bozucular

Cache'i bozan değişiklikler hata vermiyor; tek belirti okunan token'ların sıfıra düşmesi ve faturanın artması. Belgenin invalidation tablosu ve diagnostics sayfası en sık sebepleri sayıyor.

System prompt'a her istekte değişen bir değer koymak: $"Today is {DateTime.Now}" gibi bir satır, system'den sonraki her şeyi her istekte yeniden yazdırır. Değişen değer gerekiyorsa yeni user mesajına ya da konuşma ortası system mesajına gitmeli. İkinci yazıda gördüğümüz konuşma ortası system mesajları Opus 5.5, Sonnet 5.5, Haiku 5.5 ve Fable 5.2'de cache'i bozmadan talimat eklemenin yolu.

Sırası değişen ya da kararsız serialize edilen tool listesi. Tool'ları her istekte bir HashSet'ten ya da bir veritabanı sorgusundan sırasız üretmek tools_changed demek. Beşinci yazıdaki BookstoreTools.Definitions sabit sıralı bir liste. Tool loop'unda her tur aynı tool'ları ve büyüyen geçmişi yeniden gönderdiği için orada da otomatik caching'i açtık:

    public MessageCreateParams BuildRequest(IReadOnlyList<MessageParam> messages) => new()
    {
        Model = options.Value.Model,
        MaxTokens = 4096,
        System = SystemPrompt,
        Tools = [.. BookstoreTools.Definitions, BookstoreTools.AuthorWebSearchTool],
        Messages = messages,
        // Her tur tools, system ve büyüyen geçmişi yeniden gönderir; breakpoint son block'u izler.
        CacheControl = new CacheControlEphemeral(),
    };

Prompt'u etkileyen parametreleri değiştirmek. Belgeye göre tool_choice ve resim ekleme messages cache'ini, web search ve citations açıp kapatmak system cache'ini, speed değişikliği system ve messages cache'ini bozuyor. Thinking ayarı ve output_config.effort modele göre tools ve system cache'ini de etkileyebiliyor; effort'u açıkça modelin varsayılan değerine set etmek ise hiç göndermemekle aynı. Altıncı yazıdaki output_config.format değişikliği de bu listede.

Modeli değiştirmek. Cache model başına. Bir A/B testi ya da fallback başka bir modeli seçerse belge bunu model_changed olarak raporluyor.

Geçmişi yeniden serialize etmek. Assistant cevabını ve tool sonuçlarını geri gönderirken dokunmamak gerekiyor; bu serinin new ContentBlockParam(block.Json) alışkanlığı tam da bunu sağlıyor. Geçmişi kırpmak messages_changed üretir.

Tool Loop ve Uzun Konuşmalar

Konuşma context window'a yaklaştığında iki beta özellik var; ikisi de cache ile etkileşiyor. Compaction eski turların yerine sunucuda Claude'un yazdığı bir özet koyuyor. Talep üzerine çalışan sürüm compact-2026-09-04, eşik aşılınca kendiliğinden çalışan sürüm compact-2026-01-12 beta header'ıyla açılıyor; belge yeni uygulamalarda talep üzerine olanı öneriyor. Özet yeni bir içerik olduğu için cache'e yeniden yazılıyor; system'in sonundaki ayrı breakpoint bu yüzden önemli. Context editing (context-management-2025-06-27) ise eski tool sonuçlarını ya da thinking block'larını kurala göre siliyor. Silme o noktadan sonrasının cache'ini bozuyor; belge bu yüzden clear_at_least ile her seferinde cache'i bozmaya değecek kadar token silinmesini öneriyor. Agent context'inin genel stratejisi AI Agent'ları ve MCP serisinin dördüncü yazısında.

Üretimde Dikkat

Cache hit oranını metrik olarak izle. cache_read_input_tokens / (input_tokens + cache_read_input_tokens + cache_creation_input_tokens) oranı bir deploy'dan sonra düşüyorsa, büyük ihtimalle prefix'e değişen bir şey girdi. Diagnostics açıksa log'daki sebep tipi doğrudan yeri gösteriyor.

Paralel istekleri düşün. Belgeye göre bir cache kaydı ancak ilk cevap başladığında kullanılabilir oluyor. Aynı prefix'le aynı anda yüz istek atan bir batch işi yüz kez yazar. İlk isteğin cevabını bekleyip sonra paralelleşmek ya da belgedeki max_tokens: 0 ile yapılan pre-warm isteği bu sorunu çözüyor; pre-warm isteği boş content ve stop_reason: max_tokens dönüyor, yalnız cache yazma ücreti çıkıyor.

Workspace'leri unutma. Cache Claude API'de workspace bazında ayrı tutuluyor. Destek botu ile katalog servisi farklı workspace'lerdeyse aynı politikayı paylaşsalar bile birbirlerinin cache'ini okuyamaz.

Prefix'i sürümle. Politika metni değiştiğinde ilk istek cache'i yeniden yazar; bu beklenen bir maliyet. Ama metnin her deploy'da yeniden üretilip küçük farklarla değişmesi beklenen bir şey değil. Satır sonlarını normalize etmemizin sebebi bu: Windows'ta checkout edilmiş bir dosya ile Linux'taki build farklı byte'lar üretmesin.

Sık Düşülen Tuzaklar

Değişen içeriği breakpoint'ten önce koymak. Zaman, istek id'si ya da kullanıcı adı system prompt'ta durursa cache her istekte yeniden yazılır.

Kısa prompt'u cache'lemeye çalışmak. 512 token'ın altındaki prefix hata vermeden cache'siz işlenir; iki cache alanının da 0 olması bunun işareti.

input_tokens'ı toplam input sanmak. Caching açıkken asıl hacim cache_read_input_tokens'ta.

Breakpoint'i her istekte değişen block'a koymak. Breakpoint, istekler arasında aynı kalan son block'ta olmalı; değişen yeni mesajda değil. Otomatik caching bunu konuşma için zaten yapıyor.

Opsiyonel SDK alanına null atamak. Alan null olarak gönderilir; göndermek istemiyorsan set etme.

1 saatlik TTL'yi her yere koymak. Sık kullanılan bir prefix için 5 dakikalık cache ücretsiz yenilenir; 1 saat yazmayı iki katına çıkarır.

Küçük Bir Deneme

★☆☆ Değerlendirme aracındaki cache komutunu kendi varsayımlarınla çalıştır: prefix'i 2.000 token'a indir ve istek sayısını 20'ye düşür. Tasarrufun nasıl değiştiğini ve hangi noktada caching'in kendini amorti etmediğini bul.

★★☆ SupportKnowledge'a politika metninin hash'ini SHA256.HashData ile hesaplayan bir property ekle ve uygulama açılırken log'la. İki farklı makinede build alıp hash'lerin aynı olduğunu doğrula.

★★★ PromptCachingTests'e, politika bloğunu 1 saatlik TTL ile işaretleyen bir varyasyon için test yaz: istekte 1 saatlik breakpoint'in 5 dakikalık otomatik breakpoint'ten önce geldiğini JSON üzerinden doğrula. Sonra ClaudePricing'in CacheWrite1h alanını kullanarak 1 saatlik cache'in hangi istek aralığında 5 dakikalıktan ucuz olduğunu hesapla.

Prompt caching tek istekte tekrar eden metni ucuzlatıyor. Sekizinci yazıda başka bir indirime geçiyoruz: acil olmayan binlerce isteği Message Batches API ile yarı fiyatına çalıştırmak ve aynı PDF'i Files API ile bir kez yükleyip birden çok istekte kullanmak.