Accéder au contenu principal

OpenAI Realtime API (API en temps réel) : Un guide avec des exemples

Apprenez à créer des applications d'IA en temps réel avec l'API temps réel d'OpenAI. Ce tutoriel couvre les WebSockets, la configuration de Node.js, la messagerie texte/audio, l'appel de fonctions et le déploiement d'une démo d'assistant vocal React.
Actualisé 16 janv. 2025  · 15 min de lecture

La nouvelle OpenAI Realtime API nous permet d'intégrer des expériences multimodales rapides et à faible latence dans nos applications. Grâce à cette API, nous pouvons créer des interactions vocales transparentes entre les utilisateurs et de grands modèles linguistiques (LLM). grands modèles de langage (LLM).

Grâce à cette API, il n'est plus nécessaire de recourir à plusieurs modèles pour obtenir des expériences vocales, car elle offre une solution complète dans une API intégrée. Il vise non seulement à réduire la latence, mais aussi à conserver les nuances émotionnelles et le flux naturel des conversations.

Dans cet article, nous allons apprendre à utiliser l'API OpenAI Realtime pour créer des assistants IA contrôlés par la voix. Nous créerons des connexions WebSocket persistantes à l'aide de Node.js et nous verrons comment les utiliser dans le terminal pour communiquer avec l'API. En outre, je vous guiderai dans le déploiement d'une application React qui utilise les capacités de cette API.

Générer la clé API pour l'API OpenAI Realtime

Pour utiliser l'outil OpenAI Realtime API, nous devons d'abord générer une clé API. Pour ce faire, rendez-vous sur la page de la clé API. Notez qu'un compte est nécessaire pour cela. En haut de la page, cliquez sur le bouton "Créer une nouvelle clé secrète".

Créer une nouvelle clé secrète pour l'API OpenAI Realtim

Une fenêtre pop-up s'ouvre. Nous pouvons utiliser les options par défaut et cliquer sur "Créer une clé secrète".

Options par défaut pour créer une clé API OpenAI

Lorsque la clé est créée, nous avons la possibilité de la copier. Veillez à le copier avant de fermer la fenêtre, car c'est la seule fois où il sera affiché.

Copier la clé API OpenAI

Si la clé est perdue, nous pouvons toujours la supprimer et en créer une nouvelle.

Pour stocker la clé, nous vous recommandons de créer un fichier nommé .env et d'y enregistrer la clé au format suivant :

OPENAI_API_KEY=<paste_they_key_here>

Nous utiliserons ce fichier tout au long de cet article pour nous connecter à l'API.

Considérations sur les coûts de l'API en temps réel

Avant de poursuivre, notez que l'API en temps réel n'est pas gratuite et que nous devons ajouter des crédits à notre compte pour l'utiliser. Nous pouvons ajouter des crédits sur la page de facturationqui se trouve dans notre profil.

Pour vous donner une idée des coûts, voici un aperçu de ce que m'a coûté l'expérimentation de l'API pendant que je travaillais sur cet article :

Coûts d'utilisation de l'API OpenAI Realtime

J'ai dépensé environ cinq dollars au total. Ce n'est pas énorme, mais c'est beaucoup plus cher que les autres API fournies par OpenAI. Gardez cela à l'esprit lorsque vous travaillez sur cet article.

La majeure partie du coût (première barre) correspond au fait que j'ai joué avec l'application React console que nous explorons à la fin de cet article. Le reste, environ un demi-dollar, correspond à ce que m'a coûté l'utilisation de l'API à l'aide de WebSockets. Il est donc possible de suivre cet article pour moins d'un dollar.

Pour plus de détails sur la tarification de l'API, consultez la section "Realtime API" sur leur page de tarification. leur page de tarification.

Développer des applications d'IA

Apprenez à créer des applications d'IA à l'aide de l'API OpenAI.
Commencez à Upskiller gratuitement

Utiliser l'API temps réel avec les WebSockets

Contrairement à d'autres composants de l OpenAI APIOpenAI, l'API temps réel utilise les WebSockets. WebSockets est un protocole de communication qui établit un canal de communication bidirectionnel entre un client et un serveur. Contrairement au modèle conventionnel requête-réponse utilisé par HTTP, les WebSockets permettent des interactions continues et en temps réel. Les WebSockets sont donc particulièrement adaptés aux applications en temps réel, telles que le chat vocal.

Cet article explique le fonctionnement des WebSockets et présente plusieurs exemples d'interaction avec l'API Realtime. 

Nous allons utiliser Node.js, nous devons donc nous assurer qu'il est installé sur notre ordinateur. Si ce n'est pas le cas, nous pouvons télécharger et installer Node.js à partir de son site officiel.

