Tu as construit ta boucle agentique. Les outils sont appelés, les résultats reviennent, le modèle raisonne sur la suite — le tout ronronne comme un petit cerveau autonome. Puis tu ouvres le dashboard de consommation Anthropic après une journée de tests. Ce chiffre n'est pas une faute de frappe. C'est le prix de renvoyer le même system prompt et les mêmes schémas d'outils à chaque itération, plein tarif, à une API stateless qui n'a aucun souvenir de ce que tu lui as envoyé trois secondes plus tôt.

Et si tu as upgradé vers Opus 4.7 le 16 avril 2026 — ce que tu as probablement fait — Simon Willison a mesuré que le nouveau tokenizer produisait 1,46× plus de tokens pour des system prompts identiques. Tes coûts ont bondi de 35-40% du jour au lendemain pour littéralement le même texte. L'analyse de Finout (27 avril 2026) confirme la tendance sur l'ensemble des workloads.

Pourquoi « optimise tes prompts » ne suffit pas

La réponse évidente : raccourcis tes prompts. Bien sûr, dégraisse. Mais dans une vraie boucle agentique, ton préfixe statique — le system prompt, les définitions d'outils (les schémas qui disent au modèle quels outils il peut appeler et quels paramètres ils acceptent), et l'historique de conversation accumulé — atteint facilement 10 000+ tokens. Avec 6 outils et un system prompt correct, tu es à ~11 000 tokens de contenu identique renvoyé à chaque tour.

Sur 10 itérations, ça fait 110 000 tokens de pure répétition à 5 $/MTok sur Opus 4.7. Soit 0,55 $ rien qu'en coûts d'input — juste pour des octets que le serveur a déjà vus. Monte à 50 tours et tu crames des dollars sur une seule conversation.

Aucune optimisation de prompt ne corrige un protocole fondamentalement stateless. Il faut que le serveur se souvienne.

La solution : le prompt caching

L'API de prompt caching d'Anthropic (en GA depuis le 17 décembre 2024) te permet de taguer des blocs de messages avec un breakpoint cache_control — un marqueur qui dit au serveur « stocke tout jusqu'à ce point ». Lors des requêtes suivantes, si les octets correspondent exactement, le serveur lit depuis le cache et te facture 10% du prix d'input normal. Selon la documentation d'Anthropic, le cache 5 minutes (par défaut) coûte 1,25× à l'écriture initiale ; le cache 1 heure coûte 2×.

Le calcul est simple : paye une surprime de 25% une fois, obtiens 90% de réduction sur chaque lecture suivante. Dans une boucle de 10 tours, c'est 1 écriture + 9 lectures. C'est rentabilisé dès le premier cache hit.

Étape 1 : Cache le system prompt

Le system prompt est le contenu le plus stable de ta boucle. Il ne change jamais entre les itérations. Rends-le cacheable :

response = client.messages.create(
    model="claude-opus-4-7",
    max_tokens=4096,
    system=[{
        "type": "text",
        "text": "Ton long system prompt avec les instructions...",
        "cache_control": {"type": "ephemeral"},  # TTL de 5 min
    }],
    messages=[...],
)

Le détail crucial : sur Opus 4.7 (ainsi que Opus 4.5 et Haiku 4.5), le bloc minimum cacheable est de 4 096 tokens. Si ton system prompt est plus court, Anthropic ignore silencieusement le marqueur cache_control — pas d'erreur, pas de cache, juste de l'intention gaspillée. Sur Sonnet, le minimum est de 1 024 tokens.

Étape 2 : Cache les définitions d'outils

Les schémas d'outils — les descriptions JSON du nom, des paramètres et de la fonction de chaque outil — se trouvent dans le tableau tools et sont traités avant le system prompt dans la hiérarchie du cache. Place le cache_control sur le dernier outil du tableau pour tous les cacher en un seul breakpoint :

tools = [
    {"name": "search_web", "description": "...", "input_schema": {...}},
    {"name": "read_file", "description": "...", "input_schema": {...}},
    {"name": "run_code", "description": "...", "input_schema": {...}},
    {"name": "write_file", "description": "...", "input_schema": {...}},
    {"name": "list_dir", "description": "...", "input_schema": {...}},
    {"name": "submit_result", "description": "...", "input_schema": {...},
     "cache_control": {"type": "ephemeral"}},  # cache TOUS les outils ci-dessus
]

Règle critique : la hiérarchie du cache est tools → system → messages. Modifier une seule définition d'outil entre les itérations (ajouter un outil, renommer un paramètre) invalide le cache des outils, du system ET des messages. Dans une boucle agentique, garde ton jeu d'outils figé.

Étape 3 : Cache le préfixe de l'historique de conversation

