Mise en cache des prompts LLM en Python : tutoriel avec du code fonctionnel
Sommaire
- 0. Installation
- 1. Appel avec cache (identique pour tous les fournisseurs)
- 2. Anthropic Claude — Marqueurs cache_control explicites
- 3. OpenAI GPT-5.x — Mise en cache automatique
- 4. Google Gemini — Mise en cache implicite
- 5. DeepSeek-v4-flash — Cache automatique sur disque
- 6. Alibaba Qwen — Hit signalé, réduction variable
- 7. Benchmark entre fournisseurs (mesures du 2026-05-25)
- 8. Checklist avant mise en production
- 9. Schémas tenant compte du TTL
- 8.1 Workloads liés à une session (chat, assistants IDE)
- 8.2 Heartbeat pour les batchs et les cron
- 8.3 Documents rarement consultés
- 10. Ce que la gateway apporte réellement
- FAQ
TL;DR — Un seul SDK OpenAI, une seule base_url, tous les principaux LLM. Les chiffres de cet article ont été mesurés le 2026-05-25 sur la gateway Synthorai en production, avec un system prompt stable d’environ 7 300 tokens. L’intérêt de la gateway est simple : un seul endpoint, un seul header d’authentification et un champ usage.cost qui évite de maintenir une grille tarifaire par fournisseur. Le fonctionnement du cache côté Transformer est détaillé dans la Partie 1 : principes de mise en cache ; les choix d’implémentation de chaque fournisseur sont présentés dans la Partie 2 : comparaison des fournisseurs.
Série : Partie 3 sur 5 · Précédemment : Partie 1 — Principes de mise en cache · Partie 2 — Comparaison et évaluation des fournisseurs · À suivre : Partie 4 — Meilleur LLM selon le cas d’usage · Partie 5 — Intégration avec LangChain
0. Installation
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",
)
La gateway utilise le format d’échange d’OpenAI pour tous les modèles qu’elle expose (GPT, Claude, Gemini, DeepSeek, Qwen). Il suffit de modifier le champ model, pas de changer de SDK. L’authentification passe par Authorization: Bearer <key>.
Modèles compatibles avec le cache disponibles sur la gateway publique (état en 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. La liste complète et à jour est disponible via GET /v1/models.
1. Appel avec cache (identique pour tous les fournisseurs)
Aucune activation n’est nécessaire. Lorsqu’un modèle prend en charge la mise en cache des prompts en amont, la gateway transmet simplement les métadonnées de la réponse. Deux champs permettent de savoir ce qui s’est passé :
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 correspond au nombre de tokens d’entrée trouvés dans le cache de préfixes du fournisseur. usage.cost donne le prix en USD de cet appel, calculé par la gateway. Il n’est donc pas nécessaire de conserver localement les tarifs de chaque fournisseur.
Deux règles découlent de cette architecture et s’appliquent à tous les fournisseurs :
- Placez le contenu stable en premier et le contenu variable en dernier. La comparaison du préfixe commence au token zéro. La modification d’un seul octet au début invalide tout le préfixe.
- Ne mettez aucune donnée dynamique dans le system prompt. Les timestamps, ID de session et UUID de requête rendent tous le cache inutilisable.
Les sections suivantes déclinent simplement ce même principe pour chaque fournisseur.
2. Anthropic Claude — Marqueurs cache_control explicites
Claude appartient à la famille des caches à marqueurs explicites : l’API d’Anthropic n’active pas automatiquement le cache. Pour obtenir un hit, ajoutez jusqu’à quatre points de rupture cache_control dans vos tableaux system ou messages. Une lecture du cache coûte environ 10 % du tarif d’entrée ; une écriture coûte 125 %, soit une majoration de 25 %.
Le moyen le plus propre d’utiliser cache_control via la gateway consiste à employer le SDK officiel anthropic en le faisant pointer vers l’endpoint Anthropic natif de la gateway. Le chemin compatible OpenAI /chat/completions ne transmet pas encore les marqueurs cache_control : utilisez /v1/messages pour le cache Claude.
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=...)
Options de TTL. {"type": "ephemeral"} utilise par défaut un TTL glissant de 5 minutes : chaque hit repousse l’expiration. Pour les workloads avec des périodes d’inactivité supérieures à 5 minutes, demandez un TTL d’une heure sur le même marqueur :
"cache_control": {"type": "ephemeral", "ttl": "1h"}
Points de rupture en couches. Les quatre marqueurs permettent de mettre en cache séparément les sections qui « ne changent jamais », « changent rarement » et « changent selon la tâche ». C’est particulièrement efficace pour les agents et les workloads RAG, dont les différentes parties du prompt évoluent à des rythmes distincts. Même si la dernière couche, par exemple les documents récupérés, change entre deux appels, les couches précédentes restent en cache.
Choix du modèle. Modèles Claude disponibles sur la gateway en 2026-05 : claude-haiku-4-5, claude-sonnet-4-5 / 4-6, claude-opus-4-5 / 4-6 / 4-7. Haiku convient au chat à faible coût ; Sonnet aux usages généralistes, avec le meilleur schéma de cache pour les agents ; Opus aux tâches de raisonnement les plus difficiles.
Référence mesurée pour un hit, une écriture et un appel sans cache (2026-05-25, system prompt d’environ 7 976 tokens, max_tokens=64) :
| Modèle | Écriture du cache | Lecture du cache | Réf. sans cache | Réduction en lecture | TTFT sur hit (stream) |
|---|---|---|---|---|---|
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 |
La réduction est homogène sur toute la gamme. L’écriture coûte environ 25 % de plus qu’un appel sans cache, conformément au tarif publié par Anthropic. Un seul hit suffit à rentabiliser l’écriture.
3. OpenAI GPT-5.x — Mise en cache automatique
OpenAI met automatiquement en cache toute requête dont le préfixe est suffisamment long. Aucun changement de code ni marqueur n’est nécessaire.
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
Le même prompt de 6 887 tokens est envoyé deux fois. Au second appel, 93 % du system prompt est servi depuis le cache et la latence totale passe de 3.6 s à 1.2 s. Ici, le coût varie peu, car la réduction liée au cache est compensée par une complétion plus longue lors du premier appel. La §7 fournit des comparaisons plus propres entre fournisseurs.
La réduction apparaît plus nettement avec gpt-5.4-nano : 44 % de baisse sur le hit. Pour une interface de chat où seul le délai jusqu’au premier token compte, les mesures en streaming sont les plus pertinentes :
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 mesuré sur le passage en cache : 0.73 s pour gpt-5.4-mini, 1.00 s pour gpt-5.4-nano.
4. Google Gemini — Mise en cache implicite
Le cache de Gemini est lui aussi automatique via la gateway. Vous n’avez pas à créer de ressource 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)
Pour un system prompt d’environ 7 300 tokens, un hit mesuré sur gemini-2.5-flash a donné 7 140 tokens en cache (97 %). Le coût est passé de $0.00198 à $0.00024, soit une réduction de 88 % sur ce passage.
Deux pièges à connaître :
- Les variantes
*-prode Gemini sont des modèles de raisonnement. Avec unmax_tokensfaible,completion_tokens=0est fréquent, car le budget est entièrement consommé par le raisonnement interne. Utilisezmax_tokens≥256 pour toute réponse destinée à l’utilisateur. - Le TTL du cache implicite est court et n’est pas officiellement documenté. Lors de notre test, deux appels espacés de 5 s ont produit un hit ; un troisième effectué environ 10 s plus tard produisait parfois un miss. Ne concevez pas votre logique en partant du principe que le hit est garanti. Vérifiez
cached_tokenset prévoyez un fonctionnement normal en cas de miss.
5. DeepSeek-v4-flash — Cache automatique sur disque
Le cache automatique de DeepSeek persiste plus longtemps que les caches en mémoire GPU des autres fournisseurs. La forme de l’appel reste identique :
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 en streaming sur le passage en cache : 2.93 s. DeepSeek n’offre pas la latence la plus faible de cette sélection. Son intérêt tient au coût et à un cache qui reste chaud même après plusieurs heures d’inactivité.
6. Alibaba Qwen — Hit signalé, réduction variable
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
Limite observée pendant notre test : cached_tokens signale bien un hit (7 040 sur 7 234, soit 97 %), mais usage.cost n’a pas baissé sur le passage en cache et reste à ≈ $0.0055. Le cache du fournisseur a donc bien été utilisé, comme le confirme le TTFT plus rapide : 1.53 s contre 3.03 s à froid. En revanche, à cette date, le champ de coût de la gateway ne répercutait pas encore le tarif réduit du cache pour ce fournisseur. Si le coût de Qwen est un critère important, surveillez cached_tokens et fiez-vous aux pages tarifaires du fournisseur jusqu’à ce que la situation se stabilise.
7. Benchmark entre fournisseurs (mesures du 2026-05-25)
Une seule exécution séquentielle. System prompt stable de 7 284 caractères, soit environ 6 900 à 7 300 tokens selon le tokenizer. max_tokens=64. Un premier appel en miss, immédiatement suivi d’un appel en hit.
Fournisseurs avec cache automatique, sans marqueur :
| Modèle | Coût miss | Coût hit | Écart de coût | Total miss | Total hit | TTFT sur hit (stream) | Taux de hit |
|---|---|---|---|---|---|---|---|
gpt-5.4-nano | $0.00131 | $0.00074 | −44% | 2.18 s | 1.48 s | 1.00 s | 5,888 / 6,887 (85%) |
gpt-5.4-mini | $0.00267 | $0.00257 | −4%* | 3.63 s | 1.23 s | 0.73 s | 6,400 / 6,887 (93%) |
gemini-2.5-flash | $0.00198 | $0.00024† | −88% | 2.49 s | 1.37 s | n/a‡ | 7,140 / 7,322 (97%) |
gemini-2.5-pro | $0.00824 | $0.00205† | −75% | 2.99 s | 1.76 s | n/a‡ | 6,120 / 7,328 (84%) |
deepseek-v4-flash | $0.00091 | $0.00023 | −74% | 4.02 s | 3.71 s | 2.93 s | 6,784 / 7,101 (96%) |
qwen3-max | $0.00553 | $0.00549 | −1%§ | 4.80 s | 2.37 s | 1.53 s | 7,040 / 7,234 (97%) |
* La complétion du miss de gpt-5.4-mini comptait 44 tokens, contre 19 pour le hit. L’écart de coût combine donc la réduction liée au cache et la différence de longueur des complétions. La baisse de latence, de 3.63 à 1.23 s, constitue ici un indicateur plus fiable.
† Coût du passage en streaming, pour lequel cached_tokens était renseigné. Avec Gemini, le passage hors streaming renvoyait parfois cached_tokens=null sans baisse de coût. Les métadonnées Gemini fournies par la gateway manquent encore de cohérence : fiez-vous à cached_tokens lorsqu’il est présent.
‡ Avec un faible max_tokens, les modèles de raisonnement Gemini *-pro / *-flash ne produisent souvent aucun token de contenu. Le TTFT n’a donc aucun sens avec ce budget. Augmentez max_tokens pour effectuer cette mesure en production.
§ Voir la §6 : le cache du fournisseur a bien été utilisé, comme le confirme la baisse de latence, mais le champ usage.cost de la gateway n’a pas répercuté la réduction pour qwen3-max à cette date.
Anthropic Claude repose sur des marqueurs explicites. Les chiffres figurent dans un tableau séparé, car la réduction doit être activée via cache_control ; voir la §2 pour le schéma. Même prompt, avec comparaison entre écriture et lecture du cache :
| Modèle | Coût d’écriture | Coût de lecture | Réduction en lecture | TTFT sur hit (stream) |
|---|---|---|---|---|
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 |
Vos résultats varieront selon la région, l’heure et la présence éventuelle de préfixes identiques déjà chargés par d’autres tenants. Il s’agit d’une seule exécution à une date précise : ces chiffres ne constituent pas une vérité absolue.
8. Checklist avant mise en production
Avant de déployer un prompt conçu pour tirer parti du cache :
- Contenu stable en premier — placez le system prompt, la base de connaissances et les schémas d’outils en haut de
messages. - Contenu variable en dernier — placez la saisie utilisateur, les documents récupérés et les timestamps en bas.
- Aucune variable dynamique dans
system— l’heure courante, l’ID utilisateur ou une seed aléatoire invalident votre préfixe. - Loguez
cached_tokensà chaque appel. Si le taux de hit est inférieur à 50 % en production, votre préfixe n’est pas réellement stable. Analysez les prompts qui produisent un miss. - Ne tirez aucune conclusion d’un seul hit. Les TTL sont courts. Concevez le système pour
hit_rate ∈ [0, 1), pas pour un cache toujours disponible.
9. Schémas tenant compte du TTL
Le problème le plus fréquent en production n’est pas l’oubli d’activer le cache. C’est un taux de hit de 12 %, parce que les requêtes n’arrivent pas dans la fenêtre du TTL.
8.1 Workloads liés à une session (chat, assistants IDE)
Le rythme naturel des requêtes est largement inférieur au TTL. Structurez correctement le prompt et le cache restera chaud tout seul. Aucun mécanisme supplémentaire n’est nécessaire.
8.2 Heartbeat pour les batchs et les cron
Supposons qu’un rapport quotidien lancé à 09:00 appelle le modèle 50 fois en 3 minutes. La première écriture du cache à 09:00 ne profite d’aucune donnée de la veille, puisque le cache a refroidi pendant la nuit. À partir de 08:55, envoyez toutes les TTL/2 un « ping » d’un token avec le préfixe à mettre en cache :
def keepalive():
oai.chat.completions.create(
model="gpt-5.4-mini",
max_tokens=1,
messages=[
{"role": "system", "content": LONG_STABLE_PROMPT},
{"role": "user", "content": "."},
],
)
Chaque ping coûte le nombre de tokens d’entrée multiplié par le tarif du cache. Pour notre préfixe de 7K tokens sur gpt-5.4-mini, cela représente environ $0.0026. C’est bien moins cher que de laisser le batch payer un prefill complet sur les 50 premiers appels réels.
8.3 Documents rarement consultés
Pour les documents interrogés ponctuellement, par exemple une fois par heure dans la journée, les caches en mémoire seront froids la plupart du temps. À ce jour, la gateway ne propose pas d’endpoint hébergé permettant de créer explicitement un cache. Pour les besoins de TTL longs, utilisez deepseek-v4-flash, dont le cache sur disque résiste en pratique à plusieurs heures d’inactivité, ou appelez directement l’API native cachedContent de Google en dehors de la gateway.
10. Ce que la gateway apporte réellement
Prétendre que la gateway « gère le cache à votre place » serait trompeur. La mise en cache intervient au niveau du modèle ; la gateway ne fait qu’exposer les mécanismes disponibles. Par rapport à l’utilisation directe du SDK natif de chaque fournisseur, elle apporte trois choses :
- Une seule base_url, un seul header d’authentification, tous les modèles. Changez le champ
modelsans modifier la forme de l’appel. Le tableaumessageset la structure du champusagerestent identiques. Plus besoin d’embarquer cinq SDK pour cinq fournisseurs. usage.costen USD pour chaque appel. La gateway calcule le coût à partir des tarifs actuels des fournisseurs et l’inclut dans chaque réponse. Vous n’avez pas à maintenir une grille tarifaire dans votre code ni à suivre les notifications de changement de prix de chaque fournisseur.- Un champ
cached_tokensuniforme. Anthropic renvoie les hits danscache_read_input_tokens, OpenAI dansprompt_tokens_details.cached_tokenset DeepSeek dansprompt_cache_hit_tokens. La gateway normalise ces valeurs au format OpenAI, ce qui évite d’ajouter des branches par fournisseur dans le code d’observabilité.
C’est tout. Le choix des données à mettre en cache, la structure des prompts et le modèle à utiliser restent à votre charge. Le prochain article traite précisément de ces choix.
À suivre : Partie 4 — Comment choisir le meilleur LLM selon le cas d’usage : chat, API et agents IA — une matrice de décision qui associe chaque type de workload au modèle et à la stratégie de cache les plus adaptés, avec le détail des coûts.
FAQ
Pourquoi utiliser le SDK OpenAI avec des modèles qui ne viennent pas d’OpenAI ?
La gateway expose tous les fournisseurs au format d’échange d’OpenAI. Le SDK officiel openai fournit des réponses typées, des retries automatiques et des helpers pour le streaming. Il n’y a aucune raison de développer et maintenir cinq clients HTTP différents.
Le cache fonctionne-t-il avec les réponses en streaming ?
Oui. L’objet usage du dernier chunk indique le nombre de hits du cache lorsque vous passez stream_options={"include_usage": True}. Le gain de latence est particulièrement visible en streaming, car le TTFT correspond au délai perçu par l’utilisateur.
Quel fournisseur offre la meilleure réduction liée au cache pour mon workload ?
Avec les tarifs de 2026-05 et un taux de hit supérieur à 70 %, gemini-2.5-flash et deepseek-v4-flash sont les moins chers du tableau de la §7. gpt-5.4-mini offre le meilleur TTFT. Pour obtenir la réduction de 90 % annoncée par Claude, ajoutez jusqu’à quatre points de rupture cache_control ; voir la §2. Exécutez le même benchmark avec votre propre prompt. Cela demande une journée, pas une migration de plusieurs semaines.
Quand faut-il utiliser les marqueurs cache_control ?
Uniquement avec Anthropic Claude ; voir la §2. Pour OpenAI, Gemini, DeepSeek et Qwen, le fournisseur met automatiquement en cache tout préfixe suffisamment long. Aucun marqueur n’est nécessaire et le champ est silencieusement ignoré par ces fournisseurs.
Ces chiffres sont-ils récents ? Ils ont été mesurés le 2026-05-25 sur la gateway publique. Considérez-les comme un point de mesure unique : les prix et les latences changent à chaque cycle de release.
Qu’en est-il d’Anthropic Claude ?
Claude est disponible via la gateway avec des marqueurs cache_control explicites. Utilisez le SDK anthropic avec base_url="https://synthorai.io/" ; le SDK ajoute /v1/messages. Le chemin compatible OpenAI /chat/completions ne transmet pas encore les marqueurs. Pour le cache Claude, utilisez donc le chemin Anthropic natif présenté dans la §2.
Sources et vérification : toutes les mesures ont été effectuées sur https://synthorai.io/v1 le 2026-05-25 avec la version 2.38.0 du SDK openai. Pages tarifaires des fournisseurs : Mise en cache des prompts Anthropic · Mise en cache des prompts OpenAI · Mise en cache du contexte Google Gemini · Guide du cache KV de DeepSeek · Cache de contexte Alibaba Bailian.