Initialisation du script

Pour suivre le processus, nous vous recommandons de créer un dossier avec le fichier .env créé ci-dessus. Dans ce dossier, exécutez la commande suivante pour initialiser le script :

npm init -y && touch index.js

Une fois cette commande terminée, ces fichiers devraient se trouver dans le dossier :

Structure initiale des répertoires

Installation des dépendances

Commencez par installer deux paquets :

  • ws: Il s'agit du paquet WebSocket, le principal paquet nécessaire pour interagir avec l'API.
  • dotenv: Un paquet utilitaire qui charge la clé API à partir du fichier .env.

Installez-les en exécutant la commande suivante :

npm install ws dotenv

Connexion à l'API temps réel

Pour établir une connexion avec l'API en temps réel, nous créons un nouvel objet WebSocket en lui transmettant l'URL de l'API et les en-têtes contenant les informations nécessaires pour s'y connecter :

// Import the web socket library
const WebSocket = require("ws");
// Load the .env file into memory so the code has access to the key
const dotenv = require("dotenv");
dotenv.config();
function main() {
  // Connect to the API
  const url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01";
  const ws = new WebSocket(url, {
      headers: {
          "Authorization": "Bearer " + process.env.OPENAI_API_KEY,
          "OpenAI-Beta": "realtime=v1",
      },
  });
}
main();

Mise en place d'une action lors de l'ouverture de la connexion

Le code ci-dessus crée la connexion web socket à l'API mais n'en fait rien pour l'instant.

Les WebSockets nous permettent de mettre en place des actions à exécuter lorsque certains événements se produisent. Nous pouvons utiliser l'événement open pour spécifier le code à exécuter une fois la connexion établie.

La syntaxe générique pour ajouter un récepteur d'événements est la suivante :

ws.on(<event>, <function>);

Remplacer par une chaîne de caractères contenant le nom de l'événement et par une fonction à exécuter lorsque l'événement se produit. 

Voici comment nous pouvons afficher du texte une fois que la connexion est prête :

// Add inside the main() function of index.js after creating ws
async function handleOpen() {
  console.log("Connection is opened");
}
ws.on("open", handleOpen);

Pour exécuter ce code, nous utilisons la commande :

node index.js

Si la clé API est correctement définie, nous verrons le message "Connection is open" dans le terminal. Le script continuera à s'exécuter parce que la connexion est toujours ouverte, nous devons donc l'arrêter manuellement.

Mise en place d'une action lors de la réception d'un message

Un autre événement auquel nous pouvons répondre lorsque nous utilisons les WebSockets est l'événement message. Elle est déclenchée à chaque fois qu'un message est reçu du serveur. Ajoutons une fonction qui affiche chaque message reçu :

// Add inside the main() function of index.js
 async function handleMessage(messageStr) {
    const message = JSON.parse(messageStr);
    console.log(message);
}
ws.on("message", handleMessage);

L'exécution du script devrait également permettre d'afficher l'événement session.created événement que l'API envoie lorsque la session est initialisée.

Autres événements WebSocket

Ci-dessus, nous avons appris à ajouter des récepteurs aux événements open et message. Les WebSockets prennent en charge deux événements supplémentaires que nous n'utiliserons pas dans nos exemples.

L'événement close peut être utilisé pour ajouter un rappel lorsque la socket est fermée :

async function handleClose() {
  console.log(“Socket closed”);
}
ws.on(“close”, handleClose);

L'événement error est utilisé pour ajouter un rappel en cas d'erreur :

async function handleError(error) {
  console.log(“Error”, error);
}
ws.on(“error”, handleError);

Communiquer avec l'API temps réel

Travailler avec des sockets Web nous oblige à programmer dans un environnement événementiel d'événements. Des messages sont envoyés dans les deux sens sur le canal de communication et nous ne pouvons pas contrôler le moment où ces messages seront livrés ou reçus. 

Le code qui initie la communication doit être ajouté à l'intérieur de handleOpen(). O Dans le cas contraire, une erreur se produirait car ce code pourrait être exécuté avant que le canal de communication de la socket web ne soit créé.

Il en va de même pour le code de traitement des messages. Toute la logique doit être intégrée dans la fonction handleMessage().

Dans les exemples suivants, nous utiliserons le code suivant comme point de départ. La plupart des changements concernent la mise à jour des sites handleOpen() et handleMessage().

