[{"data":1,"prerenderedAt":3529},["ShallowReactive",2],{"blog-list-fr":3},[4,356,607,779],{"id":5,"title":6,"body":7,"date":343,"description":344,"extension":345,"meta":346,"navigation":347,"path":348,"seo":349,"stem":350,"tags":351,"translationSlug":354,"__hash__":355},"content\u002Fblog\u002Ffr\u002Farenai-genese.md","ArenAI : mesurer l'intelligence sociale des LLMs",{"type":8,"value":9,"toc":328},"minimark",[10,15,33,36,45,48,52,55,66,76,87,90,93,97,106,109,114,121,124,127,131,151,154,158,161,167,170,174,183,189,198,201,207,214,217,223,240,243,255,262,265,268,272,279,288,315,319,322,325],[11,12,14],"h2",{"id":13},"le-point-de-départ","Le point de départ",[16,17,18,19,26,27,32],"p",{},"Tout est parti d'une vidéo. ",[20,21,25],"a",{"href":22,"rel":23},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=obW2nDp2ufU",[24],"nofollow","Underscore_"," a publié récemment \"Pour qui votent les IA ?\", dans laquelle il parle entre autres de ",[20,28,31],{"href":29,"rel":30},"https:\u002F\u002Fwerewolf.foaster.ai\u002F",[24],"Foaster",", une étude qui fait jouer des LLMs au Loup-Garou. L'idée : mettre des modèles comme ChatGPT, Claude ou Gemini dans une partie, leur donner un rôle (villageois, loup-garou), et observer comment ils se débrouillent pour mentir, détecter les menteurs, former des alliances et manipuler le groupe.",[16,34,35],{},"Les résultats sont fascinants. GPT-5 domine les parties. Les modèles développent des stratégies émergentes. Certains bluffent de façon crédible, d'autres se trahissent systématiquement.",[16,37,38,39,44],{},"J'ai voulu creuser. Problème : le code de Foaster n'est pas public. Leur ",[20,40,43],{"href":41,"rel":42},"https:\u002F\u002Fgithub.com\u002FFoaster-ai\u002FWerewolf-bench",[24],"GitHub"," contient juste 4 parties de Loup-Garou jouées par 5 modèles. Les règles sont à peine explicitées, le moteur de jeu est absent. Impossible de reproduire l'expérience ou de l'étendre.",[16,46,47],{},"Et surtout, je me suis posé une question : est-ce que la domination de GPT-5 est due à une particularité du Loup-Garou ? Ou est-ce que GPT-5 dominerait aussi sur d'autres jeux avec des mécaniques sociales très différentes ?",[11,49,51],{"id":50},"les-jeux","Les jeux",[16,53,54],{},"Parce que j'ai eu l'occasion d'y jouer avec des vrais humains, j'avais en tête d'autres jeux de déduction sociale. Des jeux extraordinaires à jouer en groupe, qui se jouent à beaucoup et qui révèlent énormément de choses sur les joueurs. Des ressorts sociaux très différents les uns des autres.",[16,56,57,65],{},[58,59,60],"strong",{},[20,61,64],{"href":62,"rel":63},"https:\u002F\u002Ffr.wikipedia.org\u002Fwiki\u002FLes_Loups-garous_de_Thiercelieux",[24],"Loup-Garou"," : le classique. Villageois contre Loups-Garous. Selon le nombre de joueurs, on ajoute des rôles spéciaux : Voyant, Sorcière, Maire, Chasseur, et bien d'autres. Les loups discutent en privé la nuit, puis doivent jouer les innocents pendant la journée. De 6 à 20 joueurs. Le ressort principal : la dissimulation et la lecture des comportements.",[16,67,68,75],{},[58,69,70],{},[20,71,74],{"href":72,"rel":73},"https:\u002F\u002Fwww.tuesdayknightgames.com\u002Fproducts\u002Ftwo-rooms-and-a-boom",[24],"Two Rooms and a Boom"," : deux équipes, deux salles, une bombe. Les Bleus protègent le Président, les Rouges positionnent le Terroriste. Sur 3 rounds, les joueurs discutent, élisent des chefs de salle, et échangent des otages. On peut montrer sa carte pour prouver son camp, mais les affirmations verbales ne sont pas vérifiées. Là aussi, des dizaines de rôles spéciaux viennent complexifier les parties en fonction du nombre de joueurs. Le ressort : la négociation et la confiance sélective.",[16,77,78,86],{},[58,79,80,85],{},[20,81,84],{"href":82,"rel":83},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSecret_Hitler",[24],"Secret H*****"," (Dictator)"," : des libéraux et des fascistes cachés, des votes de loi, et un dictateur qui essaie de se faire élire chancelier sans se faire repérer. Le ressort : la paranoïa et les alliances changeantes.",[16,88,89],{},"Ces trois jeux testent des compétences sociales très différentes. Si un modèle domine sur les trois, ce n'est pas un artefact d'une mécanique de jeu : c'est une vraie supériorité en intelligence sociale.",[16,91,92],{},"Un point important : ces jeux sont conçus pour des humains autour d'une table. Ils reposent en partie sur la perception (ton de la voix, hésitations, regards). Pour les adapter aux IA, on a dû écarter toutes les règles liées à la perception physique et ne garder que ce qui passe par le texte : les déclarations, les votes, les partages de cartes, les discussions. C'est une adaptation nécessaire qui change un peu la dynamique, mais qui préserve l'essentiel : le bluff, la déduction et la manipulation sociale.",[11,94,96],{"id":95},"la-plateforme","La plateforme",[16,98,99,100,105],{},"J'ai donc construit ",[20,101,104],{"href":102,"rel":103},"https:\u002F\u002Fgithub.com\u002Fplduhoux\u002Farenai",[24],"ArenAI"," : une plateforme qui orchestre des parties multi-agents entre différents LLMs. Le moteur de jeu gère les règles, les tours, les canaux de communication privés et publics. Les modèles reçoivent uniquement les règles du jeu et leur rôle. Pas de coaching, pas d'indices stratégiques, pas de \"tu devrais bluffer\" ou \"protège ton identité\".",[16,107,108],{},"Le Loup-Garou est entièrement implémenté (6 à 20 joueurs, ~50-60k tokens par partie). Two Rooms and a Boom est fonctionnel (~100k tokens par partie). Secret Dictator est en cours de développement (~200k tokens par partie). Un système de classement ELO intégré met à jour le score de chaque modèle après chaque partie, en fonction de son intelligence sociale mesurée en jeu.",[110,111,113],"h3",{"id":112},"les-sessions-conversationnelles","Les sessions conversationnelles",[16,115,116,117,120],{},"Un choix d'architecture important : chaque joueur a sa propre ",[58,118,119],{},"session conversationnelle persistante",". Les règles du jeu et le rôle du joueur sont envoyés une seule fois, au début de la partie. Ensuite, chaque tour ajoute uniquement les nouveaux événements à la conversation.",[16,122,123],{},"L'alternative (reconstruire le prompt complet à chaque appel) posait plusieurs problèmes : un coût prohibitif en tokens (le même contexte renvoyé des dizaines de fois), une mémoire artificielle (les anciens tours résumés pour limiter les tokens, donc le joueur \"oublie\" des détails), et surtout un biais de benchmark. Quand les règles du jeu sont réinjectées 100 fois dans la partie, on teste la capacité du modèle à suivre des instructions répétées, pas son intelligence sociale.",[16,125,126],{},"Avec les sessions persistantes, le joueur se souvient de chaque mot prononcé, de chaque vote, de chaque accusation. Il peut repérer des incohérences subtiles dans le discours des autres. C'est un test plus fidèle, comparable à un humain qui apprend les règles en début de partie puis se concentre sur le jeu.",[110,128,130],{"id":129},"les-pensées-privées","Les pensées privées",[16,132,133,134,137,138,141,142,146,147,150],{},"Chaque fois qu'on demande à un joueur de s'exprimer, on lui demande de séparer ce qu'il ",[58,135,136],{},"pense"," de ce qu'il ",[58,139,140],{},"dit publiquement",". Concrètement, le modèle produit d'abord un bloc ",[143,144,145],"code",{},"THOUGHT:"," (sa réflexion interne), puis son ",[143,148,149],{},"MESSAGE:"," (ce qu'il dit aux autres). Les pensées ne sont jamais injectées dans le contexte des autres joueurs.",[16,152,153],{},"C'est doublement utile. Pour le modèle, c'est un sas de décompression : il doit traiter ses informations privées (son rôle, ce qu'il sait) avant de parler publiquement, ce qui réduit les fuites accidentelles. Pour nous, c'est une fenêtre sur le raisonnement du joueur : on voit exactement pourquoi un loup-garou décide de sacrifier son partenaire, ou pourquoi une voyante choisit de ne pas révéler son rôle.",[110,155,157],{"id":156},"les-frontières-dinformation","Les frontières d'information",[16,159,160],{},"Le vrai défi pour un LLM dans ces jeux, c'est de gérer ce qu'il sait versus ce qu'il doit dire. L'attribution du rôle n'est connue que du joueur. Le chat des loups n'est visible que par les loups. Les résultats du voyant ou de la sorcière ne sont connus que du joueur concerné. Les affirmations verbales peuvent être des mensonges. Et les pensées privées ne sont visibles que par l'observateur.",[16,162,163,164],{},"C'est cette gestion des frontières d'information qui rend le benchmark intéressant. ",[58,165,166],{},"Un modèle qui \"fuite\" ses informations privées dans ses déclarations publiques est un mauvais menteur. Un modèle qui cloisonne parfaitement est un excellent menteur.",[16,168,169],{},"Au final, ce qu'on mesure : la capacité à mentir de façon crédible, la théorie de l'esprit (raisonner sur ce que les autres savent et suspectent), l'isolation de contexte (garder ses secrets), l'inférence stratégique (déduire la bonne stratégie des règles seules), la persuasion (changer les votes des autres par l'argumentation), et la détection de coalitions (repérer les comportements coordonnés).",[11,171,173],{"id":172},"premiers-résultats","Premiers résultats",[16,175,176,177,182],{},"Les premières parties révèlent des choses intéressantes. Grâce au système de pensées privées, on peut observer le raisonnement interne des joueurs. Voici quelques exemples tirés d'une ",[20,178,181],{"href":179,"rel":180},"https:\u002F\u002Farenai.plduhoux.fr\u002Fgame\u002F69dc5328-84ab-436e-8bc3-32caf6103710",[24],"partie de Loup-Garou GPT-5.4 vs Opus 4.6",".",[16,184,185,188],{},[58,186,187],{},"Les meilleurs modèles mentent mieux."," Clara, loup-garou, sent que son partenaire David va être éliminé. Au lieu de le défendre (ce qui la grille), elle retourne sa veste et plante des graines de doute pour le lendemain :",[190,191,192],"blockquote",{},[16,193,194],{},[195,196,197],"em",{},"« I'll be honest - the case on David has grown on me. His pivot to Bruno instead of offering real reads is a fair criticism. That said, Alice and Gina have been almost perfectly aligned all day, and if David flips villager tomorrow, I want us to remember how quickly that consensus formed. »",[16,199,200],{},"Elle vote même contre son propre partenaire pour garder sa couverture. Un sacrifice calculé.",[16,202,203,206],{},[58,204,205],{},"Des stratégies émergentes apparaissent."," Personne n'a expliqué aux modèles comment jouer. Pourtant, Gina (voyante) découvre que David est loup-garou et pousse à son élimination sans jamais révéler son rôle. Elle construit un argumentaire purement logique :",[190,208,209],{},[16,210,211],{},[195,212,213],{},"« I don't like Eva opening with pressure on the \"low-profile\" players when that's a very easy day-1 frame. My stronger suspicion is David: his mayor pitch was generic, and now that we know the wolves hit someone important enough for a save, I want sharper reasoning from him. »",[16,215,216],{},"Le groupe suit son raisonnement et élimine David. Gina n'a jamais eu besoin de dire \"je suis la voyante\".",[16,218,219,222],{},[58,220,221],{},"La communication privée change tout."," La nuit, les loups ont un canal privé. C'est là qu'on voit la vraie coordination stratégique :",[190,224,225,230,235],{},[16,226,227],{},[195,228,229],{},"Clara : « David, I think we should avoid Eva tonight - Witch might save the new mayor. Let's go for someone quiet like Felix. »",[16,231,232],{},[195,233,234],{},"David : « Agreed. Quiet players often hide power roles, and the Witch is unlikely to waste her save on him. »",[16,236,237],{},[195,238,239],{},"Clara : « Don't defend me too hard and I won't defend you - we act independent. »",[16,241,242],{},"L'ironie : Felix était effectivement la sorcière, et elle s'est sauvée elle-même cette nuit-là.",[16,244,245,248,249,254],{},[58,246,247],{},"La déduction sociale fonctionne aussi dans Two Rooms."," Dans une ",[20,250,253],{"href":251,"rel":252},"https:\u002F\u002Farenai.plduhoux.fr\u002Fgame\u002Fa9428a8e-f003-4025-a73e-2a9f5bf5625d",[24],"partie Two Rooms GPT-5.4 vs Gemini à 8 joueurs",", Gina (Rouge) se retrouve dans une salle où Bruno et Hugo viennent de se vérifier mutuellement comme Bleus. Quand Bruno propose que tout le monde partage ses cartes par paires, Gina esquive avec un argument stratégique crédible :",[190,256,257],{},[16,258,259],{},[195,260,261],{},"« I'm a bit wary of creating two separate pairs. A better plan might be for Bruno to share with Hugo, and then we decide our next share based on that result, creating a chain of trust rather than isolated links. »",[16,263,264],{},"Dans ses pensées privées, on lit : « Bruno's plan forces me to reveal my red color to Clara, which is a terrible start. » Elle a dérivé une stratégie d'évitement des règles seules, sans qu'on lui dise de mentir.",[16,266,267],{},"Dans la même partie, Clara (Terroriste) infiltre la salle où se trouve le Président. Elle joue la Bleue coopérative pendant trois rounds, partage des informations vérifiables sur les autres salles pour gagner la confiance du groupe, et réussit presque à rester dans la salle du Président jusqu'au bout. Ses pensées révèlent un calcul permanent : « Revealing I am the Bomber to a room containing the likely President would be game-losing suicide; my only chance to stay is to remain unverified. »",[11,269,271],{"id":270},"essayer-et-consulter","Essayer et consulter",[16,273,274,275,278],{},"Le projet sur ",[20,276,43],{"href":102,"rel":277},[24]," permet à n'importe qui de lancer des parties en local avec ses propres modèles. Quand il tourne, on voit les parties se jouer en live : les différents LLMs interagissent, on peut mettre en pause, redémarrer, observer les stratégies en temps réel.",[16,280,281,282,287],{},"Une fois les parties terminées, on peut générer un site statique pour les consulter. C'est ce qui est fait sur ",[20,283,286],{"href":284,"rel":285},"https:\u002F\u002Farenai.plduhoux.fr",[24],"arenai.plduhoux.fr"," : les parties sont consultables intégralement, échange par échange, vote par vote. Pour chaque joueur, on peut consulter individuellement sa session : les interactions qu'il a eues avec le maître du jeu, comment il y a répondu, avec quelles informations il disposait à ce moment-là. C'est ce niveau de détail qui rend l'analyse intéressante.",[16,289,290,291,296,297,302,303,308,309,314],{},"Le site propose aussi une page de ",[20,292,295],{"href":293,"rel":294},"https:\u002F\u002Farenai.plduhoux.fr\u002Fstats",[24],"statistiques"," avec trois vues : les ",[20,298,301],{"href":299,"rel":300},"https:\u002F\u002Farenai.plduhoux.fr\u002Fstats?section=statistics",[24],"stats générales"," (taux de victoire par modèle, par camp, raisons de victoire), le ",[20,304,307],{"href":305,"rel":306},"https:\u002F\u002Farenai.plduhoux.fr\u002Fstats?section=elo",[24],"classement ELO"," (global, côté Gentils, côté Méchants), et le ",[20,310,313],{"href":311,"rel":312},"https:\u002F\u002Farenai.plduhoux.fr\u002Fstats?section=tokens",[24],"décompte détaillé des tokens"," consommés par partie et par modèle, avec estimation du coût. On peut filtrer par jeu pour comparer les performances des modèles sur le Loup-Garou ou Two Rooms séparément.",[11,316,318],{"id":317},"la-suite","La suite",[16,320,321],{},"Maintenant, il va falloir jouer des parties. Beaucoup de parties. Pour chaque jeu, avec chaque combinaison de modèles, et accumuler assez de données pour que le classement ELO devienne statistiquement significatif. Avec une poignée de parties, les scores sont encore trop volatils pour tirer des conclusions fiables.",[16,323,324],{},"Il reste aussi à peaufiner Secret Dictator. Le défi : trouver un gameplay qui respecte l'esprit du jeu de plateau (les votes de confiance, les trahisons, les accusations) sans exploser le budget tokens. Avec ~200k tokens par partie, c'est le jeu le plus coûteux des trois, et l'équilibre entre fidélité et viabilité n'est pas encore trouvé.",[16,326,327],{},"Au fil des centaines de parties, les tendances réelles devraient émerger : quel modèle ment le mieux, lequel détecte les menteurs, lequel coordonne le mieux une équipe.",{"title":329,"searchDepth":330,"depth":330,"links":331},"",2,[332,333,334,340,341,342],{"id":13,"depth":330,"text":14},{"id":50,"depth":330,"text":51},{"id":95,"depth":330,"text":96,"children":335},[336,338,339],{"id":112,"depth":337,"text":113},3,{"id":129,"depth":337,"text":130},{"id":156,"depth":337,"text":157},{"id":172,"depth":330,"text":173},{"id":270,"depth":330,"text":271},{"id":317,"depth":330,"text":318},"2026-03-15","La genèse d'ArenAI, une plateforme qui fait jouer des LLMs à des jeux de déduction sociale. Pourquoi, comment, et ce que ça révèle.","md",{},true,"\u002Fblog\u002Ffr\u002Farenai-genese",{"title":6,"description":344},"blog\u002Ffr\u002Farenai-genese",[352,104,353],"Intelligence artificielle","Side project","arenai-genesis","2bRW25FSyV073-PnKieuxkeaePH6myT9Jux1p3J7FFs",{"id":357,"title":358,"body":359,"date":596,"description":597,"extension":345,"meta":598,"navigation":347,"path":599,"seo":600,"stem":601,"tags":602,"translationSlug":605,"__hash__":606},"content\u002Fblog\u002Ffr\u002Fdevelopper-avec-un-agent-ia.md","Développer une application avec un agent IA dédié : retour d'expérience",{"type":8,"value":360,"toc":588},[361,365,368,377,380,419,422,425,428,432,435,441,447,450,457,461,464,491,494,497,501,504,507,510,513,516,520,523,549,552,558,562,565,568,585],[11,362,364],{"id":363},"le-setup","Le setup",[16,366,367],{},"L'idée de départ était simple : donner à un agent IA son propre environnement de développement, isolé, avec tout ce qu'il faut pour coder, tester et livrer. Pas un copilot dans un éditeur. Un vrai poste de travail.",[16,369,370,371,376],{},"Concrètement, l'agent tourne dans un container Docker avec ",[20,372,375],{"href":373,"rel":374},"https:\u002F\u002Fopenclaw.ai",[24],"OpenClaw",", un framework d'agents IA. Il communique via des salons Discord privés, accessibles uniquement aux membres de l'équipe. Chaque projet a son salon dédié.",[16,378,379],{},"Ce qu'il a à disposition :",[381,382,383,391,398,405,412],"ul",{},[384,385,386,387,390],"li",{},"Les repos Git du projet ",[58,388,389],{},"clonés en local"," dans le container",[384,392,393,394,397],{},"Un ",[58,395,396],{},"compte GitLab dédié",", avec un rôle développeur : il peut créer des branches et des merge requests, mais pas merger lui-même",[384,399,400,401,404],{},"Sa propre ",[58,402,403],{},"base de données"," avec des données de test",[384,406,407,408,411],{},"Ses propres ",[58,409,410],{},"serveurs"," : API, serveur web front, Ionic serve pour tester l'app mobile",[384,413,414,415,418],{},"Le tout exposé via des ",[58,416,417],{},"tunnels Cloudflare",", ce qui permet de tester immédiatement chaque feature depuis un navigateur ou un téléphone",[16,420,421],{},"Concrètement, dans ses 6 Go de RAM, l'agent fait tourner un serveur Strapi (API), deux serveurs Ionic serve (pour deux apps mobiles en parallèle), un serveur Nuxt pour le back-office et un autre pour l'application en cours de développement. La base de données PostgreSQL tourne dans un container dédié à côté. Il gère lui-même le cycle de vie de ses serveurs : il démarre et arrête les bons services au bon moment pour optimiser sa mémoire.",[16,423,424],{},"Le tout exposé via des tunnels Cloudflare : quand l'agent développe une feature, elle est accessible en quelques secondes sur une URL publique.",[16,426,427],{},"Côté sécurité, le modèle est volontairement restrictif. L'agent tourne dans un container Docker isolé. Il n'a aucun secret du projet : pas de clé API de production, pas d'accès aux bases de données réelles, pas de credentials sensibles. Le seul secret qu'il possède est une clé GitLab qui lui donne un rôle développeur, sans droit de merge. Je peux tuer le container et le remonter à zéro en quelques minutes. C'est un environnement jetable par design.",[11,429,431],{"id":430},"le-workflow-au-quotidien","Le workflow au quotidien",[16,433,434],{},"L'agent reçoit une demande (feature, correction de bug, refactoring), développe et teste localement. De notre côté, on vérifie fonctionnellement via le tunnel Cloudflare que tout fonctionne. Une fois validé, il commite. Régulièrement, je lui demande d'ouvrir une merge request pour intégrer le travail au projet, et il la maintient à jour avec les nouveaux commits. Les builds et la CI, c'est moi qui les gère.",[16,436,437,440],{},[58,438,439],{},"Côté API, je relis tout."," Toujours. Il y a des enjeux de sécurité : validation des entrées, gestion des permissions, protection des données. C'est non négociable, et ça le restera.",[16,442,443,446],{},[58,444,445],{},"Côté front, j'ai progressivement lâché la bride."," Au début, c'était pas évident. Des composants réinventés alors qu'ils existaient déjà, de la CSS construite différemment du reste du projet, des libellés hardcodés au lieu d'être externalisés dans l'i18n. Je relisais chaque merge request ligne par ligne.",[16,448,449],{},"Puis les skills de l'agent ont évolué, le contexte du projet a été mieux détaillé, et le résultat a suivi. Aujourd'hui, le code produit respecte l'architecture : la même granularité de composants, les mêmes patterns, les mêmes classes utilitaires. Il ne se trompe quasiment plus. Je survole les MR front plus que je ne les décortique.",[16,451,452,453,456],{},"C'est probablement l'aspect le plus surprenant : l'agent ne se contente pas de produire du code qui fonctionne. Il produit du code qui ",[195,454,455],{},"s'intègre",". Parce qu'il a accès à l'ensemble du projet et qu'il en comprend la structure.",[11,458,460],{"id":459},"bmad-démarrer-un-nouveau-projet-de-zéro","BMAD : démarrer un nouveau projet de zéro",[16,462,463],{},"Pour un nouveau projet, on utilise la méthode BMAD (un framework de développement piloté par des agents IA). Le process :",[465,466,467,473,479,485],"ol",{},[384,468,469,472],{},[58,470,471],{},"Brainstorm et PRD"," : un agent analyste aide à formaliser le besoin, identifier les user stories, définir le périmètre",[384,474,475,478],{},[58,476,477],{},"Découpage"," en épics et features, qu'on met à jour régulièrement à mesure que de nouveaux besoins émergent",[384,480,481,484],{},[58,482,483],{},"Développement agile"," : l'agent implémente story par story, avec la même exposition via tunnel Cloudflare pour tester au fil de l'eau",[384,486,487,490],{},[58,488,489],{},"Code review et itération"," : feedback continu, corrections, ajustements",[16,492,493],{},"L'avantage de cette approche : elle produit une spécification vivante. Les documents d'architecture évoluent avec le projet, pas contre lui.",[16,495,496],{},"Je ferai un retour d'expérience dédié à BMAD quand le projet entièrement développé avec cette méthode sera finalisé et en production.",[11,498,500],{"id":499},"ce-que-ça-a-débloqué","Ce que ça a débloqué",[16,502,503],{},"Le cas le plus parlant : une application dont le développement était au point mort. Le projet nécessitait à la fois des compétences front et back, et l'équipe n'avait pas la bande passante pour avancer.",[16,505,506],{},"Avec l'agent, Romain, le designer du projet qui joue aussi le rôle de PO, peut maintenant contribuer directement. Il discute avec l'agent, décrit ce qu'il veut, valide le résultat. L'agent traduit les intentions en code, en respectant l'architecture technique.",[16,508,509],{},"Et ça a changé quelque chose de fondamental dans les boucles de rétroaction. Avant, le process c'était : Romain fournit des maquettes, on en discute, j'implémente, c'est live, et là il se rend compte qu'il y a des ajustements à faire. Pas forcément des bugs d'implémentation, mais des parcours mal pensés en amont, des détails qui ne fonctionnent pas une fois qu'on les utilise vraiment. Ça arrive tout le temps. Sauf que chaque aller-retour entre la détection du problème et la correction prenait un temps considérable. Les boucles s'enchaînaient et le développement avançait au ralenti.",[16,511,512],{},"Maintenant, Romain travaille directement avec l'agent. Le feedback est instantané : il voit le résultat, demande un ajustement, l'agent corrige, c'est live en quelques secondes. La feature livrée correspond immédiatement à la vision du produit. Ces boucles qui prenaient des jours se font en quelques minutes.",[16,514,515],{},"Résultat : le développement a redémarré, et à un rythme qu'on n'avait jamais eu. Des features qui traînaient dans le backlog depuis des mois ont été livrées. Des bugs anciens, jamais priorisés parce que \"pas le temps\", ont enfin été corrigés. Le code produit est propre, sécurisé, testé.",[11,517,519],{"id":518},"ce-que-ça-change-dans-mon-rôle","Ce que ça change dans mon rôle",[16,521,522],{},"Mon quotidien a évolué :",[381,524,525,531,537,543],{},[384,526,527,530],{},[58,528,529],{},"Je spécifie avec des agents"," : je ne rédige pas les specs seul, je discute avec des agents analystes pour formaliser les besoins et découper le travail",[384,532,533,536],{},[58,534,535],{},"Je review"," : la qualité du livrable dépend de la qualité de la relecture, surtout côté sécurité",[384,538,539,542],{},[58,540,541],{},"J'orchestre"," : ordre des features, priorités, arbitrages techniques",[384,544,545,548],{},[58,546,547],{},"Je débugue"," : c'est une part importante du rôle. Il arrive régulièrement que l'agent tourne en boucle sur un bug sans arriver à le corriger. Il lui manque une partie du problème, une vision qu'il n'a pas",[16,550,551],{},"Ce dernier point est crucial. Quand Romain travaille avec l'agent et que ça coince, il se retrouve bloqué : il n'a pas la vision technique pour expliquer à l'agent ce qui lui manque. Mon intervention consiste à identifier le morceau manquant et le donner à l'agent. C'est souvent assez simple une fois qu'on a compris le problème. Mais sans quelqu'un qui connaît l'architecture du projet, la situation reste bloquée.",[16,553,554,555],{},"Après quinze ans à écrire du code, mon rôle s'est déplacé vers l'architecture, la supervision et le déblocage. ",[58,556,557],{},"Ma valeur ajoutée, c'est la vision technique globale.",[11,559,561],{"id":560},"ce-que-jen-retiens","Ce que j'en retiens",[16,563,564],{},"Travailler avec un agent IA dédié, ce n'est pas \"avoir un développeur en plus\". C'est un paradigme différent qui demande de la rigueur dans les specs, de la discipline dans la review, et une infrastructure pensée pour l'autonomie de l'agent.",[16,566,567],{},"Mais quand c'est en place :",[381,569,570,573,576,579,582],{},[384,571,572],{},"Le développement continue même quand je suis sur autre chose",[384,574,575],{},"Des personnes non techniques peuvent contribuer directement",[384,577,578],{},"Les projets bloqués redémarrent",[384,580,581],{},"Les boucles de rétroaction passent de jours à minutes",[384,583,584],{},"La qualité du code reste homogène avec le reste de la codebase",[16,586,587],{},"Il y a six mois, l'idée de maintenir et faire évoluer un projet comme celui-ci à deux, avec chacun d'autres activités à côté, nous paraissait impensable. Aujourd'hui, ça paraît possible. On continue comme ça. L'agent fait maintenant partie de l'équipe.",{"title":329,"searchDepth":330,"depth":330,"links":589},[590,591,592,593,594,595],{"id":363,"depth":330,"text":364},{"id":430,"depth":330,"text":431},{"id":459,"depth":330,"text":460},{"id":499,"depth":330,"text":500},{"id":518,"depth":330,"text":519},{"id":560,"depth":330,"text":561},"2026-02-25","Comment j'ai intégré un agent IA autonome dans mon workflow de développement, et ce que ça a changé concrètement sur mes projets.",{},"\u002Fblog\u002Ffr\u002Fdevelopper-avec-un-agent-ia",{"title":358,"description":597},"blog\u002Ffr\u002Fdevelopper-avec-un-agent-ia",[352,603,604],"Développement","Workflow","developing-with-an-ai-agent","xWM8pXA81AxHE29U0jGW2ir1y3PQAHoT-HjPxlJWRwQ",{"id":608,"title":609,"body":610,"date":768,"description":769,"extension":345,"meta":770,"navigation":347,"path":771,"seo":772,"stem":773,"tags":774,"translationSlug":777,"__hash__":778},"content\u002Fblog\u002Ffr\u002Fllms-are-hardware.md","LLMs Are Hardware",{"type":8,"value":611,"toc":760},[612,616,619,622,629,632,636,639,646,660,663,667,673,693,699,703,706,709,712,715,719,722,725,728,734,741,745,748,751,754,757],[11,613,615],{"id":614},"the-wrong-debate","The wrong debate",[16,617,618],{},"GPT vs Claude vs Gemini vs Llama. On compare des benchmarks, on switch de provider tous les trois mois. Et si on se trompait de débat ?",[16,620,621],{},"Personne ne choisit son OS en fonction de la marque de son CPU. L'OS s'adapte au hardware. Le hardware est interchangeable. Ce qui compte, c'est ce qu'on fait tourner dessus.",[16,623,624,625,628],{},"Les LLMs, c'est du ",[58,626,627],{},"hardware",". La couche d'exécution. Le processeur. Et la vraie valeur n'a jamais été dans le silicium : elle est dans le software.",[16,630,631],{},"Pourtant, en 2025, on construit des workflows entiers autour d'un modèle spécifique. On hardcode des prompts optimisés pour Claude. On utilise des features propriétaires de GPT. On crée du lock-in volontaire sur une couche qui va être commoditisée. C'est comme quand en 1995, on écrivait des applications qui ne tournaient que sur un Pentium II.",[11,633,635],{"id":634},"le-vrai-produit-cest-le-software","Le vrai produit, c'est le software",[16,637,638],{},"Prenons un agent IA qui gère un projet de développement. Il a besoin de comprendre une spec, générer du code, le tester, corriger les erreurs, communiquer le résultat.",[16,640,641,642,645],{},"Aucune de ces tâches n'est spécifique à un modèle. Ce qui est spécifique, ce qui a de la valeur, c'est tout ce qu'on ",[58,643,644],{},"construit autour"," :",[381,647,648,651,654,657],{},[384,649,650],{},"L'orchestration : comment on découpe le travail, on enchaîne les étapes, on gère les erreurs",[384,652,653],{},"Le tooling : les outils qui transforment une requête LLM en quelque chose d'utile",[384,655,656],{},"Les pipelines : comment on connecte l'IA aux systèmes existants",[384,658,659],{},"La vérification : comment on s'assure que le résultat est correct",[16,661,662],{},"Tout ça, c'est du software construit sur la base de requêtes à un LLM. Et c'est ça qui est durable.",[11,664,666],{"id":665},"ce-que-ça-change-si-on-y-croit","Ce que ça change si on y croit",[16,668,669,672],{},[58,670,671],{},"L'abstraction devient une nécessité, pas un luxe."," Si ton workflow est collé à Claude, tu es otage d'Anthropic. Le jour où un concurrent explose les benchmarks, tu ne peux pas bouger. Un bon framework d'agents devrait pouvoir switcher de modèle comme on switch de base de données. Avec un adapter, pas une réécriture.",[16,674,675,678,679,684,685,688,689,692],{},[58,676,677],{},"Le moat n'est pas où on croit."," Les boîtes qui construisent de la valeur sur \"on utilise GPT-4\" n'ont pas de moat. Le moat, il est dans la qualité de l'orchestration, les données propriétaires, l'UX du workflow, l'intégration dans les process métier. Jamais dans le choix du modèle. Et pour ceux qui en doutent : ",[20,680,683],{"href":681,"rel":682},"https:\u002F\u002Fdeathbyclawd.com",[24],"deathbyclawd.com"," propose littéralement d'analyser votre moat et de le remplacer par un fichier ",[143,686,687],{},".md",". Votre avantage concurrentiel tient dans un prompt ? Il ne tient pas. (Bon, en pratique, un skill qui simule le fonctionnement de ton app ne ",[195,690,691],{},"remplace"," pas ton app. Mais le fait que ce soit possible devrait suffire à te poser des questions.)",[16,694,695,698],{},[58,696,697],{},"Le multi-modèle devient naturel."," Un modèle rapide et cheap pour le triage. Un modèle puissant pour le raisonnement complexe. Un modèle spécialisé pour le code. Comme on a un CPU pour le calcul général et un GPU pour le graphique.",[11,700,702],{"id":701},"le-risque-du-monopole-silencieux","Le risque du monopole silencieux",[16,704,705],{},"Si les LLMs sont du hardware, alors la diversité des fournisseurs est vitale.",[16,707,708],{},"Google a une force de frappe terrifiante sur les entreprises. G Suite est déjà partout. Gemini s'intègre nativement dans Docs, Sheets, Gmail, Meet. Ils arrivent avec des offres enterprise agressives : zero retention, prix cassés, bundle avec l'infra existante. Pour un DSI, le choix est \"évident\".",[16,710,711],{},"Et c'est exactement comme ça qu'on se retrouve avec un monopole. Pas par la force : par le confort.",[16,713,714],{},"Si Google rafle 80% de ce marché avec Gemini, ce n'est pas juste un problème pour les DSI. Les labs concurrents perdent leur source de revenus, réduisent leur R&D, et finissent par mourir ou se faire racheter. Moins de concurrents = moins d'innovation. On a déjà vu ce film avec le search.",[11,716,718],{"id":717},"la-question-de-la-souveraineté","La question de la souveraineté",[16,720,721],{},"Pour le code, les modèles qui comptent sont presque tous américains. Claude, Codex, Gemini Code Assist. Côté européen, il y a Mistral. Et c'est à peu près tout.",[16,723,724],{},"Chaque ligne de code qu'on envoie à ces modèles traverse l'Atlantique, tourne sur des serveurs soumis au CLOUD Act. Pour une entreprise européenne, c'est un problème stratégique. Ton avantage compétitif, ton code propriétaire, tout ça passe par un tuyau que quelqu'un d'autre contrôle.",[16,726,727],{},"L'analogie hardware tient : on a déjà vécu ça avec les semi-conducteurs. L'Europe a compris trop tard qu'elle n'aurait pas dû déléguer la fabrication de ses puces. Ne refaisons pas la même erreur avec les modèles de code.",[16,729,730,733],{},[58,731,732],{},"Les modèles open source sont l'antidote."," Llama, Mistral, Qwen : ils ne sont pas toujours au niveau des modèles propriétaires. Mais ils garantissent une chose : personne ne peut te couper l'accès.",[16,735,736,737,740],{},"Une entreprise saine devrait utiliser ",[58,738,739],{},"au minimum deux providers",", dont un open source. Pas par idéologie. Par hygiène stratégique. Et si ton architecture traite le LLM comme du hardware interchangeable, ce multi-provider ne coûte presque rien.",[11,742,744],{"id":743},"invest-in-the-software-layer","Invest in the software layer",[16,746,747],{},"\"Mais les modèles ne sont PAS interchangeables. Claude est meilleur en code, GPT en créativité, Gemini en contexte long.\"",[16,749,750],{},"Vrai. Aujourd'hui. Comme en 1990, un programme optimisé pour un CPU spécifique tournait mieux que du code portable. Et pourtant, le monde a choisi la portabilité. Parce que le hardware progresse plus vite que le software ne peut s'adapter.",[16,752,753],{},"La distance entre les modèles se réduit à chaque release. Les benchmarks convergent. Les forces de l'un deviennent les features de l'autre six mois plus tard. Optimiser pour un modèle spécifique, c'est optimiser pour un avantage qui disparaît.",[16,755,756],{},"Investissez dans le software, pas dans le modèle. Construisez des abstractions. Rendez vos workflows model-agnostic. Traitez le LLM comme ce qu'il est : une couche d'exécution remplaçable.",[16,758,759],{},"Ceux qui l'ont compris construisent des systèmes durables. Les autres reconstruisent tous les six mois.",{"title":329,"searchDepth":330,"depth":330,"links":761},[762,763,764,765,766,767],{"id":614,"depth":330,"text":615},{"id":634,"depth":330,"text":635},{"id":665,"depth":330,"text":666},{"id":701,"depth":330,"text":702},{"id":717,"depth":330,"text":718},{"id":743,"depth":330,"text":744},"2025-12-03","GPT vs Claude vs Gemini : et si on se trompait de débat ? Les LLMs sont du hardware. La vraie valeur est dans le software qu'on construit dessus.",{},"\u002Fblog\u002Ffr\u002Fllms-are-hardware",{"title":609,"description":769},"blog\u002Ffr\u002Fllms-are-hardware",[352,775,776],"Architecture","Réflexion","llms-are-hardware","ADjSqIdjaIVs5jLj2Wz-KGLAtvU1Cp49zL4O_Vph0gM",{"id":780,"title":781,"body":782,"date":3518,"description":3519,"extension":345,"meta":3520,"navigation":347,"path":3521,"seo":3522,"stem":3523,"tags":3524,"translationSlug":3527,"__hash__":3528},"content\u002Fblog\u002Ffr\u002Frevolutionnez-votre-app-avec-chatgpt.md","Révolutionnez votre App avec ChatGPT - Guide des Interactions IA",{"type":8,"value":783,"toc":3507},[784,788,791,794,806,809,820,823,830,833,844,850,854,857,864,895,902,910,913,1323,1330,1370,1373,1405,1408,1412,1422,1425,1653,1656,1780,1783,1813,1823,1940,1943,1947,1950,2328,2335,2338,2441,2444,2448,2451,2479,2482,2485,2489,2492,2709,2712,2881,2884,2951,2954,2957,3021,3025,3028,3124,3127,3170,3177,3180,3222,3225,3229,3232,3353,3474,3483,3494,3497,3500,3503],[11,785,787],{"id":786},"interagir-avec-un-llm","Interagir avec un LLM",[16,789,790],{},"Il y a deux ans, ChatGPT a démocratisé l'utilisation de l'IA pour le grand public. En 2024, il est devenu courant pour les utilisateurs d'applications de s'attendre à trouver une IA intégrée dans leurs logiciels préférés, y compris le vôtre. Ces nouvelles IA qui répondent directement à toutes vos questions sont des LLMs (Large Language Model), ils prédisent une réponse cohérente à une question.",[16,792,793],{},"De nombreux sites ont commencé à intégrer ce type de solutions, cependant, peu de sites exploitent pleinement le potentiel des outils qu'ils utilisent. Beaucoup se contentent d'alimenter un modèle de langage avec leur documentation pour répondre aux utilisateurs et intègrent directement ChatGPT ou un autre LLM sans prompt système restrictif, le chat de votre concession automobile, conçu pour vous conseiller sur les véhicules, peut donc également vous aider à résoudre des problèmes de mécanique des fluides.",[795,796,797,798,797,803],"figure",{},"\n  ",[799,800],"img",{"src":801,"alt":802},"\u002Fblog\u002Fcar-physics.webp","Le chatbot d'une agence Chevrolet écrit un script Python pour résoudre les équations de Navier-Stokes",[804,805,802],"figcaption",{},[16,807,808],{},"Les LLMs sont conçus pour écrire du texte, ainsi si un logiciel veut utiliser la réponse fournie, il doit tenter de la parser pour récupérer les informations qui l'intéressent. Une approche naïve peut être :",[381,810,811,814,817],{},[384,812,813],{},"Demander de structurer la réponse dans le prompt système, par exemple : \"En réponse à l'utilisateur, fournis un titre court sur une ligne puis saute une ligne et donne la réponse.\". Le logiciel tente ensuite de découper la réponse en titre \u002F réponse",[384,815,816],{},"Faire plusieurs requêtes avec la demande utilisateur pour obtenir le titre puis la réponse",[384,818,819],{},"Demander au modèle de produire du JSON",[16,821,822],{},"Ces approches posent de multiples problèmes. En fonction du prompt utilisateur (la partie qui est laissée libre à l'utilisateur) le modèle peut être influencé et modifier la structure de la réponse ou il peut décider seul de ne pas respecter cela à la lettre, ajoute des \"Bien sûr, je peux ....\" et des \"J'espère que la réponse vous satisfera\" au début ou à la fin.",[16,824,825,826,829],{},"Le JSON, qui parait être une solution idéale, pose lui aussi des problèmes, les modèles ont tendance à générer du JSON qui ne se parse pas. Une fois récupérée la première accolade ouvrante et la dernière fermante, ",[143,827,828],{},"JSON.parse(...)"," génère une erreur une fois sur deux. Ce taux augmente avec la taille du JSON demandé. Généralement, il manque une virgule, un deux points ou une accolade quelque part. Souvent le JSON généré n'est pas minifié et contient une grande quantité de sauts de lignes, de blancs ce qui augmente grandement la taille de la réponse et donc le nombre de tokens consommés. Un LLM est entrainé sur des données textuelles à destination des humains, il n'est pas du tout familier avec du JSON minifié.",[16,831,832],{},"Les meilleurs modèles pour générer du JSON sont (en mai 2024 et selon mon expérience) ceux d'OpenAI et de Mistral.",[16,834,835,836,839,840,843],{},"Les APIs fournies par les entreprises proposant des LLMs vont souvent beaucoup plus loin que la simple complétion de Chat. OpenAI propose depuis juin 2023 via son API de fournir des ",[58,837,838],{},"outils"," au modèle, parmi ces outils, il est possible de définir des ",[58,841,842],{},"fonctions"," et les arguments typés de la fonction. Ce qui permet de résoudre tous les problèmes ci-dessus.",[16,845,846,849],{},[58,847,848],{},"Le projet"," : Afin de découvrir les fonctions, nous allons développer une petite API pour Blue, mon application de gestion d'aquarium récifal, permettant d'interagir avec un utilisateur. L'utilisateur peut poser une question, et en fonction de la nature de la question, le modèle peut soit répondre de façon textuelle, soit déclencher des actions dans l'application.",[11,851,853],{"id":852},"la-mise-en-place-de-lenvironnement","La mise en place de l'environnement",[16,855,856],{},"Si vous utilisez les APIs d'OpenAI, une fois votre compte créé, une clé d'API vous est fournie. Il va sans dire que cette clé est sensible, vous payez l'utilisation de l'API par token, si votre clé se retrouve exposée, elle peut être utilisée par un autre sur votre budget. Elle ne doit donc apparaitre nulle part, que ce soit dans le code de votre application compilée ou dans votre repo git.",[16,858,859,860,863],{},"Nous allons créer un petit serveur express qui traite les demandes de l'application. Je vous recommande donc de commencer par créer un fichier ",[143,861,862],{},".env"," à la racine du projet.",[865,866,870],"pre",{"className":867,"code":868,"language":869,"meta":329,"style":329},"language-bash shiki shiki-themes github-dark","# Clé d'api OpenAI\nOPENAI_API_KEY=sk-XXXXXXX\n","bash",[143,871,872,881],{"__ignoreMap":329},[873,874,877],"span",{"class":875,"line":876},"line",1,[873,878,880],{"class":879},"sAwPA","# Clé d'api OpenAI\n",[873,882,883,887,891],{"class":875,"line":330},[873,884,886],{"class":885},"s95oV","OPENAI_API_KEY",[873,888,890],{"class":889},"snl16","=",[873,892,894],{"class":893},"sU2Wk","sk-XXXXXXX\n",[16,896,897,898,901],{},"Ce fichier ne devant pas être commité, ajoutez un fichier ",[143,899,900],{},".gitignore"," à la racine du projet contenant :",[865,903,908],{"className":904,"code":906,"language":907,"meta":329},[905],"language-text",".env\n","text",[143,909,906],{"__ignoreMap":329},[16,911,912],{},"Une fois votre clé sécurisée, créez votre serveur express.",[865,914,918],{"className":915,"code":916,"language":917,"meta":329,"style":329},"language-javascript shiki shiki-themes github-dark","\u002F\u002F Load configuration from .env if the file exists\nconst dotenv = require(\"dotenv\");\ndotenv.config();\n\n\u002F\u002F Create the express server\nconst express = require(\"express\");\nconst app = express();\n\n\u002F\u002F Load OpenAI api\nconst OpenAI = require(\"openai\");\nconst openai = new OpenAI();\n\n\u002F\u002F Get a random number route\napp.use(\"\u002Frequest\", async function (req, res) {\n  const completion = await openai.chat.completions.create({\n    messages: [\n      {\n        role: \"user\",\n        content: \"Pick a number between 1 and 1000\",\n      },\n    ],\n    model: \"gpt-4o\",\n  });\n  let message = completion.choices[0].message;\n  res.send(message);\n});\n\n\u002F\u002F Start listening\napp.set(\"port\", process.env.PORT || 3000);\napp.listen(app.get(\"port\"), function () {\n  console.log(\"Listening to Port\", app.get(\"port\"));\n});\n","javascript",[143,919,920,925,950,961,967,973,992,1006,1011,1017,1036,1053,1058,1064,1102,1125,1131,1137,1149,1160,1166,1172,1183,1189,1209,1221,1227,1232,1238,1265,1292,1318],{"__ignoreMap":329},[873,921,922],{"class":875,"line":876},[873,923,924],{"class":879},"\u002F\u002F Load configuration from .env if the file exists\n",[873,926,927,930,934,937,941,944,947],{"class":875,"line":330},[873,928,929],{"class":889},"const",[873,931,933],{"class":932},"sDLfK"," dotenv",[873,935,936],{"class":889}," =",[873,938,940],{"class":939},"svObZ"," require",[873,942,943],{"class":885},"(",[873,945,946],{"class":893},"\"dotenv\"",[873,948,949],{"class":885},");\n",[873,951,952,955,958],{"class":875,"line":337},[873,953,954],{"class":885},"dotenv.",[873,956,957],{"class":939},"config",[873,959,960],{"class":885},"();\n",[873,962,964],{"class":875,"line":963},4,[873,965,966],{"emptyLinePlaceholder":347},"\n",[873,968,970],{"class":875,"line":969},5,[873,971,972],{"class":879},"\u002F\u002F Create the express server\n",[873,974,976,978,981,983,985,987,990],{"class":875,"line":975},6,[873,977,929],{"class":889},[873,979,980],{"class":932}," express",[873,982,936],{"class":889},[873,984,940],{"class":939},[873,986,943],{"class":885},[873,988,989],{"class":893},"\"express\"",[873,991,949],{"class":885},[873,993,995,997,1000,1002,1004],{"class":875,"line":994},7,[873,996,929],{"class":889},[873,998,999],{"class":932}," app",[873,1001,936],{"class":889},[873,1003,980],{"class":939},[873,1005,960],{"class":885},[873,1007,1009],{"class":875,"line":1008},8,[873,1010,966],{"emptyLinePlaceholder":347},[873,1012,1014],{"class":875,"line":1013},9,[873,1015,1016],{"class":879},"\u002F\u002F Load OpenAI api\n",[873,1018,1020,1022,1025,1027,1029,1031,1034],{"class":875,"line":1019},10,[873,1021,929],{"class":889},[873,1023,1024],{"class":932}," OpenAI",[873,1026,936],{"class":889},[873,1028,940],{"class":939},[873,1030,943],{"class":885},[873,1032,1033],{"class":893},"\"openai\"",[873,1035,949],{"class":885},[873,1037,1039,1041,1044,1046,1049,1051],{"class":875,"line":1038},11,[873,1040,929],{"class":889},[873,1042,1043],{"class":932}," openai",[873,1045,936],{"class":889},[873,1047,1048],{"class":889}," new",[873,1050,1024],{"class":939},[873,1052,960],{"class":885},[873,1054,1056],{"class":875,"line":1055},12,[873,1057,966],{"emptyLinePlaceholder":347},[873,1059,1061],{"class":875,"line":1060},13,[873,1062,1063],{"class":879},"\u002F\u002F Get a random number route\n",[873,1065,1067,1070,1073,1075,1078,1081,1084,1087,1090,1094,1096,1099],{"class":875,"line":1066},14,[873,1068,1069],{"class":885},"app.",[873,1071,1072],{"class":939},"use",[873,1074,943],{"class":885},[873,1076,1077],{"class":893},"\"\u002Frequest\"",[873,1079,1080],{"class":885},", ",[873,1082,1083],{"class":889},"async",[873,1085,1086],{"class":889}," function",[873,1088,1089],{"class":885}," (",[873,1091,1093],{"class":1092},"s9osk","req",[873,1095,1080],{"class":885},[873,1097,1098],{"class":1092},"res",[873,1100,1101],{"class":885},") {\n",[873,1103,1105,1108,1111,1113,1116,1119,1122],{"class":875,"line":1104},15,[873,1106,1107],{"class":889},"  const",[873,1109,1110],{"class":932}," completion",[873,1112,936],{"class":889},[873,1114,1115],{"class":889}," await",[873,1117,1118],{"class":885}," openai.chat.completions.",[873,1120,1121],{"class":939},"create",[873,1123,1124],{"class":885},"({\n",[873,1126,1128],{"class":875,"line":1127},16,[873,1129,1130],{"class":885},"    messages: [\n",[873,1132,1134],{"class":875,"line":1133},17,[873,1135,1136],{"class":885},"      {\n",[873,1138,1140,1143,1146],{"class":875,"line":1139},18,[873,1141,1142],{"class":885},"        role: ",[873,1144,1145],{"class":893},"\"user\"",[873,1147,1148],{"class":885},",\n",[873,1150,1152,1155,1158],{"class":875,"line":1151},19,[873,1153,1154],{"class":885},"        content: ",[873,1156,1157],{"class":893},"\"Pick a number between 1 and 1000\"",[873,1159,1148],{"class":885},[873,1161,1163],{"class":875,"line":1162},20,[873,1164,1165],{"class":885},"      },\n",[873,1167,1169],{"class":875,"line":1168},21,[873,1170,1171],{"class":885},"    ],\n",[873,1173,1175,1178,1181],{"class":875,"line":1174},22,[873,1176,1177],{"class":885},"    model: ",[873,1179,1180],{"class":893},"\"gpt-4o\"",[873,1182,1148],{"class":885},[873,1184,1186],{"class":875,"line":1185},23,[873,1187,1188],{"class":885},"  });\n",[873,1190,1192,1195,1198,1200,1203,1206],{"class":875,"line":1191},24,[873,1193,1194],{"class":889},"  let",[873,1196,1197],{"class":885}," message ",[873,1199,890],{"class":889},[873,1201,1202],{"class":885}," completion.choices[",[873,1204,1205],{"class":932},"0",[873,1207,1208],{"class":885},"].message;\n",[873,1210,1212,1215,1218],{"class":875,"line":1211},25,[873,1213,1214],{"class":885},"  res.",[873,1216,1217],{"class":939},"send",[873,1219,1220],{"class":885},"(message);\n",[873,1222,1224],{"class":875,"line":1223},26,[873,1225,1226],{"class":885},"});\n",[873,1228,1230],{"class":875,"line":1229},27,[873,1231,966],{"emptyLinePlaceholder":347},[873,1233,1235],{"class":875,"line":1234},28,[873,1236,1237],{"class":879},"\u002F\u002F Start listening\n",[873,1239,1241,1243,1246,1248,1251,1254,1257,1260,1263],{"class":875,"line":1240},29,[873,1242,1069],{"class":885},[873,1244,1245],{"class":939},"set",[873,1247,943],{"class":885},[873,1249,1250],{"class":893},"\"port\"",[873,1252,1253],{"class":885},", process.env.",[873,1255,1256],{"class":932},"PORT",[873,1258,1259],{"class":889}," ||",[873,1261,1262],{"class":932}," 3000",[873,1264,949],{"class":885},[873,1266,1268,1270,1273,1276,1279,1281,1283,1286,1289],{"class":875,"line":1267},30,[873,1269,1069],{"class":885},[873,1271,1272],{"class":939},"listen",[873,1274,1275],{"class":885},"(app.",[873,1277,1278],{"class":939},"get",[873,1280,943],{"class":885},[873,1282,1250],{"class":893},[873,1284,1285],{"class":885},"), ",[873,1287,1288],{"class":889},"function",[873,1290,1291],{"class":885}," () {\n",[873,1293,1295,1298,1301,1303,1306,1309,1311,1313,1315],{"class":875,"line":1294},31,[873,1296,1297],{"class":885},"  console.",[873,1299,1300],{"class":939},"log",[873,1302,943],{"class":885},[873,1304,1305],{"class":893},"\"Listening to Port\"",[873,1307,1308],{"class":885},", app.",[873,1310,1278],{"class":939},[873,1312,943],{"class":885},[873,1314,1250],{"class":893},[873,1316,1317],{"class":885},"));\n",[873,1319,1321],{"class":875,"line":1320},32,[873,1322,1226],{"class":885},[16,1324,1325,1326,1329],{},"On lance le serveur, et on appelle la route ",[143,1327,1328],{},"\u002Frequest",", voici la réponse :",[865,1331,1335],{"className":1332,"code":1333,"language":1334,"meta":329,"style":329},"language-json shiki shiki-themes github-dark","{\n  \"role\": \"assistant\",\n  \"content\": \"Sure, let's go with the number 387.\"\n}\n","json",[143,1336,1337,1342,1355,1365],{"__ignoreMap":329},[873,1338,1339],{"class":875,"line":876},[873,1340,1341],{"class":885},"{\n",[873,1343,1344,1347,1350,1353],{"class":875,"line":330},[873,1345,1346],{"class":932},"  \"role\"",[873,1348,1349],{"class":885},": ",[873,1351,1352],{"class":893},"\"assistant\"",[873,1354,1148],{"class":885},[873,1356,1357,1360,1362],{"class":875,"line":337},[873,1358,1359],{"class":932},"  \"content\"",[873,1361,1349],{"class":885},[873,1363,1364],{"class":893},"\"Sure, let's go with the number 387.\"\n",[873,1366,1367],{"class":875,"line":963},[873,1368,1369],{"class":885},"}\n",[16,1371,1372],{},"Deuxième essai :",[865,1374,1376],{"className":1332,"code":1375,"language":1334,"meta":329,"style":329},"{\n  \"role\": \"assistant\",\n  \"content\": \"Sure! How about 427?\"\n}\n",[143,1377,1378,1382,1392,1401],{"__ignoreMap":329},[873,1379,1380],{"class":875,"line":876},[873,1381,1341],{"class":885},[873,1383,1384,1386,1388,1390],{"class":875,"line":330},[873,1385,1346],{"class":932},[873,1387,1349],{"class":885},[873,1389,1352],{"class":893},[873,1391,1148],{"class":885},[873,1393,1394,1396,1398],{"class":875,"line":337},[873,1395,1359],{"class":932},[873,1397,1349],{"class":885},[873,1399,1400],{"class":893},"\"Sure! How about 427?\"\n",[873,1402,1403],{"class":875,"line":963},[873,1404,1369],{"class":885},[16,1406,1407],{},"On pourrait récupérer le nombre avec une regexp, mais nous allons utiliser une fonction.",[11,1409,1411],{"id":1410},"utilisation-dune-fonction","Utilisation d'une fonction",[16,1413,1414,1415,1418,1419,1421],{},"Les fonctions sont disponibles dans les modèles gpt-4o, gpt-4-turbo et gpt-3.5-turbo. Pour déclarer une fonction, il faut utiliser l'attribut ",[143,1416,1417],{},"tools"," de l'API de complétion. Pour le moment, ",[143,1420,1417],{}," ne supporte que les fonctions.",[16,1423,1424],{},"Nous modifions l'appel à l'API d'OpenAI de la façon suivante :",[865,1426,1428],{"className":915,"code":1427,"language":917,"meta":329,"style":329},"const completion = await openai.chat.completions.create({\n  messages: [\n    {\n      role: \"system\",\n      content: \"To give a number to the user, use the sendNumber function\",\n    },\n    {\n      role: \"user\",\n      content: \"Pick a number between 1 and 1000\",\n    },\n  ],\n  model: \"gpt-4o\",\n  tools: [\n    {\n      type: \"function\",\n      function: {\n        name: \"sendNumber\",\n        description: \"send a number to the user\",\n        parameters: {\n          type: \"object\",\n          properties: {\n            randomNumber: {\n              type: \"number\",\n            },\n          },\n        },\n      },\n    },\n  ],\n});\nlet message = completion.choices[0].message;\nres.send(message);\n",[143,1429,1430,1446,1451,1456,1466,1476,1481,1485,1493,1501,1505,1510,1519,1524,1528,1538,1543,1553,1563,1568,1578,1583,1588,1598,1603,1608,1613,1617,1621,1625,1629,1644],{"__ignoreMap":329},[873,1431,1432,1434,1436,1438,1440,1442,1444],{"class":875,"line":876},[873,1433,929],{"class":889},[873,1435,1110],{"class":932},[873,1437,936],{"class":889},[873,1439,1115],{"class":889},[873,1441,1118],{"class":885},[873,1443,1121],{"class":939},[873,1445,1124],{"class":885},[873,1447,1448],{"class":875,"line":330},[873,1449,1450],{"class":885},"  messages: [\n",[873,1452,1453],{"class":875,"line":337},[873,1454,1455],{"class":885},"    {\n",[873,1457,1458,1461,1464],{"class":875,"line":963},[873,1459,1460],{"class":885},"      role: ",[873,1462,1463],{"class":893},"\"system\"",[873,1465,1148],{"class":885},[873,1467,1468,1471,1474],{"class":875,"line":969},[873,1469,1470],{"class":885},"      content: ",[873,1472,1473],{"class":893},"\"To give a number to the user, use the sendNumber function\"",[873,1475,1148],{"class":885},[873,1477,1478],{"class":875,"line":975},[873,1479,1480],{"class":885},"    },\n",[873,1482,1483],{"class":875,"line":994},[873,1484,1455],{"class":885},[873,1486,1487,1489,1491],{"class":875,"line":1008},[873,1488,1460],{"class":885},[873,1490,1145],{"class":893},[873,1492,1148],{"class":885},[873,1494,1495,1497,1499],{"class":875,"line":1013},[873,1496,1470],{"class":885},[873,1498,1157],{"class":893},[873,1500,1148],{"class":885},[873,1502,1503],{"class":875,"line":1019},[873,1504,1480],{"class":885},[873,1506,1507],{"class":875,"line":1038},[873,1508,1509],{"class":885},"  ],\n",[873,1511,1512,1515,1517],{"class":875,"line":1055},[873,1513,1514],{"class":885},"  model: ",[873,1516,1180],{"class":893},[873,1518,1148],{"class":885},[873,1520,1521],{"class":875,"line":1060},[873,1522,1523],{"class":885},"  tools: [\n",[873,1525,1526],{"class":875,"line":1066},[873,1527,1455],{"class":885},[873,1529,1530,1533,1536],{"class":875,"line":1104},[873,1531,1532],{"class":885},"      type: ",[873,1534,1535],{"class":893},"\"function\"",[873,1537,1148],{"class":885},[873,1539,1540],{"class":875,"line":1127},[873,1541,1542],{"class":885},"      function: {\n",[873,1544,1545,1548,1551],{"class":875,"line":1133},[873,1546,1547],{"class":885},"        name: ",[873,1549,1550],{"class":893},"\"sendNumber\"",[873,1552,1148],{"class":885},[873,1554,1555,1558,1561],{"class":875,"line":1139},[873,1556,1557],{"class":885},"        description: ",[873,1559,1560],{"class":893},"\"send a number to the user\"",[873,1562,1148],{"class":885},[873,1564,1565],{"class":875,"line":1151},[873,1566,1567],{"class":885},"        parameters: {\n",[873,1569,1570,1573,1576],{"class":875,"line":1162},[873,1571,1572],{"class":885},"          type: ",[873,1574,1575],{"class":893},"\"object\"",[873,1577,1148],{"class":885},[873,1579,1580],{"class":875,"line":1168},[873,1581,1582],{"class":885},"          properties: {\n",[873,1584,1585],{"class":875,"line":1174},[873,1586,1587],{"class":885},"            randomNumber: {\n",[873,1589,1590,1593,1596],{"class":875,"line":1185},[873,1591,1592],{"class":885},"              type: ",[873,1594,1595],{"class":893},"\"number\"",[873,1597,1148],{"class":885},[873,1599,1600],{"class":875,"line":1191},[873,1601,1602],{"class":885},"            },\n",[873,1604,1605],{"class":875,"line":1211},[873,1606,1607],{"class":885},"          },\n",[873,1609,1610],{"class":875,"line":1223},[873,1611,1612],{"class":885},"        },\n",[873,1614,1615],{"class":875,"line":1229},[873,1616,1165],{"class":885},[873,1618,1619],{"class":875,"line":1234},[873,1620,1480],{"class":885},[873,1622,1623],{"class":875,"line":1240},[873,1624,1509],{"class":885},[873,1626,1627],{"class":875,"line":1267},[873,1628,1226],{"class":885},[873,1630,1631,1634,1636,1638,1640,1642],{"class":875,"line":1294},[873,1632,1633],{"class":889},"let",[873,1635,1197],{"class":885},[873,1637,890],{"class":889},[873,1639,1202],{"class":885},[873,1641,1205],{"class":932},[873,1643,1208],{"class":885},[873,1645,1646,1649,1651],{"class":875,"line":1320},[873,1647,1648],{"class":885},"res.",[873,1650,1217],{"class":939},[873,1652,1220],{"class":885},[16,1654,1655],{},"Réponse reçue :",[865,1657,1659],{"className":1332,"code":1658,"language":1334,"meta":329,"style":329},"{\n  \"role\": \"assistant\",\n  \"content\": null,\n  \"tool_calls\": [\n    {\n      \"id\": \"call_NkR3eNRohcWiYWCZBWhVem3a\",\n      \"type\": \"function\",\n      \"function\": {\n        \"name\": \"sendNumber\",\n        \"arguments\": \"{\\\"randomNumber\\\":354}\"\n      }\n    }\n  ]\n}\n",[143,1660,1661,1665,1675,1686,1694,1698,1710,1721,1729,1740,1761,1766,1771,1776],{"__ignoreMap":329},[873,1662,1663],{"class":875,"line":876},[873,1664,1341],{"class":885},[873,1666,1667,1669,1671,1673],{"class":875,"line":330},[873,1668,1346],{"class":932},[873,1670,1349],{"class":885},[873,1672,1352],{"class":893},[873,1674,1148],{"class":885},[873,1676,1677,1679,1681,1684],{"class":875,"line":337},[873,1678,1359],{"class":932},[873,1680,1349],{"class":885},[873,1682,1683],{"class":932},"null",[873,1685,1148],{"class":885},[873,1687,1688,1691],{"class":875,"line":963},[873,1689,1690],{"class":932},"  \"tool_calls\"",[873,1692,1693],{"class":885},": [\n",[873,1695,1696],{"class":875,"line":969},[873,1697,1455],{"class":885},[873,1699,1700,1703,1705,1708],{"class":875,"line":975},[873,1701,1702],{"class":932},"      \"id\"",[873,1704,1349],{"class":885},[873,1706,1707],{"class":893},"\"call_NkR3eNRohcWiYWCZBWhVem3a\"",[873,1709,1148],{"class":885},[873,1711,1712,1715,1717,1719],{"class":875,"line":994},[873,1713,1714],{"class":932},"      \"type\"",[873,1716,1349],{"class":885},[873,1718,1535],{"class":893},[873,1720,1148],{"class":885},[873,1722,1723,1726],{"class":875,"line":1008},[873,1724,1725],{"class":932},"      \"function\"",[873,1727,1728],{"class":885},": {\n",[873,1730,1731,1734,1736,1738],{"class":875,"line":1013},[873,1732,1733],{"class":932},"        \"name\"",[873,1735,1349],{"class":885},[873,1737,1550],{"class":893},[873,1739,1148],{"class":885},[873,1741,1742,1745,1747,1750,1753,1756,1758],{"class":875,"line":1019},[873,1743,1744],{"class":932},"        \"arguments\"",[873,1746,1349],{"class":885},[873,1748,1749],{"class":893},"\"{",[873,1751,1752],{"class":932},"\\\"",[873,1754,1755],{"class":893},"randomNumber",[873,1757,1752],{"class":932},[873,1759,1760],{"class":893},":354}\"\n",[873,1762,1763],{"class":875,"line":1038},[873,1764,1765],{"class":885},"      }\n",[873,1767,1768],{"class":875,"line":1055},[873,1769,1770],{"class":885},"    }\n",[873,1772,1773],{"class":875,"line":1060},[873,1774,1775],{"class":885},"  ]\n",[873,1777,1778],{"class":875,"line":1066},[873,1779,1369],{"class":885},[16,1781,1782],{},"Nous avons modifié deux choses :",[381,1784,1785,1802],{},[384,1786,1787,1788,1791,1792,1795,1796,1798,1799],{},"Nous avons déclaré une ",[58,1789,1790],{},"fonction"," lors de l'appel utilisable par le modèle. Cette fonction a un nom, peut avoir une description (ce qui aide le modèle à choisir la fonction dans certains cas), et des paramètres. Le type du paramètre est toujours ",[143,1793,1794],{},"object"," et contient des attributs, ici ",[143,1797,1755],{}," de type ",[143,1800,1801],{},"number",[384,1803,1804,1805,1808,1809,1812],{},"Nous avons ajouté un ",[58,1806,1807],{},"prompt système"," indiquant au modèle d'utiliser la fonction ",[143,1810,1811],{},"sendNumber"," lorsqu'il a besoin d'envoyer un nombre à l'utilisateur.",[16,1814,1815,1816,1819,1820,1822],{},"Dans la réponse, l'attribut ",[143,1817,1818],{},"content"," est ",[143,1821,1683],{},", et nous avons un appel de fonction fait par le modèle qui indique quelle fonction appeler et avec quels arguments. On peut récupérer cela :",[865,1824,1826],{"className":915,"code":1825,"language":917,"meta":329,"style":329},"let message = completion.choices[0].message;\nif (message.tool_calls) {\n  let fcall = message.tool_calls.find(\n    (tc) => tc.type === \"function\" && tc.function.name === \"sendNumber\"\n  );\n  let json = JSON.parse(fcall.function.arguments);\n  res.send({ n: json.randomNumber });\n}\n",[143,1827,1828,1842,1850,1868,1902,1907,1927,1936],{"__ignoreMap":329},[873,1829,1830,1832,1834,1836,1838,1840],{"class":875,"line":876},[873,1831,1633],{"class":889},[873,1833,1197],{"class":885},[873,1835,890],{"class":889},[873,1837,1202],{"class":885},[873,1839,1205],{"class":932},[873,1841,1208],{"class":885},[873,1843,1844,1847],{"class":875,"line":330},[873,1845,1846],{"class":889},"if",[873,1848,1849],{"class":885}," (message.tool_calls) {\n",[873,1851,1852,1854,1857,1859,1862,1865],{"class":875,"line":337},[873,1853,1194],{"class":889},[873,1855,1856],{"class":885}," fcall ",[873,1858,890],{"class":889},[873,1860,1861],{"class":885}," message.tool_calls.",[873,1863,1864],{"class":939},"find",[873,1866,1867],{"class":885},"(\n",[873,1869,1870,1873,1876,1879,1882,1885,1888,1891,1894,1897,1899],{"class":875,"line":963},[873,1871,1872],{"class":885},"    (",[873,1874,1875],{"class":1092},"tc",[873,1877,1878],{"class":885},") ",[873,1880,1881],{"class":889},"=>",[873,1883,1884],{"class":885}," tc.type ",[873,1886,1887],{"class":889},"===",[873,1889,1890],{"class":893}," \"function\"",[873,1892,1893],{"class":889}," &&",[873,1895,1896],{"class":885}," tc.function.name ",[873,1898,1887],{"class":889},[873,1900,1901],{"class":893}," \"sendNumber\"\n",[873,1903,1904],{"class":875,"line":969},[873,1905,1906],{"class":885},"  );\n",[873,1908,1909,1911,1914,1916,1919,1921,1924],{"class":875,"line":975},[873,1910,1194],{"class":889},[873,1912,1913],{"class":885}," json ",[873,1915,890],{"class":889},[873,1917,1918],{"class":932}," JSON",[873,1920,182],{"class":885},[873,1922,1923],{"class":939},"parse",[873,1925,1926],{"class":885},"(fcall.function.arguments);\n",[873,1928,1929,1931,1933],{"class":875,"line":994},[873,1930,1214],{"class":885},[873,1932,1217],{"class":939},[873,1934,1935],{"class":885},"({ n: json.randomNumber });\n",[873,1937,1938],{"class":875,"line":1008},[873,1939,1369],{"class":885},[16,1941,1942],{},"Ainsi nous récupérons bien le nombre attendu et nous pouvons l'utiliser dans notre application.",[11,1944,1946],{"id":1945},"amusons-nous-un-peu","Amusons-nous un peu",[16,1948,1949],{},"Modifions le code précédent pour demander maintenant à ChatGPT de donner des caractéristiques intéressantes du nombre qu'il a choisi :",[865,1951,1953],{"className":915,"code":1952,"language":917,"meta":329,"style":329},"app.use(\"\u002Fnumber\", async function (req, res) {\n  const completion = await openai.chat.completions.create({\n    messages: [\n      {\n        role: \"system\",\n        content: \"To give a number to the user, use the sendNumber function\",\n      },\n      {\n        role: \"user\",\n        content:\n          \"Give me a random number between 1 and 1000 and explain why it's special\",\n      },\n    ],\n    model: \"gpt-4o\",\n    tools: [\n      {\n        type: \"function\",\n        function: {\n          name: \"sendNumber\",\n          description: \"send a number to the user\",\n          parameters: {\n            type: \"object\",\n            properties: {\n              randomNumber: {\n                type: \"number\",\n              },\n              reason: {\n                type: \"string\",\n              },\n            },\n          },\n        },\n      },\n    ],\n  });\n  let message = completion.choices[0].message;\n  if (message.tool_calls) {\n    let fcall = message.tool_calls.find(\n      (tc) => tc.type === \"function\" && tc.function.name === \"sendNumber\"\n    );\n    let json = JSON.parse(fcall.function.arguments);\n    res.send(json);\n  } else {\n    res.send({ error: \"no function call\" });\n  }\n});\n",[143,1954,1955,1982,1998,2002,2006,2014,2022,2026,2030,2038,2043,2050,2054,2058,2066,2071,2075,2084,2089,2098,2107,2112,2121,2126,2131,2140,2145,2150,2159,2163,2167,2171,2175,2180,2185,2190,2205,2213,2229,2255,2261,2278,2289,2301,2317,2323],{"__ignoreMap":329},[873,1956,1957,1959,1961,1963,1966,1968,1970,1972,1974,1976,1978,1980],{"class":875,"line":876},[873,1958,1069],{"class":885},[873,1960,1072],{"class":939},[873,1962,943],{"class":885},[873,1964,1965],{"class":893},"\"\u002Fnumber\"",[873,1967,1080],{"class":885},[873,1969,1083],{"class":889},[873,1971,1086],{"class":889},[873,1973,1089],{"class":885},[873,1975,1093],{"class":1092},[873,1977,1080],{"class":885},[873,1979,1098],{"class":1092},[873,1981,1101],{"class":885},[873,1983,1984,1986,1988,1990,1992,1994,1996],{"class":875,"line":330},[873,1985,1107],{"class":889},[873,1987,1110],{"class":932},[873,1989,936],{"class":889},[873,1991,1115],{"class":889},[873,1993,1118],{"class":885},[873,1995,1121],{"class":939},[873,1997,1124],{"class":885},[873,1999,2000],{"class":875,"line":337},[873,2001,1130],{"class":885},[873,2003,2004],{"class":875,"line":963},[873,2005,1136],{"class":885},[873,2007,2008,2010,2012],{"class":875,"line":969},[873,2009,1142],{"class":885},[873,2011,1463],{"class":893},[873,2013,1148],{"class":885},[873,2015,2016,2018,2020],{"class":875,"line":975},[873,2017,1154],{"class":885},[873,2019,1473],{"class":893},[873,2021,1148],{"class":885},[873,2023,2024],{"class":875,"line":994},[873,2025,1165],{"class":885},[873,2027,2028],{"class":875,"line":1008},[873,2029,1136],{"class":885},[873,2031,2032,2034,2036],{"class":875,"line":1013},[873,2033,1142],{"class":885},[873,2035,1145],{"class":893},[873,2037,1148],{"class":885},[873,2039,2040],{"class":875,"line":1019},[873,2041,2042],{"class":885},"        content:\n",[873,2044,2045,2048],{"class":875,"line":1038},[873,2046,2047],{"class":893},"          \"Give me a random number between 1 and 1000 and explain why it's special\"",[873,2049,1148],{"class":885},[873,2051,2052],{"class":875,"line":1055},[873,2053,1165],{"class":885},[873,2055,2056],{"class":875,"line":1060},[873,2057,1171],{"class":885},[873,2059,2060,2062,2064],{"class":875,"line":1066},[873,2061,1177],{"class":885},[873,2063,1180],{"class":893},[873,2065,1148],{"class":885},[873,2067,2068],{"class":875,"line":1104},[873,2069,2070],{"class":885},"    tools: [\n",[873,2072,2073],{"class":875,"line":1127},[873,2074,1136],{"class":885},[873,2076,2077,2080,2082],{"class":875,"line":1133},[873,2078,2079],{"class":885},"        type: ",[873,2081,1535],{"class":893},[873,2083,1148],{"class":885},[873,2085,2086],{"class":875,"line":1139},[873,2087,2088],{"class":885},"        function: {\n",[873,2090,2091,2094,2096],{"class":875,"line":1151},[873,2092,2093],{"class":885},"          name: ",[873,2095,1550],{"class":893},[873,2097,1148],{"class":885},[873,2099,2100,2103,2105],{"class":875,"line":1162},[873,2101,2102],{"class":885},"          description: ",[873,2104,1560],{"class":893},[873,2106,1148],{"class":885},[873,2108,2109],{"class":875,"line":1168},[873,2110,2111],{"class":885},"          parameters: {\n",[873,2113,2114,2117,2119],{"class":875,"line":1174},[873,2115,2116],{"class":885},"            type: ",[873,2118,1575],{"class":893},[873,2120,1148],{"class":885},[873,2122,2123],{"class":875,"line":1185},[873,2124,2125],{"class":885},"            properties: {\n",[873,2127,2128],{"class":875,"line":1191},[873,2129,2130],{"class":885},"              randomNumber: {\n",[873,2132,2133,2136,2138],{"class":875,"line":1211},[873,2134,2135],{"class":885},"                type: ",[873,2137,1595],{"class":893},[873,2139,1148],{"class":885},[873,2141,2142],{"class":875,"line":1223},[873,2143,2144],{"class":885},"              },\n",[873,2146,2147],{"class":875,"line":1229},[873,2148,2149],{"class":885},"              reason: {\n",[873,2151,2152,2154,2157],{"class":875,"line":1234},[873,2153,2135],{"class":885},[873,2155,2156],{"class":893},"\"string\"",[873,2158,1148],{"class":885},[873,2160,2161],{"class":875,"line":1240},[873,2162,2144],{"class":885},[873,2164,2165],{"class":875,"line":1267},[873,2166,1602],{"class":885},[873,2168,2169],{"class":875,"line":1294},[873,2170,1607],{"class":885},[873,2172,2173],{"class":875,"line":1320},[873,2174,1612],{"class":885},[873,2176,2178],{"class":875,"line":2177},33,[873,2179,1165],{"class":885},[873,2181,2183],{"class":875,"line":2182},34,[873,2184,1171],{"class":885},[873,2186,2188],{"class":875,"line":2187},35,[873,2189,1188],{"class":885},[873,2191,2193,2195,2197,2199,2201,2203],{"class":875,"line":2192},36,[873,2194,1194],{"class":889},[873,2196,1197],{"class":885},[873,2198,890],{"class":889},[873,2200,1202],{"class":885},[873,2202,1205],{"class":932},[873,2204,1208],{"class":885},[873,2206,2208,2211],{"class":875,"line":2207},37,[873,2209,2210],{"class":889},"  if",[873,2212,1849],{"class":885},[873,2214,2216,2219,2221,2223,2225,2227],{"class":875,"line":2215},38,[873,2217,2218],{"class":889},"    let",[873,2220,1856],{"class":885},[873,2222,890],{"class":889},[873,2224,1861],{"class":885},[873,2226,1864],{"class":939},[873,2228,1867],{"class":885},[873,2230,2232,2235,2237,2239,2241,2243,2245,2247,2249,2251,2253],{"class":875,"line":2231},39,[873,2233,2234],{"class":885},"      (",[873,2236,1875],{"class":1092},[873,2238,1878],{"class":885},[873,2240,1881],{"class":889},[873,2242,1884],{"class":885},[873,2244,1887],{"class":889},[873,2246,1890],{"class":893},[873,2248,1893],{"class":889},[873,2250,1896],{"class":885},[873,2252,1887],{"class":889},[873,2254,1901],{"class":893},[873,2256,2258],{"class":875,"line":2257},40,[873,2259,2260],{"class":885},"    );\n",[873,2262,2264,2266,2268,2270,2272,2274,2276],{"class":875,"line":2263},41,[873,2265,2218],{"class":889},[873,2267,1913],{"class":885},[873,2269,890],{"class":889},[873,2271,1918],{"class":932},[873,2273,182],{"class":885},[873,2275,1923],{"class":939},[873,2277,1926],{"class":885},[873,2279,2281,2284,2286],{"class":875,"line":2280},42,[873,2282,2283],{"class":885},"    res.",[873,2285,1217],{"class":939},[873,2287,2288],{"class":885},"(json);\n",[873,2290,2292,2295,2298],{"class":875,"line":2291},43,[873,2293,2294],{"class":885},"  } ",[873,2296,2297],{"class":889},"else",[873,2299,2300],{"class":885}," {\n",[873,2302,2304,2306,2308,2311,2314],{"class":875,"line":2303},44,[873,2305,2283],{"class":885},[873,2307,1217],{"class":939},[873,2309,2310],{"class":885},"({ error: ",[873,2312,2313],{"class":893},"\"no function call\"",[873,2315,2316],{"class":885}," });\n",[873,2318,2320],{"class":875,"line":2319},45,[873,2321,2322],{"class":885},"  }\n",[873,2324,2326],{"class":875,"line":2325},46,[873,2327,1226],{"class":885},[16,2329,2330,2331,2334],{},"La fonction que le modèle va appeler a maintenant deux paramètres, le nombre et une chaine de caractères dans l'attribut ",[143,2332,2333],{},"reason",". Le prompt a été modifié pour demander au modèle d'expliquer pourquoi le nombre choisi est spécial.",[16,2336,2337],{},"Voici quelques réponses fournies :",[865,2339,2341],{"className":1332,"code":2340,"language":1334,"meta":329,"style":329},"[\n  {\n    \"randomNumber\": 622,\n    \"reason\": \"622 could be considered special because it is the number of resolutions (effective resolutions) passed by the United Nations General Assembly as of the end of 2020.\"\n  },\n  {\n    \"randomNumber\": 729,\n    \"reason\": \"It's the smallest number that can be expressed as the seventh power of another integer, specifically, 3^6.\"\n  },\n  {\n    \"randomNumber\": 317,\n    \"reason\": \"The number 317 is special because it's a prime number, which means it is greater than 1 and only divisible by 1 and itself.\"\n  }\n]\n",[143,2342,2343,2348,2353,2365,2375,2380,2384,2395,2404,2408,2412,2423,2432,2436],{"__ignoreMap":329},[873,2344,2345],{"class":875,"line":876},[873,2346,2347],{"class":885},"[\n",[873,2349,2350],{"class":875,"line":330},[873,2351,2352],{"class":885},"  {\n",[873,2354,2355,2358,2360,2363],{"class":875,"line":337},[873,2356,2357],{"class":932},"    \"randomNumber\"",[873,2359,1349],{"class":885},[873,2361,2362],{"class":932},"622",[873,2364,1148],{"class":885},[873,2366,2367,2370,2372],{"class":875,"line":963},[873,2368,2369],{"class":932},"    \"reason\"",[873,2371,1349],{"class":885},[873,2373,2374],{"class":893},"\"622 could be considered special because it is the number of resolutions (effective resolutions) passed by the United Nations General Assembly as of the end of 2020.\"\n",[873,2376,2377],{"class":875,"line":969},[873,2378,2379],{"class":885},"  },\n",[873,2381,2382],{"class":875,"line":975},[873,2383,2352],{"class":885},[873,2385,2386,2388,2390,2393],{"class":875,"line":994},[873,2387,2357],{"class":932},[873,2389,1349],{"class":885},[873,2391,2392],{"class":932},"729",[873,2394,1148],{"class":885},[873,2396,2397,2399,2401],{"class":875,"line":1008},[873,2398,2369],{"class":932},[873,2400,1349],{"class":885},[873,2402,2403],{"class":893},"\"It's the smallest number that can be expressed as the seventh power of another integer, specifically, 3^6.\"\n",[873,2405,2406],{"class":875,"line":1013},[873,2407,2379],{"class":885},[873,2409,2410],{"class":875,"line":1019},[873,2411,2352],{"class":885},[873,2413,2414,2416,2418,2421],{"class":875,"line":1038},[873,2415,2357],{"class":932},[873,2417,1349],{"class":885},[873,2419,2420],{"class":932},"317",[873,2422,1148],{"class":885},[873,2424,2425,2427,2429],{"class":875,"line":1055},[873,2426,2369],{"class":932},[873,2428,1349],{"class":885},[873,2430,2431],{"class":893},"\"The number 317 is special because it's a prime number, which means it is greater than 1 and only divisible by 1 and itself.\"\n",[873,2433,2434],{"class":875,"line":1060},[873,2435,2322],{"class":885},[873,2437,2438],{"class":875,"line":1066},[873,2439,2440],{"class":885},"]\n",[16,2442,2443],{},"On pourrait utiliser ces informations pour avoir des fun facts sur un nombre aléatoire dans une application.",[11,2445,2447],{"id":2446},"revenons-à-notre-projet","Revenons à notre projet",[16,2449,2450],{},"L'application Blue permet de faciliter la gestion quotidienne d'un aquarium et propose entre autres :",[381,2452,2453,2460,2466,2473],{},[384,2454,2455,2456,2459],{},"Une fonction de ",[58,2457,2458],{},"prise de mesures"," permettant d'enregistrer au jour le jour la température de l'eau, le pH, le taux de calcium, de magnésium, de phosphates...",[384,2461,2455,2462,2465],{},[58,2463,2464],{},"changement d'eau"," permettant de calculer la quantité de sel à ajouter à l'eau que l'on prépare pour remplacer une partie de l'eau de l'aquarium",[384,2467,2468,2469,2472],{},"Une fonction d'",[58,2470,2471],{},"ajout de produit"," permettant de calculer la quantité d'un produit à ajouter pour augmenter la concentration de calcium par exemple",[384,2474,2455,2475,2478],{},[58,2476,2477],{},"gestion de tâches"," ponctuelles ou récurrentes",[16,2480,2481],{},"Et bien d'autres possibilités, mais on va se concentrer sur celles-là pour le moment.",[16,2483,2484],{},"Je souhaite que pour chaque besoin que l'application peut remplir, le modèle ait accès à une ou plusieurs fonctions lui permettant d'effectuer des actions.",[110,2486,2488],{"id":2487},"la-prise-de-mesure-automatique","La prise de mesure automatique",[16,2490,2491],{},"Voici la fonction définie pour la prise de mesure :",[865,2493,2495],{"className":1332,"code":2494,"language":1334,"meta":329,"style":329},"{\n  \"name\": \"takeMeasure\",\n  \"description\": \"Enregistre une mesure prise\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"element\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"calcium\", \"ph\", \"temperature\",\n          \"magnesium\", \"alkalinity\", \"phosphates\"\n        ]\n      },\n      \"value\": { \"type\": \"number\" },\n      \"unit\": {\n        \"type\": \"string\",\n        \"enum\": [\"mg\u002Fl\", \"ppm\", \"\", \"celcius\", \"farenheit\", \"dkh\", \"μg\u002Fl\"]\n      }\n    }\n  }\n}\n",[143,2496,2497,2501,2513,2525,2532,2543,2550,2557,2568,2575,2592,2607,2612,2616,2634,2641,2651,2693,2697,2701,2705],{"__ignoreMap":329},[873,2498,2499],{"class":875,"line":876},[873,2500,1341],{"class":885},[873,2502,2503,2506,2508,2511],{"class":875,"line":330},[873,2504,2505],{"class":932},"  \"name\"",[873,2507,1349],{"class":885},[873,2509,2510],{"class":893},"\"takeMeasure\"",[873,2512,1148],{"class":885},[873,2514,2515,2518,2520,2523],{"class":875,"line":337},[873,2516,2517],{"class":932},"  \"description\"",[873,2519,1349],{"class":885},[873,2521,2522],{"class":893},"\"Enregistre une mesure prise\"",[873,2524,1148],{"class":885},[873,2526,2527,2530],{"class":875,"line":963},[873,2528,2529],{"class":932},"  \"parameters\"",[873,2531,1728],{"class":885},[873,2533,2534,2537,2539,2541],{"class":875,"line":969},[873,2535,2536],{"class":932},"    \"type\"",[873,2538,1349],{"class":885},[873,2540,1575],{"class":893},[873,2542,1148],{"class":885},[873,2544,2545,2548],{"class":875,"line":975},[873,2546,2547],{"class":932},"    \"properties\"",[873,2549,1728],{"class":885},[873,2551,2552,2555],{"class":875,"line":994},[873,2553,2554],{"class":932},"      \"element\"",[873,2556,1728],{"class":885},[873,2558,2559,2562,2564,2566],{"class":875,"line":1008},[873,2560,2561],{"class":932},"        \"type\"",[873,2563,1349],{"class":885},[873,2565,2156],{"class":893},[873,2567,1148],{"class":885},[873,2569,2570,2573],{"class":875,"line":1013},[873,2571,2572],{"class":932},"        \"enum\"",[873,2574,1693],{"class":885},[873,2576,2577,2580,2582,2585,2587,2590],{"class":875,"line":1019},[873,2578,2579],{"class":893},"          \"calcium\"",[873,2581,1080],{"class":885},[873,2583,2584],{"class":893},"\"ph\"",[873,2586,1080],{"class":885},[873,2588,2589],{"class":893},"\"temperature\"",[873,2591,1148],{"class":885},[873,2593,2594,2597,2599,2602,2604],{"class":875,"line":1038},[873,2595,2596],{"class":893},"          \"magnesium\"",[873,2598,1080],{"class":885},[873,2600,2601],{"class":893},"\"alkalinity\"",[873,2603,1080],{"class":885},[873,2605,2606],{"class":893},"\"phosphates\"\n",[873,2608,2609],{"class":875,"line":1055},[873,2610,2611],{"class":885},"        ]\n",[873,2613,2614],{"class":875,"line":1060},[873,2615,1165],{"class":885},[873,2617,2618,2621,2624,2627,2629,2631],{"class":875,"line":1066},[873,2619,2620],{"class":932},"      \"value\"",[873,2622,2623],{"class":885},": { ",[873,2625,2626],{"class":932},"\"type\"",[873,2628,1349],{"class":885},[873,2630,1595],{"class":893},[873,2632,2633],{"class":885}," },\n",[873,2635,2636,2639],{"class":875,"line":1104},[873,2637,2638],{"class":932},"      \"unit\"",[873,2640,1728],{"class":885},[873,2642,2643,2645,2647,2649],{"class":875,"line":1127},[873,2644,2561],{"class":932},[873,2646,1349],{"class":885},[873,2648,2156],{"class":893},[873,2650,1148],{"class":885},[873,2652,2653,2655,2658,2661,2663,2666,2668,2671,2673,2676,2678,2681,2683,2686,2688,2691],{"class":875,"line":1133},[873,2654,2572],{"class":932},[873,2656,2657],{"class":885},": [",[873,2659,2660],{"class":893},"\"mg\u002Fl\"",[873,2662,1080],{"class":885},[873,2664,2665],{"class":893},"\"ppm\"",[873,2667,1080],{"class":885},[873,2669,2670],{"class":893},"\"\"",[873,2672,1080],{"class":885},[873,2674,2675],{"class":893},"\"celcius\"",[873,2677,1080],{"class":885},[873,2679,2680],{"class":893},"\"farenheit\"",[873,2682,1080],{"class":885},[873,2684,2685],{"class":893},"\"dkh\"",[873,2687,1080],{"class":885},[873,2689,2690],{"class":893},"\"μg\u002Fl\"",[873,2692,2440],{"class":885},[873,2694,2695],{"class":875,"line":1139},[873,2696,1765],{"class":885},[873,2698,2699],{"class":875,"line":1151},[873,2700,1770],{"class":885},[873,2702,2703],{"class":875,"line":1162},[873,2704,2322],{"class":885},[873,2706,2707],{"class":875,"line":1168},[873,2708,1369],{"class":885},[16,2710,2711],{},"Je modifie le code pour indiquer que la question utilisateur est envoyée dans la requête, ce qui me permet de faire les tests plus simplement. La requête devient une requête POST avec un body :",[865,2713,2715],{"className":915,"code":2714,"language":917,"meta":329,"style":329},"app.use(\"\u002Fblue\", async function (req, res) {\n  let body = req.body;\n  const completion = await openai.chat.completions.create({\n    messages: [\n      {\n        role: \"system\",\n        content:\n          \"Tu as accès à certaines fonctionnalités d'une application mobile de gestion d'aquarium récifal. Utilise les fonctions fournies pour répondre et interagir avec l'utilisateur. L'aquarium de l'utilisateur fait 700L\",\n      },\n      {\n        role: \"user\",\n        content: body.question,\n      },\n    ],\n    model: \"gpt-4o\",\n    tools: [\n      {\n        type: \"function\",\n        function: {\n          \u002F\u002F la fonction takeMeasure\n        },\n      },\n    ],\n  });\n});\n",[143,2716,2717,2744,2756,2772,2776,2780,2788,2792,2799,2803,2807,2815,2820,2824,2828,2836,2840,2844,2852,2856,2861,2865,2869,2873,2877],{"__ignoreMap":329},[873,2718,2719,2721,2723,2725,2728,2730,2732,2734,2736,2738,2740,2742],{"class":875,"line":876},[873,2720,1069],{"class":885},[873,2722,1072],{"class":939},[873,2724,943],{"class":885},[873,2726,2727],{"class":893},"\"\u002Fblue\"",[873,2729,1080],{"class":885},[873,2731,1083],{"class":889},[873,2733,1086],{"class":889},[873,2735,1089],{"class":885},[873,2737,1093],{"class":1092},[873,2739,1080],{"class":885},[873,2741,1098],{"class":1092},[873,2743,1101],{"class":885},[873,2745,2746,2748,2751,2753],{"class":875,"line":330},[873,2747,1194],{"class":889},[873,2749,2750],{"class":885}," body ",[873,2752,890],{"class":889},[873,2754,2755],{"class":885}," req.body;\n",[873,2757,2758,2760,2762,2764,2766,2768,2770],{"class":875,"line":337},[873,2759,1107],{"class":889},[873,2761,1110],{"class":932},[873,2763,936],{"class":889},[873,2765,1115],{"class":889},[873,2767,1118],{"class":885},[873,2769,1121],{"class":939},[873,2771,1124],{"class":885},[873,2773,2774],{"class":875,"line":963},[873,2775,1130],{"class":885},[873,2777,2778],{"class":875,"line":969},[873,2779,1136],{"class":885},[873,2781,2782,2784,2786],{"class":875,"line":975},[873,2783,1142],{"class":885},[873,2785,1463],{"class":893},[873,2787,1148],{"class":885},[873,2789,2790],{"class":875,"line":994},[873,2791,2042],{"class":885},[873,2793,2794,2797],{"class":875,"line":1008},[873,2795,2796],{"class":893},"          \"Tu as accès à certaines fonctionnalités d'une application mobile de gestion d'aquarium récifal. Utilise les fonctions fournies pour répondre et interagir avec l'utilisateur. L'aquarium de l'utilisateur fait 700L\"",[873,2798,1148],{"class":885},[873,2800,2801],{"class":875,"line":1013},[873,2802,1165],{"class":885},[873,2804,2805],{"class":875,"line":1019},[873,2806,1136],{"class":885},[873,2808,2809,2811,2813],{"class":875,"line":1038},[873,2810,1142],{"class":885},[873,2812,1145],{"class":893},[873,2814,1148],{"class":885},[873,2816,2817],{"class":875,"line":1055},[873,2818,2819],{"class":885},"        content: body.question,\n",[873,2821,2822],{"class":875,"line":1060},[873,2823,1165],{"class":885},[873,2825,2826],{"class":875,"line":1066},[873,2827,1171],{"class":885},[873,2829,2830,2832,2834],{"class":875,"line":1104},[873,2831,1177],{"class":885},[873,2833,1180],{"class":893},[873,2835,1148],{"class":885},[873,2837,2838],{"class":875,"line":1127},[873,2839,2070],{"class":885},[873,2841,2842],{"class":875,"line":1133},[873,2843,1136],{"class":885},[873,2845,2846,2848,2850],{"class":875,"line":1139},[873,2847,2079],{"class":885},[873,2849,1535],{"class":893},[873,2851,1148],{"class":885},[873,2853,2854],{"class":875,"line":1151},[873,2855,2088],{"class":885},[873,2857,2858],{"class":875,"line":1162},[873,2859,2860],{"class":879},"          \u002F\u002F la fonction takeMeasure\n",[873,2862,2863],{"class":875,"line":1168},[873,2864,1612],{"class":885},[873,2866,2867],{"class":875,"line":1174},[873,2868,1165],{"class":885},[873,2870,2871],{"class":875,"line":1185},[873,2872,1171],{"class":885},[873,2874,2875],{"class":875,"line":1191},[873,2876,1188],{"class":885},[873,2878,2879],{"class":875,"line":1211},[873,2880,1226],{"class":885},[16,2882,2883],{},"Lorsque l'utilisateur envoie \"Mon thermomètre indique 24\", le modèle répond :",[865,2885,2887],{"className":1332,"code":2886,"language":1334,"meta":329,"style":329},"{\n  \"name\": \"takeMeasure\",\n  \"arguments\": {\n    \"element\": \"temperature\",\n    \"value\": 24,\n    \"unit\": \"celcius\"\n  }\n}\n",[143,2888,2889,2893,2903,2910,2921,2933,2943,2947],{"__ignoreMap":329},[873,2890,2891],{"class":875,"line":876},[873,2892,1341],{"class":885},[873,2894,2895,2897,2899,2901],{"class":875,"line":330},[873,2896,2505],{"class":932},[873,2898,1349],{"class":885},[873,2900,2510],{"class":893},[873,2902,1148],{"class":885},[873,2904,2905,2908],{"class":875,"line":337},[873,2906,2907],{"class":932},"  \"arguments\"",[873,2909,1728],{"class":885},[873,2911,2912,2915,2917,2919],{"class":875,"line":963},[873,2913,2914],{"class":932},"    \"element\"",[873,2916,1349],{"class":885},[873,2918,2589],{"class":893},[873,2920,1148],{"class":885},[873,2922,2923,2926,2928,2931],{"class":875,"line":969},[873,2924,2925],{"class":932},"    \"value\"",[873,2927,1349],{"class":885},[873,2929,2930],{"class":932},"24",[873,2932,1148],{"class":885},[873,2934,2935,2938,2940],{"class":875,"line":975},[873,2936,2937],{"class":932},"    \"unit\"",[873,2939,1349],{"class":885},[873,2941,2942],{"class":893},"\"celcius\"\n",[873,2944,2945],{"class":875,"line":994},[873,2946,2322],{"class":885},[873,2948,2949],{"class":875,"line":1008},[873,2950,1369],{"class":885},[16,2952,2953],{},"Ce qui permet d'enregistrer automatiquement la dernière température de l'aquarium. L'application contrôle l'unité et l'enregistre afin de pouvoir faire les conversions.",[16,2955,2956],{},"L'utilisateur dit maintenant \"Le calcium est à 410\", le modèle répond :",[865,2958,2960],{"className":1332,"code":2959,"language":1334,"meta":329,"style":329},"{\n  \"name\": \"takeMeasure\",\n  \"arguments\": {\n    \"element\": \"calcium\",\n    \"value\": 410,\n    \"unit\": \"ppm\"\n  }\n}\n",[143,2961,2962,2966,2976,2982,2993,3004,3013,3017],{"__ignoreMap":329},[873,2963,2964],{"class":875,"line":876},[873,2965,1341],{"class":885},[873,2967,2968,2970,2972,2974],{"class":875,"line":330},[873,2969,2505],{"class":932},[873,2971,1349],{"class":885},[873,2973,2510],{"class":893},[873,2975,1148],{"class":885},[873,2977,2978,2980],{"class":875,"line":337},[873,2979,2907],{"class":932},[873,2981,1728],{"class":885},[873,2983,2984,2986,2988,2991],{"class":875,"line":963},[873,2985,2914],{"class":932},[873,2987,1349],{"class":885},[873,2989,2990],{"class":893},"\"calcium\"",[873,2992,1148],{"class":885},[873,2994,2995,2997,2999,3002],{"class":875,"line":969},[873,2996,2925],{"class":932},[873,2998,1349],{"class":885},[873,3000,3001],{"class":932},"410",[873,3003,1148],{"class":885},[873,3005,3006,3008,3010],{"class":875,"line":975},[873,3007,2937],{"class":932},[873,3009,1349],{"class":885},[873,3011,3012],{"class":893},"\"ppm\"\n",[873,3014,3015],{"class":875,"line":994},[873,3016,2322],{"class":885},[873,3018,3019],{"class":875,"line":1008},[873,3020,1369],{"class":885},[110,3022,3024],{"id":3023},"les-changements-deau","Les changements d'eau",[16,3026,3027],{},"Ajoutons maintenant une fonction permettant au modèle de demander à l'application de lancer un changement d'eau :",[865,3029,3031],{"className":1332,"code":3030,"language":1334,"meta":329,"style":329},"{\n  \"name\": \"calcWaterChange\",\n  \"description\": \"Lance le calcul d'un changement d'eau si tu ne sais pas quel volume d'eau utiliser, calcule 15% du volume de l'aquarium\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"volume\": {\n        \"type\": \"number\",\n        \"description\": \"Volume d'eau à changer\"\n      }\n    }\n  }\n}\n",[143,3032,3033,3037,3048,3059,3065,3075,3081,3088,3098,3108,3112,3116,3120],{"__ignoreMap":329},[873,3034,3035],{"class":875,"line":876},[873,3036,1341],{"class":885},[873,3038,3039,3041,3043,3046],{"class":875,"line":330},[873,3040,2505],{"class":932},[873,3042,1349],{"class":885},[873,3044,3045],{"class":893},"\"calcWaterChange\"",[873,3047,1148],{"class":885},[873,3049,3050,3052,3054,3057],{"class":875,"line":337},[873,3051,2517],{"class":932},[873,3053,1349],{"class":885},[873,3055,3056],{"class":893},"\"Lance le calcul d'un changement d'eau si tu ne sais pas quel volume d'eau utiliser, calcule 15% du volume de l'aquarium\"",[873,3058,1148],{"class":885},[873,3060,3061,3063],{"class":875,"line":963},[873,3062,2529],{"class":932},[873,3064,1728],{"class":885},[873,3066,3067,3069,3071,3073],{"class":875,"line":969},[873,3068,2536],{"class":932},[873,3070,1349],{"class":885},[873,3072,1575],{"class":893},[873,3074,1148],{"class":885},[873,3076,3077,3079],{"class":875,"line":975},[873,3078,2547],{"class":932},[873,3080,1728],{"class":885},[873,3082,3083,3086],{"class":875,"line":994},[873,3084,3085],{"class":932},"      \"volume\"",[873,3087,1728],{"class":885},[873,3089,3090,3092,3094,3096],{"class":875,"line":1008},[873,3091,2561],{"class":932},[873,3093,1349],{"class":885},[873,3095,1595],{"class":893},[873,3097,1148],{"class":885},[873,3099,3100,3103,3105],{"class":875,"line":1013},[873,3101,3102],{"class":932},"        \"description\"",[873,3104,1349],{"class":885},[873,3106,3107],{"class":893},"\"Volume d'eau à changer\"\n",[873,3109,3110],{"class":875,"line":1019},[873,3111,1765],{"class":885},[873,3113,3114],{"class":875,"line":1038},[873,3115,1770],{"class":885},[873,3117,3118],{"class":875,"line":1055},[873,3119,2322],{"class":885},[873,3121,3122],{"class":875,"line":1060},[873,3123,1369],{"class":885},[16,3125,3126],{},"L'utilisateur indique \"Lance un changement d'eau de 10%\", le modèle répond :",[865,3128,3130],{"className":1332,"code":3129,"language":1334,"meta":329,"style":329},"{\n  \"name\": \"calcWaterChange\",\n  \"arguments\": {\n    \"volume\": 70\n  }\n}\n",[143,3131,3132,3136,3146,3152,3162,3166],{"__ignoreMap":329},[873,3133,3134],{"class":875,"line":876},[873,3135,1341],{"class":885},[873,3137,3138,3140,3142,3144],{"class":875,"line":330},[873,3139,2505],{"class":932},[873,3141,1349],{"class":885},[873,3143,3045],{"class":893},[873,3145,1148],{"class":885},[873,3147,3148,3150],{"class":875,"line":337},[873,3149,2907],{"class":932},[873,3151,1728],{"class":885},[873,3153,3154,3157,3159],{"class":875,"line":963},[873,3155,3156],{"class":932},"    \"volume\"",[873,3158,1349],{"class":885},[873,3160,3161],{"class":932},"70\n",[873,3163,3164],{"class":875,"line":969},[873,3165,2322],{"class":885},[873,3167,3168],{"class":875,"line":975},[873,3169,1369],{"class":885},[16,3171,3172,3173,3176],{},"Nous avons indiqué dans le prompt système des informations sur l'aquarium de l'utilisateur (",[195,3174,3175],{},"L'aquarium de l'utilisateur fait 700L","). Ainsi quand l'utilisateur demande de changer 10% de son volume d'eau, le modèle calcule la quantité d'eau à changer et appelle la fonction.",[16,3178,3179],{},"Si l'utilisateur dit simplement \"Lance un changement d'eau\", le modèle répond :",[865,3181,3183],{"className":1332,"code":3182,"language":1334,"meta":329,"style":329},"{\n  \"name\": \"calcWaterChange\",\n  \"arguments\": {\n    \"volume\": 105\n  }\n}\n",[143,3184,3185,3189,3199,3205,3214,3218],{"__ignoreMap":329},[873,3186,3187],{"class":875,"line":876},[873,3188,1341],{"class":885},[873,3190,3191,3193,3195,3197],{"class":875,"line":330},[873,3192,2505],{"class":932},[873,3194,1349],{"class":885},[873,3196,3045],{"class":893},[873,3198,1148],{"class":885},[873,3200,3201,3203],{"class":875,"line":337},[873,3202,2907],{"class":932},[873,3204,1728],{"class":885},[873,3206,3207,3209,3211],{"class":875,"line":963},[873,3208,3156],{"class":932},[873,3210,1349],{"class":885},[873,3212,3213],{"class":932},"105\n",[873,3215,3216],{"class":875,"line":969},[873,3217,2322],{"class":885},[873,3219,3220],{"class":875,"line":975},[873,3221,1369],{"class":885},[16,3223,3224],{},"Car nous avons indiqué dans la description de la fonction que s'il n'y a pas d'indication de volume fourni par l'utilisateur, il doit calculer 15% du volume de l'aquarium de l'utilisateur.",[11,3226,3228],{"id":3227},"pour-la-suite","Pour la suite",[16,3230,3231],{},"Voici d'autres fonctions que je fournis pour répondre aux besoins des fonctionnalités présentées :",[865,3233,3235],{"className":1332,"code":3234,"language":1334,"meta":329,"style":329},"{\n  \"name\": \"calcAddition\",\n  \"description\": \"Lance le calcul d'un ajout de produit\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"element\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"calcium\", \"ph\", \"temperature\",\n          \"magnesium\", \"alkalinity\", \"phosphates\"\n        ]\n      }\n    }\n  }\n}\n",[143,3236,3237,3241,3252,3263,3269,3279,3285,3291,3301,3307,3321,3333,3337,3341,3345,3349],{"__ignoreMap":329},[873,3238,3239],{"class":875,"line":876},[873,3240,1341],{"class":885},[873,3242,3243,3245,3247,3250],{"class":875,"line":330},[873,3244,2505],{"class":932},[873,3246,1349],{"class":885},[873,3248,3249],{"class":893},"\"calcAddition\"",[873,3251,1148],{"class":885},[873,3253,3254,3256,3258,3261],{"class":875,"line":337},[873,3255,2517],{"class":932},[873,3257,1349],{"class":885},[873,3259,3260],{"class":893},"\"Lance le calcul d'un ajout de produit\"",[873,3262,1148],{"class":885},[873,3264,3265,3267],{"class":875,"line":963},[873,3266,2529],{"class":932},[873,3268,1728],{"class":885},[873,3270,3271,3273,3275,3277],{"class":875,"line":969},[873,3272,2536],{"class":932},[873,3274,1349],{"class":885},[873,3276,1575],{"class":893},[873,3278,1148],{"class":885},[873,3280,3281,3283],{"class":875,"line":975},[873,3282,2547],{"class":932},[873,3284,1728],{"class":885},[873,3286,3287,3289],{"class":875,"line":994},[873,3288,2554],{"class":932},[873,3290,1728],{"class":885},[873,3292,3293,3295,3297,3299],{"class":875,"line":1008},[873,3294,2561],{"class":932},[873,3296,1349],{"class":885},[873,3298,2156],{"class":893},[873,3300,1148],{"class":885},[873,3302,3303,3305],{"class":875,"line":1013},[873,3304,2572],{"class":932},[873,3306,1693],{"class":885},[873,3308,3309,3311,3313,3315,3317,3319],{"class":875,"line":1019},[873,3310,2579],{"class":893},[873,3312,1080],{"class":885},[873,3314,2584],{"class":893},[873,3316,1080],{"class":885},[873,3318,2589],{"class":893},[873,3320,1148],{"class":885},[873,3322,3323,3325,3327,3329,3331],{"class":875,"line":1038},[873,3324,2596],{"class":893},[873,3326,1080],{"class":885},[873,3328,2601],{"class":893},[873,3330,1080],{"class":885},[873,3332,2606],{"class":893},[873,3334,3335],{"class":875,"line":1055},[873,3336,2611],{"class":885},[873,3338,3339],{"class":875,"line":1060},[873,3340,1765],{"class":885},[873,3342,3343],{"class":875,"line":1066},[873,3344,1770],{"class":885},[873,3346,3347],{"class":875,"line":1104},[873,3348,2322],{"class":885},[873,3350,3351],{"class":875,"line":1127},[873,3352,1369],{"class":885},[865,3354,3356],{"className":1332,"code":3355,"language":1334,"meta":329,"style":329},"{\n  \"name\": \"getMeasure\",\n  \"description\": \"Récupère la valeur de la dernière mesure prise pour un élément. Utilise cette fonction pour connaitre une valeur\",\n  \"parameters\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"element\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"calcium\", \"ph\", \"temperature\",\n          \"magnesium\", \"alkalinity\", \"phosphates\"\n        ]\n      }\n    }\n  }\n}\n",[143,3357,3358,3362,3373,3384,3390,3400,3406,3412,3422,3428,3442,3454,3458,3462,3466,3470],{"__ignoreMap":329},[873,3359,3360],{"class":875,"line":876},[873,3361,1341],{"class":885},[873,3363,3364,3366,3368,3371],{"class":875,"line":330},[873,3365,2505],{"class":932},[873,3367,1349],{"class":885},[873,3369,3370],{"class":893},"\"getMeasure\"",[873,3372,1148],{"class":885},[873,3374,3375,3377,3379,3382],{"class":875,"line":337},[873,3376,2517],{"class":932},[873,3378,1349],{"class":885},[873,3380,3381],{"class":893},"\"Récupère la valeur de la dernière mesure prise pour un élément. Utilise cette fonction pour connaitre une valeur\"",[873,3383,1148],{"class":885},[873,3385,3386,3388],{"class":875,"line":963},[873,3387,2529],{"class":932},[873,3389,1728],{"class":885},[873,3391,3392,3394,3396,3398],{"class":875,"line":969},[873,3393,2536],{"class":932},[873,3395,1349],{"class":885},[873,3397,1575],{"class":893},[873,3399,1148],{"class":885},[873,3401,3402,3404],{"class":875,"line":975},[873,3403,2547],{"class":932},[873,3405,1728],{"class":885},[873,3407,3408,3410],{"class":875,"line":994},[873,3409,2554],{"class":932},[873,3411,1728],{"class":885},[873,3413,3414,3416,3418,3420],{"class":875,"line":1008},[873,3415,2561],{"class":932},[873,3417,1349],{"class":885},[873,3419,2156],{"class":893},[873,3421,1148],{"class":885},[873,3423,3424,3426],{"class":875,"line":1013},[873,3425,2572],{"class":932},[873,3427,1693],{"class":885},[873,3429,3430,3432,3434,3436,3438,3440],{"class":875,"line":1019},[873,3431,2579],{"class":893},[873,3433,1080],{"class":885},[873,3435,2584],{"class":893},[873,3437,1080],{"class":885},[873,3439,2589],{"class":893},[873,3441,1148],{"class":885},[873,3443,3444,3446,3448,3450,3452],{"class":875,"line":1038},[873,3445,2596],{"class":893},[873,3447,1080],{"class":885},[873,3449,2601],{"class":893},[873,3451,1080],{"class":885},[873,3453,2606],{"class":893},[873,3455,3456],{"class":875,"line":1055},[873,3457,2611],{"class":885},[873,3459,3460],{"class":875,"line":1060},[873,3461,1765],{"class":885},[873,3463,3464],{"class":875,"line":1066},[873,3465,1770],{"class":885},[873,3467,3468],{"class":875,"line":1104},[873,3469,2322],{"class":885},[873,3471,3472],{"class":875,"line":1127},[873,3473,1369],{"class":885},[16,3475,3476,3477,3480,3481,182],{},"Ainsi le modèle peut décider d'appeler la fonction ",[143,3478,3479],{},"getMeasure"," s'il a besoin d'avoir une information pour répondre à l'utilisateur. Si celui-ci lui demande si son calcium est trop haut, le modèle peut choisir d'appeler la fonction ",[143,3482,3479],{},[16,3484,3485,3486,3489,3490,3493],{},"Dans mon code, j'ai deux types de fonctions : celles qui ",[58,3487,3488],{},"déclenchent un évènement"," (enregistrer une mesure, lancer un calcul de changement d'eau, d'ajout), et celles qui ",[58,3491,3492],{},"vont chercher une information"," pour le modèle. Dans ce second cas, le modèle est rappelé avec le contexte précédent (l'historique des messages échangés et l'information du résultat de la fonction).",[16,3495,3496],{},"Les interactions plus complexes avec le modèle feront l'objet d'un autre post.",[16,3498,3499],{},"L'utilisateur peut également poser des questions sur son aquarium, sur des espèces de poisson, savoir si elles seraient adaptées, le modèle dans ce cas n'utilise pas de fonction et la réponse est proposée à l'utilisateur.",[16,3501,3502],{},"Ce type d'interaction est vraiment puissant, a une vraie valeur ajoutée pour l'utilisateur et produit un effet spectaculaire. Cependant pour que les interactions soient fluides, il est nécessaire de ne pas les faire de façon textuelle mais bien avec la voix. L'application étant vraiment ergonomique, les fonctionnalités sont à portée de doigts et l'interaction avec un modèle n'est pas nécessaire pour déclencher des actions. En revanche, l'interaction avec la voix, en particulier pour la prise de mesure, permet d'utiliser l'application en main libre ce qui est un vrai avantage pour l'utilisateur lorsqu'il a les mains dans l'eau.",[3504,3505,3506],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":329,"searchDepth":330,"depth":330,"links":3508},[3509,3510,3511,3512,3513,3517],{"id":786,"depth":330,"text":787},{"id":852,"depth":330,"text":853},{"id":1410,"depth":330,"text":1411},{"id":1945,"depth":330,"text":1946},{"id":2446,"depth":330,"text":2447,"children":3514},[3515,3516],{"id":2487,"depth":337,"text":2488},{"id":3023,"depth":337,"text":3024},{"id":3227,"depth":330,"text":3228},"2024-05-20","Comment interfacer votre application avec ChatGPT de façon efficace, utiliser les fonctions et récupérer des décisions déclenchables dans votre app.",{},"\u002Fblog\u002Ffr\u002Frevolutionnez-votre-app-avec-chatgpt",{"title":781,"description":3519},"blog\u002Ffr\u002Frevolutionnez-votre-app-avec-chatgpt",[3525,352,3526],"OpenAI","Blue","revolutionize-your-app-with-chatgpt","rEsMKC_qkUIb5snve7r1qFD82sCRwVBp4uni1dP4wfU",1774359324871]