Construire facilement un Agent IA grâce à l'AI SDK

Fait partie de la série Agents IA et serveur MCP au service du web agentique

Avant d'aller plus loin, qu'est-ce qu'un Agent IA ? Clarifier ce concept rendra la suite de la série plus facile à suivre.

Un Agent IA est un logiciel qui utilise un modèle pour avancer vers l'objectif d'un utilisateur. Il peut interpréter une demande en langage naturel, décider s'il a besoin d'une capacité externe, l'utiliser, puis continuer jusqu'à pouvoir fournir un résultat utile. Les interactions avec les logiciels deviennent ainsi plus naturelles : l'utilisateur décrit le résultat attendu plutôt que de coordonner chaque étape lui-même.

Dans toute la série, nous utiliserons ces cinq termes de façon cohérente :

  • Une application IA est le produit de chat utilisé par une personne.
  • Un Agent IA est l'orchestration côté serveur qui donne au modèle accès à des outils et lui permet de continuer après les avoir utilisés.
  • Un outil est une fonction que l'Agent peut demander à l'application hôte d'exécuter.
  • Un serveur MCP expose des outils via le Model Context Protocol.
  • Un client MCP connecte l'Agent IA à un serveur MCP.

Ce que peut faire le modèle

Pour garder cette série ciblée, nous travaillerons avec des LLM textuels. Dans cet exemple, le modèle peut :

  1. Générer du texte à partir des messages qu'il reçoit.
  2. Utiliser le contexte disponible pour décider quoi dire ou faire ensuite.
  3. Demander l'appel d'un outil pour que l'application hôte réalise une action en dehors du modèle.

Note

Selon le provider, des fonctionnalités supplémentaires peuvent être disponibles, comme des outils intégrés ou des outils MCP. En fin de compte, tout est considéré comme un outil, qu'il s'agisse d'une fonction intégrée, d'une API externe via MCP ou d'un outil personnalisé.

Lorsque vous interagissez avec une IA, le modèle peut utiliser une ou plusieurs de ces capacités. Son raisonnement interne dépend du modèle et du fournisseur, et il n'est pas toujours exposé à votre application. Un modèle peut aussi produire du texte avant, entre ou après des appels d'outils.

Ce qui nous importe est le suivant : une fois l'appel d'un outil terminé, son résultat doit être redonné au modèle pour qu'il décide de la suite. Les modèles les plus récents peuvent appeler plusieurs outils, en séquence ou en parallèle, et générer des mises à jour de progression autour de ces appels.

Sans orchestration, un appel d'outil peut être le dernier événement traité par l'application. L'utilisateur verrait que le calcul a été effectué, mais ne recevrait pas la réponse finale du modèle. Notre Agent IA gérera automatiquement cette étape suivante.

Note

Il est important de noter que l'IA n'exécute pas directement les outils personnalisés. Elle demande au client, via une réponse dédiée, d'appeler l'outil pour elle. C'est pourquoi définir des outils revient souvent à définir un schéma JSON. Je recommande de lire le flux d'appel d'outils dans la documentation OpenAI.

Continuer après un appel d'outil

Pour résoudre ce problème, l'application a besoin d'une boucle bornée. Quand le modèle demande un outil, l'application l'exécute, ajoute le résultat à la conversation et laisse le modèle continuer.

En pseudo-code simplifié, cela ressemble à ceci :

js
messages = [userInput]

while (hasStepsRemaining) {
  response = AI.ask(messages)
  messages.push(response)

  if (response.hasReasoning()) {
    showProgress(response.reasoning)
  }

  if (response.isText()) {
    break
  }

  toolResult = runTool(response.toolCall)
  messages.push(toolResult)
}

response.hasReasoning() est facultatif : la réception de raisonnement dépend du modèle et du fournisseur. Lorsqu'il est disponible, il peut être affiché comme indicateur de progression, mais la réponse complète doit toujours être ajoutée à la conversation afin que l'étape suivante dispose de l'appel d'outil et du contexte du modèle associé. Cette boucle ne définit pas à elle seule un Agent IA. C'est le mécanisme qui permet à un Agent de traiter une tâche en plusieurs étapes de modèle et d'outils.

Note

Pour éviter une boucle infinie, définissez un nombre maximal d'itérations.

Notre agent

Je pense que nous sommes prêts à commencer à construire notre Agent IA.

Pour cette série, nous allons utiliser l'AI SDK. Nous aurions pu utiliser un SDK fourni par un prestataire comme OpenAI ou Anthropic, mais le niveau d'abstraction plus élevé de l'AI SDK nous facilitera la tâche tout en évitant l'enfermement fournisseur.

Pour nous assurer que nous sommes tous sur la même longueur d'onde, voici un aperçu de l'infrastructure que nous construisons.

