Optimisation de la latence
La latence : l’ennemi de la conversation naturelle
Dans une conversation humaine, le temps de réponse moyen est d’environ 200 millisecondes. Au-delà de 500 ms, l’interlocuteur commence à percevoir un délai. Pour qu’un assistant vocal soit perçu comme naturel, la latence totale entre la fin de la question et le début de la réponse doit rester sous la seconde.
Voxtral affiche une latence modèle d’environ 90 ms — mais la latence totale perçue par l’utilisateur est bien plus élevée. Comprendre et optimiser chaque composant est essentiel.
Anatomie de la latence totale
La latence totale se décompose en plusieurs segments :
Latence réseau (aller)
Le temps pour envoyer la requête de votre serveur aux serveurs Mistral. Variable selon votre localisation géographique et la qualité de votre connexion. En Europe, comptez 20-80 ms.
Latence modèle (~90 ms)
Le temps de traitement par le modèle Voxtral pour générer le premier chunk audio. C’est la partie que vous ne pouvez pas optimiser — elle dépend de l’infrastructure Mistral.
Latence d’encodage
Le temps pour encoder l’audio dans le format demandé. C’est ici que le choix du format impacte directement la latence :
- PCM : ~0 ms (pas d’encodage, audio brut)
- Opus : ~5-10 ms (codec rapide)
- MP3 : ~200-500 ms (le codec MP3 a besoin d’accumuler des données avant de produire le premier frame)
Latence réseau (retour)
Le temps pour recevoir les chunks audio. En streaming, les chunks sont petits, donc cette latence est faible.
Latence de buffer côté client
Votre application accumule généralement 2-3 chunks avant de commencer la lecture pour éviter les micro-coupures. Ajoutez 50-150 ms.
Streaming vs batch : l’impact chiffré
| Métrique | Batch (sans streaming) | Streaming PCM | Streaming MP3 |
|---|---|---|---|
| Premier son | 1-3 secondes | ~0,7 seconde | ~2 secondes |
| Latence perçue | Élevée | Très basse | Moyenne |
| Bande passante | Pic unique | Répartie | Répartie |
Le streaming avec PCM divise la latence perçue par 3 à 4 par rapport au batch.
Optimisation côté serveur
Choisir le bon format
Pour une application conversationnelle, le format PCM est le choix optimal :
with client.audio.speech.complete(
model="voxtral-mini-tts-2603",
input=texte,
voice_id=voice_id,
response_format="pcm", # Latence minimale
stream=True,
) as stream:
for event in stream:
if event.event == "speech.audio.delta":
jouer_audio(base64.b64decode(event.data.audio_data))
Utiliser une voix sauvegardée
Le voice_id est plus rapide que ref_audio : l’échantillon n’a pas besoin d’être traité à chaque requête. Gain : 50-100 ms.
Réduire la longueur du texte
Plus le texte est court, plus le premier chunk arrive vite. Pour un assistant vocal, générez phrase par phrase :
def generer_par_phrases(texte_complet, voice_id):
"""Génère l'audio phrase par phrase pour minimiser la latence."""
phrases = texte_complet.split(". ")
for phrase in phrases:
phrase = phrase.strip()
if not phrase:
continue
if not phrase.endswith("."):
phrase += "."
with client.audio.speech.complete(
model="voxtral-mini-tts-2603",
input=phrase,
voice_id=voice_id,
response_format="pcm",
stream=True,
) as stream:
for event in stream:
if event.event == "speech.audio.delta":
yield base64.b64decode(event.data.audio_data)
Optimisation côté client
Buffer adaptatif
Au lieu d’un buffer fixe, adaptez sa taille en fonction des conditions réseau :
class BufferAdaptatif:
def __init__(self):
self.buffer = []
self.seuil_min = 2 # chunks minimum avant lecture
self.seuil_max = 5 # ne pas accumuler plus
self.lecture_commencee = False
def ajouter_chunk(self, chunk):
self.buffer.append(chunk)
if not self.lecture_commencee:
if len(self.buffer) >= self.seuil_min:
self.lecture_commencee = True
return self.vider_buffer()
else:
return chunk
return None
def vider_buffer(self):
data = b"".join(self.buffer)
self.buffer = []
return data
Pré-chauffage de la connexion
Envoyez une requête de test au démarrage de votre application pour établir la connexion TCP et le handshake TLS. Les requêtes suivantes seront plus rapides grâce au keep-alive HTTP.
Mesurer la latence
Pour diagnostiquer les goulots d’étranglement, mesurez chaque segment :
import time
t0 = time.perf_counter()
with client.audio.speech.complete(
model="voxtral-mini-tts-2603",
input="Test de latence.",
voice_id=voice_id,
response_format="pcm",
stream=True,
) as stream:
for event in stream:
if event.event == "speech.audio.delta":
t1 = time.perf_counter()
print(f"Premier chunk reçu en {(t1 - t0)*1000:.0f} ms")
break
Points clés à retenir
- La latence totale comprend le réseau, le modèle (~90 ms), l’encodage et le buffer client
- Le format PCM offre la latence la plus basse car il n’y a pas d’encodage
- Le streaming réduit la latence perçue d’un facteur 3 à 4 par rapport au batch
- Utilisez un
voice_idplutôt queref_audiopour gagner 50-100 ms - Générez phrase par phrase pour minimiser le temps avant le premier son