Claude Fable 5 pour agents : refus de tool calls, coût vs GLM 5.2
Sommaire
Lors de notre évaluation, Claude Fable 5 a refusé de poursuivre en plein appel d’outil sur 11 des 44 tours d’un agent de code, y compris pour des tâches aussi banales que la correction d’une valeur par défaut dans une configuration. Le refus arrive sous la forme stop_reason: "refusal" pendant la génération des arguments de l’outil. Les arguments tronqués restent pourtant du JSON valide. Une boucle agentique qui exécute les appels d’outils sans vérifier la raison de l’arrêt écrira donc sans hésiter un fichier incomplet sur le disque. Avant même de parler de prix, c’est ce comportement qu’il faut gérer pour intégrer Fable 5 à un agent.
TL;DR
- Claude Fable 5 a renvoyé
stop_reason: "refusal"en plein appel d’outil sur des tâches agentiques banales, comme corriger une valeur par défaut dans une configuration ou réserver une salle de réunion. Les arguments tronqués dewrite_filerestaient analysables. Une boucle qui ne vérifie pas la raison de l’arrêt exécute donc l’écriture de fichiers incomplets. - La réflexion de Fable 5 est adaptative et impossible à désactiver :
enabledetdisabledsont tous deux rejetés. Le seul réglage disponible estoutput_config.effort. - Le surcoût de Fable 5 dépend du profil du workload : une tâche de code en quatre tours a coûté $0.045 contre $0.003 sur glm-5.2, soit 15 fois plus, mais seulement 5 fois plus que sonnet-5 sur un traitement batch avec cache chaud.
- Fable 5 impose une conservation des données pendant 30 jours.
Toutes les mesures ci-dessous ont été réalisées le 2026-07-05 via la gateway Synthorai, à l’aide d’un petit harness couvrant cinq profils de workloads agentiques : une boucle de code utilisant des outils, des questions-réponses RAG, une orchestration faisant beaucoup appel aux outils, une classification batch et une conversation de 15 tours. Nous avons testé claude-fable-5, claude-opus-4-8, claude-sonnet-5 et glm-5.2, avec trois exécutions par tâche lorsque la variance était pertinente. Les tâches sont volontairement triviales : le taux de réussite sert de contrôle de cohérence, pas de benchmark de capacités. Les coûts correspondent au champ usage.cost facturé par la gateway.
Vérifier stop_reason avant d’exécuter les appels d’outils
La documentation ne prévient pas de ce cas de panne, alors qu’il peut corrompre l’état. L’agent lit app.py, décide d’écrire le correctif et commence à produire un appel write_file. Au milieu du contenu du fichier, le flux s’arrête :
{
"stop_reason": "refusal",
"content": [{
"type": "tool_use",
"name": "write_file",
"input": {
"path": "app.py",
"content": "DEFAULTS = {\n \"timeout_s\": 30,\n "
}
}]
}
L’objet input est du JSON complet et valide. Rien n’indique qu’il a été interrompu. Si votre boucle suit le contrat « des appels d’outils sont présents, donc je les exécute », elle vient d’écraser app.py avec un fragment de 38 caractères qui s’arrête au milieu d’un dictionnaire et n’est plus du Python valide. Le tour suivant renvoie lui aussi un refus, et la boucle se termine en laissant le workspace corrompu.
Les données permettent de tirer trois conclusions :
- Le problème survient sur des tâches banales. Les refus ont concerné la correction d’une
KeyErrorlors de la lecture d’une configuration, l’implémentation d’une fonction slugify, la réservation d’une salle de réunion et la création d’un brouillon de facture. Rien à double usage ni de sensible. - Le comportement est reproductible, pas aléatoire. Une tâche de code a provoqué un refus lors des trois exécutions, en streaming comme sans streaming. D’autres tâches n’ont jamais déclenché de refus. Selon les conditions, Fable 5 a réussi 58 à 75 % de nos épisodes de code triviaux, contre 100 % pour claude-opus-4-8, claude-sonnet-5 et glm-5.2. Tous les échecs provenaient d’un refus, jamais d’un code incorrect.
- Dès qu’un refus entre dans la conversation, l’épisode est terminé. Les tours suivants renvoyaient
stop_reason: "refusal"avec une sortie vide. Réessayer dans le même contexte n’a pas permis de reprendre.
Le contenu de la tâche n’est pas le déclencheur, et les données sont sans ambiguïté. La tâche refusée à chaque exécution consistait à corriger en neuf lignes une KeyError dans un dictionnaire de configuration, sans identifiants ni exploits. En parallèle, le scénario batch a classé sans aucun refus des tickets de support portant sur du cryptomining, des clés Stripe divulguées et des pages de phishing. Le scénario RAG a également répondu sans problème à partir de documents contenant des secrets AES-256-GCM et des procédures de réponse aux violations. Tous les refus se sont produits dans les deux scénarios multi-tours exécutant des outils. Les trois scénarios en un seul appel n’en ont déclenché aucun, malgré un contenu plus sensible. C’est donc la forme de la boucle agentique qui pose problème, pas les mots employés. Nettoyer les entrées ne l’empêchera pas.
Le correctif tient en une ligne avant l’étape d’exécution des outils :
if response.stop_reason == "refusal":
# do NOT execute tool calls from this turn: arguments may be truncated
raise AgentInterrupted("model refused; restart episode or escalate")
Anthropic décrit le mécanisme : un refus déclenché avant toute sortie renvoie un tableau content vide et n’est pas facturé. Un refus en cours de streaming facture la sortie déjà transmise, et il faut ignorer le contenu partiel. La réponse contient également un objet stop_details avec une catégorie, par exemple cyber, bio ou null, qui permet de distinguer un blocage par classifieur d’un refus ordinaire. La documentation n’explicite pas le cas rencontré ici avec les outils : le refus peut survenir pendant la génération des arguments, et rien ne permet de distinguer les arguments partiels d’arguments complets.
Il existe aussi un mécanisme officiel de reprise. Sur l’API Claude, le paramètre beta fallbacks (betas: ["server-side-fallback-2026-06-01"], fallbacks: [{"model": "claude-opus-4-8"}]) relance dans le même appel une requête refusée sur un modèle de fallback. Le refus initial n’est pas facturé s’il s’est produit avant toute sortie. Cette fonctionnalité n’est pas disponible sur Amazon Bedrock, Vertex AI ni Microsoft Foundry, où les SDK fournissent à la place un middleware de fallback côté client. Dans tous les cas, le garde-fou précédent reste indispensable : n’exécutez jamais les appels d’outils d’un tour dont la raison d’arrêt est un refus.
Coût des cinq profils agentiques
Coût médian par unité terminée, qu’il s’agisse d’une tâche, d’une requête, d’un élément ou d’une conversation, avec les mêmes prompts et le même jour :
| Scénario | fable-5 | opus-4-8 | sonnet-5 | glm-5.2 |
|---|---|---|---|---|
| Boucle de code (par tâche, médiane de 4 tours) | $0.045 | $0.012 | $0.0059 | $0.0031 |
| Réponse RAG (par requête) | $0.024 | $0.0075 | $0.0036 | $0.0031 |
| Orchestration d’outils (par tâche) | $0.048 | $0.011 | $0.0045 | $0.0027 |
| Classification batch (par élément, cache chaud) | $0.0024 | $0.0012 | $0.00046 | $0.00057 |
| Conversation de 15 tours (total) | $0.94 | $0.34 | $0.26 | $0.083 |
Deux enseignements de ce tableau comptent davantage qu’une valeur isolée :
- Le modèle le moins cher dépend du profil. glm-5.2 est le plus économique pour les boucles et la conversation longue. En revanche, claude-sonnet-5 est le classifieur batch le moins cher du groupe, devant glm-5.2, car son tarif de lancement profite d’une part de 97 % de lectures du cache une fois le prompt d’échafaudage en cache.
- Le surcoût de Fable 5 dépend lui aussi du profil : 15 fois plus cher que glm-5.2 sur la boucle de code et 11 fois plus sur la conversation, mais seulement 5 fois plus cher que sonnet-5 sur les éléments batch avec cache chaud, où le caching absorbe l’essentiel du prompt.
Pour maîtriser ces coûts, il faut d’abord actionner les bons leviers, puis tenir compte de deux facteurs qui les font discrètement remonter.
Réduire la facture d’un agent
Le caching reste de loin le levier le plus efficace, et son fonctionnement ne change pas sur Fable 5. Les données agentiques montrent son impact : sans marqueurs cache_control, le coût de la même tâche de code a été multiplié par 2,0 et celui des éléments batch avec cache chaud par 6,8. Sur opus-4-8, les mêmes tests donnent respectivement 3,8 et 6,9. Dans une boucle, le pattern de marqueurs glissants n’est pas une simple optimisation : il détermine si la facture reste viable.
L’ordre du prompt est le deuxième levier, avec des résultats cohérents sur tous les modèles testés. Placer les règles stables avant le contexte propre à chaque requête, plutôt qu’après, a réduit le coût des requêtes RAG de 26 à 37 % sur les quatre modèles. Pour les modèles Claude, le mauvais ordre ajoute aussi la majoration de 1,25 fois sur l’écriture en cache à chaque appel. Le mécanisme est détaillé dans l’article sur le caching avec LangChain. Les mesures présentées ici confirment simplement qu’il s’applique tel quel à Fable 5.
Fable 5 apporte également deux leviers spécifiques. D’abord, le seuil d’éligibilité au cache passe à 2,048 tokens, soit la moitié des 4,096 tokens d’Opus 4.8. Ce détail a un effet direct sur les économies d’un agent : la partie répétée, composée du prompt système, des définitions d’outils et du préfixe glissant de la conversation, n’est mise en cache que si elle dépasse ce seuil. Sur Opus 4.8, un agent utilisant beaucoup d’outils dont le préfixe par tour se situait entre 2,048 et 4,096 tokens ne bénéficiait d’aucun caching. Sur Fable 5, ce même préfixe devient éligible : après le premier tour, sa lecture ne coûte plus qu’environ 10 % du prix normal. L’inverse est également vrai : un préfixe artificiellement gonflé pour dépasser l’ancien seuil de 4,096 peut désormais contenir du poids mort. Vérifiez cache_read_input_tokens sur une réponse réelle au lieu de supposer que le cache est actif, car la remise commence plus tôt avec Fable 5.
Ensuite, les budgets de tâche (beta, header task-budgets-2026-03-13) répondent directement au problème mis en évidence par cette comparaison : une boucle Fable 5 peut vite faire grimper la facture, et max_tokens n’y remédie pas. Il s’agit d’une limite stricte par réponse, invisible pour le modèle. Celui-ci planifie donc comme si l’espace était illimité, puis se fait couper au milieu de son raisonnement. Le budget de tâche fonctionne différemment. Vous attribuez à la boucle un plafond de tokens, avec un minimum de 20,000, que le modèle voit sous forme de compte à rebours. Il adapte son rythme et conclut proprement au lieu d’être tronqué. Le budget compte ce que le modèle génère et les résultats d’outils qu’il lit pendant le tour, pas l’intégralité de l’historique renvoyé à chaque requête. Pour un modèle dont un tour de boucle de code coûte 15 fois plus que glm-5.2, un budget que le modèle peut lui-même gérer est le garde-fou le moins cher à ajouter.
Deux coûts cachés que la documentation ne signale pas
Même avec ces réglages, deux facteurs plus discrets ont encore fait évoluer notre facture dans un sens que la documentation ne permet pas d’anticiper.
L’effort « low » n’a pas coûté moins cher. La profondeur de réflexion de Fable 5 se règle avec output_config.effort, et l’on pourrait s’attendre à ce que low réduise le coût. Ce n’est pas ce que nous avons observé. Avec effort: "low", notre boucle de code a coûté $0.0478 par tâche, contre $0.0451 avec le réglage par défaut, et a produit davantage de tokens de sortie. Nous avons constaté le même phénomène sur GLM 5.2, où les niveaux d’effort ne correspondent pas non plus au nombre de tokens. Pour ces deux familles de modèles, mesurez ce réglage sur votre propre workload avant de supposer que « low » signifie « moins ». Les chiffres sont difficiles à prévoir pour une autre raison : avec le même modèle et le même jour, la réflexion adaptative représentait 2 % des tokens de sortie sur la boucle de code, 30 % sur les réponses RAG et 52 % sur la classification batch. Dimensionnez le budget de tokens de sortie par profil de workload, pas par modèle.
Ne rejouez jamais reasoning_content. Sur les modèles compatibles avec l’API OpenAI, le champ de raisonnement ne fait pas partie de l’historique de conversation. L’API de DeepSeek impose de le retirer. Sur GLM 5.2, le rejouer est autorisé, mais facturé. Le réinjecter dans l’historique des messages a augmenté d’environ 28 % le coût de notre boucle GLM, jusqu’à ce que nous le supprimions. Les blocs de réflexion natifs d’Anthropic suivent une autre logique : sur le même modèle, ils doivent être rejoués sans modification. En revanche, si un bloc de réflexion Fable 5 est envoyé à un autre modèle, par exemple lors d’un fallback vers Opus, il est automatiquement retiré du prompt et n’est pas facturé. Il n’y a donc rien à supprimer.
Évolutions de l’interface de requête
Fable 5 partage l’essentiel de son interface de requête avec Opus 4.7/4.8 et Sonnet 5. Selon la documentation, les éléments suivants disparaissent :
thinking: {type: "enabled", budget_tokens: N}renvoie une erreur 400. La réflexion étendue avec budget de tokens, utilisée de Claude 3.7 Sonnet jusqu’à la famille 4.5, est abandonnée sur toute la gamme 4.7+ au profit de la réflexion adaptative.thinking: {type: "disabled"}renvoie une erreur 400, et cette restriction est propre à Fable 5. Opus 4.7/4.8 et Sonnet 5 permettent encore de désactiver la réflexion, contrairement à Fable 5.temperature,top_pettop_ksont rejetés dès qu’ils prennent une valeur différente de celle par défaut.- Les préremplissages de message assistant, c’est-à-dire un tour
assistantfinal, renvoient une erreur 400.
La suppression de temperature/top_p/top_k et celle du préremplissage sont les deux changements qui cassent le plus souvent une requête portée depuis un autre modèle. Les évolutions concernant la réflexion et la conservation des données sont décrites plus haut et dans l’article sur la conservation des données pendant 30 jours.
Conclusion
Avec Fable 5, l’intégration dans un agent est d’abord un problème d’ingénierie, avant d’être un problème de budget. Vérifiez stop_reason: "refusal" avant d’exécuter les appels d’outils. Sinon, une écriture tronquée peut corrompre l’état sur une tâche aussi banale qu’une correction de configuration. Le coût doit ensuite être piloté en fonction du profil : le caching est le levier principal, le seuil d’éligibilité passe à 2,048 tokens et impose donc de réévaluer vos préfixes, un budget de tâche empêche une boucle d’accumuler le coût par tour le plus élevé de cette comparaison, et effort: "low" n’apporte pas la remise que son nom laisse entendre. Le budget doit aussi être défini par type de workload : le même modèle coûte 15 fois plus que glm-5.2 sur une boucle de code, mais 5 fois plus que sonnet-5 sur un traitement batch avec cache chaud. Il ne s’agit pas de recommander ou d’écarter le modèle. Les valeurs par défaut ne sont simplement pas neutres : la facture comme les modes de panne dépendent de la forme de votre agent.
FAQ
Fable 5 refuse-t-il souvent les appels d’outils ?
Les refus se sont concentrés sur des tâches précises : une correction de configuration a été refusée à chaque exécution, tandis que d’autres tâches ne l’ont jamais été. Les mêmes tâches ont reproduit le comportement en streaming et sans streaming. Ce n’est donc pas une erreur rare qu’un simple retry permettra de contourner. Les taux varieront selon votre workload, mais la réponse technique reste la même : vérifiez stop_reason avant d’exécuter les appels d’outils.
Puis-je désactiver la réflexion de Fable 5 ?
Non. thinking.type.disabled et enabled sont tous deux rejetés. La réflexion est adaptative par défaut, et output_config.effort est le seul réglage disponible. Dans notre boucle, le niveau low n’a pas réduit le coût.
Fable 5 est-il parfois l’option la moins chère ? Pas dans cet ensemble de tests. Son surcoût le plus faible apparaît sur les traitements batch avec cache chaud, où il coûte environ 5 fois plus que sonnet-5 parce que le cache absorbe l’essentiel du prompt. Pour les boucles et la conversation longue, c’est le modèle le plus cher que nous ayons testé.
Vérification : toutes les mesures ont été réalisées le 2026-07-05 sur https://synthorai.io/ (/v1/messages natif Anthropic pour la gamme Claude, /v1/chat/completions pour glm-5.2), soit 505 épisodes et 1,022 appels répartis sur cinq profils de scénarios, avec trois exécutions par tâche lorsque la variance était pertinente. Les coûts correspondent au champ usage.cost communiqué par la gateway ; les médianes sont indiquées. Les tâches sont volontairement simples : le taux de réussite sert de contrôle de cohérence, pas de benchmark de capacités. Nous ne publions aucune affirmation sur des capacités que nous n’avons pas mesurées. Le comportement de refus a été reproduit en streaming et sans streaming. Vos résultats varieront selon les prompts, la région et la charge.