Infrastructure de l'Agent IA.
Infrastructure de l'Agent IA.

Pour cette partie, nous allons nous concentrer sur l'implémentation côté serveur et la communication avec le fournisseur.

Pour cela, nous utiliserons Nitro, mais vous pouvez utiliser n'importe quel framework backend JS de votre choix.

Installation

Créez d'abord un nouveau projet Nitro :

bash
pnpm dlx giget@latest nitro ai-agent --install

Puis supprimez le fichier server/routes/index.ts, dont nous n'aurons pas besoin :

bash
rm server/routes/index.ts

Démarrer avec l'AI SDK

Maintenant que notre backend est en place, nous pouvons commencer à travailler avec l'AI SDK.

Installez le SDK :

bash
pnpm add ai @ai-sdk/openai

Nous installons aussi l'adaptateur OpenAI pour communiquer avec l'API OpenAI, mais vous pouvez utiliser n'importe quel autre adaptateur que vous préférez.

Pour communiquer avec OpenAI, configurez une clé API. Vous pouvez en obtenir une en créant un compte sur le site d'OpenAI. Puis définissez la variable d'environnement OPENAI_API_KEY dans votre fichier .env :

ini
NITRO_OPEN_AI_API_KEY=

Enfin, créez une variable d'exécution dans la configuration Nitro :

ts
export default defineNitroConfig({
  runtimeConfig: {
    openAiApiKey: '',
  },
  // ...
})

Nous sommes maintenant prêts à commencer à construire notre Agent IA.

Diffuser du texte

La première étape consiste à s'assurer que notre Agent IA peut générer du texte à partir de l'entrée utilisateur. Cela peut être plus difficile qu'il n'y paraît, mais l'AI SDK fournit un moyen simple d'y parvenir.

Créez un nouvel endpoint dans le serveur Nitro qui recevra la requête utilisateur et renverra la réponse générée par l'IA :

bash
mkdir server/api
touch server/api/chat.ts

Ensuite, créez l'endpoint dans server/api/chat.ts :

ts
import { createOpenAI } from '@ai-sdk/openai'
import { convertToModelMessages, streamText } from 'ai'
import { defineEventHandler, defineLazyEventHandler, readBody } from 'h3'
import { useRuntimeConfig } from 'nitropack/runtime'

export default defineLazyEventHandler(() => {
  const runtimeConfig = useRuntimeConfig()

  const model = createOpenAI({
    apiKey: runtimeConfig.openAiApiKey,
  })

  return defineEventHandler(async (event) => {
    const { messages } = await readBody(event)

    return streamText({
      model: model('gpt-5-nano'),
      system: `You are a helpful assistant.`,
      messages: convertToModelMessages(messages),
    }).toUIMessageStreamResponse()
  })
})

Il y a deux sections importantes dans ce fichier.

  1. Création du modèle OpenAI :
ts
const model = createOpenAI({
  apiKey: runtimeConfig.openAiApiKey,
})

Cela crée l'adaptateur pour le modèle OpenAI. Si vous souhaitez utiliser un autre fournisseur, changez le code de création de l'adaptateur. C'est aussi ici que nous utilisons openAiApiKey depuis la configuration d'exécution.

  1. Diffuser des réponses textuelles :
ts
return streamText({
  model: model('gpt-5-nano'),
  system: `You are a helpful assistant.`,
  messages: convertToModelMessages(messages),
}).toUIMessageStreamResponse()

La deuxième section importante est la diffusion des réponses textuelles. Nous utilisons la fonction streamText de l'AI SDK pour diffuser les réponses générées par l'IA au client. Nous lui donnons le modèle, un prompt système, et les messages de l'utilisateur. La fonction toUIMessageStreamResponse transforme la réponse dans un format adapté à la construction du frontend. Si vous voulez uniquement le texte, vous pouvez utiliser toTextStreamResponse.

Tous ces composants sont encapsulés dans un gestionnaire d'événements "lazy" pour garantir que le modèle n'est créé qu'une seule fois, lors de la première requête. Cela évite que chaque requête ne crée une nouvelle instance du modèle.

Vous pouvez tester l'endpoint de votre Agent IA avec la commande curl suivante :

bash
curl -X POST http://localhost:3000/api/chat \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "role": "user",
        "parts": [
          {
            "type": "text",
            "text": "Why is the sky blue?"
          }
        ]
      }
    ]
  }'

Cette commande envoie un message utilisateur d'exemple à votre endpoint /api/chat et diffuse la réponse générée par l'IA.

L'Agent IA diffusant des réponses textuelles.

À ce stade, l'endpoint peut diffuser une réponse textuelle.

