Novità Registrati gratis, 10 chiamate le offriamo noi. Fino a $1, senza carta.
Prompt caching per LLM in Python: tutorial con codice funzionante

Prompt caching per LLM in Python: tutorial con codice funzionante

Indice
  1. 0. Configurazione
  2. 1. Chiamate con prompt caching, identiche per tutti i provider
  3. 2. Anthropic Claude: marker cache_control espliciti
  4. 3. OpenAI GPT-5.x: caching automatico
  5. 4. Google Gemini: caching implicito
  6. 5. DeepSeek-v4-flash: cache automatica su disco
  7. 6. Alibaba Qwen: hit segnalato, sconto variabile
  8. 7. Benchmark tra provider, misurato il 2026-05-25
  9. 8. Checklist prima del rilascio
  10. 9. Pattern che tengono conto del TTL
  11. 8.1 Workload legati alla sessione, come chat e assistenti IDE
  12. 8.2 Heartbeat per processi batch e cron
  13. 8.3 Documenti in cold storage
  14. 10. Che cosa aggiunge davvero il gateway
  15. FAQ

TL;DR: Un solo SDK OpenAI e un solo base_url per tutti i principali LLM. I dati di questo articolo sono stati misurati sul gateway Synthorai in produzione il 2026-05-25, usando un system prompt stabile da circa 7.300 token. Il vantaggio del gateway è semplice: un unico endpoint, un unico header di autenticazione e un campo usage.cost che evita di dover mantenere una matrice dei prezzi per ogni vendor. La matematica dei Transformer alla base del caching è descritta nella Parte 1: principi del caching; le scelte progettuali dei singoli provider sono trattate nella Parte 2: confronto tra provider.

Serie: Parte 3 di 5 · Articoli precedenti: Parte 1 — Principi del caching · Parte 2 — Confronto e valutazione dei provider · Prossimi articoli: Parte 4 — Miglior LLM per caso d’uso · Parte 5 — Integrazione con LangChain


0. Configurazione

pip install openai
# common.py — reused across every example
import os, time
from openai import OpenAI

oai = OpenAI(
    api_key=os.environ["SYNTHORAI_KEY"],
    base_url="https://synthorai.io/v1",
)

Il gateway espone il protocollo OpenAI per tutti i modelli supportati: GPT, Claude, Gemini, DeepSeek e Qwen. Basta cambiare il campo model, non l’SDK. L’autenticazione usa Authorization: Bearer <key>.

Modelli con prompt caching disponibili sul gateway pubblico, secondo la rilevazione del 2026-05: claude-haiku-4-5, claude-sonnet-4-5 / 4-6, claude-opus-4-5 / 4-6 / 4-7, gpt-5.4-mini, gpt-5.4-nano, gpt-5.2, gpt-5.5-pro, gemini-2.5-flash, gemini-2.5-pro, gemini-3.1-pro-preview, deepseek-v4-flash, qwen3-max, qwen3.5-flash. L’elenco completo e aggiornato è disponibile con GET /v1/models.


1. Chiamate con prompt caching, identiche per tutti i provider

Non serve abilitarlo esplicitamente. Se il modello upstream supporta il prompt caching, il gateway inoltra direttamente i metadati della risposta. Due campi indicano che cosa è successo:

resp = oai.chat.completions.create(
    model="gpt-5.4-mini",
    max_tokens=128,
    messages=[
        {"role": "system", "content": LONG_STABLE_PROMPT},   # ~7K tokens
        {"role": "user",   "content": "First question"},
    ],
)
print(resp.usage.prompt_tokens_details.cached_tokens)   # cache hit count
print(resp.usage.cost)                                  # USD, gateway-computed

cached_tokens indica quanti token di input hanno trovato corrispondenza nella prefix cache upstream. usage.cost è il costo in USD calcolato dal gateway per la singola chiamata, quindi non serve mantenere localmente un listino per ogni provider.