À mesure que la conversation s'allonge, les anciens messages deviennent statiques — ils ne changeront plus. Place un breakpoint sur le dernier message de la portion « stable » :

messages = [
    {"role": "user", "content": "Tâche initiale..."},
    {"role": "assistant", "content": "Je vais commencer par..."},
    {"role": "user", "content": [{  # dernier message stable
        "type": "text",
        "text": "Résultat de l'outil de l'étape 3...",
        "cache_control": {"type": "ephemeral"},
    }]},
    {"role": "assistant", "content": "Traitement en cours..."},  # nouveau, non caché
]

Tu as maintenant 3 breakpoints (tools, system, historique). L'API en autorise 4 maximum. Garde le dernier slot pour les cas limites.

Étape 4 : L'astuce de la relocalisation

Celle-ci vient de l'équipe engineering de ProjectDiscovery, qui a publié ses résultats le 10 avril 2026. Ils font tourner un scanner de sécurité agentique de 26 étapes avec 40 appels d'outils. Leur taux de cache hit initial était un pathétique 7%.

Le problème : leur system prompt contenait des timestamps, de la mémoire de travail et des variables runtime qui changeaient à chaque appel. Le matching du cache exige une égalité octet par octet — un seul caractère différent et tout le bloc manque le cache.

La solution : déplacer tout le contenu dynamique hors du system prompt et dans un message rôle utilisateur à la fin de la conversation. Le system prompt devient octet-pour-octet identique entre les appels. Leur taux de cache hit est passé de 7% à 74% du jour au lendemain, pour atteindre 84% avec des ajustements supplémentaires — une réduction de coûts de 70%.

Étape 5 : Vérifie que ça marche vraiment

Les cache hits sont invisibles si tu ne regardes pas. La plupart des wrappers SDK et des frameworks de logging ne remontent pas les métriques de cache. Tu dois vérifier explicitement :

usage = response.usage
print(f"Cache écrit:    {usage.cache_creation_input_tokens}")
print(f"Cache lu:       {usage.cache_read_input_tokens}")
print(f"Non caché:      {usage.input_tokens}")

hit_rate = usage.cache_read_input_tokens / (
    usage.cache_read_input_tokens +
    usage.cache_creation_input_tokens +
    usage.input_tokens
) * 100
print(f"Taux de cache hit: {hit_rate:.1f}%")

À ta deuxième itération de boucle, cache_read_input_tokens devrait être élevé. S'il est à zéro, quelque chose invalide ton cache — cherche des différences au niveau des octets dans ton contenu « statique ».

Les pièges qui vont te coûter cher

1. La falaise des 5 minutes. Le TTL par défaut (time-to-live — combien de temps le serveur se souvient de ton contenu caché) est de 5 minutes. Si un appel d'outil prend 6 minutes (timeout d'API externe, calcul long), le cache expire. Tu repayes la surprime d'écriture. Solution : utilise "ttl": "1h" pour les workflows lents, mais sache que ça coûte 2× au lieu de 1,25× à l'écriture. Et les TTL longs doivent apparaître avant les courts dans la requête — l'inverse provoque une erreur.

2. L'ordre des clés JSON. Certains langages randomisent l'ordre des clés JSON à la sérialisation. Ordre différent = octets différents = cache miss. Fixe ton ordre de sérialisation ou utilise sort_keys=True.

3. Le lookback de 20 blocs. Le système cherche au maximum 20 blocs de contenu en arrière pour trouver les entrées cachées. Dans les conversations longues (>20 blocs), les anciens breakpoints tombent hors de la fenêtre de recherche. Ajoute des breakpoints frais tous les ~18 blocs. L'étude de PwC de janvier 2026 a montré que c'est là que la plupart des « cache miss mystérieux » se produisent.

4. Cacher du contenu court coûte plus cher. PwC a mesuré que les prompts de moins de 500 tokens montrent 10-18% de latence en plus avec le caching à cause de l'overhead. Ne cache pas ton system prompt de 200 tokens. De toute façon, il doit dépasser le minimum de 4 096 tokens sur Opus.

Ce que tu peux faire maintenant

Tu as trois breakpoints — tools, system, préfixe de l'historique — et un pattern de monitoring. La même boucle agentique qui brûlait 0,55 $ par conversation de 10 tours sur Opus 4.7 coûte maintenant ~0,12 $. Ce n'est pas une erreur d'arrondi ; c'est la différence entre un prototype que tu montres une fois et un système que tu mets réellement en production. La taxe tokenizer de 35% d'Opus 4.7 ? Toujours là, mais tu la payes une fois par écriture de cache au lieu de chaque tour. Bienvenue dans la partie où la page de facturation arrête de faire peur.