Dokumentasi

Dokumentasi

caching prompt

Tandai satu kali prefiks prompt yang stabil dengan cache_control — atau akses model Claude dalam format OpenAI, yang tidak memiliki kolom tersebut, dan Kunavo akan menempatkan breakpoint untuk Anda. Pada setiap panggilan berikutnya yang menggunakan kembali prefiks itu, prefiks tersebut ditagih sebesar sebagian dari harga input — 10% input pada sebagian besar model chat, serendah 2.5%. Lapisan perutean Kunavo menjaga cache tetap hangat dengan mengikat percakapan Anda ke kunci penyedia hulu yang sama.

Pada system prompt 30 ribu token dengan Claude Sonnet 5, pembacaan cache dikenai biaya $0.14 / 1M alih-alih $1.4 / 1M. Untuk agen yang mengirim ulang konteks yang sama 50 kali dalam satu sesi, cache menghemat ~90% biaya input.

Periksa cache hit dengan dua permintaan

Kirim permintaan yang sama dua kali dalam lima menit. Usage pada respons pertama mencatat prefiks yang ditandai di cache_creation_input_tokens; respons kedua mencatatnya di cache_read_input_tokens, bagian yang ditagih dengan tarif baca cache yang tertera di halaman model.

check_cache.sh
# Send this twice within five minutes.
curl https://api.kunavo.com/v1/messages \
  -H "Authorization: Bearer $KUNAVO_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d @request.json        # a system block with "cache_control": {"type": "ephemeral"}

# First response — the prefix is written:
#   "usage": {"input_tokens": 21, "cache_creation_input_tokens": 30412,
#             "cache_read_input_tokens": 0, "output_tokens": 180}
# Second response — the prefix is read back:
#   "usage": {"input_tokens": 21, "cache_creation_input_tokens": 0,
#             "cache_read_input_tokens": 30412, "output_tokens": 175}

Tiga hal mengubah baca itu kembali menjadi tulis ketika permintaan melewati gateway, dan tidak satu pun mengembalikan error:

  • Permintaan sampai ke penyedia hulu lain. Cache hanya ada di tempat ia ditulis. Karena itu Kunavo mematok prefiks sebuah percakapan ke satu kunci penyedia hulu (perutean afinitas, di bawah).
  • Ada yang mengubah prefiks. Pesan sistem yang ditulis ulang atau daftar tool yang urutannya diubah adalah prefiks yang berbeda, dan prefiks yang berbeda tidak menghasilkan cache hit.
  • Jedanya lebih lama dari umur entri. Sebuah entri kedaluwarsa lima menit setelah terakhir dibaca, jadi agen yang berhenti lebih lama di antara giliran akan membangunnya ulang setiap kali.

Prompt caching Claude membahas ketiganya satu per satu beserta angka usage yang membedakannya.

Cara mengaktifkannya

Tambahkan breakpoint cache_control: { type: "ephemeral" } pada blok sistem, pesan, atau definisi alat mana pun. Model menyimpan rentang hingga breakpoint itu ke dalam cache pada panggilan pertama; panggilan berikutnya yang memiliki byte prefiks yang sama akan menggunakan cache.

cache_example.py
from openai import OpenAI

client = OpenAI(
    api_key="sk-kn-...",
    base_url="https://api.kunavo.com/v1",
)

# Mark the large, stable prefix once with cache_control.
# Every subsequent call that sends the same prefix reads it back
# from cache at 0.10× the input rate on Claude Sonnet 5.
# Per-model rates are in the pricing table below.
SYSTEM = [
    {
        "type": "text",
        "text": open("long_system_prompt.txt").read(),
        "cache_control": {"type": "ephemeral"},
    },
]

resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[
        {"role": "system", "content": SYSTEM},
        {"role": "user", "content": "Summarize the doc above in 3 bullets."},
    ],
)

u = resp.usage
# OpenAI-compat fields populated by Kunavo:
# - prompt_tokens_details.cached_tokens — read from cache
# - cache_creation_input_tokens          — written to cache this call
print(u.prompt_tokens_details.cached_tokens,
      getattr(u, "cache_creation_input_tokens", 0))