Dall’architettura derivano due regole valide per tutti i provider:

  1. Prima i contenuti stabili, poi quelli variabili. Il prefisso viene confrontato a partire dal token zero: basta modificare un byte all’inizio per invalidarlo completamente.
  2. Non inserire dati dinamici nel system prompt. Timestamp correnti, ID di sessione e UUID delle richieste invalidano la cache.

Gli esempi seguenti applicano lo stesso schema ai singoli vendor.


2. Anthropic Claude: marker cache_control espliciti

Claude appartiene alla famiglia con marker espliciti: l’API di Anthropic non attiva automaticamente la cache. Per ottenere un cache hit, bisogna inserire fino a quattro breakpoint cache_control negli array system o messages. Le letture dalla cache costano circa il 10% della tariffa di input; le scritture costano il 125%, quindi hanno un sovrapprezzo del 25%.

Il modo più semplice per usare cache_control tramite il gateway è configurare l’SDK ufficiale anthropic con l’endpoint Anthropic-native del gateway. Il percorso OpenAI-compatible /chat/completions al momento non propaga i marker cache_control; per il caching di Claude va usato /v1/messages.

import os
from anthropic import Anthropic

anth = Anthropic(
    api_key=os.environ["SYNTHORAI_KEY"],
    base_url="https://synthorai.io/",   # SDK appends /v1/messages
)

msg = anth.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=512,
    system=[
        {"type": "text", "text": SYSTEM_INSTRUCTIONS,
         "cache_control": {"type": "ephemeral"}},       # BP 1: never changes
        {"type": "text", "text": TOOL_DESCRIPTIONS,
         "cache_control": {"type": "ephemeral"}},       # BP 2: rarely changes
        {"type": "text", "text": RETRIEVED_DOCUMENTS},  # changes per call — not cached
    ],
    messages=[{"role": "user", "content": question}],
)

print(msg.usage)
# Usage(input_tokens=18, output_tokens=64,
#       cache_creation_input_tokens=0, cache_read_input_tokens=8123,
#       cost=...)

Opzioni per il TTL. {"type": "ephemeral"} usa per impostazione predefinita un TTL sliding di 5 minuti: ogni hit posticipa la scadenza. Per workload con periodi di inattività superiori a 5 minuti, si può richiedere un TTL di 1 ora sullo stesso marker:

"cache_control": {"type": "ephemeral", "ttl": "1h"}

Breakpoint a livelli. Con un massimo di quattro marker si possono memorizzare separatamente sezioni che non cambiano mai, cambiano raramente o cambiano per attività. È una soluzione particolarmente efficace per agent e workload RAG, nei quali le sezioni del prompt vengono aggiornate con frequenze diverse. Anche quando cambia il livello finale, per esempio i documenti recuperati, i livelli precedenti continuano a produrre cache hit.

Scelta del modello. Modelli Claude disponibili sul gateway al 2026-05: claude-haiku-4-5, claude-sonnet-4-5 / 4-6, claude-opus-4-5 / 4-6 / 4-7. Haiku è adatto a chat economiche; Sonnet è il modello general-purpose con lo schema di caching più efficace per gli agent; Opus è pensato per i task di reasoning più complessi.

Riferimento misurato per cache hit, scrittura e assenza di cache (2026-05-25, system prompt da circa 7.976 token, max_tokens=64):

ModelloScrittura cacheLettura cacheRiferimento senza cacheSconto in letturaTTFT con hit (streaming)
claude-haiku-4-5$0.00916$0.00086$0.00725−88%1.31 s
claude-sonnet-4-5$0.02713$0.00247$0.02175−89%1.76 s
claude-sonnet-4-6$0.02736$0.00253$0.02198−88%1.81 s
claude-opus-4-5$0.04522$0.00409$0.03624−89%2.08 s
claude-opus-4-6$0.04522$0.00411$0.03625−89%2.55 s
claude-opus-4-7$0.06545$0.00609$0.05259−88%2.30 s

