cd ../back to blog
$Guide//June 4, 2026//8 min read

Prompt caching en profondeur : comment couper 60 à 90 % de ton coût d’entrée

Style Anthropic cache_control et cache automatique OpenAI, expliqués pas à pas : les pièges qui invalident le cache et comment vérifier ton taux de hit sur Brievio.

Si tu envoies un long prompt système — contexte RAG, un catalogue d’outils, des règles d’agent, des exemples — tu paies probablement le plein tarif d’entrée à chaque appel. Le prompt caching d’Anthropic réduit ça à 10 % du tarif sur la portion cachée. OpenAI fait la même chose implicitement. La plupart des équipes jugent l’opération rentable pour 30 minutes de travail, parce qu’elle retranche de manière fiable 60 à 90 % de leur poste de coût d’entrée.

Brievio fait passer les deux variantes sans modification, face au modèle authentique. Le cache_control de style Anthropic fonctionne sur l’API Messages ; le cache automatique de style OpenAI fonctionne sur l’API Chat Completions. Cet article parcourt les deux, les pièges qui invalident discrètement les caches, et comment vérifier ton taux de hit.

L’avant-après

Une boucle naïve qui envoie dix fois le même prompt système de 18K tokens :

naive.py
# Le point de départ habituel : chaque appel repaie le prompt en entier.
import anthropic

client = anthropic.Anthropic(
    api_key="sk-brievio-...",
    base_url="https://api.brievio.com",
)

SYSTEM = open("system-prompt.md").read()        # 18 000 tokens de règles + exemples

# 10 questions utilisateur dans une session. Chaque appel envoie le bloc système de 18K tokens.
for question in questions:
    resp = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=600,
        system=SYSTEM,
        messages=[{"role": "user", "content": question}],
    )

# Coût par appel (entrée seule) : 18 000 × $3 / 1M = $0.054
# 10 appels : $0.54 rien qu'en entrée.

Maintenant, marque le bloc système comme cacheable — un champ de plus :

cached.py
# Le correctif : marque le préfixe statique comme cacheable. Après le premier
# appel, les appels suivants dans les ~5 minutes paient 10 % du tarif d'entrée sur
# la portion cachée. Même réponse, 89 % moins cher.
resp = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=600,
    system=[
        {
            "type": "text",
            "text": SYSTEM,
            "cache_control": {"type": "ephemeral"},  # marquer comme cacheable
        }
    ],
    messages=[{"role": "user", "content": question}],
)

# Premier appel (écriture cache) : 18 000 × $3   / 1M = $0.054
# Appels 2 à 10 (hit de cache) :    18 000 × $0.30 / 1M = $0.0054 chacun
# Total : $0.054 + 9 × $0.0054 = $0.103  (c'était $0.54 — 81 % économisés)

Tu viens d’économiser 81 % du coût d’entrée sur cette session. Le premier appel est en réalité légèrement plus cher que la version naïve (~1,25× le tarif d’entrée pour écrire le cache). Les appels 2 à 10 paient 10 % du tarif. Le point de bascule est à l’appel 2 — dès l’appel 3 tu es gagnant. À l’appel 10, c’est un raz-de-marée.

Style OpenAI : rien à faire

Si tu utilises le SDK OpenAI, Chat Completions cache le préfixe de n’importe quel prompt de plus de 1 024 tokens automatiquement. Aucun drapeau, aucune config. Le même modèle derrière Brievio obtient la remise, que tu sois passé par la voie Anthropic ou OpenAI dans :

openai_style.py
# Style OpenAI Chat Completions — le cache est automatique pour les prompts ≥1024 tokens.
# Aucun drapeau à poser. L'objet "usage" t'indique ce qui a été caché.
from openai import OpenAI

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

resp = client.chat.completions.create(
    model="claude-sonnet-4-6",
    messages=[
        {"role": "system", "content": LONG_SYSTEM_PROMPT},  # >1024 tokens
        {"role": "user", "content": "Latest question…"},
    ],
)

# Dans la réponse :
#   resp.usage.prompt_tokens_details.cached_tokens  →  17 800
#   resp.usage.prompt_tokens                        →  18 200
# 17,8K/18,2K = 98 % de l'entrée vient du cache. La facture le reflète automatiquement.

Lis usage.prompt_tokens_details.cached_tokens pour voir quelle part a été servie depuis le cache. Plus le préfixe fixe est grand, plus l’économie est grande. La règle empirique : si ton prompt système est plus court que ton contenu utilisateur variable, le cache ne fait pas grand-chose. Remodèle le prompt pour que les parties statiques soient grandes et placées en tête.

Cache multicouche — jusqu’à 4 points de coupure

Pour les boucles d’agent où certaines couches changent plus vite que d’autres, pose plusieurs points de coupure cache_control. Chacun est un instantané de tout ce qui précède jusqu’à ce point :