Format kawat identik pada Messages API native — cache_control diteruskan ke Claude tanpa diterjemahkan:

messages_api.sh
# Native Anthropic Messages API — cache_control passes through untranslated.
curl https://api.kunavo.com/v1/messages \
  -H "Authorization: Bearer $KUNAVO_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "system": [
      {
        "type": "text",
        "text": "<your large stable system prompt>",
        "cache_control": { "type": "ephemeral" }
      }
    ],
    "messages": [
      { "role": "user", "content": "Summarize the doc above in 3 bullets." }
    ],
    "max_tokens": 1024
  }'
Byte yang stabil itu penting. Kunci cache ditentukan berdasarkan konten. Satu karakter berbeda di mana pun dalam prefiks akan membatalkan cache untuk rentang tersebut. Letakkan konten dinamis (stempel waktu, nama pengguna, ID per panggilan) setelah blok yang di-cache, bukan di dalamnya.

Breakpoint otomatis pada endpoint berformat OpenAI

Format OpenAI Chat Completions dan Responses sama sekali tidak memiliki kolom cache_control — OpenAI melakukan caching secara implisit di servernya sendiri. Claude tidak: Claude hanya menyimpan ke cache bagian yang ditandai breakpoint. Karena itu, klien yang menggunakan protokol OpenAI tidak dapat meminta caching, dan permintaan yang diterjemahkan secara langsung akan sampai ke Claude tanpa breakpoint — sehingga setiap giliran dalam percakapan panjang menagih ulang seluruh riwayat dengan tarif input penuh.

Jadi, saat Anda mengakses model Claude melalui /v1/chat/completions atau /v1/responses, Kunavo menetapkan breakpoint yang tidak dapat dibawa oleh protokol tersebut. Tidak ada yang perlu diaktifkan. Ini hanya berlaku untuk model Claude — setiap keluarga model lainnya dilayani melalui protokol yang vendor-nya melakukan caching secara implisit, tanpa breakpoint yang perlu ditempatkan.

no_cache_control.py
# Plain OpenAI Chat Completions — no cache_control anywhere in the body.
# The format has no field for one; Kunavo inserts the breakpoints instead.
resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[
        {"role": "system", "content": LONG_SYSTEM_PROMPT},
        {"role": "user", "content": "first question"},
        {"role": "assistant", "content": "...previous answer..."},
        {"role": "user", "content": "follow-up"},
    ],
)

u = resp.usage
# Turn that writes the cache:
#   u.cache_creation_input_tokens            -> 18,000
#   u.prompt_tokens_details.cached_tokens    ->      0
# Next turn, same prefix, inside the 5-minute window:
#   u.cache_creation_input_tokens            ->    240   (only the new turn)
#   u.prompt_tokens_details.cached_tokens    -> 18,000
#
# Both stuck at 0 on a conversation well over the threshold? The prefix is
# changing between calls — a timestamp or request id inside the cached span.

Lokasi breakpoint dan urutan penggunaan anggarannya:

  • Breakpoint bergulir pada pesan terakhir — pada blok konten terakhir dari pesan terakhir, sehingga semua bagian sebelumnya masuk ke cache; jika permintaan diakhiri dengan pesan sistem yang tetap berada di percakapan (lihat di bawah), breakpoint ini justru ditempatkan pada blok tepat sebelum pesan tersebut. Hanya ditambahkan jika messages sudah berisi giliran asisten: penulisan cache menagih 1.25× input, jadi menandai prompt yang tidak akan dikirim ulang akan menambah biaya seperempat tanpa menghemat apa pun. Giliran asisten sebelumnya menjadi bukti bahwa prefiks yang sama persis ini akan dikirim ulang.
  • Satu pada system, satu pada tools — bagian awal prompt yang stabil. Layak ditandai bahkan pada panggilan sekali pakai, karena pemanggil yang mengirim prompt sistem sekali hampir selalu mengirim prompt yang sama pada panggilan berikutnya.
  • Di depan pesan sistem yang tetap berada di percakapan — sisa anggaran setelah breakpoint bergulir serta breakpoint pada system dan tools ditempatkan pada blok tepat sebelum pesan semacam itu yang terakhir dan masih diikuti sesuatu, lalu sebelum pesan semacam itu yang sebelumnya. Diisi paling akhir, karena hanya klien yang memindahkan pesan semacam itu dari giliran ke giliran yang membutuhkannya; percakapan tanpa pesan semacam itu, yaitu hampir semua percakapan, tidak memiliki posisi ini.

