
Partager:
Hamza est un ingénieur logiciel basé à Chicago. Il travaille avec Webrtc.ventures, une entreprise leader dans la fourniture de solutions WebRTC. Il travaille également en tant que développeur Full Stack chez Vonage en aidant la plateforme Video à mieux répondre aux besoins de ses clients. Fier d'être introverti, il aime passer son temps libre à jouer avec ses chats.
Améliorez vos Video Conférences avec ChatGPT : Rencontrez votre assistant IA en direct
Temps de lecture : 6 minutes
Introduction
La technologie, en particulier l'intelligence artificielle, a changé la façon dont nous parlons et travaillons en ligne. Alors que le monde s'oriente de plus en plus vers des interactions en ligne par le biais de webinaires, de conférences et de réunions individuelles, il existe un besoin indéniable d'un outil capable d'améliorer la productivité de ces réunions virtuelles.
Imaginez : un assistant IA à vos côtés lors de ces rendez-vous virtuels, prêt à répondre à vos questions, à prendre des notes sur les actions à entreprendre et à distiller des informations essentielles sous forme de résumés concis. Aujourd'hui, nous créons un tel assistant, en nous concentrant principalement sur les conversations individuelles. Cependant, les possibilités sont illimitées, ce concept s'appliquant à de nombreux scénarios. Alors, sans plus attendre, construisons "Sushi" !
Vous trouverez ici une vidéo de démonstration :
Conditions préalables
A Compte API Vonage. Accédez à votre tableau de bord API Vonage pour trouver votre clé API et votre secret API.
Un compte Compte OpenAI et un secret API
Node 16.20.1+
npm
Pour installer l'application sur votre machine, clonez d'abord le dépôt.
git clone https://github.com/hamzanasir/vonage-openai-demoAllez maintenant dans le dépôt et installez les paquets associés via :
npm installNous devons maintenant configurer la clé et le secret de l'API de Vonage. Commençons par copier le modèle .env :
cp .envcopy .envIl ne vous reste plus qu'à remplacer la clé et le secret de l'API dans le fichier .env par vos informations d'identification. Vous pouvez trouver votre clé API et votre secret sur la page de projet de votre Video API Account de Vonage (https://tokbox.com/account). Votre fichier .env devrait ressembler à ceci :
# enter your TokBox API key after the '=' sign below
TOKBOX_API_KEY=your_api_key
# enter your TokBox api secret after the '=' sign below
TOKBOX_SECRET=your_project_secretVous devez également créer un projet OpenAI. Vous pouvez le faire en allant sur https://platform.openai.com/signup. Une fois que vous êtes inscrit, vous pouvez aller à la section API Keys pour créer une nouvelle clé secrète.

Collez votre clé secrète dans le fichier .env.
# enter your OpenAI Secret after the '=' sign below
OPENAI_SECRET=your_openai_secretMaintenant que c'est fait, vous pouvez démarrer l'application avec :
npm startAllez maintenant sur http://localhost:8080/. Pour commencer à converser avec Sushi, assurez-vous d'appuyer sur les boutons de démarrage des sous-titres en bas à gauche pour lancer le service de sous-titrage en direct de Vonage. Pour une expérience optimale, assurez-vous de porter des écouteurs, afin que la voix de Sushi ne se répercute pas sur le microphone de l'éditeur.
Tout le code dont nous parlons dans ce blog se trouve dans le fichier public/js/app.js dans ce blog. Si vous êtes curieux de savoir comment fonctionnent les sous-titres en direct, n'oubliez pas de consulter le fichier routes/index.js pour voir comment le service de sous-titrage démarre et s'arrête.
Architecture de haut niveau
Pour comprendre le fonctionnement interne de notre assistant virtuel, nous allons nous plonger dans son cadre architectural.

