DEV Community

Olivier
Olivier

Posted on

Agent vocal IA sous 500 ms : l'architecture réelle STT LLM TTS

500 ms. C'est le budget total entre l'instant où l'appelant se tait et le premier son de la réponse. Au-delà de 800 ms, l'oreille décroche : la conversation sonne robotique, l'appelant coupe la parole ou raccroche.

Un agent vocal pour le service client, c'est trois briques en série — reconnaissance vocale (STT), modèle de langage (LLM), synthèse vocale (TTS) — plus une boucle d'outils pour interroger ton CRM. Tenir le budget dépend de deux choses, et presque jamais celles qu'on croit. Le LLM n'est pas le goulet d'étranglement. Ce qui casse tout, c'est la détection de fin de parole et l'appel au CRM dans la boucle. Voici l'architecture qu'on déploie en production, le code qui compte, et les pièges qu'on ne paie qu'une fois.

Où part vraiment la latence ?

Décompose une réponse « instantanée » et tu vois où le temps se perd :

  • Détection de fin de parole (endpointing) : 200–800 ms — le vrai tueur
  • STT final : 50–150 ms (l'essentiel se fait en streaming pendant que l'appelant parle)
  • LLM, premier token (TTFT) : 200–600 ms selon le modèle
  • TTS, premier octet audio (TTFB) : 100–300 ms
  • Aller-retour réseau + téléphonie : 2 × 50–150 ms

Additionne en série, sans rien optimiser : tu es entre 1,5 et 2 secondes. Injouable. La règle qui sauve le budget tient en un mot : tu n'attends jamais qu'une brique finisse avant de lancer la suivante. Tout se streame, et l'endpointing se règle au millième.

Le vrai tueur : l'endpointing

Le VAD (Voice Activity Detection) te dit si quelqu'un parle. L'endpointing décide quand il a fini. Naïvement, on attend un silence fixe — disons 700 ms — avant de considérer le tour terminé. Résultat : 700 ms ajoutés à chaque réponse, avant même que le STT ne rende son verdict.

Le réglage a un compromis brutal. Un seuil trop court coupe l'appelant en plein mot, surtout quand il hésite ou épelle un numéro. Un seuil trop long ajoute du délai à chaque tour. On descend le silence fixe à 300–500 ms et on ajoute un endpointing sémantique : si la transcription partielle forme une phrase complète (« je veux suivre ma commande »), on coupe tôt ; si elle finit sur « euh… mon numéro c'est », on attend. Deepgram et LiveKit exposent ce réglage ; sinon, une heuristique sur la ponctuation du STT fait déjà 80 % du travail.

Streamer, sinon tout s'effondre

Le second levier, c'est de ne jamais attendre la fin d'une réponse LLM pour parler. Dès qu'une phrase est complète, on l'envoie au TTS pendant que le modèle génère la suite :

async def stream_reply(llm_stream, tts, audio_out):
    buffer = ""
    async for token in llm_stream:            # tokens du LLM en flux
        buffer += token
        if token and token[-1] in ".!?…":     # phrase terminée
            await tts.speak(buffer, audio_out)  # on parle déjà
            buffer = ""
    if buffer.strip():
        await tts.speak(buffer, audio_out)
Enter fullscreen mode Exit fullscreen mode

L'appelant entend la première phrase pendant que le LLM écrit la troisième. Sur une réponse de trois phrases, ce seul changement fait gagner 600 à 900 ms perçus. Des frameworks comme Pipecat ou LiveKit Agents câblent ce pipeline pour toi — utile, mais comprends ce qu'ils font, sinon le premier bug de latence en prod te bloque une journée.

L'appel CRM : le piège du silence

C'est là que la moitié des démos s'effondrent au contact du réel. Répondre « votre commande arrive jeudi » suppose d'interroger ton CRM en plein appel. Le LLM décide d'appeler une fonction, tu interroges l'API, tu renvoies le résultat au LLM, il reprend : deux allers-retours modèle plus la latence du CRM. Facile 1 à 2 secondes de silence complet — que l'appelant lit comme une ligne coupée.

La parade tient en une ligne : combler le vide avant d'appeler le CRM.

if response.stop_reason == "tool_use":
    call = response.tool_calls[0]
    # on parle AVANT l'appel réseau, pas après
    await tts.speak("Je vérifie ça, un instant.", audio_out)
    result = await crm.get_order_status(call.args["order_id"])  # 200–500 ms
    messages.append(tool_result(call.id, result))
    # on relance le LLM avec la donnée, et on re-streame
    await stream_reply(llm.stream(messages), tts, audio_out)
