Documentazione

Documentazione

caching dei prompt

Contrassegna una volta con cache_control un prefisso stabile del prompt oppure raggiungi un modello Claude in formato OpenAI, che non dispone di questo campo, e Kunavo inserirà i punti di interruzione al posto tuo. Ogni chiamata successiva che riutilizza il prefisso lo addebita a una frazione del prezzo di input — 10% dell'input sulla maggior parte dei modelli di chat, fino a un minimo di 2.5%. Il livello di instradamento di Kunavo mantiene calda la cache vincolando la conversazione alla stessa chiave upstream.

Con un prompt di sistema di 30.000 token con Claude Sonnet 5, una lettura dalla cache costa $0.14 / 1M invece di $1.4 / 1M. Per un agente che reinvia lo stesso contesto 50 volte a sessione, la cache fa risparmiare ~90% del costo di input.

Come abilitarla

Aggiungi un punto di interruzione cache_control: { type: "ephemeral" } a qualsiasi blocco di sistema, messaggio o definizione di strumento. Alla prima chiamata il modello memorizza nella cache la porzione fino a quel punto; le chiamate successive con gli stessi byte di prefisso raggiungono la 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))

Formato wire identico nella Messages API nativa: cache_control viene inoltrato a Claude senza traduzioni:

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
  }'
La stabilità dei byte è fondamentale. La chiave della cache è basata sul contenuto. Basta un solo carattere diverso in qualsiasi punto del prefisso per invalidare la cache per quella porzione. Inserisci i contenuti dinamici (timestamp, nomi utente, ID per chiamata) dopo il blocco memorizzato nella cache, non al suo interno.

Punti di interruzione automatici sugli endpoint in formato OpenAI

I formati OpenAI Chat Completions e Responses non hanno alcun campo cache_control: OpenAI gestisce la cache in modo implicito sui propri server. Claude no: memorizza nella cache solo ciò che viene contrassegnato da un punto di interruzione. Un client che usa OpenAI non ha quindi modo di richiedere la memorizzazione nella cache e una traduzione diretta della richiesta arriva a Claude senza alcun punto di interruzione: ogni turno di una chat lunga comporta così un nuovo addebito per l'intera cronologia alla tariffa di input piena.

Quando raggiungi un modello Claude tramite /v1/chat/completions o /v1/responses, Kunavo imposta i punti di interruzione che il protocollo non può trasportare. Non devi attivare nulla. La funzione si applica solo ai modelli Claude: tutte le altre famiglie usano un protocollo il cui fornitore gestisce la cache in modo implicito, senza punti di interruzione da inserire.

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.

Dove vengono inseriti e in quale ordine viene utilizzato il budget:

  • Un punto di interruzione scorrevole sull'ultimo messaggio — nell'ultimo blocco di contenuto dell'ultimo messaggio, così da memorizzare nella cache tutto ciò che lo precede; se la richiesta termina con un messaggio di sistema che resta nella conversazione (vedi sotto), va invece sul blocco subito prima di quel messaggio. Viene aggiunto solo quando messages contiene già un turno dell'assistente: una scrittura nella cache viene addebitata a 1.25× input, quindi contrassegnare un prompt che nessuno reinvia costa un quarto in più senza far risparmiare nulla. Un turno precedente dell'assistente dimostra che esiste l'intenzione di reinviare esattamente questo prefisso.
  • Uno su system, uno su tools — la parte iniziale stabile del prompt. Vale la pena contrassegnarla anche in una chiamata singola, perché chi invia un prompt di sistema una volta quasi sempre ne invia uno identico alla chiamata successiva.
  • Davanti a un messaggio di sistema che resta nella conversazione — ciò che il punto scorrevole e i punti su system e tools lasciano del budget, sul blocco subito prima dell'ultimo messaggio di questo tipo seguito da qualcos'altro, poi davanti al precedente. Viene riempito per ultimo, perché serve solo a un client che sposta un messaggio del genere da un turno all'altro; una conversazione senza messaggi di questo tipo, cioè quasi tutte, non ha questa posizione.

