Lección 5 — Le temps réel avec la Live API

La Live API : une conversation sur WebSocket

6 min de lectura

Este contenido aún no está traducido. Se muestra la versión original.

La Live API : une conversation sur WebSocket

Pourquoi pas une simple requête ?

Avec generate_content, on envoie tout, on attend, on reçoit tout. Pour une conversation, c'est trop lent : il faudrait attendre la fin de la phrase, téléverser, attendre la réponse complète, la télécharger. La Live API ouvre une connexion WebSocket : un canal réseau qui reste ouvert et dans lequel les deux côtés peuvent envoyer des messages à tout moment.

  • Le client envoie l'audio du micro par petits morceaux (ici 100 ms), au fil de l'eau.
  • Le serveur détecte tout seul que la personne a fini de parler : c'est la VAD (Voice Activity Detection, détection d'activité vocale).
  • La réponse arrive en flux : les premiers morceaux d'audio se jouent pendant que la suite est encore générée.
  • Si la personne reprend la parole pendant la réponse, le serveur s'interrompt (barge-in) : c'est ce qui rend l'échange naturel.
sequenceDiagram
  participant U as "Client (micro)"
  participant WS as "WebSocket Live API"
  participant M as "gemini-3.8-live"
  U->>WS: "connect(config : AUDIO, consigne système)"
  loop "toutes les 100 ms"
    U->>WS: "send_realtime_input(PCM 16 kHz)"
  end
  Note over WS: "VAD : silence détecté, fin du tour"
  WS->>M: "tour utilisateur"
  M-->>U: "morceaux audio 24 kHz + transcription"
  M-->>U: "turn_complete"

Les formats à respecter

SensFormatD'après
EntréePCM 16 bits, 16 kHz, mono, little-endian (audio/pcm;rate=16000)guide de la Live API
SortiePCM 16 bits, 24 kHz, monoguide de la Live API
Textepossible en entrée (send_client_content) comme en sortie (transcriptions)idem

Notre question vient d'un TTS (24 kHz) : to_pcm16k() la rééchantillonne par interpolation linéaire avec numpy, ce qui suffit pour la voix (la doc précise d'ailleurs que l'API rééchantillonne si besoin).

Le code

files/live_demo.py
cfg = types.LiveConnectConfig(
    response_modalities=["AUDIO"],
    system_instruction=SYSTEM,
    input_audio_transcription=types.AudioTranscriptionConfig(),   # ce que l'API a entendu
    output_audio_transcription=types.AudioTranscriptionConfig(),  # ce qu'elle dit
)
async with c.aio.live.connect(model=LIVE_MODEL, config=cfg) as session:
    async def micro():  # simule un micro : envoi en temps réel
        for i, ch in enumerate(speech + silence):
            if i == len(speech):
                speech_end["t"] = time.time()  # fin de la parole, début du silence
            await session.send_realtime_input(audio=types.Blob(data=ch, mime_type=f"audio/pcm;rate={IN_RATE}"))
            await asyncio.sleep(CHUNK_MS / 1000)

    sender = asyncio.create_task(micro())     # on envoie ET on écoute en même temps
    async for msg in session.receive():
        if msg.data:                            # un morceau d'audio de la réponse
            first = first or time.time(); audio += msg.data
        ...
        if msg.server_content and msg.server_content.turn_complete:
            break

Deux détails que nous avons appris en testant :

  • Envoyer tout l'audio d'un coup, sans pause, puis attendre : la session est restée muette jusqu'au délai d'expiration. Il faut cadencer l'envoi comme un vrai micro (asyncio.sleep) et finir par du silence pour que la VAD conclue le tour.
  • Envoi et réception doivent tourner en parallèle (une tâche asyncio pour le micro), sinon on ne voit pas la réponse commencer pendant qu'on parle encore.

Pourquoi live_demo.py ajoute-t-il une seconde de silence après la question ?

Limites d'une session

Limite (documentation, à revérifier)Conséquence
Session audio seule limitée à environ 15 minutes sans compression du contexteactiver la compression (context_window_compression) pour les longs appels
Connexion WebSocket d'une durée de vie d'environ 10 minutesutiliser la reprise de session (session resumption)
Clé d'API à ne jamais mettre dans un navigateur ou une appli mobilepasser par un serveur, ou par des jetons éphémères (ephemeral tokens) à courte durée de vie

Résultat obtenu

Sorties réelles du 2026-10-15. D'abord une question en texte (send_client_content), réponse en audio :

gemini-3.8-live connected 0.61
first audio 0.66 total 6.54 282240 Bonjour, je vais vérifier immédiatement le statut de votre commande 4521 et vous tenir informé.

Puis python live_demo.py avec une question en voix (fichier exemple_question.wav) :

WebSocket ouvert en 0.32 s (gemini-3.8-live)
Entendu : Bonjour, ma commande 4521 n'est toujours pas arrivée. Vous pouvez me dire où elle en est ?
Répondu : Bonjour, je vais vérifier l'état de votre commande 4521 et revenir vers vous rapidement. Pourriez-vous me confirmer votre numéro de téléphone ?
Latence fin de parole -> 1er audio : 1.00 s ; réponse de 7.9 s -> sortie/live_reponse.wav
Réponse vocale de l'agent Live

Où doit vivre la clé d'API dans une application web de conversation vocale ?

Exercice : afficher les sous-titres en direct

Modifiez la boucle de réception pour afficher la transcription de sortie au fur et à mesure qu'elle arrive (sous-titres en direct), au lieu de l'afficher à la fin.

Voir la solution
async for msg in session.receive():
    sc = msg.server_content
    if sc and sc.output_transcription and sc.output_transcription.text:
        print(sc.output_transcription.text, end="", flush=True)  # morceau par morceau
    if msg.data:
        audio += msg.data
    if sc and sc.turn_complete:
        print()
        break

flush=True force l'affichage immédiat. Dans une interface web, on enverrait chaque morceau au navigateur (par exemple via le même WebSocket applicatif) pour l'ajouter au sous-titre.

Je sais faire

  • Expliquer ce qu'apporte un WebSocket par rapport à une requête classique
  • Ouvrir une session Live, envoyer de l'audio 16 kHz cadencé et lire la réponse en flux
  • Expliquer le rôle de la VAD et de l'interruption
  • Citer les limites de session et protéger la clé avec un jeton éphémère
Siguiente