Les outils échouent, et le modèle peut aider
Un outil peut échouer. L’article n’existe peut-être pas, le service est peut-être lent, ou le modèle a envoyé une mauvaise valeur. Si votre programme plante, tout l’assistant s’arrête.
Si votre programme renvoie l’erreur comme résultat, le modèle peut la lire et se corriger.
À la fin de cette leçon
Remarque
Je peux renvoyer l’erreur d’un outil comme résultat pour que le modèle puisse s’en sortir.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Exception | une erreur qu’une fonction lève quand elle ne peut pas finir |
| Résultat d’erreur | un message d’outil qui dit ce qui a mal tourné |
| Nouvel essai | refaire le même appel |
| Délai dépassé | un appel trop long, abandonné |
| Abandonner | cesser d’essayer et signaler l’échec |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur est un résultat à lire
Entourez l’appel d’un try. Quand il lève une erreur, ne vous arrêtez pas. Mettez le message dans un message d’outil, comme Erreur : introuvable : kiwi, et redemandez au modèle.
Le modèle voit les mots. Il peut s’excuser auprès de l’utilisateur ou essayer un autre article. La boucle garde sa limite d’étapes : elle ne peut pas durer toujours.
Exemple travaillé : ce que devient chaque échec
| Ce qui a mal tourné | Réessayer ? | Message pour le modèle |
|---|---|---|
| L’article n’est pas en boutique | Non | Erreur : introuvable : kiwi |
| La valeur n’est pas permise | Non | Erreur : nombre négatif |
| Les arguments ne conviennent pas | Non | Erreur : arguments invalides |
| Le service a dépassé le délai | Oui, quelques fois | Erreur : délai dépassé, après le dernier essai |
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.
Envelopper un outil qui échoue et un instable
docker exec -w /home/node/project agent node -e "let n=0;const instable=()=>{n++;if(n<3)throw Error('délai');return 'ok'};const prix=a=>{if((a.article=='pomme')===false)throw Error('introuvable : '+a.article);return 40};const appeler=(f,a,essais)=>{for(let i=1;;i++){try{return{ok:true,resultat:String(f(a)),tentatives:i}}catch(e){if(e.message=='délai'&&i<essais)continue;return{ok:false,erreur:e.message,tentatives:i}}}};console.log(JSON.stringify(appeler(prix,{article:'pomme'},3)));console.log(JSON.stringify(appeler(prix,{article:'kiwi'},3)));console.log(JSON.stringify(appeler(instable,{},3)))"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.
Ne réessayez que ce qui peut marcher
Un délai dépassé peut passer : réessayer peut aider. Un article absent restera absent, donc réessayer ne fait que perdre du temps.
Fixez un nombre d’essais, comptez chaque appel et abandonnez avec un message clair quand ils sont épuisés. Indiquez combien de tentatives vous avez faites.
Renvoyer une erreur à un modèle
docker exec -w /home/node/project agent node -e "const{poser}=require('./ia.cjs');const P={pomme:40,poire:45};const T=[{type:'function',function:{name:'prix_de',description:'Un prix.',parameters:{type:'object',properties:{article:{type:'string'}}}}}];const go=async(m,f)=>{try{return await poser(m,T)}catch(e){console.log('Refus ('+e.message+'). Exemple enregistré :');return f}};(async()=>{const m=[{role:'user',content:'Prix d’un kiwi ?'}];const a=await go(m,{role:'assistant',tool_calls:[{id:'c1',function:{name:'prix_de',arguments:JSON.stringify({article:'kiwi'})}}]});m.push(a);const c=a.tool_calls?.[0];if(c==null)return console.log('Réponse : '+a.content);const i=JSON.parse(c.function.arguments).article;const r=i in P?String(P[i]):'Erreur : introuvable : '+i;console.log('Outil : '+r);m.push({role:'tool',tool_call_id:c.id,content:r});const b=await go(m,{role:'assistant',content:'Pas de kiwi.'});console.log('Ensuite : '+(b.content||JSON.stringify(b.tool_calls[0].function)))})()"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 qu’un outil en échec veut dire que toute la conversation a échoué. Ce n’est pas le cas. Une erreur n’est qu’un résultat de plus. Renvoyez-la sous forme de texte, gardez la limite d’étapes et laissez le modèle décider quoi faire.
Pour aller plus loin : changer les essais
Dans l’exécution qui enveloppe les outils, changez le nombre d’essais de 3 en 2 dans appeler(instable,{},3) et relancez. Regardez la dernière ligne : elle dit maintenant ok faux, avec délai comme erreur, après 2 tentatives.
Écrivez pourquoi une limite d’essais compte.