Appeler un outil

Maintenant, nous pouvons ajouter un outil à notre Agent IA. Pour rappel, les outils sont des fonctions que l'Agent IA peut appeler pour effectuer des tâches spécifiques ; le code s'exécute côté serveur, pas chez le fournisseur d'IA.

Pour cet exemple, nous allons ajouter un simple outil d'addition. Avant d'aller plus loin, installez Zod pour créer des schémas :

bash
pnpm add zod

Ajoutez maintenant l'outil addition à notre Agent IA :

ts
return streamText({
  model: model('gpt-5-nano'),
  system: `You are a helpful assistant.`,
  tools: {
    addition: tool({
      description: 'Adds two numbers',
      inputSchema: z.object({
        a: z.number().describe('The first number'),
        b: z.number().describe('The second number'),
      }),
      execute: ({ a, b }) => ({
        a,
        b,
        result: a + b
      }),
    }),
  },
  messages: convertToModelMessages(messages),
}).toUIMessageStreamResponse()

Enfin, mettez à jour notre prompt pour s'assurer que l'IA utilisera le nouvel outil :

ts
return streamText({
  model: model('gpt-5-nano'),
  system: `You are a helpful assistant. You can use the tool to add two numbers together.`,
  // ...
}).toUIMessageStreamResponse()

Et essayons :

bash
curl -X POST http://localhost:3000/api/chat \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "role": "user",
        "parts": [
          {
            "type": "text",
            "text": "What is 2 + 2?"
          }
        ]
      }
    ]
  }'
L'Agent IA utilisant un outil pour effectuer un calcul.

Remarquez ce qui manque dans la réponse.

L'IA effectue le calcul en utilisant l'outil d'addition :

txt
data: {"type":"tool-output-available","toolCallId":"call_Zmi5NtcVDRFZsZHcsULlutnG","output":{"a":12,"b":24,"result":36}}

L'outil s'est exécuté correctement, mais cette exécution s'arrête après son résultat. Le modèle n'a pas encore reçu ce résultat dans une nouvelle étape et n'a donc pas généré de réponse finale, telle que « La réponse est 36 ». Nous devons le laisser continuer.

En faire un agent

L'AI SDK gère cette boucle pour nous. Nous devons seulement fixer un nombre maximum d'étapes.

ts
import { stepCountIs, streamText } from 'ai'

return streamText({
  model: model('gpt-5-nano'),
  system: `You are a helpful assistant. You can use the tool to add two numbers together.`,
  stopWhen: stepCountIs(2),
  // ...
}).toUIMessageStreamResponse()

L'option stopWhen: stepCountIs(2) autorise jusqu'à deux étapes : une pour l'appel d'outil et une pour la réponse fondée sur son résultat. Le SDK s'arrête aussi lorsque la dernière étape produit une réponse textuelle finale. Dans un vrai Agent IA, une limite plus haute, mais toujours bornée, peut être nécessaire lorsqu'une tâche demande plusieurs outils.

Note

Je recommande de lire la page Loop Control de la documentation pour comprendre ce qui se passe sous le capot.

Essayons :

bash
curl -X POST http://localhost:3000/api/chat \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {
        "role": "user",
        "parts": [
          {
            "type": "text",
            "text": "What is 2 + 2?"
          }
        ]
      }
    ]
  }'
L'Agent IA utilisant un outil et générant une réponse.

Notre Agent IA peut maintenant utiliser un outil et générer une réponse à partir de son résultat. Dans l'article suivant, nous déplacerons cet outil dans un serveur MCP afin qu'il puisse être réutilisé au-delà de cette seule application.

Pd

Merci de me lire ! Je m'appelle Estéban, et j'adore écrire sur le développement web et le parcours humain qui l'entoure.

Je code depuis plusieurs années maintenant, et j'apprends encore de nouvelles choses chaque jour. J'aime partager mes connaissances avec les autres, car j'aurais aimé avoir accès à des ressources aussi claires et complètes lorsque j'ai commencé à apprendre la programmation.

Si vous avez des questions ou souhaitez discuter, n'hésitez pas à commenter ci-dessous ou à me contacter sur Bluesky, X, et LinkedIn.

J'espère que vous avez apprécié cet article et appris quelque chose de nouveau. N'hésitez pas à le partager avec vos amis ou sur les réseaux sociaux, et laissez un commentaire ou une réaction ci-dessous, cela me ferait très plaisir ! Si vous souhaitez soutenir mon travail, vous pouvez me sponsoriser sur GitHub !

Continuer la lectureMCP pour fournir des capacités supplémentaires à l'Agent IA

Réactions

Discussions

Ajouter un commentaire

Vous devez être connecté pour accéder à cette fonctionnalité.