Enter fullscreen mode Exit fullscreen mode

La phrase de transition n'est pas cosmétique : elle masque toute la latence du tour d'outil. Et garde tes schémas de tool minces — un CRM qui répond en 200 ms plutôt que 500 change plus la sensation que n'importe quel changement de modèle.

Transférer à un humain sans le faire répéter

Un agent honnête connaît ses limites. On déclenche l'escalade sur trois signaux : demande explicite, deux tours de suite sous un seuil de confiance, ou détection de frustration. Le code est trivial ; le piège est ailleurs.

def should_escalate(intent, confidence, turns_low_conf):
    if intent == "parler_a_un_humain":
        return True
    return turns_low_conf >= 2 and confidence < 0.6
Enter fullscreen mode Exit fullscreen mode

Le vrai piège, c'est le transfert « froid » : l'agent passe l'appel, et l'humain décroche sans contexte. L'appelant répète tout depuis le début. C'est la raison n°1 pour laquelle les gens détestent les voicebots. Fais un transfert « chaud » : pousse un résumé de la conversation au conseiller (pop CRM, ticket pré-rempli, ou en-têtes SIP REFER). Il reprend là où la machine s'est arrêtée, et l'appelant ne le vit pas comme un échec.

Le français, les numéros de commande et le DTMF

Le STT français atteint 93–95 % en conditions réelles sur du langage courant. Il s'effondre sur l'alphanumérique. Un numéro de commande épelé (« A comme Anatole, B, 1, 2, 3 ») ou un e-mail dicté partent en bouillie, et aucun re-prompt ne rattrape ça de façon fiable.

Ne t'obstine pas sur la reconnaissance vocale pour ces cas. Bascule sur le clavier (DTMF) : « Tapez votre numéro de commande sur votre téléphone. » Les touches sont déterministes, la reconnaissance est parfaite, et l'appelant préfère taper huit chiffres plutôt que les répéter trois fois. C'est moins élégant qu'une conversation pure — c'est surtout ce qui marche.

La ligne que l'AI Act impose en dur

Depuis le 2 août 2026, l'article 50 du règlement européen sur l'IA oblige à informer clairement l'interlocuteur qu'il parle à une machine. L'interdit est absolu. Si l'appelant demande « vous êtes un humain ? », mentir est une infraction. Les sanctions montent jusqu'à 35 M€ ou 7 % du CA mondial, et le RGPD s'ajoute par-dessus puisque tu traites de la voix.

Conséquence technique directe : ne délègue pas cette obligation au LLM. Un modèle probabiliste finira par oublier la phrase un appel sur mille. La divulgation se code en dur, jouée avant le premier token généré :

GREETING = ("Bonjour, vous êtes en relation avec l'assistant vocal "
            "de [Entreprise]. Je peux répondre ou vous passer un conseiller.")
await tts.speak(GREETING, audio_out)  # en dur, jamais généré par le modèle
Enter fullscreen mode Exit fullscreen mode

La conformité ne se joue pas aux probabilités.

Questions fréquentes

Quel STT/TTS pour le français ? Côté STT, Deepgram et Whisper large tiennent 93–95 % sur du courant ; côté TTS, Cartesia ou ElevenLabs descendent sous 300 ms de TTFB en streaming. Teste sur tes appels réels, pas sur des démos studio.

Faut-il fine-tuner le LLM ? Rarement. Un bon prompt système avec tes procédures, plus le tool-calling vers ton CRM, couvre la majorité des cas de niveau 1. Le fine-tuning se justifie quand le vocabulaire métier est très spécifique.

SaaS ou sur-mesure ? Volume faible et cas standard : un SaaS no-code sous 200 €/mois suffit, ne code pas ça toi-même. Dès qu'il faut brancher un SI propriétaire, contrôler la donnée ou éviter le lock-in, le sur-mesure reprend l'avantage. On détaille l'arbitrage dans le guide complet.

Comment gérer les interruptions (barge-in) ? Détecte la voix pendant que le TTS joue, coupe la synthèse immédiatement et vide le buffer audio. Sans ça, l'agent continue de parler par-dessus l'appelant — le défaut qui trahit un robot en une seconde.

On a câblé cette architecture plusieurs fois, et la difficulté n'est jamais le « hello world » : c'est l'endpointing, la latence du CRM et le transfert propre. Si tu veux apprendre à construire ce genre d'agent avec Claude Code — du pipeline au tool-calling —, notre formation Claude Code prend le sujet du côté du code. Et si tu préfères qu'on le branche directement sur ton SI, c'est notre métier de studio.

Top comments (0)