Lo sconto è uniforme in tutta la famiglia. Il sovrapprezzo in scrittura è circa il 25% rispetto alla chiamata senza cache, in linea con la tariffa dichiarata da Anthropic; basta un cache hit per raggiungere il break-even.


3. OpenAI GPT-5.x: caching automatico

OpenAI memorizza automaticamente le richieste con un prefisso sufficientemente lungo. Non servono modifiche al codice né marker.

def ask_gpt(question: str):
    t0 = time.perf_counter()
    resp = oai.chat.completions.create(
        model="gpt-5.4-mini",
        max_tokens=64,
        messages=[
            {"role": "system", "content": LONG_STABLE_PROMPT},
            {"role": "user",   "content": question},
        ],
    )
    return resp, time.perf_counter() - t0

r1, t1 = ask_gpt("Which export formats are supported?")
r2, t2 = ask_gpt("How long is the refund window for annual plans?")

print(t1, r1.usage.prompt_tokens_details.cached_tokens, r1.usage.cost)
# 3.63   0       0.00267
print(t2, r2.usage.prompt_tokens_details.cached_tokens, r2.usage.cost)
# 1.23   6400    0.00257

Lo stesso prompt da 6.887 token viene inviato due volte. Alla seconda chiamata, il 93% del system prompt arriva dalla cache e la latenza totale scende da 3.6 s a 1.2 s. In questo caso il costo cambia poco perché lo sconto della cache viene compensato da un completamento più lungo nella prima chiamata. La §7 riporta dati più adatti al confronto tra provider.

Con gpt-5.4-nano lo sconto è più evidente: il costo della chiamata con hit scende del 44%. Per le UI di chat, dove conta soprattutto il time-to-first-token, sono più significativi i dati in streaming:

def ttft(model, question):
    t0 = time.perf_counter()
    stream = oai.chat.completions.create(
        model=model, max_tokens=64,
        messages=[
            {"role": "system", "content": LONG_STABLE_PROMPT},
            {"role": "user",   "content": question},
        ],
        stream=True, stream_options={"include_usage": True},
    )
    for ev in stream:
        if ev.choices and ev.choices[0].delta and ev.choices[0].delta.content:
            return time.perf_counter() - t0     # first content token

TTFT misurato sulla chiamata con cache hit: 0.73 s per gpt-5.4-mini e 1.00 s per gpt-5.4-nano.


4. Google Gemini: caching implicito

Anche il caching di Gemini è automatico quando si usa il gateway. Non serve creare una risorsa cachedContent.

r = oai.chat.completions.create(
    model="gemini-2.5-flash",
    max_tokens=128,
    messages=[
        {"role": "system", "content": LONG_STABLE_PROMPT},
        {"role": "user",   "content": "Summarize section 6 in two bullets."},
    ],
)
print(r.usage.prompt_tokens_details.cached_tokens, r.usage.cost)

Con un system prompt da circa 7.300 token, un hit misurato su gemini-2.5-flash ha prodotto 7.140 token in cache (97%). Il costo è sceso da $0.00198 a $0.00024, pari a uno sconto dell’88% per quella chiamata.

Due aspetti da tenere presenti:

  • Le varianti *-pro di Gemini sono modelli di reasoning. Con valori bassi di max_tokens, spesso si ottiene completion_tokens=0 perché il budget viene consumato dal ragionamento nascosto. Per risposte destinate all’utente, impostare max_tokens su almeno 256.
  • Il TTL della cache implicita è breve e non è documentato ufficialmente. Nei nostri test, due chiamate distanti 5 s hanno prodotto un hit; una terza, eseguita circa 10 s dopo, a volte ha prodotto un miss. La logica applicativa non deve presupporre un hit: controllare cached_tokens e gestire il miss senza errori.

5. DeepSeek-v4-flash: cache automatica su disco

La cache automatica di DeepSeek dura più a lungo delle cache residenti nella memoria GPU degli altri vendor. La chiamata mantiene la stessa struttura:

r1 = oai.chat.completions.create(
    model="deepseek-v4-flash", max_tokens=128,
    messages=[{"role": "system", "content": LONG_STABLE_PROMPT},
              {"role": "user",   "content": "Q1"}],
)
# r1.usage.cost = $0.00091, cached_tokens = 0

r2 = oai.chat.completions.create(
    model="deepseek-v4-flash", max_tokens=128,
    messages=[{"role": "system", "content": LONG_STABLE_PROMPT},
              {"role": "user",   "content": "Q2"}],
)
# r2.usage.cost = $0.00023, cached_tokens = 6784  →  74% saved

TTFT in streaming sulla chiamata con cache hit: 2.93 s. DeepSeek non offre la latenza più bassa del gruppo. I vantaggi principali sono il costo e la capacità della cache di rimanere calda anche dopo intervalli di alcune ore.


6. Alibaba Qwen: hit segnalato, sconto variabile

r = oai.chat.completions.create(
    model="qwen3-max", max_tokens=128,
    messages=[{"role": "system", "content": LONG_STABLE_PROMPT},
              {"role": "user",   "content": "Q1"}],
)
print(r.usage.prompt_tokens_details.cached_tokens, r.usage.cost)
# 7040    0.00549

Nel nostro test, cached_tokens segnala un hit, con 7.040 token su 7.234 pari al 97%, ma usage.cost non è diminuito nella chiamata con cache ed è rimasto intorno a $0.0055. La cache upstream è stata usata, come mostra il miglioramento del TTFT da 3.03 s a 1.53 s, ma in quella data il campo del costo calcolato dal gateway non riportava ancora lo sconto della tariffa cached per questo provider. Se il costo di Qwen è un fattore critico, monitorare cached_tokens e fare riferimento ai listini upstream finché il comportamento non viene uniformato.


7. Benchmark tra provider, misurato il 2026-05-25

Una singola esecuzione sequenziale. System prompt stabile di 7.284 caratteri, pari a circa 6.900-7.300 token a seconda del tokenizer. max_tokens=64. Prima una chiamata con miss, seguita immediatamente da una chiamata con hit.

Provider con cache automatica, senza marker:

ModelloCosto missCosto hitVariazione costoDurata totale missDurata totale hitTTFT con hit (streaming)Tasso di cache hit
gpt-5.4-nano$0.00131$0.00074−44%2.18 s1.48 s1.00 s5.888 / 6.887 (85%)
gpt-5.4-mini$0.00267$0.00257−4%*3.63 s1.23 s0.73 s6.400 / 6.887 (93%)
gemini-2.5-flash$0.00198$0.00024†−88%2.49 s1.37 sn/d‡7.140 / 7.322 (97%)
gemini-2.5-pro$0.00824$0.00205†−75%2.99 s1.76 sn/d‡6.120 / 7.328 (84%)
deepseek-v4-flash$0.00091$0.00023−74%4.02 s3.71 s2.93 s6.784 / 7.101 (96%)
qwen3-max$0.00553$0.00549−1%§4.80 s2.37 s1.53 s7.040 / 7.234 (97%)

* Nella chiamata con miss, gpt-5.4-mini ha generato un completamento di 44 token, contro i 19 della chiamata con hit. La variazione di costo combina quindi lo sconto della cache con la diversa lunghezza del completamento. Il calo della latenza, da 3.63 a 1.23 s, è il dato più significativo. † Costo della chiamata in streaming, nella quale è stato riportato cached_tokens. Per Gemini, la chiamata senza streaming ha restituito occasionalmente cached_tokens=null e il costo non è diminuito. Al momento i metadati del gateway per Gemini non sono uniformi: fare riferimento a cached_tokens quando è presente. ‡ Con valori bassi di max_tokens, i modelli di reasoning Gemini *-pro / *-flash spesso non emettono alcun content token. Con quel budget, il TTFT non è quindi significativo. Aumentare max_tokens per misurarlo in produzione. § Vedere §6: la cache upstream ha prodotto un hit, come mostra la riduzione della latenza, ma in quella data il campo usage.cost del gateway non riportava lo sconto per qwen3-max.