// Import the web socket library
const WebSocket = require("ws");
// Load the .env file into memory so the code has access to the key
const dotenv = require("dotenv");
dotenv.config();
function main() {
  // Connect to the API
  const url = "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01";
  const ws = new WebSocket(url, {
      headers: {
          "Authorization": "Bearer " + process.env.OPENAI_API_KEY,
          "OpenAI-Beta": "realtime=v1",
      },
  });
  async function handleOpen() {
    console.log("Connection is opened");
  }
  ws.on("open", handleOpen);
  async function handleMessage(messageStr) {
    const message = JSON.parse(messageStr);
    console.log(message);
  }
  ws.on("message", handleMessage);  
}
main();

Utiliser l'API temps réel pour envoyer et recevoir du texte

La communication avec l'API en temps réel se fait à l'aide d'événements. L'API de documentation en temps réel OpenAI API de documentation en temps réel d'OpenAI dresse la liste des événements qu'elle prend en charge. Nous utilisons l'option conversation.item.create événement pour initier une conversation. Les événements sont représentés par des objets JSON dont les champs sont décrits dans la documentation.

Voici un exemple de conversation.item.create envoyant l'invite "Expliquez en une phrase ce qu'est un socket web" :

const createConversationEvent = {
  type: "conversation.item.create",
  item: {
    type: "message",
    role: "user",
    content: [
      {
        type: "input_text",
        text: "Explain in one sentence what a web socket is"
      }
    ]
  }
};

Cet événement indique à l'API que nous voulons entamer une conversation textuelle. Elle est spécifiée dans le champ content, en utilisant le type ”input_text” et en fournissant une invite textuelle.

Nous utilisons la méthode ws.send() pour envoyer un message. Le paquetage web socket attend une chaîne comme argument, nous devons donc convertir notre événement JSON en chaîne à l'aide de la fonction JSON.stringify(). En combinant ces éléments, voici comment nous pouvons envoyer l'événement ci-dessus :

ws.send(JSON.stringify(createConversationEvent));

Cela permettra d'engager la conversation, mais ne déclenchera pas l'envoi automatique d'une réponse par l'API. Pour déclencher une réponse, nous envoyons un response.create événement. En voici un exemple :

const createResponseEvent = {
  type: "response.create",
  response: {
      modalities: ["text"],
      instructions: "Please assist the user.",
  }
}
ws.send(JSON.stringify(createResponseEvent));

Cet événement utilise le paramètre de réponse modalities pour demander une réponse textuelle. Les instructions sont la partie la plus importante, décrivant ce que nous voulons que le modèle fasse, dans ce cas, une invite générique demandant d'aider l'utilisateur.

Nous envoyons ces deux événements dans la fonction handleOpen() afin qu'une conversation soit engagée dès que la connexion est établie. Voici la mise en œuvre complète de la fonction handleOpen() de cet exemple :

async function handleOpen() {

  // Define what happens when the connection is opened

  // Create and send an event to initiate a conversation

  const createConversationEvent = {

    type: "conversation.item.create",

    item: {

      type: "message",

      role: "user",

      content: [

        {

          type: "input_text",

          text: "Explain in one sentence what a web socket is"

        }

      ]

    }

  };

  // Create and send an event to initiate a response

  ws.send(JSON.stringify(createConversationEvent));

  const createResponseEvent = {

    type: "response.create",

    response: {

        modalities: ["text"],

        instructions: "Please assist the user.",

    }

  }

  ws.send(JSON.stringify(createResponseEvent));

}

En ce qui concerne les messages entrants, trois types d'événements méritent d'être signalés dans cet exemple : les événements response.text.delta, response.text.done et response.done:

  • Les response.text.delta événements contiennent la réponse décomposée en morceaux dans le champ delta. Ils sont importants lorsque nous voulons offrir une expérience en temps réel, car ils nous permettent de diffuser directement la réponse morceau par morceau. 
  • L'événement response.text.done événement marque la fin de la réponse textuelle et contient la réponse complète dans le champ text.
  • L'événement response.done événement marque la fin de la réponse.

Nous pouvons spécifier comment nous voulons que notre script réponde à ces événements en utilisant une déclaration switch dans la fonction handleMessage():

async function handleMessage(messageStr) {
  const message = JSON.parse(messageStr);
  // Define what happens when a message is received
  switch(message.type) {
    case "response.text.delta":
      // We got a new text chunk, print it
      process.stdout.write(message.delta);
    break;
    case "response.text.done":
      // The text is complete, print a new line
      process.stdout.write("\n");
    break;
    case "response.done":
      // Response complete, close the socket
      ws.close();
    break;
  }
}

