Doper votre chatbot avec des outils (Appel de fonction) - Partie 2
Dans la première partie, nous avons construit un chatbot de base. Bien qu'impressionnant, ses capacités étaient limitées aux connaissances intégrées dans le LLM. Pour rendre notre chatbot réellement puissant, nous devons lui donner la capacité d'interagir avec le monde extérieur ou d'effectuer des actions spécifiques. C'est là que les "outils" (souvent désignés sous le terme d'appel de fonction) entrent en jeu.

Technologies Used
Le SDK IA de Vercel offre un excellent support pour l'intégration d'outils dans vos flux de chat. Dans cet article, nous allons modifier notre chatbot pour :
- Définissez un « outil » pour que notre LLM puisse l'utiliser (par exemple, une fonction fictive de récupération de la météo).
- Gérez la demande du LLM d'utiliser un outil.
- Exécutez l'outil et renvoyez le résultat au LLM pour continuer la conversation.
Prérequis
- Achèvement de la Partie 1 ou d'une configuration similaire.
- Un LLM qui prend en charge l'appel de fonction/l'utilisation d'outils (par exemple, OpenAI's
gpt-3.5-turbo
,gpt-4o
, ou plus récent).
Comprendre les outils/l'appel de fonctions
Les outils permettent à un modèle de langage à grande échelle d'indiquer qu'il doit appeler une fonction prédéfinie que vous avez mise à sa disposition. Le processus est généralement le suivant :
- L'utilisateur envoie un message.
- LLM analyse le message et, si cela est approprié, décide d'utiliser l'un de ses outils disponibles.
- Au lieu d'une réponse textuelle, le LLM répond par une demande d'appel d'outil, en spécifiant le nom de l'outil et les arguments.
- Votre code d'application reçoit cette requête et exécute la fonction réelle (l'« outil »).
- Le résultat de l'exécution de la fonction est renvoyé au LLM sous forme de nouveau message.
- Le LLM utilise ce résultat pour formuler sa réponse textuelle finale à l'utilisateur.
Étape 1 : Définition d'un outil (côté client ou partagé)
Définissons un outil simple qui simule la récupération de la météo pour une ville donnée. Nous utiliserons zod
pour la validation de schéma, ce qui s'intègre bien avec le SDK Vercel AI.
Installez zod
:
npm install zod
# ou
yarn add zod
Maintenant, définissons notre outil. Cela peut être dans un fichier d'utilitaires partagés ou directement dans votre route API, mais pour plus de clarté, imaginons un lib/tools.ts
:
// lib/tools.ts (ou directement dans votre route API)
import { z } from 'zod';
import { tool } from 'ai';
export const weatherTool = tool({
description: 'Obtenez la météo actuelle pour un lieu spécifique',
parameters: z.object({
city: z.string().describe('La ville pour laquelle obtenir la météo (par exemple, San Francisco)'),
unit: z.enum(['celsius', 'fahrenheit']).optional().default('celsius').describe('L'unité de température'),
}),
execute: async ({ city, unit }) => {
// Dans une application réelle, vous appelleriez une API météo ici
console.log(`APPEL DE L'OUTIL : Récupération de la météo pour ${city} en ${unit}`);
let temperature;
let condition;
if (city.toLowerCase().includes('san francisco')) {
temperature = unit === 'celsius' ? 15 : 59;
condition = 'Nuageux avec une possibilité de brouillard';
} else if (city.toLowerCase().includes('tokyo')) {
temperature = unit === 'celsius' ? 22 : 72;
condition = 'Ensoleillé';
} else if (city.toLowerCase().includes('london')) {
temperature = unit === 'celsius' ? 12 : 54;
condition = 'Pluvieux';
} else {
temperature = unit === 'celsius' ? 20 : 68;
condition = 'Partiellement nuageux';
}
return {
city,
temperature,
unit,
condition,
mockData: true,
};
},
});
Explication :
outil
: Un assistant du paquetai
pour définir un outil.description
: Indique au LLM à quoi sert l'outil. Il est crucial que le LLM sache quand l'utiliser.paramètres
: Un schémazod
définissant les arguments attendus par l'outil. Le LLM essaiera d'extraire ceux-ci de la requête de l'utilisateur.décrire
aide le LLM à comprendre chaque paramètre.exécuter
: Une fonction asynchrone qui réalise l'action de l'outil. Elle reçoit les paramètres validés. Cette fonction est exécutée sur votre serveur.
Étape 2 : Mise à jour de la route API
Maintenant, modifions app/api/chat/route.ts
pour rendre le LLM conscient de cet outil et gérer les appels de l'outil.
// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';
// Permet des réponses en streaming jusqu'à 60 secondes
export const maxDuration = 60;
export const runtime = 'edge';
export const POST = async (req: Request) => {
const { messages } = await req.json();
const result = await streamText({
model: openai('gpt-4o'), // Utilisez un modèle qui supporte bien les outils
messages,
tools: {
//Outil de météo défini précédemment
getWeather: weatherTool, // Exposez l'outil au LLM
},
// Le gestionnaire `onToolCall` est appelé lorsque le LLM souhaite utiliser un outil
// Il est responsable de l'exécution de l'outil et de retourner le résultat au LLM
// Par défaut, cela n'est pas nécessaire si vous définissez la fonction `execute` sur l'outil.
// Le SDK AI de Vercel appellera automatiquement la méthode `execute` de l'outil.
// Cependant, vous pouvez le définir pour des scénarios plus complexes ou pour la journalisation.
// onToolCall: async ({ toolCall, appendToolCallMessage, stream }) => {
// console.log('Appel d'outil reçu sur le serveur :', toolCall);
// // ... logique personnalisée si nécessaire ...
// // Le comportement par défaut est géré si execute est sur l'outil.
// // Si vous implémentez onToolCall, vous DEVEZ gérer l'exécution de l'outil.
// }
});
return result.toDataStreamResponse();
}
Modifications clés :
- Nous utilisons
gpt-4o
car il offre de meilleures capacités de suivi des outils. Vous pouvez essayer avecgpt-3.5-turbo
, mais cela pourrait être moins fiable. outils : { getWeather: weatherTool }
: Nous transmettons notre ou nos outil(s) défini(s) àstreamText
. La clé (getWeather
) est le nom que le LLM utilisera pour faire référence à cet outil.- The
tool
definition now includes anexecute
function. The Vercel AI SDK will automatically call this function when the LLM decides to use thegetWeather
tool. The result ofexecute
is automatically sent back to the LLM. - Le gestionnaire
onToolCall
dansstreamText
est disponible pour des scénarios plus avancés où vous pourriez vouloir intercepter les appels d'outils avant exécution ou les gérer différemment, mais pour des cas simples avecexecute
défini sur l'outil, il n'est souvent pas nécessaire.
Étape 3 : Modifications côté client (minimales)
La beauté de useChat
est qu'il prend déjà en charge le flux d'invocation d'outil ! Lorsque le LLM répond avec un appel d'outil, useChat
va :
- Ajoutez un message à
messages
avecrôle : 'outil'
etcontenu
étant les informations de l'appel de l'outil (c'est souvent une étape interne). - Votre route API gère l'exécution de l'outil.
- L'API renvoie le résultat de l'outil.
useChat
ajoute un autre message avecrole: 'tool'
etcontent
étant la sortie sous forme de chaîne de caractères de l'outil.- Le LLM génère ensuite la réponse textuelle finale, que
useChat
ajoute en tant queassistant
message.
Votre app/page.tsx
depuis la Partie 1 devrait principalement fonctionner tel quel. Cependant, vous pourriez vouloir afficher les messages des outils différemment ou les enregistrer.
// app/page.tsx
'use client';
import { useChat, Message } from '@ai-sdk/react';
const Chat = () => {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
// Facultativement, vous pouvez fournir un gestionnaire `onToolCall` sur le client également,
// bien que pour cet exemple, l'exécution côté serveur soit plus courante et sécurisée.
// experimental_onToolCall: async (toolCalls, appendToolCallMessage) => {
// console.log("Appel d'outil côté client:", toolCalls);
// // Gérez les outils côté client s'il y en a, ou reconnaissez simplement
// }
});
return (
<div className="flex flex-col w-full max-w-md py-24 mx-auto stretch">
{messages.map((message) => (
<div key={m.id} className="whitespace-pre-wrap py-2">
<strong>
{message.role === 'user' ? 'Utilisateur : ' : 'IA : '}
</strong>
{message.parts.map((part, i) => {
switch (part.type) {
case 'text':
return <div key={`${message.id}-${i}`}>{part.text}</div>;
case 'tool-invocation':
return (
<pre key={`${message.id}-${i}`}>{JSON.stringify(part.toolInvocation, null, 2)}</pre>
);
}
})}
</div>
))}
<form onSubmit={handleSubmit}>
<input
className="fixed bottom-0 w-full max-w-md p-2 mb-8 border border-gray-300 rounded shadow-xl text-black"
value={input}
placeholder="Demandez la météo (par exemple, 'Quel temps fait-il à Londres ?')"
onChange={handleInputChange}
disabled={isLoading}
/>
<button type="submit" disabled={isLoading} className="fixed bottom-0 right-0 p-2 mb-8 mr-2">
Envoyer
</button>
</form>
</div>
);
}
export default Chat
Note on Message
type: The Message type from @ai-sdk/react
now includes tool_calls
(an array of objects when the assistant wants to call tools) and tool_result
(when the role is 'tool', this contains the result from your tool execution). The useChat
hook manages adding these messages appropriately. The rendering above is a more detailed way to see these.
Étape 4 : Utilisation de l'outil de test
Exécutez votre application (pnpm dev
). Essayez des commandes telles que :
- "Quel temps fait-il à San Francisco ?"
- "Pouvez-vous me dire la température à Tokyo en degrés Fahrenheit ?"
Observez la sortie de la console sur votre serveur (où vous exécutez pnpm dev
). Vous devriez voir le journal "APPEL DE L'OUTIL : Récupération de la météo...". Le chatbot devrait ensuite répondre en utilisant les données météorologiques (fictives).
Si vous inspectez le tableau des messages
(par exemple, en l'enregistrant dans votre composant), vous verrez le déroulement complet :
- Message de l'utilisateur.
- Message d'assistant avec
tool_calls
(demande d'utilisergetWeather
). - Message de l'outil avec
tool_call_id
ettool_result
(contenant les données météorologiques). - Message final de l'assistant à l'utilisateur, intégrant les informations météorologiques.
Activation de l'exécution de l'outil en plusieurs étapes
Vous avez peut-être remarqué que bien que les résultats de l'outil apparaissent dans l'interface de chat, le modèle ne les utilise pas pour répondre à votre requête initiale. Cela se produit parce que une fois que le modèle génère un appel d'outil, il considère sa réponse comme complète.
Pour y remédier, vous pouvez activer les appels d'outils en plusieurs étapes en définissant l'option maxSteps
dans votre crochet useChat
. Cette fonctionnalité alimente automatiquement les résultats de l'outil dans le modèle, provoquant une génération de suivi. Dans ce scénario, elle garantit que le modèle utilise la sortie de l'outil météorologique pour répondre à la question de l'utilisateur.
Mettez à jour votre code côté client
Modify your app/page.tsx
file to include the maxSteps
option:
//app/page.tsx
'use client';
import { useChat } from '@ai-sdk/react';
const Chat = () => {
const { messages, input, handleInputChange, handleSubmit } = useChat({
maxSteps: 5,
});
// ... reste de votre code composant
}
export default Chat
Retournez au navigateur et demandez la météo pour un lieu spécifique. Cette fois, vous devriez voir le modèle utiliser les résultats de l'outil météorologique pour répondre directement à votre question.
En définissant maxSteps
à 5, vous permettez au modèle de prendre jusqu'à cinq étapes lors d'une seule génération. Cela permet des interactions plus complexes, donnant au modèle la capacité de rassembler et de traiter des informations sur plusieurs étapes si nécessaire. Vous pouvez voir cela en action en ajoutant un autre outil—par exemple, un qui prévoit la température.
// lib/tools.ts (ou en complément de votre outil météorologique actuel)
import { z } from 'zod';
import { tool } from 'ai';
export const forecastTool = tool({
description: 'Obtenez des prévisions météorologiques sur 3 jours pour une ville spécifique',
parameters: z.object({
city: z.string().describe('La ville pour laquelle obtenir les prévisions météorologiques (par exemple, Paris)'),
unit: z.enum(['celsius', 'fahrenheit']).default('celsius').describe('Unité de température'),
}),
execute: async ({ city, unit }) => {
console.log(`Récupération des prévisions sur 3 jours pour ${city} en ${unit}`);
// Données de prévision fictives
const forecasts = {
'paris': [
{ day: 'Demain', condition: 'Ensoleillé', temperature: unit === 'celsius' ? 23 : 73 },
{ day: 'Après-demain', condition: 'Nuageux', temperature: unit === 'celsius' ? 19 : 66 },
{ day: 'Dans 3 jours', condition: 'Pluie légère', temperature: unit === 'celsius' ? 17 : 62 }
],
'new york': [
{ day: 'Demain', condition: 'Pluvieux', temperature: unit === 'celsius' ? 16 : 61 },
{ day: 'Après-demain', condition: 'Venteux', temperature: unit === 'celsius' ? 18 : 64 },
{ day: 'Dans 3 jours', condition: 'Ensoleillé', temperature: unit === 'celsius' ? 22 : 72 }
]
};
const forecast = forecasts[city.toLowerCase()] || [
{ day: 'Demain', condition: 'Partiellement nuageux', temperature: unit === 'celsius' ? 20 : 68 },
{ day: 'Après-demain', condition: 'Ensoleillé', temperature: unit === 'celsius' ? 22 : 71 },
{ day: 'Dans 3 jours', condition: 'Couvert', temperature: unit === 'celsius' ? 18 : 65 }
];
return {
city,
unit,
forecast,
mockData: true,
};
},
});
Mettez à jour votre gestionnaire de route
Mettez à jour votre fichier app/api/chat/route.ts
pour ajouter un nouvel outil permettant de convertir la température de Fahrenheit en Celsius :
// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';
// Autoriser les réponses en streaming jusqu'à 60 secondes
export const maxDuration = 60;
export const runtime = 'edge';
export const POST = async (req: Request) => {
const { messages } = await req.json();
const result = await streamText({
model: openai('gpt-4o'), // Utiliser un modèle qui supporte bien les outils
messages,
tools: {
//Utiliser l'outil Météo défini précédemment
getWeather: weatherTool,
getForecast: forecastTool
},
});
return result.toDataStreamResponse();
}
Maintenant, lorsque vous demandez "Quel temps fait-il dans une ville en Celsius ?", vous devriez voir une interaction plus complète :
- Le modèle appellera l'outil météorologique pour la ville.
- Vous verrez le résultat de l'outil affiché.
- Il appellera ensuite l'outil de prévision pour fournir des prévisions météorologiques sur 3 jours.
- Le modèle utilisera ensuite ces informations pour fournir une réponse en langage naturel concernant la météo dans la ville.
Points clés
- Les outils permettent aux LLM d'interagir avec des systèmes externes ou d'exécuter du code spécifique que vous définissez.
zod
est excellent pour définir les schémas de paramètres des outils.- L'assistant d'outils et l'intégration du SDK IA de Vercel au sein de
streamText
etuseChat
rendent l'implémentation des outils assez fluide. - Le LLM nécessite des descriptions claires des outils et des paramètres pour les utiliser efficacement.
- L'exécution des outils se fait généralement sur le serveur pour des raisons de sécurité et pour accéder aux ressources du backend.
Quelle est la prochaine étape ?
Jusqu'à présent, nous nous sommes concentrés sur l'IA conversationnelle. Mais le SDK IA de Vercel est également excellent pour d'autres tâches d'IA. Dans la Partie 3, nous explorerons les "complétions" – générer du texte pour des cas d'utilisation autres que le chat, comme la résumé ou la création de contenu simple.