Anthropic Claude richiede marker espliciti. I dati sono riportati in una tabella separata perché lo sconto deve essere attivato con cache_control; lo schema è descritto nella §2. Stesso prompt, con confronto tra scrittura e lettura della cache:

ModelloCosto scritturaCosto letturaSconto in letturaTTFT con hit (streaming)
claude-haiku-4-5$0.00916$0.00086−88%1.31 s
claude-sonnet-4-5$0.02713$0.00247−89%1.76 s
claude-sonnet-4-6$0.02736$0.00253−88%1.81 s
claude-opus-4-5$0.04522$0.00409−89%2.08 s
claude-opus-4-6$0.04522$0.00411−89%2.55 s
claude-opus-4-7$0.06545$0.00609−88%2.30 s

I risultati variano in base alla regione, all’orario e alla presenza di prefissi caldi generati da altri tenant. Si tratta di una sola esecuzione in una sola data: non considerare questi dati come valori assoluti di benchmark.


8. Checklist prima del rilascio

Prima di portare in produzione un prompt progettato per il caching:

  1. Prima i contenuti stabili: system prompt, knowledge base e schemi dei tool all’inizio di messages.
  2. Poi i contenuti variabili: input dell’utente, documenti recuperati e timestamp alla fine.
  3. Nessuna variabile dinamica in system: ora corrente, ID utente e seed casuali invalidano il prefisso.
  4. Registrare cached_tokens a ogni chiamata. Se in produzione il tasso di hit è inferiore al 50%, il prefisso non è davvero stabile. Analizzare i prompt che producono miss.
  5. Non basarsi su un solo hit. I TTL sono brevi: progettare per hit_rate ∈ [0, 1), non per una cache che produce sempre hit.

9. Pattern che tengono conto del TTL

Il problema più comune in produzione non è dimenticare di abilitare il caching. È ottenere un tasso di hit del 12% perché le richieste non arrivano entro la finestra del TTL.

8.1 Workload legati alla sessione, come chat e assistenti IDE

La frequenza naturale delle richieste è molto inferiore al TTL. Se il prompt è strutturato correttamente, la cache rimane calda da sola e non serve altro.

8.2 Heartbeat per processi batch e cron

Supponiamo che un report giornaliero parta alle 09:00 ed esegua 50 chiamate al modello nell’arco di 3 minuti. La prima scrittura in cache delle 09:00 non sfrutta alcun dato precedente, perché durante la notte la cache si è raffreddata. Dalle 08:55 in poi, inviare ogni TTL/2 un “ping” da 1 token con il prefisso da memorizzare, così da mantenerlo caldo:

def keepalive():
    oai.chat.completions.create(
        model="gpt-5.4-mini",
        max_tokens=1,
        messages=[
            {"role": "system", "content": LONG_STABLE_PROMPT},
            {"role": "user",   "content": "."},
        ],
    )

Il costo di ogni ping è pari ai token di input moltiplicati per la tariffa cached. Per il nostro prefisso da 7K token su gpt-5.4-mini è circa $0.0026, molto meno del costo del prefill completo sulla prima delle 50 chiamate reali del batch.

8.3 Documenti in cold storage

Per documenti interrogati sporadicamente, per esempio una volta all’ora durante la giornata, le cache in memoria sono quasi sempre fredde. Al momento il gateway non espone un endpoint hosted per creare esplicitamente una cache. Per TTL lunghi, usare deepseek-v4-flash, che salva la cache su disco e in pratica la conserva anche dopo intervalli di alcune ore, oppure chiamare direttamente l’API nativa cachedContent di Google senza passare dal gateway.


10. Che cosa aggiunge davvero il gateway

