cours
OpenAI Realtime API (API en temps réel) : Un guide avec des exemples
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".
Une fenêtre pop-up s'ouvre. Nous pouvons utiliser les options par défaut et cliquer sur "Créer une clé secrète".
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é.
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 :
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
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 :
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 champdelta
. 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 champtext
. - 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 :
- Mettez à jour le contenu du site
createConversationEvent
pour utiliser le type”input_audio”
au lieu de”input_text
et remplacez le champtext
paraudio: base64AudioData
. - Ajoutez
”audio”
à la modalité de réponse danscreateResponseEvent
.
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 :
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ésa
etb
.
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 :
- Obtenir les informations sur la fonction et appeler la fonction.
- Envoi du résultat de l'appel de fonction.
- 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
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 :
- Un frontend React qui consiste en l'interface utilisateur web de l'application.
- Un serveur relais est utilisé comme intermédiaire entre le frontend et l'API OpenAI.
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.
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.
Une fois la connexion établie, un nouveau bouton apparaît au centre, permettant de parler à l'assistant IA.
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.
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.
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 :
- Le nom de l'événement.
- 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 :
Apprenez à développer des applications d'IA !
cursus
Developing AI Applications
cours
Fully Automated MLOps
blog
Les 32 meilleures questions d'entretien sur AWS et leurs réponses pour 2024
blog
2022-2023 Rapport annuel DataCamp Classrooms
blog
Les 20 meilleures questions d'entretien pour les flocons de neige, à tous les niveaux

Nisha Arya Ahmed
20 min
blog
Q2 2023 DataCamp Donates Digest
blog
Célébration de Saghar Hazinyar : Une boursière de DataCamp Donates et une diplômée de Code to Inspire

Fereshteh Forough
4 min
blog