Due limiti ne delimitano l'uso. Se il prompt — messaggi, system e schemi degli strumenti insieme — è inferiore a 8,192 caratteri, non viene inserito nulla: Anthropic ignora silenziosamente i punti di interruzione su prefissi inferiori alla lunghezza minima memorizzabile nella propria cache (1,024 token, 2,048 su Haiku) e non addebita né una scrittura né una lettura, quindi inserirne uno non servirebbe a nulla. Inoltre Anthropic accetta al massimo 4 punti di interruzione per richiesta: il quinto causa un errore 400, non viene ignorato silenziosamente.

Un messaggio di sistema o developer inviato dopo la prima risposta del modello resta nella conversazione, nel turno in cui è stato inviato, invece di essere spostato in system: spostarlo cambierebbe l'inizio del prompt nel turno in cui viene aggiunto e riscriverebbe nella cache l'intera cronologia. Claude lo riceve come messaggio di sistema dove l'upstream ne accetta uno, altrimenti come testo in quel turno utente, tra tag <system-reminder>. Un messaggio di sistema inviato prima della prima risposta del modello, o uno il cui punto di interruzione chiede la cache di un'ora, va comunque in system.

I tuoi punti di interruzione non vengono mai spostati né rimossi. Rientrano nello stesso limite di 4 e Kunavo inserisce punti solo nelle posizioni che hai lasciato vuote. Se ne invii 4, non ne viene aggiunto nessuno; se ne invii 1, se ne possono aggiungere fino a 3. Quando il budget è limitato, viene riempita per prima la posizione più profonda: un punto di interruzione verso la fine del prompt memorizza nella cache tutto ciò che lo precede, quindi quello scorrevole è il più utile. Sull'endpoint /v1/messages nativo non viene inserito nulla: il corpo della richiesta passa esattamente come inviato e ogni punto di interruzione al suo interno è tuo.
Un punto di interruzione non è un cache hit. Contrassegna un prefisso come memorizzabile nella cache; perché si verifichi un hit, gli stessi byte di prefisso devono essere reinviati entro la finestra effimera di Anthropic — cinque minuti, rinnovata a ogni lettura — e la richiesta deve raggiungere la stessa chiave upstream. È a questo che serve l'instradamento per affinità descritto sotto. La prima chiamata con un prefisso nuovo è sempre una scrittura, mai una lettura, e un prompt che cambia a ogni turno non produrrà hit, indipendentemente dalla posizione dei punti di interruzione.

La reportistica non cambia: gli stessi campi cached_tokens e cache_creation_input_tokens descritti in Campi della risposta vengono valorizzati sia quando i punti di interruzione provengono da te, sia quando li inserisce il gateway. L'oggetto usage ti permette quindi di verificare se un punto inserito ha effettivamente generato un hit.

Prezzi

Le tariffe della cache derivano dal prezzo di input di ciascun modello, secondo i rapporti stabiliti dal fornitore: le tabelle seguenti sono la fonte di riferimento e ogni tariffa è calcolata a partire dal prezzo di input del modello, anziché essere inserita manualmente. Le letture dalla cache sono addebitate al 0.10× dell'input sia su Anthropic sia su OpenAI, con 2 eccezioni: 0.025× on Claude Fable 5.1, 0.05× on Claude Opus 5.5. Le scritture nella cache sono addebitate al 1.25× dell'input per ogni modello Claude e con GPT-6 Astra, GPT-6 Sol, GPT-6 Luna, GPT-5.6 Sol e GPT-5.6 Terra. Kunavo addebita le scritture con TTL di 1 ora di Claude allo stesso 1.25×, meno di 2× applicato da Anthropic. Per tutti gli altri modelli non esiste un prezzo di scrittura separato: le scritture vengono addebitate alla tariffa standard di input. In entrambi i casi, riutilizzare un prefisso anche una sola volta costa meno che inviarlo senza cache. Ogni classe mantiene lo sconto specifico del modello, quindi i prezzi della cache applicano lo stesso sconto rispetto al listino del fornitore previsto per input e output.

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×
I prompt lunghi vengono addebitati secondo la tariffa del fornitore per il contesto lungo. Oltre la soglia, l'intera richiesta viene addebitata a un multiplo dei prezzi di input e output del modello, incluse le letture e le scritture della cache: oltre 272K token di input su GPT-6 Astra, GPT-6 Sol, GPT-6 Luna, GPT-5.6 Sol, GPT-5.6 Terra e GPT-5.5 (2× input, 1.5× output). Claude non applica alcun sovrapprezzo per il contesto esteso: il suo contesto da 1M viene fatturato alla tariffa standard.
La soglia minima del catalogo tiene conto della cache. Kunavo addebita max(catalog_cost, upstream × markup) per chiamata: la soglia minima viene calcolata sulla tariffa di input memorizzato nella cache, non su quella di input non memorizzato, così il risparmio arriva a te invece di essere annullato.
Le tariffe qui sopra sono i prezzi del catalogo. L'addebito effettivo per chiamata viene calcolato in base ai crediti upstream realmente consumati dal modello (con un ricarico) e poi confrontato con il prezzo minimo del catalogo. In caso di cache hit, il fornitore upstream addebita meno crediti: il ramo basato su upstream × ricarico scende quindi di solito sotto il prezzo del catalogo e prevale il ramo del catalogo. In breve: la tariffa del catalogo è il tuo prezzo massimo; le chiamate con cache possono costare meno, ma mai di più.