Dans cet exemple, nous utilisons l'événement response.text.delta pour imprimer des morceaux de la réponse sur la console au fur et à mesure que nous la recevons. Lorsque la réponse est complète, l'événement response.text.done est déclenché et nous imprimons une nouvelle ligne pour indiquer que la sortie est terminée. Enfin, nous fermons le socket web lorsque nous recevons l'événement response.done.

Pour exécuter cet exemple, nous collons ces fonctions dans le code du modèle ci-dessus et nous l'exécutons à l'aide de la commande :

node index.js

Cela génère une réponse dans le terminal à l'invite "Expliquez en une phrase ce qu'est un socket web", comme lorsque nous utilisons la fonction ChatGPT.

Le code complet de l'exemple de texte est disponible ici.

Utiliser l'API temps réel pour envoyer et recevoir de l'audio

L'exemple précédent montre comment nous traitons les données textuelles. Cependant, le véritable intérêt de l'API Realtime est de créer un assistant vocal qui répond en temps réel.

Le traitement des données audio est un peu plus compliqué que celui des données textuelles. Nous passerons sur certains détails spécifiques au fonctionnement de l'audio, car ils nous éloigneraient du sujet principal de cet article.

Tout d'abord, nous installons deux paquets :

npm install node-record-lpcm16 speaker
  • node-record-lpcm16 enregistre le son du microphone afin de pouvoir envoyer une invite vocale.
  • speaker est utilisée pour diffuser la réponse vocale de l'IA.

Nous devons également installer SoX (Sound eXchange), un utilitaire de ligne de commande pour le traitement audio que la bibliothèque node utilisera pour interfacer avec le microphone et enregistrer de l'audio. Utilisez brew install sox pour l'installer sur macOS ou sudo apt install sox sur Linux.

Une fois ces paquets installés, nous les importons et ajoutons une fonction startRecording() qui enregistre les invites audio de l'utilisateur. Nous n'expliquons pas la fonction en détail car cela nous éloignerait trop de notre sujet principal. 

Ajoutez le code suivant au fichier index.js après avoir chargé l'environnement :

// Add to index.js before the main() function
// Import the web socket library
const WebSocket = require("ws");
// Load the .env file into memory so the code has access to the key
const dotenv = require("dotenv");
dotenv.config();
const Speaker = require("speaker");
const record = require("node-record-lpcm16");
// Function to start recording audio
function startRecording() {
  return new Promise((resolve, reject) => {
    console.log("Speak to send a message to the assistant. Press Enter when done.");
    // Create a buffer to hold the audio data
    const audioData = [];
    // Start recording in PCM16 format
    const recordingStream = record.record({
      sampleRate: 16000, // 16kHz sample rate (standard for speech recognition)
      threshold: 0, // Start recording immediately
      verbose: false,
      recordProgram: "sox", // Specify the program
    });
    // Capture audio data
    recordingStream.stream().on("data", (chunk) => {
      audioData.push(chunk); // Store the audio chunks
    });
    // Handle errors in the recording stream
    recordingStream.stream().on("error", (err) => {
      console.error("Error in recording stream:", err);
      reject(err);
    });
    // Set up standard input to listen for the Enter key press
    process.stdin.resume(); // Start listening to stdin
    process.stdin.on("data", () => {
      console.log("Recording stopped.");
      recordingStream.stop(); // Correctly stop the recording stream
      process.stdin.pause(); // Stop listening to stdin
      // Convert audio data to a single Buffer
      const audioBuffer = Buffer.concat(audioData);
      // Convert the Buffer to Base64
      const base64Audio = audioBuffer.toString("base64");
      resolve(base64Audio); // Resolve the promise with Base64 audio
    });
  });
};

La fonction startRecording() enregistre le son du microphone et attend que vous appuyiez sur la touche "Enter". 

Ensuite, nous mettons à jour la fonction main() en initialisant le fichier Speaker() qui est utilisé pour jouer la réponse de l'IA :

// Add to the main() function after ws is initialized
const speaker = new Speaker({
  channels: 1, // Mono or Stereo
  bitDepth: 16, // PCM16 (16-bit audio)
  sampleRate: 24000, // Common sample rate (44.1kHz)
});

Ceci étant dit, nous pouvons mettre en œuvre handleOpen() et handleMessage() pour traiter l'audio.

Dans la fonction handleOpen(), il suffit d'appeler la fonction startRecording() pour enregistrer l'invite audio de l'utilisateur. Nous devons également mettre à jour légèrement les événements :

  1. Mettez à jour le contenu du site createConversationEvent pour utiliser le type ”input_audio” au lieu de ”input_text et remplacez le champ text par           audio: base64AudioData.
  2. Ajoutez ”audio” à la modalité de réponse dans createResponseEvent.

