Las herramientas fallan, y el modelo puede ayudar
Una herramienta puede fallar. Quizá el artículo no existe, el servicio va lento o el modelo envió un valor malo. Si tu programa se cae, todo el asistente se detiene.
Si tu programa devuelve el error como resultado, el modelo puede leerlo y corregirse.
Al final de esta lección
Nota
Puedo devolver el error de una herramienta como resultado para que el modelo pueda recuperarse.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Excepción | un error que lanza una función cuando no puede terminar |
| Resultado de error | un mensaje de herramienta que dice qué salió mal |
| Reintento | volver a hacer la misma llamada |
| Tiempo agotado | una llamada que tardó demasiado y se abandonó |
| Rendirse | dejar de intentarlo e informar del fallo |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error es un resultado legible
Envuelve la llamada en un try. Cuando lance un error, no te detengas. Pon el mensaje en un mensaje de herramienta, como Error: no encontrado: kiwi, y vuelve a preguntar al modelo.
El modelo ve las palabras. Puede pedir disculpas al usuario o probar otro artículo. El bucle conserva su límite de pasos, así que no puede durar para siempre.
Ejemplo resuelto: en qué se convierte cada fallo
| Qué salió mal | ¿Reintentar? | Mensaje para el modelo |
|---|---|---|
| El artículo no está en la tienda | No | Error: no encontrado: kiwi |
| El valor no está permitido | No | Error: número negativo |
| Los argumentos no encajan | No | Error: argumentos no válidos |
| El servicio agotó el tiempo | Sí, unas pocas veces | Error: tiempo agotado, tras el último intento |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Guarda un ayudante que llama al modelo
docker exec -w /home/node/project agent node -e "const ask=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('ai.cjs','exports.ask='+ask+';');console.log('Guardado ai.cjs: ask(messages, tools) devuelve el mensaje del modelo.')"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Envuelve una herramienta que falla y otra inestable
docker exec -w /home/node/project agent node -e "let n=0;const flaky=()=>{n++;if(n<3)throw Error('timeout');return 'ok'};const price=a=>{if((a.item=='manzana')===false)throw Error('no encontrado: '+a.item);return 40};const call=(f,a,tries)=>{for(let i=1;;i++){try{return{ok:true,result:String(f(a)),attempts:i}}catch(e){if(e.message=='timeout'&&i<tries)continue;return{ok:false,error:e.message,attempts:i}}}};console.log(JSON.stringify(call(price,{item:'manzana'},3)));console.log(JSON.stringify(call(price,{item:'kiwi'},3)));console.log(JSON.stringify(call(flaky,{},3)))"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Reintenta solo lo que puede funcionar
Un tiempo agotado puede pasar, así que volver a intentarlo puede ayudar. Un artículo que falta seguirá faltando, y reintentar solo pierde tiempo.
Fija un número de intentos, cuenta cada llamada y date por vencido con un mensaje claro cuando se acaben. Informa de cuántos intentos hiciste.
Devuelve un error a un modelo
docker exec -w /home/node/project agent node -e "const{ask}=require('./ai.cjs');const P={manzana:40,pera:45};const T=[{type:'function',function:{name:'get_price',description:'Un precio.',parameters:{type:'object',properties:{item:{type:'string'}}}}}];const go=async(m,f)=>{try{return await ask(m,T)}catch(e){console.log('Sin modelo ('+e.message+'). Muestra grabada:');return f}};(async()=>{const m=[{role:'user',content:'¿Precio de un kiwi?'}];const a=await go(m,{role:'assistant',tool_calls:[{id:'c1',function:{name:'get_price',arguments:JSON.stringify({item:'kiwi'})}}]});m.push(a);const c=a.tool_calls?.[0];if(c==null)return console.log('Respuesta: '+a.content);const i=JSON.parse(c.function.arguments).item;const r=i in P?String(P[i]):'Error: no encontrado: '+i;console.log('Resultado: '+r);m.push({role:'tool',tool_call_id:c.id,content:r});const b=await go(m,{role:'assistant',content:'No hay kiwi.'});console.log('Después: '+(b.content||JSON.stringify(b.tool_calls[0].function)))})()"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error frecuente
Advertencia
Mucha gente cree que una herramienta que falla significa que toda la conversación ha fallado. No es así. Un error es solo un resultado más. Devuélvelo como texto, conserva el límite de pasos y deja que el modelo decida qué hacer.
Para ir más lejos: cambia los intentos
En la ejecución que envuelve las herramientas, cambia el número de intentos de la última llamada, call(flaky,{},3), de 3 a 2 y vuelve a ejecutarla. Mira la última línea.
Escribe qué cambia, y por qué importa un límite de intentos.