L'instradamento per affinità mantiene calda la cache

Le cache dei prompt upstream sono associate alla chiave API. Se ogni richiesta viene indirizzata a una chiave upstream casuale, il tasso di cache hit è ~1/N. Kunavo ricava un hash stabile dal prompt di sistema e dal primo messaggio dell'utente, quindi indirizza tutte le chiamate con lo stesso prefisso alla stessa chiave upstream, usando l'hashing rendezvous ponderato. In questo modo, quando cambia il pool, vengono riassegnate solo ~1/N delle chiavi.

Non devi configurare nulla. L'instradamento per affinità si applica automaticamente a tutte le chiamate chat. (Immagini / video / TTS usano un instradamento casuale: per queste modalità non esiste una cache per chiave.)

Metriche del dashboard

Quando le chiamate iniziano a usare la cache, /app/usage mostra due metriche aggregate per la finestra selezionata:

  • Tasso di cache hit — token di input memorizzati nella cache ÷ token di input totali. Calcolo preciso basato sul conteggio dei token per chiamata.
  • Risparmio — importo in USD risparmiato in questa finestra, calcolato per modello usando la tariffa effettiva di lettura dalla cache (senza approssimazioni basate su una media fissa). È una stima basata sulle tariffe del catalogo: poiché gli addebiti effettivi hanno un limite minimo pari al prezzo del catalogo e le chiamate con cache possono costare meno del minimo, il valore è un limite superiore preciso del risparmio effettivo. Per i carichi di lavoro di chat tipici, i due valori differiscono di pochi punti percentuali.

La pagina di dettaglio di ciascuna chiamata (/app/usage/<id>) mostra il numero di token memorizzati e scritti nella cache quando è diverso da zero, oltre all'importo effettivamente addebitato.

Campi della risposta

Kunavo espone i token della cache in entrambi i formati di risposta. Nell'endpoint compatibile con OpenAI /v1/chat/completions:

CampoSignificato
usage.prompt_tokensTotale dei token di input (include quelli memorizzati e scritti nella cache).
usage.prompt_tokens_details.cached_tokensSottoinsieme dell'input servito dalla cache.
usage.cache_creation_input_tokensToken scritti nella cache in questa chiamata (addebitati alla tariffa di scrittura nella cache del modello, indicata nelle tabelle qui sopra).

Nella Messages API nativa all'indirizzo /v1/messages, vengono inoltrati i campi originali Anthropic:

CampoSignificato
input_tokensInput nuovo (non memorizzato nella cache).
cache_read_input_tokensServito dalla cache, addebitato a 0.10× dell'input (0,025× su Claude Fable 5.1, 0,05× su Claude Opus 5.5).
cache_creation_input_tokensScritto nella cache in questa chiamata (addebitato a 1.25× il prezzo di input, anche per le scritture con TTL di 1 ora: meno di 2×, applicato da Anthropic).

Prossimi passi