Voici la fonction handleOpen() mise à jour :

async function handleOpen() {
  // Define what happens when the connection is opened
  const base64AudioData = await startRecording();
  const createConversationEvent = {
    type: "conversation.item.create",
    item: {
      type: "message",
      role: "user",
      content: [
        {
          type: "input_audio",
          audio: base64AudioData,
        },
      ],
    },
  };
  ws.send(JSON.stringify(createConversationEvent));
  const createResponseEvent = {
    type: "response.create",
    response: {
      modalities: ["text", "audio"],
      instructions: "Please assist the user.",
    },
  };
  ws.send(JSON.stringify(createResponseEvent));
}

Pour mettre en œuvre la fonction handleMessage(), nous modifions l'événement ”response.audio.delta” afin de mettre à jour la mémoire tampon audio et de lire le nouveau delta sonore :

case "response.audio.delta":
  // We got a new audio chunk
  const base64AudioChunk = message.delta;
  const audioBuffer = Buffer.from(base64AudioChunk, "base64");
  speaker.write(audioBuffer);
  break;

Nous supprimons l'événement ”response.text.done” de l'instruction de commutation et mettons à jour l'événement ”response.done” pour arrêter l'orateur :

case "response.audio.done":
  speaker.end();
  ws.close();
  break;

La mise en œuvre finale de la fonction handleMessage() se présente comme suit :

function handleMessage(messageStr) {
  const message = JSON.parse(messageStr);
  // Define what happens when a message is received
  switch (message.type) {
    case "response.audio.delta":
      // We got a new audio chunk
      const base64AudioChunk = message.delta;
      const audioBuffer = Buffer.from(base64AudioChunk, "base64");
      speaker.write(audioBuffer);
      break;
    case "response.audio.done":
      speaker.end();
      ws.close();
      break;
  }
}

Pour exécuter cet exemple, appliquez ces modifications au code du modèle et exécutez-le à l'aide de la commande :

node index.js