Non sarebbe corretto affermare che il gateway “gestisce il caching al posto tuo”. Il caching avviene a livello del modello e il gateway si limita a esporlo. Rispetto all’uso diretto dell’SDK nativo di ogni vendor, aggiunge tre funzionalità:

  1. Un solo base_url e un solo header di autenticazione per tutti i modelli. Cambia solo il campo model; la struttura della chiamata resta identica. Stesso array messages, stessa struttura del campo usage. Non servono cinque SDK per cinque provider.
  2. usage.cost in USD per ogni chiamata. Il gateway calcola il costo in dollari usando le tariffe upstream correnti e lo include in ogni risposta. Non bisogna mantenere una matrice dei prezzi nel codice né iscriversi alle notifiche sui cambiamenti tariffari dei singoli vendor.
  3. Un campo cached_tokens uniforme. Anthropic segnala gli hit con cache_read_input_tokens, OpenAI con prompt_tokens_details.cached_tokens e DeepSeek con prompt_cache_hit_tokens. Il gateway li normalizza nel formato OpenAI, evitando branch specifici per provider nel codice di osservabilità.

Questo è tutto. Decidere quando usare il caching, come strutturare i prompt e quale modello scegliere è il tema del prossimo articolo.


Prossimo articolo: Parte 4, Come scegliere il miglior LLM per caso d’uso: chat, API e agent AI: una matrice decisionale che associa ogni tipo di workload al modello e alla strategia di caching più adatti, con il calcolo dei costi.


FAQ

Perché usare l’SDK OpenAI con modelli non OpenAI? Il gateway espone il protocollo OpenAI per tutti i provider supportati. L’SDK ufficiale openai offre risposte tipizzate, retry automatici e helper per lo streaming, quindi non serve implementare manualmente cinque client HTTP.

Il caching funziona con le risposte in streaming? Sì. L’oggetto usage nel chunk finale riporta il numero di cache hit, passando stream_options={"include_usage": True}. Il miglioramento della latenza è più evidente in streaming, perché il TTFT è il dato percepito dagli utenti.

Quale provider offre lo sconto della cache maggiore per il mio workload? Con i prezzi del 2026-05 e un tasso di hit superiore al 70%, gemini-2.5-flash e deepseek-v4-flash sono i modelli più economici nella tabella della §7. gpt-5.4-mini offre il TTFT migliore. Per ottenere lo sconto documentato del 90% sulla cache di Claude, inserire fino a quattro breakpoint cache_control, come descritto nella §2. Eseguire lo stesso benchmark sul proprio prompt richiede una giornata, non una migrazione di più settimane.

Quando servono i marker cache_control? Solo per Anthropic Claude; vedere §2. OpenAI, Gemini, DeepSeek e Qwen memorizzano automaticamente upstream qualsiasi prefisso sufficientemente lungo, quindi non richiedono marker. Per questi provider, il campo viene ignorato senza errori.

Quanto sono aggiornati questi dati? Sono stati misurati il 2026-05-25 sul gateway pubblico. Vanno considerati come un singolo punto di riferimento: prezzi e latenza cambiano a ogni ciclo di rilascio.

E Anthropic Claude? Il gateway supporta Claude con marker cache_control espliciti. Usare l’SDK anthropic con base_url="https://synthorai.io/"; l’SDK aggiunge /v1/messages. Al momento il percorso OpenAI-compatible /chat/completions non propaga i marker. Per il prompt caching di Claude, usare il percorso Anthropic-native mostrato nella §2.


Fonti e verifica: tutti i dati sono stati misurati su https://synthorai.io/v1 il 2026-05-25 con la versione 2.38.0 dell’SDK openai. Pagine dei prezzi dei vendor: Prompt caching di Anthropic · Prompt caching di OpenAI · Context caching di Google Gemini · Guida alla KV cache di DeepSeek · Context cache di Alibaba Bailian.

← Torna al blog