Ada dua batas. Panjang minimum yang dapat di-cache oleh Anthropic bergantung pada model, dan Kunavo sama sekali tidak menyisipkan apa pun selama prompt (gabungan pesan, system, dan skema alat) jumlah karakternya kurang dari setengah jumlah token minimum tersebut. Minimum Anthropic dan batas bawah Kunavo adalah 512 token dan 256 karakter pada Opus 5.5, Opus 5, Sonnet 5.5, Fable 5.1, dan Fable 5; 1.024 token dan 512 karakter pada Sonnet 5, Opus 4.8, Sonnet 4.6, dan Sonnet 4.5; 2.048 token dan 1.024 karakter pada Opus 4.7; serta 4.096 token dan 2.048 karakter pada Opus 4.6 dan Haiku 4.5. Batas bawah ini menghitung dua token per karakter, lebih banyak daripada token yang dipakai per karakter oleh teks berbahasa Mandarin atau Jepang, sehingga prompt di bawahnya terlalu pendek untuk di-cache. Prompt di atasnya bisa saja belum mencapai minimum, karena dalam bahasa Inggris satu token kira-kira setara dengan empat karakter: Anthropic diam-diam mengabaikan breakpoint pada prefiks yang lebih pendek dari minimum dan tidak mengenakan biaya penulisan maupun pembacaan. Anthropic juga menerima paling banyak 4 breakpoint per permintaan — breakpoint kelima tidak diabaikan diam-diam, tetapi menghasilkan error 400.

Pesan system atau developer yang dikirim setelah jawaban pertama model tetap berada di percakapan, pada giliran tempat pesan itu dikirim, alih-alih diangkat ke system: mengangkatnya akan mengubah bagian awal prompt pada giliran saat pesan itu ditambahkan dan menulis ulang seluruh riwayat ke cache. Claude menerimanya sebagai pesan sistem jika penyedia hulu menerimanya, dan jika tidak, sebagai teks di giliran pengguna tersebut di antara tag <system-reminder>. Pesan sistem yang dikirim sebelum jawaban pertama model, atau pesan yang breakpoint-nya sendiri meminta cache satu jam, tetap masuk ke system.

Breakpoint Anda sendiri tidak pernah dipindahkan atau dihapus. Breakpoint tersebut menggunakan anggaran 4 yang sama, dan Kunavo hanya mengisi posisi yang Anda biarkan kosong. Kirim 4 breakpoint Anda sendiri dan tidak ada yang ditambahkan; kirim 1 dan hingga 3 breakpoint dapat ditambahkan di sekitarnya. Saat anggaran terbatas, posisi terdalam diisi lebih dahulu — breakpoint di bagian akhir prompt menyimpan semua bagian sebelumnya ke dalam cache, jadi breakpoint bergulir paling bermanfaat. Pada endpoint native /v1/messages, tidak ada yang disisipkan: isi permintaan Anda diteruskan persis seperti yang dikirim, dan semua breakpoint di dalamnya berasal dari Anda.
Breakpoint bukan berarti cache hit. Breakpoint menandai prefiks agar dapat di-cache; cache hit tetap memerlukan byte prefiks yang sama persis untuk dikirim kembali dalam jendela sementara Anthropic — lima menit, diperbarui pada setiap pembacaan — dan diarahkan ke kunci penyedia hulu yang sama. Itulah fungsi perutean afinitas di bawah ini. Panggilan pertama untuk prefiks baru selalu merupakan penulisan, bukan pembacaan, dan prompt yang berubah pada setiap giliran tidak akan menghasilkan cache hit, di mana pun breakpoint ditempatkan.

