Un modèle avec une boîte à outils
Un seul outil, c’est simple. Un vrai assistant en a plusieurs : une recherche de prix, un convertisseur d’unités, un endroit pour garder des notes. Le modèle en choisit un pour chaque demande, et votre programme doit envoyer chaque demande à la bonne fonction.
Il doit aussi savoir réagir quand le modèle demande un outil qui n’existe pas.
À la fin de cette leçon
Remarque
Je peux proposer plusieurs outils et envoyer chaque demande à la bonne fonction.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Aiguilleur | du code qui envoie une demande à la bonne fonction |
| Table d’outils | une table qui relie chaque nom d’outil à sa fonction |
| Outil inconnu | un nom qui n’est pas dans votre table d’outils |
| Argument obligatoire | une valeur sans laquelle l’outil ne peut pas tourner |
| Validation | vérifier une demande avant d’agir |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une table d’outils relie les noms aux fonctions
Gardez vos outils dans une seule table. La clé est le nom que le modèle utilise. La valeur est votre fonction.
Pour aiguiller une demande, cherchez le nom dans la table et appelez ce que vous trouvez. Ajouter un outil, c’est alors une seule ligne, et la table est la seule liste de ce que le modèle peut utiliser.
Exemple travaillé : une boîte de trois outils
| Nom | Ce qu’il fait | Argument obligatoire |
|---|---|---|
| prix_de | Cherche le prix d’un article | article |
| km_en_miles | Convertit des kilomètres en miles | km |
| ajouter_note | Enregistre une courte note | texte |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Enregistrer un assistant d’appel du modèle
docker exec -w /home/node/project agent node -e "const poser=async(m,t,c)=>{const r=await fetch((process.env.AI_URL||'http://ai:4000')+'/v1/chat/completions',{method:'POST',headers:{'content-type':'application/json',authorization:'Bearer lab'},body:JSON.stringify({model:'lab',messages:m,tools:t,tool_choice:c,max_tokens:120})});const j=await r.json();if(r.ok)return j.choices[0].message;throw Error(r.status+' '+j.error.message)};require('fs').writeFileSync('ia.cjs','exports.poser='+poser+';');console.log('ia.cjs enregistré : poser(messages, outils) renvoie le message du modèle.')"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Refusez une demande impossible
Un modèle peut demander un nom absent de votre table. Il peut envoyer des arguments illisibles. Il peut oublier un argument obligatoire. Vérifiez ces trois cas avant d’appeler quoi que ce soit.
Un refus est un message renvoyé au modèle, comme erreur : outil inconnu tout_effacer. Ce n’est jamais un plantage, et rien n’est exécuté.
Aiguiller quatre demandes, dont deux mauvaises
docker exec -w /home/node/project agent node -e "const F={prix_de:a=>({pomme:40,poire:45})[a.article],km_en_miles:a=>a.km*0.62,ajouter_note:a=>'notée'};const R={prix_de:['article'],km_en_miles:['km'],ajouter_note:['texte']};const aiguiller=c=>{if(Object.hasOwn(F,c.nom)===false)return 'erreur : outil inconnu '+c.nom;let a;try{a=JSON.parse(c.arguments)}catch(e){return 'erreur : arguments invalides'}for(const k of R[c.nom])if((k in a)===false)return 'erreur : manque '+k;return String(F[c.nom](a))};for(const c of [{nom:'prix_de',arguments:JSON.stringify({article:'poire'})},{nom:'tout_effacer',arguments:'{}'},{nom:'prix_de',arguments:'{article:'},{nom:'km_en_miles',arguments:'{}'}])console.log(c.nom+' -> '+aiguiller(c))"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Proposer trois outils à un modèle
docker exec -w /home/node/project agent node -e "const{poser}=require('./ia.cjs');const t=(n,d,k)=>({type:'function',function:{name:n,description:d,parameters:{type:'object',properties:{[k]:{type:k=='km'?'number':'string'}}}}});const T=[t('prix_de','Prix d’un article.','article'),t('km_en_miles','Convertit des km en miles.','km'),t('ajouter_note','Enregistre une note.','texte')];const F={prix_de:a=>({pomme:40,poire:45})[a.article],km_en_miles:a=>Math.round(a.km*621.371)/1000,ajouter_note:a=>'notée : '+a.texte};(async()=>{let c;try{c=(await poser([{role:'user',content:'10 km font combien de miles ?'}],T,'required')).tool_calls[0]}catch(e){console.log('Refus ('+e.message+'). Exemple enregistré :');c={function:{name:'km_en_miles',arguments:JSON.stringify({km:10})}}}const f=c.function;console.log('Demande : '+f.name+' '+f.arguments);console.log('Résultat : '+(Object.hasOwn(F,f.name)?F[f.name](JSON.parse(f.arguments)):'outil inconnu'))})()"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une erreur courante
Attention
Beaucoup pensent que le modèle ne demande que les outils proposés. Il peut demander n’importe quel nom, et envoyer des arguments qui ne conviennent pas. Vérifiez toujours le nom dans votre table et les arguments d’après ce dont l’outil a besoin.
Pour aller plus loin : ajouter un quatrième outil
Dans l’exécution de l’aiguilleur, ajoutez un outil nommé crier qui renvoie son argument texte en majuscules. Ajoutez-le à F et à R, puis envoyez une demande pour lui.
Écrivez à combien d’endroits vous avez dû changer le code.