# Comment créer un assistant vocal avec l’API Realtime?

Date: 2024-10-11 · Author: Robert Scales · https://guidetoartificialintelligence.com/fr/apprendre/creer-assistant-vocal-api-realtime/

**Answer:** Vous ouvrez une connexion WebSocket vers l’API Realtime d’OpenAI, vous configurez la session avec des consignes, une voix, un format audio et les outils voulus, puis vous transmettez en continu l’audio de la personne qui appelle et faites jouer les réponses audio du modèle. Pour une ligne téléphonique, un service de téléphonie comme Twilio envoie l’audio de l’appel à votre serveur, qui le relaie vers OpenAI et en retour.

Jusqu’à ce mois-ci, un assistant vocal construit avec les modèles d’OpenAI était une course à relais entre trois d’entre eux : un modèle de reconnaissance de la parole comme Whisper transformait la parole en texte, un modèle de texte rédigeait la réponse, et un modèle de synthèse vocale la lisait à voix haute. Chaque passage de témoin ajoutait du délai, et le modèle du milieu n’entendait jamais le ton de la personne qui appelait. L’API Realtime, en bêta publique pour tous les développeurs payants depuis le 1er octobre, remplace cette chaîne par un seul modèle, GPT-4o, qui écoute et parle directement.

Ce guide présente l’architecture d’un petit assistant téléphonique pour un atelier de réparation de vélos imaginaire, qui répond aux questions sur les heures d’ouverture. Le même modèle fonctionne pour un site Web ou une application; seule la tuyauterie audio change.

## L’architecture : deux WebSockets et un relais

Un WebSocket est une connexion qui reste ouverte pour que les deux côtés puissent s’envoyer des messages à tout moment, ce dont une conversation en direct a besoin. Un assistant téléphonique en utilise deux, avec votre serveur au milieu :

1. **La personne compose votre numéro de téléphone.** Twilio, un service de téléphonie partenaire d’OpenAI pour le lancement, répond et, selon vos instructions, ouvre un « flux média » (*media stream*) : un WebSocket qui transporte l’audio de l’appel jusqu’à votre serveur.
2. **Votre serveur ouvre un deuxième WebSocket vers OpenAI**, au point de terminaison Realtime, avec votre clé d’API et un en-tête bêta.
3. **Votre serveur relaie l’audio dans les deux sens.** L’audio de la personne qui appelle va à OpenAI sous forme d’événements `input_audio_buffer.append`. La parole du modèle revient dans des événements `response.audio.delta`, que vous transmettez à Twilio.
4. **OpenAI décide quand la personne a fini de parler.** Avec la « détection d’activité vocale côté serveur » (*server VAD*, pour *voice activity detection*) activée, l’API détecte les pauses et commence à répondre d’elle-même.
5. **Les outils s’occupent des faits.** Quand la personne demande quelque chose que votre système connaît, comme les heures d’aujourd’hui, le modèle demande un appel de fonction; votre serveur l’exécute et renvoie le résultat.

Les réseaux téléphoniques transportent l’audio dans un format compressé appelé G.711 μ-law. L’API Realtime l’accepte directement (`g711_ulaw`) : aucune conversion n’est nécessaire.

## Un exemple concret en Node.js

Cette ébauche montre le côté OpenAI du relais, avec un outil. Elle utilise la bibliothèque `ws` et le nom du modèle bêta du lancement, et laisse de côté la route Twilio par souci de concision; l’exemple de Twilio sur GitHub montre cette partie.

```javascript
import WebSocket from "ws";

const HOURS = { mon: "fermé", tue: "9-18", wed: "9-18", thu: "9-20", fri: "9-20", sat: "10-16", sun: "fermé" }; // à titre d’exemple

export function connectToOpenAI(sendAudioToCaller) {
  const ws = new WebSocket(
    "wss://api.openai.com/v1/realtime?model=gpt-4o-realtime-preview-2024-10-01",
    { headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "OpenAI-Beta": "realtime=v1" } }
  );

  ws.on("open", () => {
    ws.send(JSON.stringify({
      type: "session.update",
      session: {
        instructions: "Tu es l’assistant téléphonique de Vélos Rue Verte. Dis dès le début que tu es un assistant IA. Limite tes réponses à deux phrases. Utilise get_hours pour les heures d’ouverture; ne devine jamais.",
        voice: "alloy",
        modalities: ["text", "audio"],
        input_audio_format: "g711_ulaw",
        output_audio_format: "g711_ulaw",
        turn_detection: { type: "server_vad" },
        tools: [{
          type: "function",
          name: "get_hours",
          description: "Heures d’ouverture pour un jour de la semaine",
          parameters: {
            type: "object",
            properties: { day: { type: "string", enum: Object.keys(HOURS) } },
            required: ["day"]
          }
        }],
        tool_choice: "auto"
      }
    }));
  });

  ws.on("message", (raw) => {
    const event = JSON.parse(raw);
    if (event.type === "response.audio.delta") {
      sendAudioToCaller(event.delta);                 // audio en base64 pour Twilio
    }
    if (event.type === "response.output_item.done" && event.item.type === "function_call") {
      const { day } = JSON.parse(event.item.arguments);
      ws.send(JSON.stringify({
        type: "conversation.item.create",
        item: { type: "function_call_output", call_id: event.item.call_id, output: JSON.stringify({ day, hours: HOURS[day] }) }
      }));
      ws.send(JSON.stringify({ type: "response.create" }));   // demande au modèle de dire la réponse
    }
    if (event.type === "error") console.error(event.error);
  });

  return (base64Audio) =>                                   // à appeler pour chaque morceau d’audio venant de Twilio
    ws.send(JSON.stringify({ type: "input_audio_buffer.append", audio: base64Audio }));
}
```