Pelaporannya tidak berubah: kolom cached_tokens dan cache_creation_input_tokens yang sama seperti dijelaskan di bagian Kolom respons akan terisi, baik breakpoint berasal dari Anda maupun dari gateway. Jadi, objek penggunaan adalah cara Anda memeriksa apakah breakpoint yang disisipkan benar-benar menghasilkan cache hit.

Harga

Tarif cache diturunkan dari harga input tiap model, dengan rasio yang ditetapkan vendor sendiri — tabel di bawah menjadi acuan, dan setiap tarif di dalamnya dihitung dari harga input model tersebut, bukan dimasukkan secara manual. Pembacaan cache ditagih sebesar 0.10× harga input baik di Anthropic maupun OpenAI, dengan 2 pengecualian: 0.025× di Claude Fable 5.1, 0.05× di Claude Opus 5.5. Penulisan cache ditagih sebesar 1.25× harga input untuk setiap model Claude dan pada GPT-6 Astra, GPT-6 Sol, GPT-6 Luna, GPT-5.6 Sol, dan GPT-5.6 Terra. Kunavo menagih penulisan Claude dengan TTL 1 jam pada tarif yang sama, yaitu 1.25×, lebih rendah daripada 2× milik Anthropic. Model lainnya tidak memiliki harga penulisan tersendiri: penulisannya ditagih dengan tarif input biasa. Dalam kedua kasus tersebut, prefiks yang digunakan kembali meski hanya sekali lebih murah jika di-cache daripada dikirim tanpa cache. Setiap kategori mendapat diskon sesuai modelnya, sehingga harga cache-nya memiliki diskon yang sama dari harga daftar vendor dengan harga input dan outputnya.

Anthropic

ModelInputCache readCache write
Claude Fable 5.1$7$0.175 0.025×$8.75 1.25×
Claude Fable 5$7$0.7 0.10×$8.75 1.25×
Claude Opus 5.5$2.8$0.14 0.050×$3.5 1.25×
Claude Opus 5$3.5$0.35 0.10×$4,375 1.25×
Claude Opus 4.8$3.5$0.35 0.10×$4,375 1.25×
Claude Opus 4.7$3.5$0.35 0.10×$4,375 1.25×
Claude Opus 4.6$3.5$0.35 0.10×$4,375 1.25×
Claude Sonnet 5$1.4$0.14 0.10×$1.75 1.25×
Claude Sonnet 4.6$2.1$0.21 0.10×$2,625 1.25×
Claude Haiku 4.5$0.7$0.07 0.10×$0.875 1.25×

OpenAI

ModelInputCache readCache write
GPT-6 Astra$4$0.4 0.10×$5 1.25×
GPT-6 Sol$0.8$0.08 0.10×$1 1.25×
GPT-6 Luna$0.04$0.004 0.10×$0.05 1.25×
GPT-5.6 Sol$2$0.2 0.10×$2.5 1.25×
GPT-5.6 Terra$0.7$0.07 0.10×$0.875 1.25×
GPT-5.5$2$0.2 0.10×$2 1×
Prompt panjang ditagih pada tingkat harga konteks panjang vendor. Di atas ambang batas, seluruh permintaan ditagih dengan kelipatan harga input dan output model, termasuk pembacaan dan penulisan cache: di atas 272K token input pada GPT-6 Astra, GPT-6 Sol, GPT-6 Luna, GPT-5.6 Sol, GPT-5.6 Terra, dan GPT-5.5 (2× input, 1.5× output). Claude tidak mengenakan biaya tambahan untuk konteks panjang: konteks 1 jutanya ditagihkan dengan tarif standar.
Batas minimum katalog memperhitungkan cache. Kunavo menagih max(catalog_cost, upstream × markup) per panggilan — batas minimum dihitung berdasarkan tarif input yang di-cache, bukan tarif input segar, sehingga penghematan diteruskan kepada Anda dan tidak terhapus.
Tarif di atas adalah harga katalog. Tagihan sebenarnya per panggilan dihitung berdasarkan kredit penyedia hulu yang benar-benar digunakan model (ditambah markup), lalu diberlakukan batas minimum sebesar tarif katalog. Pada cache hit, penyedia hulu mengenakan biaya kredit yang lebih sedikit — sehingga perhitungan biaya penyedia hulu dikalikan markup biasanya lebih rendah daripada harga katalog, dan harga katalog yang berlaku. Singkatnya: tarif katalog adalah harga tertinggi yang mungkin Anda bayar; panggilan yang menggunakan cache mungkin lebih murah, tetapi tidak pernah lebih mahal.

