Claude Fable 5 pour les agents : refus en plein appel d'outil, coût face à GLM 5.2
Sommaire
Dans notre évaluation, Claude Fable 5 a refusé en plein appel d’outil sur 11 des 44 tours d’un agent de code, pour des tâches aussi banales que corriger une valeur par défaut de config. Le refus arrive sous la forme stop_reason: "refusal" en pleine génération des arguments de l’outil ; les arguments tronqués restent du JSON valide, et une boucle d’agent qui exécute les appels d’outil sans vérifier le stop reason écrira sans broncher un fichier à moitié terminé sur le disque. C’est ce comportement, pas le prix, qu’il faut d’abord anticiper quand on met Fable 5 dans un agent.
TL;DR
- Claude Fable 5 a renvoyé
stop_reason: "refusal"en plein appel d’outil sur des tâches d’agent banales (correction d’une valeur par défaut de config, réservation d’une salle de réunion) ; les arguments tronqués dewrite_filerestaient parsables, donc une boucle qui ne vérifie pas le stop reason exécute des fichiers écrits à moitié. - Le thinking de Fable 5 est adaptatif, sans possibilité de le désactiver :
enabledcommedisabledsont rejetés ; le contrôle passe paroutput_config.effort. - Le surcoût de Fable 5 dépend de la forme de la charge : une tâche de code sur quatre tours a coûté 0,045 $ contre 0,003 $ sur glm-5.2 (15x), mais seulement 5x sonnet-5 sur du batch à chaud.
- Fable 5 impose une rétention des données de 30 jours.
Tout ce qui suit a été mesuré le 2026-07-05 via le gateway Synthorai avec un petit harnais de scénarios : cinq formes de charge de travail d’agent (une boucle de code utilisant des outils, du question-réponse RAG, de l’orchestration à forte densité d’outils, de la classification en batch, et une conversation de 15 tours), exécutées contre claude-fable-5, claude-opus-4-8, claude-sonnet-5 et glm-5.2, avec trois runs par tâche là où la variance compte. Les tâches sont volontairement triviales ; les taux de réussite servent de garde-fou, pas de benchmark de capacité. Les coûts sont le usage.cost facturé par le gateway.
Vérifiez stop_reason avant d’exécuter les appels d’outils
Voici le mode de défaillance dont la doc ne parle pas, et il corrompt l’état. L’agent lit app.py, décide d’écrire le correctif et commence à émettre un appel write_file. En plein milieu du contenu du fichier, le stream 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 un JSON complet et parsable. Rien n’indique « je me suis arrêté trop tôt ». Si le contrat de votre boucle est « j’ai des appels d’outils, je les exécute », vous venez d’écraser app.py avec un fragment de 38 caractères qui se termine au milieu d’un dictionnaire et qui n’est plus du Python valide. Le tour suivant est lui aussi un refus, donc la boucle se termine avec le workspace corrompu.
Trois constats se dégagent des données :
- Ça se déclenche sur du travail banal. Les tâches qui ont provoqué des refus étaient : corriger un
KeyErrordans une lecture de config, implémenter une fonction slugify, réserver une salle de réunion et créer un brouillon de facture. Rien à double usage, rien de sensible. - C’est reproductible, pas aléatoire. Une tâche de code a déclenché un refus sur les trois runs, en streaming comme en non-streaming. D’autres tâches n’en ont jamais déclenché. Toutes conditions confondues, 58 à 75 % de nos épisodes de code trivial passaient sur Fable 5, contre 100 % pour claude-opus-4-8, claude-sonnet-5 et glm-5.2. Et chaque échec, sans exception, remonte à un refus, jamais à du code faux.
- Une fois qu’un refus est 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 ne rétablissait rien.
Le déclencheur n’est pas le contenu de la tâche, et les données sont sans ambiguïté là-dessus. La tâche qui refusait à chaque run était un correctif de KeyError de neuf lignes dans un dictionnaire de config : pas de credentials, pas d’exploits. En parallèle, le scénario batch a classé des tickets de support portant sur du cryptomining, des clés Stripe fuitées et des pages de phishing sans le moindre refus, et le scénario RAG a répondu sur des docs bourrées de secrets AES-256-GCM et de procédures de réponse à incident, tout aussi proprement. Tous les refus se sont produits dans les deux scénarios multi-tours avec exécution d’outils ; les trois scénarios single-shot n’ont jamais refusé, même avec un contenu plus lourd. Le motif tient à la forme de la boucle d’agent, pas aux mots, ce qui veut dire qu’assainir vos entrées ne l’empêchera pas.
Le correctif tient en une ligne avant votre étape d’exécution d’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 documente la mécanique : un refus qui se déclenche avant toute sortie renvoie un tableau content vide et n’est pas facturé ; un refus en plein stream facture la sortie déjà envoyée, et la consigne est de jeter la partie partielle. La réponse porte aussi un objet stop_details avec une catégorie (par exemple cyber ou bio, ou null) qui permet de distinguer un blocage par classifieur d’un refus ordinaire. Ce que la doc ne détaille pas, c’est l’interaction avec l’usage d’outils décrite plus haut : le refus peut tomber pendant la génération des arguments, et les arguments partiels sont indiscernables des arguments complets.
Il existe aussi un chemin de récupération officiel. Sur l’API Claude, le paramètre bêta fallbacks (betas: ["server-side-fallback-2026-06-01"], fallbacks: [{"model": "claude-opus-4-8"}]) relance une requête refusée sur un modèle de repli à l’intérieur du même appel, le refus lui-même n’étant pas facturé s’il s’est produit avant toute sortie. Il n’est pas disponible sur Amazon Bedrock, Vertex AI ou Microsoft Foundry, où les SDK fournissent à la place un middleware de fallback côté client. Quel que soit le chemin retenu, le garde-fou ci-dessus reste prioritaire : n’exécutez jamais les appels d’outils d’un tour dont le stop reason est un refus.
Le coût de cinq formes d’agent
Coût médian par unité terminée (tâche, requête, élément ou conversation), mêmes prompts, même jour :
| Scénario | fable-5 | opus-4-8 | sonnet-5 | glm-5.2 |
|---|---|---|---|---|
| Boucle de codage (par tâche, 4 tours médians) | $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 en batch (par élément, à chaud) | $0.0024 | $0.0012 | $0.00046 | $0.00057 |
| Conversation de 15 tours (entière) | $0.94 | $0.34 | $0.26 | $0.083 |
Deux lectures de ce tableau comptent plus que n’importe quelle cellule prise isolément :
- Le modèle le moins cher change selon la forme. glm-5.2 gagne sur les boucles et la conversation longue, mais claude-sonnet-5 est le classifieur en batch le moins cher du lot, en dessous de glm-5.2, parce que son prix de lancement profite d’une part de lecture cache de 97 % une fois le prompt d’échafaudage à chaud.
- La surprime de Fable 5 dépend elle aussi de la forme : 15x glm-5.2 sur la boucle de codage, 11x sur la conversation, mais seulement 5x sonnet-5 sur les éléments de batch à chaud, où le cache absorbe l’essentiel du prompt.
Le reste du tableau des coûts, c’est comment maîtriser ces chiffres, puis deux choses qui les font discrètement remonter.
Faire baisser la facture d’un agent
Le cache est le levier le plus important, et sur Fable 5 son contrat n’a pas changé. Les données d’agent montrent ce qu’il vaut : en retirant les marqueurs cache_control, la même tâche de codage coûtait 2,0x plus cher, et les éléments de batch à chaud 6,8x plus. Sur opus-4-8, la même suppression donne 3,8x et 6,9x. Dans une boucle, le motif de marqueur glissant n’est pas une optimisation, c’est la différence entre une facture viable et une facture qui ne l’est pas.
L’ordre du prompt est le deuxième levier, et il a tenu sur tous les modèles testés. Placer les règles stables avant le contexte propre à la requête (plutôt qu’après) a rendu les requêtes RAG 26 à 37 % moins chères sur les quatre modèles ; sur la gamme Claude, le mauvais ordre paie en plus la surprime d’écriture cache de 1,25x à chaque appel. Les mécanismes sont dans le billet sur le cache LangChain ; les chiffres ici confirment simplement qu’ils s’appliquent tels quels à Fable 5.
Fable 5 ajoute aussi deux leviers qui lui sont propres. Le premier : le seuil d’éligibilité au cache est passé à 2 048 tokens, la moitié des 4 096 d’Opus 4.8. Ça ressemble à un détail anecdotique jusqu’à ce qu’on se rappelle d’où viennent les économies d’un agent : c’est l’échafaudage répété (prompt système, définitions d’outils, le préfixe glissant de la conversation) qui est mis en cache, et seulement s’il dépasse le seuil. Un agent riche en outils dont le préfixe par tour se situait entre 2 048 et 4 096 tokens n’avait aucun cache sur Opus 4.8, et commence à mettre en cache sur Fable 5, transformant un préfixe plein tarif en une lecture cache à environ 10 % du prix à chaque tour suivant. L’inverse est vrai aussi : un préfixe rembourré pour dépasser l’ancien seuil de 4 096 peut désormais traîner du poids mort. Lisez cache_read_input_tokens sur une réponse réelle plutôt que de supposer, car sur Fable 5 la remise commence plus tôt.
Le second, ce sont les budgets de tâche (bêta, en-tête task-budgets-2026-03-13), qui répondent au problème exact que cette comparaison ne cesse de faire remonter : une boucle Fable 5 fait grimper la facture vite, et max_tokens n’aide pas. C’est un plafond dur par réponse que le modèle ne voit pas ; il planifie donc comme s’il avait une place illimitée, puis se fait couper en pleine réflexion. Un budget de tâche fonctionne autrement. Vous donnez à la boucle un plafond de tokens (minimum 20 000) que le modèle voit comme un compte à rebours et par rapport auquel il ajuste son rythme, en concluant proprement au lieu d’être tronqué. Il compte ce que le modèle génère plus les résultats d’outils qu’il lit dans le tour, pas tout l’historique que vous renvoyez à chaque requête. Sur un modèle dont le tour de boucle de codage coûte 15x glm-5.2, un budget vers lequel le modèle se modère lui-même est le garde-fou le moins cher que vous puissiez ajouter.
Deux surprises de coût que la doc ne signale pas
Les gros leviers réglés, deux détails plus mineurs ont quand même fait bouger notre facture dans des sens que la documentation ne mentionne pas.
L’effort « low » ne coûtait pas moins cher. La profondeur de réflexion de Fable 5 se contrôle avec output_config.effort, et on s’attend intuitivement à ce que low coûte moins. Ce n’est pas le cas. Avec effort: "low", notre boucle de code tournait à 0,0478 $ par tâche contre 0,0451 $ par défaut, avec plus de tokens de sortie, pas moins. Même schéma sur GLM 5.2, où les noms d’effort ne suivent pas non plus le nombre de tokens. Sur les deux gammes de modèles, mesurez le bouton sur votre propre charge avant de supposer que « low » veut dire « moins ». Une raison pour laquelle le chiffre est difficile à prévoir : la part de la réflexion adaptative dans les tokens de sortie est passée de 2 % sur la boucle de code à 30 % sur les réponses RAG puis à 52 % sur la classification par lots, même modèle, même jour. Budgétez les tokens de sortie par forme de charge, pas par modèle.
Ne rejouez jamais reasoning_content. Sur les modèles compatibles OpenAI, le champ reasoning n’est pas de l’historique de conversation. L’API de DeepSeek exige de le retirer ; sur GLM 5.2, le rejouer est autorisé mais facturé. Le réinjecter dans l’historique des messages a gonflé le coût de notre boucle GLM d’environ 28 % jusqu’à ce qu’on le retire. Les blocs de réflexion d’Anthropic sont différents : sur le même modèle, vous devez les rejouer inchangés, mais un bloc de réflexion Fable 5 routé vers un autre modèle (par exemple lors d’un fallback vers Opus) est automatiquement retiré du prompt et non facturé, donc il n’y a rien à supprimer.
Ce qui a changé côté requête
Fable 5 partage l’essentiel de sa surface de requête avec Opus 4.7/4.8 et Sonnet 5. Ce qui a disparu, d’après la doc :
thinking: {type: "enabled", budget_tokens: N}renvoie une 400. Le thinking étendu avec un budget de tokens, le mécanisme de Claude 3.7 Sonnet jusqu’à la famille 4.5, est retiré sur toute la gamme 4.7+ au profit de la réflexion adaptative.thinking: {type: "disabled"}renvoie une 400, et c’est propre à Fable 5. Opus 4.7/4.8 et Sonnet 5 permettent encore de couper la réflexion ; Fable 5 non.temperature,top_pettop_ksont rejetés à toute valeur non par défaut.- Les prefills de message assistant (un tour
assistanten fin) renvoient une 400.
Le retrait de temperature/top_p/top_k et des prefills sont les deux qui piègent le plus souvent une requête portée ; les changements sur le thinking et la rétention sont traités plus haut et dans le billet sur la rétention.
En résumé
Fable 5 dans un agent est un problème d’ingénierie avant d’être un problème de budget. Traitez stop_reason: "refusal" avant d’exécuter les appels d’outils, sinon une écriture tronquée corrompra l’état sur une tâche aussi banale qu’une correction de config. Ensuite, traitez le coût comme quelque chose que vous façonnez : le cache est le plus gros levier, le seuil d’éligibilité est désormais à 2 048 tokens donc revérifiez votre préfixe, un budget par tâche empêche une boucle d’atteindre la facture par tour la plus élevée de ce comparatif, et effort: "low" n’est pas la remise que son nom laisse croire. Budgétez aussi par forme de charge : le même modèle coûte 15x le prix de glm-5.2 sur une boucle de code et 5x sonnet-5 sur du travail par lots à chaud. Rien de tout ça ne dit de l’utiliser ou non ; ça dit que les valeurs par défaut ne sont pas neutres, et que la facture comme les modes d’échec dépendent tous deux de la façon dont votre agent est façonné.
FAQ
Fable 5 refuse-t-il souvent les appels d’outils ?
Le refus se concentrait sur des tâches précises : une tâche de correction de config a refusé à chaque exécution, d’autres jamais, et les mêmes tâches reproduisaient le comportement en appels streaming comme non-streaming. Ce n’est donc pas un incident isolé qu’on peut contourner en réessayant. Les taux varieront selon votre charge de travail ; la réponse côté ingénierie reste la même dans tous les cas : vérifiez stop_reason avant d’exécuter les appels d’outils.
Peut-on désactiver le thinking de Fable 5 ?
Non. thinking.type.disabled et enabled sont tous les deux rejetés. Le thinking est adaptatif par défaut et output_config.effort est le seul levier ; dans notre boucle, un effort low n’a pas réduit le coût.
Fable 5 est-il parfois l’option économique ? Pas dans ce panel. Sa plus faible prime concerne le travail par lots avec cache chaud très sollicité, environ 5x sonnet-5, où un cache chaud absorbe l’essentiel du prompt. Sur les boucles et la longue conversation, c’est le modèle le plus cher que nous ayons exécuté.
Vérification : tous les chiffres mesurés le 2026-07-05 contre https://synthorai.io/ (/v1/messages natif Anthropic pour la gamme Claude, /v1/chat/completions pour glm-5.2), 505 épisodes et 1 022 appels répartis sur cinq formes de scénario, trois exécutions par tâche là où la variance compte. Les coûts sont ceux remontés par le gateway via usage.cost ; médianes affichées. Les tâches sont volontairement simples, donc les taux de réussite servent de garde-fou, pas de benchmark de capacité ; nous ne publions pas de revendications de capacité que nous n’avons pas mesurées. Le comportement de refus a été reproduit en modes streaming et non-streaming. Vos chiffres varieront selon les prompts, la région et la charge.