Le déroulement d’un appel : une personne demande « Êtes-vous ouverts jeudi soir? » L’API détecte la fin de la question et le modèle demande `get_hours` avec `{"day": "thu"}`. Votre code répond avec les heures enregistrées, et le modèle dit quelque chose comme « Oui, nous sommes ouverts jusqu’à 20 h jeudi. » La formulation varie; cette réponse est donnée à titre d’exemple.

Deux détails méritent attention. D’abord, la consigne de dire qu’il s’agit d’une IA n’est pas qu’une politesse : les politiques d’utilisation d’OpenAI exigent que les développeurs indiquent clairement aux utilisateurs qu’ils parlent à une IA, sauf si c’est évident d’après le contexte. Ensuite, les gens interrompent. Quand l’API envoie `input_audio_buffer.speech_started` pendant que l’assistant parle encore, l’exemple de Twilio arrête la lecture et envoie `conversation.item.truncate` pour que le modèle sache quelle partie de sa réponse a vraiment été entendue.

> **À essayer :** avant de brancher un numéro de téléphone, testez la session à partir d’un petit script sur votre ordinateur portable, avec de l’audio `pcm16` (le format par défaut de l’API) plutôt que `g711_ulaw`. Posez des questions sur chaque jour de la semaine et vérifiez que les heures viennent toujours de l’outil.

## Combien coûte une minute de voix?

OpenAI facture l’API Realtime en jetons, les unités que ses modèles lisent et écrivent, et les jetons audio coûtent beaucoup plus cher que les jetons texte. L’audio en entrée coûte 100 $ US par million de jetons, et l’audio en sortie, 200 $ US par million. OpenAI traduit cela par environ 0,06 $ US par minute d’audio entrant et 0,24 $ US par minute d’audio sortant. Les jetons texte, utilisés pour les consignes et les résultats d’outils, coûtent 5 $ US par million en entrée et 20 $ US par million en sortie.

Pour vous donner une idée, prenez un appel de cinq minutes où la personne parle pendant trois minutes et l’assistant pendant deux. Cela donne environ 3 × 0,06 $ US + 2 × 0,24 $ US, soit autour de 0,66 $ US en audio, plus un peu de texte. Ces chiffres sont une illustration tirée des estimations par minute, pas une soumission. C’est le temps de parole de l’assistant qui fait grimper la facture, une raison de plus de demander des réponses courtes. Les tours précédents sont aussi traités de nouveau à mesure que la conversation s’allonge; OpenAI dit prévoir d’ajouter la mise en cache des prompts pour que ces répétitions coûtent moins cher.

Votre fournisseur de téléphonie facture séparément le numéro de téléphone et les minutes d’appel. Les limites de débit comptent aussi : au lancement, les comptes du niveau 5 (*Tier 5*) peuvent faire tourner environ 100 sessions simultanées, avec des limites plus basses pour les autres niveaux. Faites l’essai avec une poignée d’appels, consultez la page d’utilisation de votre tableau de bord OpenAI et fixez une limite de dépenses mensuelle avant de publier le numéro.

## Sources

- Introducing the Realtime API: https://openai.com/index/introducing-the-realtime-api/
- speech-assistant-openai-realtime-api-node: index.js (commit of October 10, 2024): https://github.com/twilio-samples/speech-assistant-openai-realtime-api-node/blob/1d88392710c54be067531e6327fa443d70123908/index.js
- openai-realtime-api-beta: lib/client.js (commit of October 10, 2024): https://github.com/openai/openai-realtime-api-beta/blob/8b331762ff4f6851c2ac6f4536aa34d18e841a6c/lib/client.js
