« Ce modèle prend-il en charge les appels d'outils ? » ressemble à une question sur le modèle. Ce n'en est pas une. C'est une question sur le modèle et sur l'endpoint où vous l'appelez — et un même modèle peut répondre oui sur l'un et non sur l'autre. Cette page explique pourquoi, et comment Entity Enricher tranche la question en la mesurant plutôt qu'en croyant un catalogue.
Envoyez à la famille gpt-6 d'OpenAI une requête avec des outils de fonction sur l'endpoint classique /v1/chat/completions et la requête est rejetée d'emblée :
400 Bad Request Function tools with reasoning_effort are not supported. Use /v1/responses or set reasoning_effort to 'none'.
Envoyez le même modèle, les mêmes outils et le même prompt à /v1/responses et il répond normalement — sur les deux canaux de sortie structurée, avec ou sans effort de raisonnement. Rien n'a changé dans le modèle. Seule l'adresse a changé.
La conséquence
Une capacité n'est pas une propriété du modèle seul. C'est une propriété d'un couple (modèle, endpoint). Tout catalogue qui stocke un seul vrai/faux par modèle répond donc à une question qu'il ne peut pas réellement observer.
Une route, c'est simplement l'endpoint auquel une requête est envoyée. Même fournisseur, même modèle, même clé API — mais un format de transmission différent, une structure de requête différente et des règles différentes sur ce qui peut être combiné. OpenAI en propose deux, et la majeure partie du secteur a copié la plus ancienne.
| Route | Forme de l'échange | Qui la parle |
|---|---|---|
Chat Completions /v1/chat/completions2023 | Sans état : vous envoyez la liste complète des messages, vous recevez le message suivant. Rien n'est conservé sur le serveur entre deux appels. | OpenAI, et le standard de fait du secteur copié par presque tous les autres — xAI, DeepSeek, Alibaba, Groq, Together, Ollama et la plupart des passerelles compatibles OpenAI. |
API Responses /v1/responses2025 | Renvoie une liste d'éléments typés — un élément de raisonnement, un message, un appel d'outil de fonction, un appel d'outil hébergé —, conserve l'état côté serveur et peut transmettre le raisonnement d'un tour au suivant. | OpenAI uniquement. Un endpoint similaire qui parle le format Chat Completions ne sert pas automatiquement celui-ci. |
C'est pourquoi « l'API OpenAI » est aujourd'hui une expression ambiguë : il y en a deux, elles se recoupent et elles n'appliquent pas les mêmes règles.
Les modèles de raisonnement ont fait éclater le modèle des messages. Le raisonnement interne d'un modèle n'est pas un message : ce n'est pas quelque chose qu'il vous a dit, ni quelque chose que vous lui avez dit. Chat Completions ne dispose que d'une liste de messages, il n'a donc nulle part où placer le raisonnement — et ce qu'il ne peut pas représenter, il le perd d'un tour à l'autre.
La Responses API a été conçue autour de cette lacune. Le raisonnement est un élément de premier ordre dans la liste renvoyée ; il peut revenir chiffré et être rejoué au tour suivant, si bien que le modèle poursuit à partir de sa propre réflexion au lieu de la reconstruire.
Un appel d'outil de type fonction est un aller-retour : le modèle demande un outil, votre code répond, le modèle poursuit. Sur Chat Completions, il n'existe nulle part où conserver le raisonnement pendant cet aller-retour : la suite reprendrait donc silencieusement sans lui — le modèle devrait refaire le raisonnement que vous avez déjà payé, et le referait probablement autrement.
Le fournisseur refuse donc la combinaison plutôt que de se dégrader silencieusement. Le message d'erreur n'est pas une limite du modèle ; c'est l'ancienne API qui reconnaît n'avoir aucun emplacement pour ce que ce modèle produit.
Si toutes les capacités privilégiaient le même endpoint, une route serait un détail d'implémentation dont personne n'aurait à se soucier. Ce n'est pas le cas.
La sortie structurée via le canal outil est exactement la combinaison que Chat Completions refuse sur un modèle de raisonnement, et l'outil de recherche web hébergé par le fournisseur n'est accepté que là.
Joindre des octets audio bruts n'est pas quelque chose que le chemin Responses API peut transporter : un modèle compatible audio n'est donc joignable que par l'endpoint plus ancien.
Un modèle, deux capacités, deux endpoints différents. Aucun vrai/faux unique ne peut rendre compte honnêtement des deux — d'où le fait qu'Entity Enricher enregistre une carte des capacités par route, et qu'une exécution nécessitant deux capacités situées sur deux routes différentes soit refusée explicitement plutôt que de perdre silencieusement l'une d'elles.
La sonde de capacité n'envoie pas une requête synthétique écrite à la main. Elle emprunte le vrai chemin de l'agent — le même code qu'emprunte un enrichissement — avec la capacité testée forcée, et laisse la réponse du fournisseur décider. Ce qui est mesuré est donc ce que l'application peut réellement faire, et non ce que l'API pourrait théoriquement accepter.
Toute l'échelle — canaux de sortie structurée, vision, PDF, audio, recherche web, raisonnement et son contrôle d'effort par requête — s'exécute une fois par route que le routeur peut choisir. Pour presque tous les providers, il n'y a qu'une seule route, et la sonde coûte exactement ce qu'elle a toujours coûté ; seuls les providers de type OpenAI, réellement accessibles de deux manières, sont mesurés deux fois.
Une route qui ne répond pas à son premier appel en texte brut n'est tout simplement pas mesurée, et un modèle sondé est ensuite routé uniquement vers les routes qui ont répondu — il n'existe aucun repli vers un autre endpoint, ni au moment de la sélection, ni lorsqu'une exécution échoue. Un modèle auquel aucune route ne répond est retiré, tout comme celui dont les routes n'offrent aucun canal de sortie structurée : le texte n'est jamais un repli, un tel modèle ne pourrait donc servir aucun enrichissement. Seule une sonde ultérieure prouvant un canal le réintègre. Les capacités ne sont jamais modifiées parce qu'une exécution a échoué — la sonde est la seule chose qui les mesure.
La requête a été acceptée et le modèle a produit le type de réponse attendu.
Enregistré comme un vrai mesuré pour cette route — et il prime sur toute affirmation collectée.
Le provider a explicitement rejeté la capacité, ou le client a refusé de construire la requête.
Le seul résultat autorisé à écrire un false. Un rejet est également gratuit : il n'est jamais facturé.
Autre chose : une limite de débit, un niveau d'accès manquant, un dépassement de délai, une erreur 5xx du fournisseur, une erreur non reconnue.
Rien n'est écrit, et la prochaine sonde pourra trancher.
Les deux erreurs n'ont pas le même coût. Un unknown erroné coûte un nouveau sondage : la capacité est simplement mesurée à nouveau plus tard. Un no erroné est silencieux et définitif — il déclasse le modèle partout, le retirant des sélecteurs et de la décision de routage, et personne n'en découvre jamais la raison. Un échec non reconnu ne prouve donc rien, et il est enregistré comme ne prouvant rien.
Un HTTP 200 prouve que la requête a été acceptée. Il ne prouve pas que le modèle a réellement lu la pièce jointe — un fournisseur peut très bien accepter une image et la transmettre à un modèle qui ne la regarde jamais. Chaque sonde porteuse d'un contenu en transporte donc un dont la réponse peut être vérifiée :
| Capacité | Charge utile envoyée | Question posée |
|---|---|---|
| Vision | Une image rouge uni | « Quelle couleur y domine ? » |
| Entrée PDF | Un PDF contenant exactement un mot | « Quel mot figure dans le document ? » |
| Entrée audio | Un fichier audio comportant un nombre de tonalités dénombrable | « Combien de bips distincts ? » |
Deux résultats distincts, délibérément
La réponse de contenu est enregistrée à côté du verdict et n'inverse jamais l'indicateur de capacité. Un petit modèle qui interprète mal un carré rouge prend malgré tout en charge la vision — le transport a fonctionné, la modalité a été transmise, le modèle était simplement faible. Confondre « ne peut pas » et « a mal fait » supprimerait silencieusement une capacité fonctionnelle pour un seul mot erroné ; les garder distincts vous renseigne sur les deux à la fois.
Les catalogues publics de modèles comme LiteLLM ou PricePerToken énoncent les capacités comme un fait brut à propos d'un modèle, sans endpoint associé. Ce n'est pas de la négligence — il n'existe aucun champ pour cela. Mais cela signifie qu'une affirmation peut être parfaitement juste en général et néanmoins fausse pour l'endpoint qu'utilise votre exécution.
« Ce modèle prend en charge les appels d'outils. » Vrai quelque part. Muet sur l'endroit. Utile comme point de départ pour un modèle que personne n'a encore sondé.
« Sur cet endpoint, ce modèle a accepté les appels d'outils à cet instant. » Elle nomme sa route, ce qui permet de la comparer à celle qu'empruntera réellement une exécution.
Nommer la route est aussi ce qui garantit l'honnêteté d'une mesure dans le temps. Un verdict obtenu sur un endpoint que le modèle n'utilise plus n'est pas une meilleure preuve qu'une simple affirmation — c'est une preuve qui porte sur autre chose. Sans la route enregistrée à ses côtés, une ancienne mesure serait indiscernable d'une mesure actuelle et primerait sur le catalogue pour le mauvais endpoint. La route étant enregistrée, la mesure ne s'applique tout simplement pas, et l'affirmation reste valable jusqu'à ce qu'une mesure sur le bon endpoint la remplace.
Le catalogue de modèles de l'application comme le catalogue de modèles public disposent d'un sélecteur Route à côté des filtres. Il change la route pour laquelle répondent les icônes de capacités de chaque ligne.
Chaque ligne répond pour l'endpoint sur lequel ce modèle est réellement servi. C'est la vue qui correspond à ce que vous obtenez lorsque vous lancez un enrichissement, et celle à consulter sauf si vous enquêtez sur un endpoint précis.
Épinglez tout le tableau à un seul endpoint et les icônes ne répondent plus que pour cet endpoint — c'est ainsi que vous voyez une capacité apparaître ou disparaître en passant de l'un à l'autre, et que vous comparez deux modèles à conditions égales.
Un modèle n'ayant qu'une seule route joignable n'affiche aucun sélecteur : il n'y a rien à choisir, et ses icônes répondent déjà pour le seul endpoint dont il dispose. Le sélecteur apparaît précisément quand l'ambiguïté est réelle.
Rien, la plupart du temps — et c'est bien l'objectif. Vous ne choisissez jamais de route. Vous choisissez un modèle et décrivez votre exécution, et le routeur sélectionne l'endpoint capable de prendre en charge ce dont elle a besoin : les outils et la recherche web vont là où ils sont acceptés, une pièce jointe audio va là où l'audio est pris en charge.
Le seul cas que vous remarquerez est un véritable conflit — demander la recherche web et joindre un fichier audio au même modèle OpenAI, par exemple, puisqu'aucun point de terminaison ne prend en charge les deux. Cette exécution est explicitement refusée, en nommant les deux capacités et ce que chaque route peut transporter, au lieu de s'exécuter en abandonnant silencieusement l'une d'elles. Retirez l'une des exigences, ou choisissez un modèle qui prend en charge les deux, et l'exécution se poursuit.
Et quand une icône de capacité vous surprend, le sélecteur de route est l'explication : vous avez presque toujours affaire à un modèle dont la réponse dépend de l'endroit où la question a été posée.