Perutean afinitas menjaga cache tetap hangat

Cache prompt penyedia hulu berlaku per kunci API. Jika setiap permintaan diarahkan ke kunci penyedia hulu yang acak, tingkat cache hit sekitar 1/N. Kunavo membuat hash stabil dari prompt sistem Anda + pesan pengguna pertama, lalu mengarahkan semua panggilan dengan prefiks yang sama ke kunci penyedia hulu yang sama — menggunakan hashing rendezvous berbobot, sehingga hanya sekitar 1/N kunci yang dipetakan ulang saat kumpulan berubah.

Anda tidak perlu mengonfigurasi apa pun. Perutean afinitas diterapkan otomatis ke semua panggilan chat. (Gambar / video / TTS menggunakan perutean acak — tidak ada cache per kunci untuk modalitas tersebut.)

Metrik dasbor

Setelah panggilan Anda mulai menggunakan cache, /app/usage menampilkan dua metrik gabungan untuk rentang waktu yang dipilih:

  • Tingkat cache hit — token input yang di-cache ÷ total token input. Akurat, dihitung dari jumlah token per panggilan.
  • Penghematan — USD yang dihemat dalam rentang waktu ini, dihitung per model menggunakan tarif pembacaan cache sebenarnya (tanpa perkiraan rata-rata sederhana). Ini adalah perkiraan pada tarif katalog — karena tagihan sebenarnya memiliki batas minimum sebesar harga katalog dan panggilan yang menggunakan cache dapat lebih murah daripada batas tersebut, angka ini merupakan batas atas yang cukup akurat untuk penghematan sebenarnya. Untuk beban kerja chat biasa, selisih keduanya hanya beberapa persen.

Halaman detail setiap panggilan (/app/usage/<id>) menampilkan jumlah token cache + penulisan cache jika tidak nol, serta jumlah tagihan sebenarnya.

Kolom respons

Kunavo menampilkan token cache dalam kedua format respons. Pada endpoint yang kompatibel dengan OpenAI /v1/chat/completions:

KolomMakna
usage.prompt_tokensTotal token input (termasuk cache + penulisan cache).
usage.prompt_tokens_details.cached_tokensBagian input yang dilayani dari cache.
usage.cache_creation_input_tokensToken yang ditulis ke cache pada panggilan ini (ditagih sesuai tarif penulisan cache model dalam tabel di atas).

Pada Messages API native di /v1/messages, kolom asli Anthropic diteruskan:

KolomMakna
input_tokensInput segar (tidak di-cache).
cache_read_input_tokensDilayani dari cache, ditagih sebesar 0.10× input (0.025× di Claude Fable 5.1, 0.05× di Claude Opus 5.5).
cache_creation_input_tokensDitulis ke cache pada panggilan ini (ditagih 1.25× input — termasuk penulisan dengan TTL 1 jam, lebih rendah daripada 2× milik Anthropic).

Langkah selanjutnya

  • Referensi Messages API — format native Anthropic dengan contoh caching lengkap.
  • Caching prompt Claude — angka objek penggunaan untuk mendiagnosis cache, dan tiga cara perutean melalui gateway merusak tingkat cache hit.
  • Penagihan & buku besar — interaksi batas minimum max() yang memperhitungkan cache dengan biaya penyedia hulu.
  • Tabel harga lengkap — harga input segar / output untuk setiap model chat, dengan sub-tabel cache di bagian bawah.