Au cœur de cette configuration se trouve la gestion des conversations et des interactions des utilisateurs avec l'API d'OpenAI. Nous avons choisi le modèle GPT-3.5 Turbo d'OpenAI, qui excelle dans la gestion des conversations. Cependant, OpenAI offre une variété de modèles pour répondre aux objectifs spécifiques de votre projet.
Lorsque nous envoyons une requête à GPT, il lui faut un certain temps pour élaborer une réponse. Adoptons une approche plus dynamique. Nous diffusons les données au fur et à mesure qu'elles sont générées, ce qui garantit une expérience de conversation plus rapide et plus naturelle. Cette approche permet également aux utilisateurs d'interrompre et de guider la conversation, imitant ainsi les interactions de la vie réelle.
Lorsque GPT formule sa réponse, nous convertissons le texte généré en discours audible à l'aide d'un synthétiseur vocal. Diverses options sont disponibles à cet effet, y compris des synthétiseurs vocaux tiers payants qui peuvent fournir une voix plus naturelle. Pour notre projet, nous avons opté pour le module natif SpeechSynthesisUtterance du navigateur. Pour gérer les interruptions de la parole de l'utilisateur, nous utiliserons le AbortController du navigateur.
Publication et abonnement aux légendes
Voyons comment nous mettons en place et gérons notre service de sous-titrage, un élément essentiel de notre système.
Pour commencer, nous lançons le service de sous-titrage en appelant notre serveur par POST à /captions/start, en lui fournissant notre identifiant de session. Avec cette configuration, nous sommes prêts à recevoir des sous-titres pour tous les abonnés de la session. Cependant, nous voulons également que nos propres légendes soient générées en tant qu'éditeur. Pour ce faire, nous nous abonnons à notre propre éditeur avec un volume nul, afin de nous assurer que nous ne recevons pas une répétition de notre propre Voice.
Voici un extrait du code qui permet de réaliser cette opération :
const publisherOptions = {
insertMode: 'append',
width: '100%',
height: '100%',
publishCaptions: true,
};
publisher = OT.initPublisher('publisher', publisherOptions, (err) => {
if (err) {
handleError(err);
} else {
session.publish(publisher, () => {
if (error) {
console.error(error);
} else {
const captionOnlySub = session.subscribe(
publisher.stream,
document.createElement('div'),
{
audioVolume: 0
},
);
speakText(greetingMessage);
captionOnlySub.on('captionReceived', async (event) => {
if (event.isFinal) {
stopAiGenerator();
startAiGenerator(event.caption)
}
});
}
});
}
});
Dans ce code, nous publions notre audio et notre vidéo dans la session, puis nous nous abonnons à la même instance pour recevoir nos légendes. Pour que ce processus fonctionne de manière transparente, nous attachons un gestionnaire d'événement "captionReceived" à notre abonné fictif. Ce gestionnaire d'événements capture la transcription de notre discours au fur et à mesure que nous parlons. Étant donné que cet événement est généralement déclenché périodiquement, nous utilisons le booléen "isFinal" pour identifier le moment où nous avons fini de parler. Une fois que le texte généré est prêt, nous le transmettons à notre startAiGenerator pour qu'il soit traité.
Générer des réponses
La colonne vertébrale de notre logique se trouve dans la fonction startAiGenerator. Comme il s'agit d'une fonction volumineuse, nous allons la décomposer en éléments gérables. Tout d'abord, examinons de plus près la logique permettant d'effectuer des appels à l'API :
async function startAiGenerator(message) {
let aiText = '';
let utterableText = ''
abortController = new AbortController();
const userMessage = {
'role': 'user',
'content': message
}
const reqBody = {
messages: [...messages, userMessage],
temperature: 1,
max_tokens: 256,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
model: 'gpt-3.5-turbo',
stream: true
};
try {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
headers: {
'Authorization': `Bearer ${openAISecret}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(reqBody),
method: 'POST',
signal: abortController.signal
});Dans cet extrait, nous avons deux variables, aiText et utterableText. aiText stocke la réponse complète à notre requête, tandis que utterableText capture la dernière phrase complète du flux de données lisible reçu d'OpenAI. Nous avons également mis en place une instance AbortController pour gérer les interruptions de l'utilisateur pendant que l'assistant parle. Avant de lancer le générateur d'IA, une fonction distincte, stopAiGenerator(), est appelée pour arrêter tout processus de génération d'IA en cours.
function stopAiGenerator() {
if (abortController) {
abortController.abort();
abortController = null;
}
window.speechSynthesis.cancel();
}Enfin, nous appelons OpenAI avec l'historique des conversations stockées dans le tableau global des messages. Voyons maintenant comment nous diffusons les données d'OpenAI :
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
while (true) {
const chunk = await reader.read();
const { done, value } = chunk;
if (done) {
break;
}
const decodedChunk = decoder.decode(value);
const lines = decodedChunk.split('\n');
const parsedLines = lines
.map(l => l.replace(/^data: /, '').trim())
.filter(l => l !== '' && l !== '[DONE]')
.map(l => JSON.parse(l));
for (const line of parsedLines) {
const textChunk = line?.choices[0]?.delta?.content;
if (textChunk) {
utterableText += textChunk
if (textChunk.match(/[.!?:,]$/)) {
speakText(utterableText);
utterableText = '';
}
aiText += textChunk;
}
}
}
Dans ce segment, nous lançons le lecteur à partir de l'objet fetch que nous avons utilisé pour demander GPT. Nous utilisons une boucle while pour lire continuellement le flux jusqu'à ce qu'il soit épuisé. Chaque morceau de texte est ajouté aux deux variables définies précédemment. Si un morceau comprend un signe de ponctuation, nous déclenchons la fonction speakText(), qui vocalise la dernière phrase ou expression générée et réinitialise utterableText à une chaîne vide. N'oubliez pas que ce processus peut être interrompu à tout moment à l'aide d'un signal d'arrêt provenant de notre contrôleur AbortController.
Enfin, que nous mettions fin au flux ou que nous le laissions se terminer, nous sauvegardons à la fois les données de l'utilisateur et la réponse de l'OpenAI de la manière suivante :
messages.push(userMessage);
messages.push({
content: aiText,
role: 'assistant'
}) Un mot sur l'ingénierie rapide
L'"ingénierie des messages" dans le traitement du langage naturel consiste à rédiger des instructions ou des questions pour guider les modèles d'intelligence artificielle. Il s'agit de trouver le bon équilibre entre clarté et ambiguïté pour obtenir des résultats spécifiques. Avec l'ingénierie des invites appropriée, vous pourriez transformer Sushi en un faux conseiller juridique, un expert médical, un ami impertinent, etc. Pour cette démo, nous utilisons cette invite :
const messages = [
{
'role': 'system',
'content': "You are a participant called Sushi in a live call with someone. Speak concisely, as if you're having a one-on-one conversation with someone. " // Prompt engineering for AI assistant
}
];Essayez de modifier le contenu de ce code pour voir comment l'assistant se comportera !
Synthèse vocale
Cette section du projet offre une certaine flexibilité. Vous pouvez opter pour des solutions plus naturelles et plus avancées pour la synthèse vocale. La plupart de ces solutions seront payantes, donc dans cette démo, nous optons pour la fonctionnalité SpeechSynthesisUtterance par défaut du navigateur.
function speakText(text) {
let captions = '';
const utterThis = new SpeechSynthesisUtterance(text);
utterThis.voice = voices.find((v) => v.name.includes('Samantha'));
utterThis.onboundary = (event) => {
captions += `${event.utterance.text.substring(event.charIndex, event.charIndex + event.charLength)} `;
displayCaptions(captions, 'ai-assistant');
};
utterThis.onstart = () => {
animateVoiceSynthesis();
};
utterThis.onend = function() {
stopAnimateVoiceSynthesis();
};
window.speechSynthesis.speak(utterThis);
}
Nous commençons donc par initialiser le module SpeechSynthesisUtterance avec le texte que nous voulons prononcer. Ensuite, nous devons définir une voix disponible à partir du navigateur. Ces modules sont chargés de manière asynchrone, comme suit :
let voices = window.speechSynthesis.getVoices();
if (speechSynthesis.onvoiceschanged !== undefined)
speechSynthesis.onvoiceschanged = updateVoices;
function updateVoices() {
voices = window.speechSynthesis.getVoices();
}Nous utilisons la fonction onstart et onend pour animer les barres du milieu afin d'illustrer le moment où Sushi parle. En outre, l'événement onboundary est utilisé pour insérer des légendes de texte afin d'afficher le mot prononcé par le synthétiseur vocal.
Conclusion
Il est important de noter qu'il ne s'agit que d'un exemple de ce que vous pouvez faire avec une intégration avec GPT. Le but de cette démonstration est de montrer comment vous pouvez créer un pipeline entre Video Live Captions de Vonage et OpenAI, mais il n'y a pas de limite à ce que vous pouvez construire avec des messages-guides bien conçus.
Consultez la section section d'exemples de GPT pour voir la quantité infinie d'Applications. Nous sommes impatients de voir ce que vous allez créer !
Faites-nous savoir ce que vous construisez avec l'API Video de Vonage. Discutez avec nous sur notre Communauté Vonage Slack ou X, anciennement connu sous le nom de Twitter, @VonageDev.
Partager:
Hamza est un ingénieur logiciel basé à Chicago. Il travaille avec Webrtc.ventures, une entreprise leader dans la fourniture de solutions WebRTC. Il travaille également en tant que développeur Full Stack chez Vonage en aidant la plateforme Video à mieux répondre aux besoins de ses clients. Fier d'être introverti, il aime passer son temps libre à jouer avec ses chats.