breakpoints.py
# Anthropic gère jusqu'à 4 points de coupure de cache par requête — sers-t'en
# pour garder le cache chaud même quand les couches ultérieures changent.
client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=600,
    system=[
        {"type": "text",
         "text": ROLE_AND_RULES,                      # ~3 000 tokens
         "cache_control": {"type": "ephemeral"}},     # coupure 1
        {"type": "text",
         "text": LARGE_KNOWLEDGE_BASE,                # ~15 000 tokens, change rarement
         "cache_control": {"type": "ephemeral"}},     # coupure 2
    ],
    messages=[
        {"role": "user",
         "content": [
             {"type": "text",
              "text": CONVERSATION_HISTORY,           # grandit à chaque tour
              "cache_control": {"type": "ephemeral"}}, # coupure 3
             {"type": "text", "text": new_question},
         ]},
    ],
)

# Quand CONVERSATION_HISTORY change, les coupures 1+2 touchent encore le cache.
# Seules la coupure 3 + la nouvelle question paient le plein tarif d'entrée.

La clé de cache, c’est le préfixe entier. Ajoute un token en position N et chaque coupure à partir de N est invalidée. L’ordre compte : le contenu le plus stable en premier. La couche de règles devrait rarement changer ; la base de connaissances se met à jour chaque semaine ; la conversation grandit à chaque tour.

Les façons courantes de casser le cache en silence

  • Mettre la date courante ou un request_id dans le prompt. Chaque appel est un nouveau préfixe, le taux de hit de cache est de 0 %. Hache les entrées de ton prompt et compare-les d’un appel à l’autre.
  • Assemblage non déterministe du prompt système. Si tu construis le système à partir d’un dict, l’ordre d’itération du dict compte dans certaines versions de Python. Trie les clés explicitement.
  • La durée de vie du cache est d’environ 5 minutes. Les trafics clairsemés (un appel toutes les 10 minutes) n’obtiennent aucun hit. Soit tu regroupes les appels, soit tu acceptes la perte.
  • Le minimum de 1 024 tokens. En dessous de 1K tokens, le cache de style OpenAI ne s’enclenche pas. Combine les petits fragments statiques en un seul préfixe plus long.
  • Les outils / définitions de fonctions font partie du préfixe. Un nouvel outil ajouté au catalogue invalide le cache pour tout le monde. Garde le catalogue d’outils stable ; versionne-le.

Vérifier le taux de hit

Un cache que tu ne peux pas voir, ce n’est pas de l’ingénierie — c’est de l’espoir. Journalise usage à chaque appel :

observe.py
# Lis toujours usage. Si cached_tokens est à 0 alors que tu attendais un hit,
# quelque chose cloche — généralement un préfixe non déterministe.
resp = client.messages.create(...)
u = resp.usage
print({
    "input_uncached":  u.input_tokens,
    "input_cache_read": u.cache_read_input_tokens,
    "input_cache_write": u.cache_creation_input_tokens,
    "output": u.output_tokens,
})

# Piège fréquent : mettre la date du jour ou un request_id dans le prompt système
# invalide silencieusement le cache. Hache tes entrées ; vérifie que
# cache_read_input_tokens est non nul au 2e appel identique.

Dans le tableau de bord Brievio, la page Usage affiche la répartition du cache par modèle et par jour. Si tu vois cache_read_input_tokens croître en pourcentage de l’entrée totale, le cache fonctionne. S’il reste à 0 ou fluctue de manière erratique, déroule la liste des pièges ci-dessus.

Ce que ça coûte réellement sur Brievio

Le tarif de cache de chaque modèle est publié sur la page de tarifs :

  • Modèles Anthropic : les lectures de cache sont à 10 % du tarif d’entrée. Les écritures de cache sont facturées au tarif d’entrée — aucun surcoût en amont.
  • Modèles OpenAI / Gemini : les lectures de cache sont à 20 % du tarif d’entrée (le ratio publié par les fournisseurs).
  • Tous les tarifs de cache reflètent le catalogue de Brievio — environ 15 % sous le tarif officiel du fournisseur. Ainsi une lecture de cache Sonnet 4.6 sur Brievio coûte $3 × 0.95 × 0.10 = $0.285 per 1M tokens. Environ 10× moins cher que le tarif d’entrée non caché.

Quand le cache n’est pas la réponse

Quelques cas où l’effort n’en vaut pas la peine :

  • Prompts courts (<1K tokens au total). La surcharge domine ; ne te casse pas la tête.
  • Tâches one-shot sans trafic répété. Le premier appel est légèrement plus cher — le cache ne se rentabilise qu’à partir de l’appel 2.
  • Tâches à forte sortie, faible entrée (écriture créative, génération de code). L’entrée représente déjà une petite part de la facture. Concentre-toi plutôt sur des plafonds de budget de sortie.

Pour tout le reste — chatbots RAG, agents avec un catalogue d’outils fixe, classifieurs qui tournent sur une grille statique, pipelines d’extraction structurée avec des few-shots constants — le cache est l’optimisation au meilleur ROI que tu puisses livrer en un seul après-midi. Associe-le aux quatre autres techniques de notre guide d’optimisation des coûts et 70 % de réduction de coût devient réaliste, sans le moindre compromis sur la qualité de sortie.

Déjà sur Brievio ? Ouvre /app/usage et regarde la colonne de cache de ton plus gros modèle. Si elle est à zéro, tu laisses de l’argent sur la table. Guide complet : /docs/caching.