PokeGPT : prouver qu'un GPT pouvait appeler une vraie API

Un GPT personnalisé branché sur la PokeAPI via les Actions. Un Pokédex personnel, et un prétexte pour découvrir ce qui arrive quand un modèle de langue doit obéir à un schéma REST.

Client
Self-initiated
Année
2023
Statut
En ligne
Rôle
Tout, Intégration d'API, Prompt engineering
Stack
ChatGPT custom GPTs, GPT Actions, OpenAPI, PokeAPI, Gumroad
PokeGPT : prouver qu'un GPT pouvait appeler une vraie API
21
ventes

Le contexte

PokeGPT est un GPT personnalisé branché sur la PokeAPI. Posez-lui une question sur un Pokémon et il va chercher la fiche réelle : types, statistiques de base, talents, chaîne d’évolution, pool d’attaques, les chiffres tels qu’ils existent dans les jeux plutôt que tels que le modèle s’en souvient à moitié.

Je l’ai construit en novembre 2023, quelques jours après la sortie des Actions par OpenAI, parce que je voulais savoir si la fonctionnalité marchait ou si elle se contentait de faire une belle démo.

Le problème

Les GPT personnalisés sont sortis dans beaucoup de bruit, et presque tout n’était que des enrobages de prompt. Une personnalité, un ton, un message système, et en dessous le même modèle qui répond à partir des mêmes données d’entraînement. Rien de neuf.

Les Actions, c’était la partie neuve : on pouvait donner un schéma OpenAPI à un GPT et il appelait un vrai service HTTP. C’est la différence entre un chatbot qui a lu des choses sur vos données et un chatbot qui a vos données. Personne n’écrivait sur ce que ça faisait de le construire, par opposition à ce que l’annonce promettait.

La PokeAPI était le sujet de test idéal. Elle est publique, bien documentée, gratuite, poliment rate limitée, et elle a une propriété que je ne trouvais nulle part ailleurs : un domaine où le modèle sait déjà énormément de choses, de travers. Tous les modèles ont absorbé des années de disputes de forum sur les statistiques de base. Je pouvais donc poser une question, regarder ce qu’il répondait de mémoire, et comparer avec ce que renvoyait l’API. Une mauvaise réponse se voit tout de suite, contrairement à la plupart des intégrations d’API.

Mon rôle

Tout, sur mon temps libre, sans client. Le schéma, les instructions, les tests, la fiche produit.

L’approche

Je l’ai traité comme une expérience avec une hypothèse plutôt que comme un produit avec une feuille de route. La question était étroite : un GPT peut-il décider de façon fiable quand appeler un endpoint, choisir le bon, et se servir de ce qui revient plutôt que de ce qu’il croit déjà.

Ce cadrage a fixé le périmètre. Un Pokédex convient parce que les questions sont assez variées pour éprouver le routage et parce que la bonne réponse est vérifiable en quelques secondes. J’ai construit la plus petite chose capable de répondre honnêtement à la question, puis j’ai noté par écrit où elle échouait.

La construction

Le produit entier, c’est un schéma OpenAPI plus un bloc d’instructions. Pas de code que j’héberge, pas de backend. Les Actions veulent dire que c’est le runtime d’OpenAI qui fait la requête.

L’essentiel du travail est allé dans le schéma, et c’est là qu’était l’enseignement à retenir. Le GPT ne lit pas la documentation de la PokeAPI, il lit les descriptions d’opérations dans le schéma que je fournis, et c’est la seule chose qui lui dit quand appeler quel endpoint. Ma première version décrivait les endpoints avec exactitude et le modèle appelait quand même les mauvais, parce que « récupérer un Pokémon » et « récupérer une espèce de Pokémon » sont deux ressources dotées chacune d’une description exacte et d’aucune indication sur celle qui répond à une question d’évolution. Réécrire les descriptions pour dire quand utiliser une opération plutôt que ce qu’elle renvoie a corrigé presque toutes les erreurs de routage. C’est un problème de prompt engineering déguisé en schéma d’API.

Le deuxième problème, c’était la taille des réponses. Une seule réponse de la PokeAPI peut transporter des centaines d’attaques, chacune avec une liste imbriquée des versions du jeu où elle apparaît. Déverser ça dans une fenêtre de contexte brûle des tokens sur des données que personne n’a demandées et pousse la réponse dehors. Les instructions contraignent les champs à regarder selon le type de question, et disent au modèle de faire un second appel plus étroit plutôt qu’un seul appel énorme. Deux petites requêtes valent mieux qu’une grosse, ce qui est l’inverse de l’instinct qu’on rapporte de l’écriture de clients d’API ordinaires.

Le troisième était le plus intéressant : le modèle répondait de mémoire au lieu d’appeler quoi que ce soit. Interrogez-le sur un Pokémon très connu et il est sûr de lui, donc il saute l’outil et vous sort un chiffre plausible tiré d’une vieille révision de wiki. La correction a été un bloc d’instructions établissant que toute affirmation factuelle sur les statistiques, les types, les talents ou les évolutions exige un appel, et que répondre sans appel n’est pas autorisé. La connaissance apprise qui entre en concurrence avec un outil en direct est un mode de défaillance général des modèles qui utilisent des outils, et c’est là que je l’ai rencontré pour la première fois.

Les appels enchaînés, c’est là que ça s’écroulait. Une chaîne d’évolution veut dire résoudre une espèce, puis une URL de chaîne, puis parcourir la chaîne. Les séquences à plusieurs sauts n’étaient pas fiables dans cette première version, et je l’ai documenté au lieu de prétendre le contraire.

Le résultat

21 ventes. C’est le chiffre, et je ne l’habille pas : c’est un Pokédex gadget sur une boutique, et 21 personnes en ont voulu.

Le résultat qui m’importait, c’était la réponse à la question. Les Actions fonctionnaient. Un GPT pouvait vraiment appeler un service REST en direct et se servir de la réponse, et les modes de défaillance portaient sur la qualité des descriptions du schéma, sur la taille des réponses et sur la connaissance apprise qui prend le pas sur les appels d’outils, pas sur la plomberie. Chaque leçon de ces soirées est passée dans ma façon de construire aujourd’hui des systèmes qui utilisent des outils, dont le pipeline de recherche de SignalScout, qui est le même problème avec des enjeux bien plus élevés.

Ce que je ferais autrement

J’ai livré un schéma couvrant plus d’endpoints que le produit n’en avait besoin, sur la théorie que plus de capacités vaut mieux. C’est faux. Une surface plus petite route plus précisément, parce que chaque opération en trop est une chose de plus que le modèle peut choisir de travers.

J’aurais aussi mis la limitation sur les chaînes d’évolution dans la fiche de la boutique plutôt que de laisser les gens la découvrir. Dire ce qu’une chose ne sait pas faire coûte une phrase et achète de la crédibilité pour le reste de la page.