Le microphone commence à enregistrer. Nous pouvons formuler notre demande et appuyer sur "Entrée" pour l'envoyer. La réponse de l'IA est alors diffusée sur les haut-parleurs (assurez-vous que le microphone n'est pas coupé et que les haut-parleurs ont du volume).

Le code complet de l'exemple audio est disponible ici.

Appel de fonction

Une caractéristique intéressante de l'API OpenAI est la possibilité d'effectuer des appels de fonction. Nous pouvons ajouter des fonctions à l'assistant et s'il détecte que l'une de ces fonctions peut être utile pour fournir la réponse, il enverra un événement demandant l'appel d'une fonction spécifique.

La documentation OpenAI fournit le diagramme suivant expliquant le cycle de vie d'un appel de fonction :

Le cycle de vie de l'appel de fonction

Source : OpenAI

Le diagramme montre que le client doit fournir les définitions des fonctions que le LLM peut appeler. Par ailleurs, l'exécution de la fonction se fera du côté du client ; l'IA enverra un événement demandant l'appel de la fonction et ses arguments. Ensuite, nous sommes chargés de renvoyer le résultat.

Donnons à notre assistant une fonction qui additionne deux nombres. Nous construirons cet exemple en étendant l'exemple audio ci-dessus.

Pour spécifier les fonctions disponibles, nous devons fournir au LLM une liste d'outils. Chaque outil est un objet JSON qui spécifie les informations relatives à la fonction. Voici comment nous pouvons définir un outil pour la fonction somme :

const sumTool = {
  type: "function",
  name: "calculate_sum",
 description: "Use this function when asked to add numbers together, for example when asked 'What's 4 + 6'?.",
  parameters: {
      type: "object",
      properties: {
          "a": { "type": "number" },
          "b": { "type": "number" }
      },
      required: ["a", "b"]
  }
}

Expliquons la structure de l'objet :

  • Le site type précise que nous définissons une fonction.
  • Le site name est utilisé pour identifier la fonction. C'est ce que le LLM utilise pour nous indiquer la fonction qu'il souhaite appeler.
  • Le site description est utilisé pour identifier le moment où le LLM doit utiliser cette fonction.
  • Les parameters sont utilisés pour spécifier les arguments de la fonction. Dans ce cas, deux nombres nommés a et b.

L'étape suivante consiste à définir la fonction dans notre code. Nous utiliserons un dictionnaire avec la clé calculate_sum pour faciliter l'appel de la fonction appropriée lorsque nous répondons à un événement d'appel de fonction :

const functions = {
  calculate_sum: (args) => args.a + args.b,
}

L'API fournira les arguments de la fonction sous la forme d'un dictionnaire ayant la même structure que celle définie sur le site parameters ci-dessus. Dans ce cas, pour ajouter, par exemple, 3 et 5, le dictionnaire serait {“a”: 3, “b”: 5}.

Les constantes sumTool et functions peuvent être ajoutées au sommet de index.js, après les importations et avant la fonction main().

Ensuite, nous mettons à jour l'événement response.create pour indiquer au LLM que le site sumTools est disponible. Pour ce faire, les champs tools et tool_choice sont ajoutés au champ response:

const createResponseEvent = {
  type: "response.create",
  response: {
    modalities: ["text", "audio"],
    instructions: "Please assist the user.",
    tools: [sumTool], // New
    tool_choice: "auto", // New
  },
};

Lorsque le LLM décide d'appeler une fonction, il émet un événement response.function_call_arguments.done. Nous devons y répondre :

  1. Obtenir les informations sur la fonction et appeler la fonction.
  2. Envoi du résultat de l'appel de fonction.
  3. Demande de réponse.

Nous traitons ce problème en ajoutant le cas suivant à la déclaration switch à l'intérieur de la fonction hanldeMessage():

case "response.function_call_arguments.done":
  console.log(Using function ${message.name} with arguments ${message.arguments});
  // 1. Get the function information and call the function
  const function_name = message.name;
  const function_arguments = JSON.parse(message.arguments);
  const result = functions[function_name](function_arguments);
  // 2. Send the result of the function call
  const functionOutputEvent = {
    type: "conversation.item.create",
    item: {
      type: "function_call_output",
      role: "system",
      output: ${result},
    }
  };
  ws.send(JSON.stringify(functionOutputEvent));
  // 3. Request a response
  ws.send(JSON.stringify({type: "response.create"}));
  break;

Si nous exécutons maintenant le script et demandons le résultat de l'addition de deux nombres, le modèle doit appeler la fonction et fournir le résultat.

Cette fonction est relativement simple, mais comme elle est exécutée par le client, elle peut être n'importe quoi. Dans la section suivante, nous verrons deux exemples de fonctions plus complexes.

Le code complet de cet exemple est disponible ici.

Développez dès aujourd'hui vos compétences en matière de MLOps

Partez de zéro et acquérez des compétences MLOps qui vous permettront de développer votre carrière.

Application de démonstration utilisant l'API temps réel d'OpenAI

L'équipe OpenAI fournit une application React de démonstration pour présenter l'API temps réel. Nous verrons ici comment l'installer et comment il fonctionne. C'est un excellent point de départ pour construire une application plus complexe.

Configuration de l'application

Il n'est pas nécessaire d'avoir des connaissances en React pour le mettre en place et le faire fonctionner. Cependant, vous devez être familier avec React pour le modifier ou l'étendre.

Leur application est hébergée dans ce dépôt. Pour le mettre en place, commencez par clonage en utilisant les fichiers Git suivante :

git clone org-14957082@github.com:openai/openai-realtime-console.git

Vous pouvez également le télécharger manuellement depuis l'interface GitHub.

Pour installer l'application, nous utilisons la commande NPM (node package manage) suivante :

npm install

Une fois l'installation terminée, créez un fichier nommé .env dans le dossier racine du projet et collez le contenu suivant :

OPENAI_API_KEY=<openai_api_key>
REACT_APP_LOCAL_RELAY_SERVER_URL=http://localhost:8081

Remplacez par la clé API OpenAI.

L'application devrait maintenant être prête à être exécutée. Il est composé de deux parties :

  1. Un frontend React qui consiste en l'interface utilisateur web de l'application.
  2. Un serveur relais est utilisé comme intermédiaire entre le frontend et l'API OpenAI. 

Interaction entre l'API, le serveur relais et le frontend

L'objectif principal de l'implémentation d'un serveur relais entre le frontend et l'API OpenAI est de stocker de manière sécurisée la clé API. Il est impossible d'interagir avec l'API sans cette clé.

Cependant, si la clé était stockée sur le frontend, elle serait accessible à n'importe quel utilisateur. La solution consiste donc à mettre en place un serveur qui stocke la clé en toute sécurité et facilite l'échange de données entre l'API et le frontend. Dans ce scénario particulier, les problèmes de sécurité sont minimes puisque l'application ne sera exécutée que localement. 

Pour lancer l'application, il est nécessaire d'initier à la fois le serveur relais et le frontend. Pour démarrer le serveur relais, utilisez la commande suivante :

npm run relay

Pour démarrer le frontend React, utilisez la commande :

npm start

Une fois le chargement terminé, un onglet s'ouvre dans le navigateur avec l'application en cours d'exécution.

Interface de console en temps réel

Utiliser l'application

Avant de commencer à utiliser l'application, assurez-vous que l'ordinateur n'est pas en mode silencieux et autorisez l'accès du microphone à l'application.

Nous commençons par cliquer sur le bouton "connecter". Cela enverra un message "Hello" à l'API Realtime, et nous recevrons un message d'accueil.

Connexion à l'API

Une fois la connexion établie, un nouveau bouton apparaît au centre, permettant de parler à l'assistant IA. 

Enregistrement d'un message vocal

Pour l'utiliser, appuyez sur le bouton et parlez sans le relâcher. Le message est envoyé lorsque le bouton est relâché.

L'application dispose également d'un mode VAD (détection de l'activité vocale) dans lequel il n'est pas nécessaire d'appuyer sur un bouton. Dans ce mode, l'application écoute en permanence, ce qui nous permet d'avoir une conversation active avec l'assistant. Pour l'utiliser, il suffit d'appuyer sur le bouton "vad" et de parler.

Passage au mode de détection de l'activité vocale

Fonctions

Comme nous l'avons appris, l'API en temps réel offre une fonctionnalité qui permet à l'IA d'exécuter des fonctions spécifiques. Cette démonstration présente deux fonctions : l'une pour demander les prévisions météorologiques dans un lieu particulier et l'autre pour ajouter des éléments de mémoire afin de personnaliser l'assistant. 

Expérimentez ces fonctions en posant des questions telles que "Quel temps fera-t-il demain à New York ?" et en exprimant des préférences telles que "Ma couleur préférée est le bleu". L'assistant répondra verbalement à ces questions et les informations seront également affichées sur le côté droit de l'application.

Les affichages des fonctions de localisation et de mémorisation

Il est arrivé que je demande un bulletin météorologique et que la réponse indique qu'il n'était pas possible d'y accéder à ce moment-là. Cependant, les informations étaient systématiquement affichées sur le côté droit. S'agissant d'une application de démonstration, elle n'est pas censée être un produit entièrement fonctionnel, mais sert plutôt à présenter les capacités de l'API.

Comprendre le client

Cette section nécessite une compréhension de haut niveau de React pour suivre les détails de la mise en œuvre de l'application.

Nous allons examiner le fichier ConsolePage.tsx . C'est ici qu'est définie la majeure partie de la logique de l'application. L'application de démonstration n'utilise pas les WebSockets bruts comme nous l'avons fait dans nos exemples d'applications en ligne de commande Node.js. Ils ont créé un client en temps réel qui permet d'interagir avec l'API. C'est ce qui est importé en tête du fichier :

import { RealtimeClient } from '@openai/realtime-api-beta';

L'intégration avec l'API est définie dans cet appel useEffect() . Le code contenu dans ce site useEffect() est exécuté lorsque la page de la console est rendue initialement. Similaire à notre script Node.js, il décrit comment répondre aux événements de l'API. La principale différence réside dans l'utilisation de l'enveloppe client RealtimeClient.

Définir les outils

La fonction RealtimeClient.addTool() permet de définir les outils. Il prend deux paramètres :

  • L'objet JSON de définition de l'outil.
  • La fonction à exécuter.

Cette approche simplifie l'intégration des outils puisque le client est déjà équipé pour gérer les événements et automatiser les invocations de fonctions. L'outil de mémoire est défini icitandis que la définition de l'outil météo est définie ici.

Par exemple, pour ajouter l'outil somme défini précédemment, nous pouvons procéder comme suit :

client.addTool(
  {
    name: "calculate_sum",
    description: "Use this function when asked to add numbers together, for example when asked 'What's 4 + 6'?.",
    parameters: {
        type: "object",
        properties: {
            "a": { "type": "number" },
            "b": { "type": "number" }
        },
        required: ["a", "b"]
    }
  },
  (a: number, b: number): number => a + b
);

Notez que l'application utilise TypeScript, ce qui nécessite la spécification des types dans la définition de la fonction.

Écouter les événements

Pour écouter un événement, la fonction RealtimeClient.on() est utilisée. Il accepte deux paramètres : 

  1. Le nom de l'événement.
  2. La fonction de rappel à exécuter.

Cette approche est similaire à la fonction WebSocket.on() utilisée précédemment, sauf qu'elle met en œuvre un ensemble différent d'événements. Leur page GitHub fournit la liste des événements pris en charge.

Dans cet exemple particulier, les événements suivants sont utilisés :

  • L'événement realtime.event ici est utilisé pour conserver un journal de tous les événements.
  • L'événement error ici enregistre simplement les erreurs dans la console à des fins de débogage.
  • L'événement conversation.interrupted ici est utilisé pour annuler les demandes lorsque la conversion est interrompue.
  • Enfin, l'événement conversation.updated ici est utilisé pour ajouter de nouvelles données audio au flux audio lorsque de nouveaux chucks arrivent de l'API.

Conclusion

Dans ce tutoriel, nous avons exploré l'API Realtime d'OpenAI et la façon dont elle utilise les WebSockets pour la communication en temps réel. Nous avons abordé la configuration d'un environnement Node.js pour interagir avec l'API, l'envoi et la réception de messages textuels et audio, et la mise en œuvre d'appels de fonctions pour améliorer les fonctionnalités.

Nous avons également exploré l'application React de démonstration de l'OpenAI, qui montre comment déployer une application d'assistant vocal de base.

Pour en savoir plus sur les derniers outils de développement OpenAI, je vous recommande ces tutoriels :


François Aubry's photo
Author
François Aubry
LinkedIn
L'enseignement a toujours été ma passion. Dès mes premiers jours d'études, j'ai cherché avec enthousiasme des occasions de donner des cours particuliers et d'aider d'autres étudiants. Cette passion m'a amenée à poursuivre un doctorat, où j'ai également été assistante d'enseignement pour soutenir mes efforts académiques. Au cours de ces années, j'ai trouvé un immense épanouissement dans le cadre d'une classe traditionnelle, en favorisant les liens et en facilitant l'apprentissage. Cependant, avec l'avènement des plateformes d'apprentissage en ligne, j'ai reconnu le potentiel de transformation de l'éducation numérique. En fait, j'ai participé activement au développement d'une telle plateforme dans notre université. Je suis profondément engagée dans l'intégration des principes d'enseignement traditionnels avec des méthodologies numériques innovantes. Ma passion est de créer des cours qui sont non seulement attrayants et instructifs, mais aussi accessibles aux apprenants à l'ère du numérique.
Sujets

Apprenez à développer des applications d'IA !

cours

Developing LLM Applications with LangChain

3 hr
12.7K
Discover how to build AI-powered applications using LLMs, prompts, chains, and agents in LangChain.
Afficher les détailsRight Arrow
Commencer le cours
Voir plusRight Arrow
Apparenté

blog

Les 32 meilleures questions d'entretien sur AWS et leurs réponses pour 2024

Un guide complet pour explorer les questions d'entretien AWS de base, intermédiaires et avancées, ainsi que des questions basées sur des situations réelles. Il couvre tous les domaines, garantissant ainsi une stratégie de préparation bien équilibrée.
Zoumana Keita 's photo

Zoumana Keita

30 min

blog

2022-2023 Rapport annuel DataCamp Classrooms

À l'aube de la nouvelle année scolaire, DataCamp Classrooms est plus motivé que jamais pour démocratiser l'apprentissage des données, avec plus de 7 650 nouveaux Classrooms ajoutés au cours des 12 derniers mois.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

8 min

blog

Les 20 meilleures questions d'entretien pour les flocons de neige, à tous les niveaux

Vous êtes actuellement à la recherche d'un emploi qui utilise Snowflake ? Préparez-vous à répondre à ces 20 questions d'entretien sur le flocon de neige pour décrocher le poste !
Nisha Arya Ahmed's photo

Nisha Arya Ahmed

20 min

blog

Q2 2023 DataCamp Donates Digest

DataCamp Donates a offert plus de 20k bourses d'études à nos partenaires à but non lucratif au deuxième trimestre 2023. Découvrez comment des apprenants défavorisés et assidus ont transformé ces opportunités en réussites professionnelles qui ont changé leur vie.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

blog

Célébration de Saghar Hazinyar : Une boursière de DataCamp Donates et une diplômée de Code to Inspire

Découvrez le parcours inspirant de Saghar Hazinyar, diplômée de Code to Inspire, qui a surmonté les défis en Afghanistan et s'est épanouie grâce à une bourse de DataCamp Donates.
Fereshteh Forough's photo

Fereshteh Forough

4 min

blog

Nous avons fait don de bourses DataCamp Premium à un million de personnes, et ce n'est pas fini.

Réparties entre nos deux programmes d'impact social, DataCamp Classrooms et #DCDonates, les bourses offrent un accès illimité à tout ce que DataCamp Premium a à offrir.
Nathaniel Taylor-Leach's photo

Nathaniel Taylor-Leach

Voir plusVoir plus