Mise en cache de prompts LLM #3 : tutoriel Python fonctionnel
Sommaire
- 0. Configuration
- 1. L’appel sensible au cache (identique sur 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 — succès rapporté, remise variable
- 7. Benchmark inter-fournisseurs (mesuré le 2026-05-25)
- 8. Liste de vérification avant lancement
- 9. Modèles tenant compte du TTL
- 8.1 Charges de travail liées à la session (chat, assistants IDE)
- 8.2 Battement de cœur pour le traitement par lots / cron
- 8.3 Documents en stockage froid
- 10. Ce que la passerelle apporte réellement
- FAQ
TL;DR — Un seul SDK OpenAI, une seule base_url, tous les grands LLM. Les chiffres de cet article sont mesurés sur la passerelle Synthorai en production le 2026-05-25 avec un prompt système stable d’environ 7 300 tokens. L’intérêt de la passerelle ici est modeste et honnête : un seul endpoint, un seul en-tête d’authentification, et un champ usage.cost qui vous évite de maintenir une matrice de tarification par fournisseur. Les mathématiques du Transformer derrière la mise en cache sont traitées dans la Partie 1 : Principes de la mise en cache ; les choix de conception par fournisseur sont dans la Partie 2 : Comparaison des fournisseurs.
Série : Partie 3 sur 4 · Précédemment : Partie 1 — Principes de la mise en cache · Partie 2 — Comparaison et évaluation des fournisseurs · Suivant : Partie 4 — Le meilleur LLM par cas d’usage
0. Configuration
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 passerelle utilise le format de transmission OpenAI pour chaque modèle qu’elle expose (GPT, Claude, Gemini, DeepSeek, Qwen). Vous changez le champ model, pas le SDK. L’authentification utilise Authorization: Bearer <key>.
Les identifiants de modèles compatibles avec la mise en cache disponibles sur la passerelle publique (instantané de 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 en direct est disponible via GET /v1/models.
1. L’appel sensible au cache (identique sur tous les fournisseurs)
Vous n’avez pas à l’activer explicitement. Pour tout modèle qui prend en charge la mise en cache de prompt en amont, la passerelle se contente de transmettre les métadonnées de la réponse. Deux champs vous indiquent 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 est le nombre de tokens d’entrée qui ont touché le cache de préfixe en amont. usage.cost est le prix calculé par la passerelle pour cet appel unique en USD — pas besoin de conserver localement une grille tarifaire par fournisseur.
Deux règles découlent de l’architecture et s’appliquent à chaque fournisseur :
- Le contenu stable d’abord, le contenu volatil en dernier. Le préfixe est mis en correspondance à partir du token zéro ; un seul octet modifié au début invalide tout le préfixe.
- Gardez les données dynamiques hors du prompt système. Les horodatages actuels, les identifiants de session et les UUID de requête vont tous invalider le cache.
Tout ce qui suit n’est qu’une série d’exemples par fournisseur du même modèle de fonctionnement.
2. Anthropic Claude — marqueurs cache_control explicites
Claude appartient à la famille des marqueurs explicites — l’API d’Anthropic ne met pas en cache automatiquement. Pour obtenir un succès de cache, marquez jusqu’à quatre points de rupture cache_control dans votre tableau system ou messages. Les lectures de cache coûtent environ 10 % du tarif d’entrée ; les écritures de cache coûtent 125 % (une prime de 25 %).
La façon la plus propre d’utiliser cache_control via la passerelle est avec le SDK officiel anthropic pointé vers l’endpoint natif Anthropic de la passerelle (le chemin compatible OpenAI /chat/completions ne propage pas actuellement les marqueurs cache_control — utilisez /v1/messages pour la mise en cache de 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 succès repousse l’expiration). Pour les charges de travail avec des périodes d’inactivité de plus de 5 minutes, demandez le TTL d’une heure sur le même marqueur :
"cache_control": {"type": "ephemeral", "ttl": "1h"}
Points de rupture en couches. Jusqu’à quatre marqueurs vous permettent de mettre en cache « ne change jamais » + « change rarement » + « change par tâche » de manière indépendante — ce qui est le meilleur de sa catégorie pour les charges de travail d’agents et de RAG où les sections du prompt changent à des cadences différentes. Même lorsque la couche finale (p. ex. les documents récupérés) change entre les appels, les couches précédentes restent en cache.
Choisir un modèle. Identifiants Claude disponibles sur la passerelle en date de 2026-05 : claude-haiku-4-5, claude-sonnet-4-5 / 4-6, claude-opus-4-5 / 4-6 / 4-7. Haiku pour le chat économique ; Sonnet pour un usage général avec le modèle de mise en cache d’agent le plus solide ; Opus pour les tâches de raisonnement les plus difficiles.
Référence mesurée succès de cache / écriture / sans cache (2026-05-25, prompt système d’environ 7 976 tokens, max_tokens=64) :
| Modèle | Écriture de cache | Lecture de cache | Réf. sans cache | Remise lecture | TTFT succès (flux) |
|---|---|---|---|---|---|
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 remise se maintient uniformément à travers toute la famille. La prime d’écriture est d’environ 25 % au-dessus du tarif sans cache (le taux documenté d’Anthropic) ; le seuil de rentabilité est atteint dès le premier succès de cache.
3. OpenAI GPT-5.x — mise en cache automatique
OpenAI met automatiquement en cache toute requête avec un préfixe suffisamment long. Aucun changement de code, aucun marqueur.
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 deux fois. Deuxième appel : 93 % du prompt système touche le cache, la latence totale passe de 3,6 s à 1,2 s. Le coût ne change presque pas ici parce que la remise de cache est compensée par une complétion de premier appel plus longue — voir §7 pour des chiffres inter-fournisseurs plus nets.
gpt-5.4-nano montre la remise plus clairement (réduction de coût de 44 % au succès). Pour les interfaces de chat où seul le temps jusqu’au premier token vous importe, ce sont les chiffres de streaming qui comptent :
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 la passe 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 aussi automatique lorsque vous passez par la passerelle. Il n’y a aucune étape de création cachedContent que vous deviez effectuer.
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)
Un succès mesuré sur gemini-2.5-flash pour un prompt système d’environ 7 300 tokens : 7 140 tokens en cache (97 %), le coût passe de $0.00198 à $0.00024 — 88 % de réduction pour cette passe.
Deux pièges qu’il vaut la peine de connaître :
- Les variantes
*-prode Gemini sont des modèles de raisonnement. Avec un petitmax_tokens, vous voyez souventcompletion_tokens=0parce que le budget est consommé par la réflexion cachée. Augmentezmax_tokensà ≥256 pour tout ce qui est destiné à l’utilisateur. - Le TTL du cache implicite est court et n’est pas officiellement spécifié. Dans notre test, un succès entre deux appels espacés de 5 s a réussi ; un troisième appel environ 10 s plus tard a parfois échoué. N’élaborez pas de logique qui suppose le succès ; vérifiez
cached_tokenset dégradez-vous gracieusement.
5. DeepSeek-v4-flash — cache automatique sur disque
Le cache automatique de DeepSeek survit plus longtemps que les caches résidents en mémoire GPU des autres fournisseurs. Même forme d’appel :
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 la passe en cache : 2,93 s. DeepSeek n’est pas l’option à la plus faible latence de cet ensemble — les gains sont dans le coût et dans le fait que le cache reste chaud à travers des intervalles de l’ordre de l’heure.
6. Alibaba Qwen — succès rapporté, remise 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
Mise en garde observée lors de notre exécution de test : cached_tokens rapporte un succès (7 040 sur 7 234 = 97 %), mais usage.cost n’a pas baissé sur la passe en cache (toujours ≈ $0.0055). Cela signifie que le succès de cache en amont a bien eu lieu (TTFT plus rapide, 1,53 s contre 3,03 s à froid), mais le champ de coût de la passerelle pour ce fournisseur ne reflétait pas encore la remise au tarif de cache à cette date. Si vous êtes sensible au coût sur Qwen, surveillez cached_tokens et fiez-vous aux pages tarifaires en amont jusqu’à ce que cela se normalise.
7. Benchmark inter-fournisseurs (mesuré le 2026-05-25)
Exécution séquentielle unique. Prompt système stable de 7 284 caractères (environ 6 900 à 7 300 tokens selon le tokeniseur). max_tokens=64. Un appel en échec suivi immédiatement d’un appel en succès.
Les fournisseurs à cache automatique (aucun marqueur requis) :
| Modèle | Coût échec | Coût succès | Δ coût | Total échec | Total succès | TTFT succès (flux) | Taux de succès de cache |
|---|---|---|---|---|---|---|---|
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 de l’appel en échec de gpt-5.4-mini était de 44 tokens contre 19 au succès — le delta de coût mélange la remise de cache avec la différence de longueur de complétion. La baisse de latence (3,63 → 1,23 s) est le signal le plus net ici.
† Coût de la passe en streaming (où cached_tokens était rapporté) ; la passe hors flux renvoyait parfois cached_tokens=null pour Gemini et le coût ne baissait pas. Les métadonnées de la passerelle pour Gemini sont actuellement incohérentes — fiez-vous à cached_tokens lorsqu’il est présent.
‡ Les modèles de raisonnement Gemini *-pro / *-flash émettent souvent zéro token de contenu avec un petit max_tokens, donc le TTFT n’a aucun sens à ce budget. Augmentez max_tokens si vous mesurez cela en production.
§ Voir §6 — le succès de cache en amont a eu lieu (la latence a baissé), mais le champ usage.cost de la passerelle ne reflétait pas la remise pour qwen3-max à cette date.
Anthropic Claude est piloté par des marqueurs explicites ; les chiffres figurent dans un tableau séparé parce que la remise est optionnelle via cache_control (voir §2 pour le modèle). Même prompt, écriture de cache mesurée contre lecture de cache :
| Modèle | Coût écriture | Coût lecture | Remise lecture | TTFT succès (flux) |
|---|---|---|---|---|
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 chiffres différeront selon la région, l’heure de la journée et la chaleur des préfixes des autres locataires. Exécution unique, date unique — ne citez pas ces chiffres comme parole d’évangile de benchmark.
8. Liste de vérification avant lancement
Avant de déployer un prompt sensible au cache :
- Contenu stable d’abord — prompt système, base de connaissances, schémas d’outils en haut de
messages. - Contenu volatil en dernier — entrée utilisateur, documents récupérés, horodatages en bas.
- Aucune variable dynamique dans
system— l’heure actuelle, l’identifiant utilisateur, les graines aléatoires anéantiront votre préfixe. - Journalisez
cached_tokensà chaque appel. Si le taux de succès est inférieur à 50 % en production, votre préfixe n’est pas réellement stable. Inspectez les prompts qui échouent. - Ne vous fiez pas à une seule passe en succès. Les TTL sont courts ; concevez pour
hit_rate ∈ [0, 1)plutôt que pour « toujours en succès ».
9. Modèles tenant compte du TTL
Le mode d’échec le plus courant en production n’est pas « j’ai oublié d’activer la mise en cache » — c’est « mon taux de succès est de 12 % parce que mes requêtes n’arrivent pas réellement à l’intérieur de la fenêtre de TTL ».
8.1 Charges de travail liées à la session (chat, assistants IDE)
La cadence naturelle est bien en dessous du TTL. Structurez votre prompt correctement et le cache reste chaud de lui-même — n’élaborez rien d’autre.
8.2 Battement de cœur pour le traitement par lots / cron
Si vous exécutez un rapport quotidien à 09:00 qui appelle votre modèle 50 fois dans une rafale de 3 minutes, la première écriture de cache à 09:00 est gaspillée parce que le cache est devenu froid pendant la nuit. À partir de 08:55, envoyez un « ping » d’un seul token avec le préfixe en cache tous les TTL/2 pour le garder chaud :
def keepalive():
oai.chat.completions.create(
model="gpt-5.4-mini",
max_tokens=1,
messages=[
{"role": "system", "content": LONG_STABLE_PROMPT},
{"role": "user", "content": "."},
],
)
Le coût par ping est égal aux tokens d’entrée × tarif de cache, ce qui pour notre préfixe de 7K tokens sur gpt-5.4-mini est d’environ $0.0026 — bien moins que de laisser votre tâche par lots payer le préremplissage complet sur les 50 premiers appels réels.
8.3 Documents en stockage froid
Pour les documents interrogés de manière sporadique (une fois par heure tout au long de la journée), les caches en mémoire seront froids la plupart du temps. À l’heure où nous écrivons, la passerelle n’expose pas d’endpoint hébergé de création de cache explicite — pour les besoins de TTL longs, utilisez deepseek-v4-flash (sur disque ; survit en pratique à des intervalles de l’ordre de l’heure) ou appelez directement l’API native cachedContent de Google en dehors de la passerelle.
10. Ce que la passerelle apporte réellement
Il serait malhonnête de prétendre que la passerelle « fait la mise en cache pour vous ». La mise en cache se produit au niveau du modèle — la passerelle expose ce qui est là. Ce qu’elle apporte réellement, mesuré par rapport à l’utilisation directe du SDK natif de chaque fournisseur, ce sont trois choses :
- Une seule base_url, un seul en-tête d’authentification, tous les modèles. Changez le champ
modelet la forme de l’appel reste inchangée. Le même tableaumessages, la même structure de champusage. Vous ne transportez pas cinq SDK pour cinq fournisseurs. usage.costen USD par appel. La passerelle calcule le coût en dollars en utilisant les tarifs en amont actuels et l’inclut dans chaque réponse. Vous ne maintenez pas de matrice tarifaire dans votre code, et vous n’avez pas à vous abonner aux notifications de changement de prix par fournisseur.- Un champ
cached_tokensuniforme. Anthropic rapporte les succès de cache souscache_read_input_tokens, OpenAI sousprompt_tokens_details.cached_tokens, DeepSeek sousprompt_cache_hit_tokens. La passerelle les normalise dans le format OpenAI afin que votre code d’observabilité ne se ramifie pas selon le fournisseur.
C’est tout l’argumentaire. Tout le reste — quand mettre en cache, comment structurer les prompts, quel modèle choisir — est le sujet du prochain article.
Suivant : Partie 4 — Comment choisir le meilleur LLM par cas d’usage : chat, API et agents IA — une matrice de décision faisant correspondre le type de charge de travail au modèle optimal + stratégie de mise en cache, avec les calculs de coût.
FAQ
Pourquoi utiliser le SDK OpenAI pour des modèles non-OpenAI ?
La passerelle utilise le format de transmission OpenAI pour chaque fournisseur qu’elle expose. Le SDK officiel openai vous donne des réponses typées, des nouvelles tentatives automatiques et des assistants de streaming — il n’y a aucune raison de coder à la main cinq clients HTTP.
La mise en cache fonctionne-t-elle avec les réponses en streaming ?
Oui. L’objet usage dans le dernier fragment rapporte les comptes de succès de cache (lorsque vous passez stream_options={"include_usage": True}). Le gain de latence est le plus visible en streaming parce que le TTFT est ce que les utilisateurs voient.
Quel fournisseur offre la remise de cache la plus profonde sur ma charge de travail ?
Aux prix de 2026-05 et avec un taux de succès supérieur à 70 %, gemini-2.5-flash et deepseek-v4-flash sont les moins chers dans le tableau de la §7. gpt-5.4-mini gagne sur le TTFT. Pour la remise de cache de 90 % documentée de Claude, marquez jusqu’à quatre points de rupture cache_control (voir §2). Exécutez le même benchmark sur votre propre prompt — c’est un exercice d’une journée, pas une migration de plusieurs semaines.
Quand ai-je besoin des marqueurs cache_control ?
Uniquement lors de l’appel à Anthropic Claude — voir §2. Pour OpenAI/Gemini/DeepSeek/Qwen, l’amont met automatiquement en cache tout préfixe suffisamment long, donc aucun marqueur n’est requis ; le champ est silencieusement ignoré pour ces fournisseurs.
À quel point ces chiffres sont-ils récents ? Mesurés le 2026-05-25 sur la passerelle publique. Traitez-les comme un point de données unique — la tarification et la latence changent à chaque cycle de version.
Et Anthropic Claude ?
Claude est pris en charge via la passerelle 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 propage pas les marqueurs aujourd’hui ; pour la mise en cache de Claude spécifiquement, utilisez le chemin natif Anthropic montré dans la §2.
Sources et vérification : tous les chiffres ont été mesurés par rapport à https://synthorai.io/v1 le 2026-05-25 en utilisant le SDK openai 2.38.0. Pages tarifaires des fournisseurs : Anthropic Prompt Caching · OpenAI Prompt Caching · Google Gemini Context Caching · DeepSeek KV Cache Guide · Alibaba Bailian Context Cache.