Ingénierie

Architecture de dictée vocale : à l'intérieur du voice typing stack à trois modèles de Loqua

Pourquoi on sépare reconnaissance vocale, intelligence linguistique et contexte d'écran — et comment on lit honnêtement nos chiffres internes.

TL;DR

Voici un regard de blog sur l'architecture de dictée vocale de Loqua — et sur la raison pour laquelle la bonne voice ai architecture pour la dictée n'est pas un seul gros modèle ASR. Loqua est construit en trois couches coopérantes : reconnaissance vocale, intelligence linguistique et contexte multimodal. La raison est simple : la qualité de dictée ne se limite pas au word error rate. Un dictation model stack utile doit entendre les mots, comprendre les noms techniques et façonner la sortie pour l'app de destination. Nos cibles internes sont une réactivité de classe 200 ms, une reconnaissance élevée du vocabulaire technique et un WER à un chiffre bas dans les conditions supportées ; tant qu'on n'a pas publié de page de benchmark, traite-les comme des mesures internes plutôt que comme des chiffres tiers.

On dirige une petite équipe de chercheurs en algorithmes qui utilisent la dictée vocale tous les jours. On a construit Loqua parce que les outils de dictée qu'on avait évalués — la plupart bien construits, la plupart vraiment utiles — atteignaient tous le même plafond dès qu'on les poussait sur le code, le mélange multilingue et le formatage conscient de l'app. Ce plafond est structurel, pas un problème de réglage. Voici l'architecture sur laquelle on a atterri après avoir décidé que la structure devait changer.

Ce n'est pas un papier. C'est une visite guidée de blog — l'intuition derrière chaque couche, les chiffres qui en sont sortis, et ce qu'on en fait vraiment. Si tu veux le contexte académique plus profond, la section lectures complémentaires en fin d'article pointe vers les papiers dont on s'est inspiré.

Le piège du wrap-Whisper

Whisper d'OpenAI a changé ce qui est possible en reconnaissance vocale anglaise. C'est un modèle remarquable. Le papier Whisper a montré que mettre à l'échelle un entraînement audio faiblement supervisé produit un ASR généraliste robuste — à travers les accents, les conditions, et 99 langues — sans fine-tuning par domaine. C'est une victoire structurelle pour le champ.

Mais Whisper est un modèle ASR, pas un produit de dictée. L'écart entre « transcrit la parole avec précision » et « produit un texte que je peux coller dans un email sans éditer » est large. L'approche wrap-Whisper — prendre la sortie de Whisper et la passer dans une couche de formatage — réduit une partie de cet écart. Elle échoue à trois endroits qui nous tenaient à cœur :

  • Vocabulaire technique. Whisper entend « react query » et te rend « react query ». Il ne sait pas que dans ton codebase, c'est @tanstack/react-query et que l'import du package est ce que tu voulais. Le NER sur le vocabulaire technique demande un modèle qui voit le contexte environnant, pas un modèle qui entend des phonèmes.
  • Formatage conscient de l'app. Whisper transcrit ; il ne sait pas si tu es dans Slack ou dans un fichier Python. Boulonner un formateur par-dessus demande une heuristique — fragile — ou un appel LLM lourd par énonciation, ce qui est lent et dépendant du cloud.
  • Streaming sous budgets de latence serrés. Whisper est excellent pour la transcription batch. Du Whisper en streaming, basse latence, on-device demande soit un compromis (modèle plus petit, précision plus basse), soit une ingénierie agressive qui se bat contre la structure du modèle.

On a essayé la voie wrap-Whisper pour un prototype de recherche. C'était assez bien pour tester des idées. Pas assez bien pour devenir notre outil quotidien.

L'intuition à trois modèles : un speech recognition stack d'un autre genre

L'intuition, c'est que la dictée vocale, c'est trois jobs, et que le même modèle est mauvais aux trois. Les trois jobs :

  1. Entendre les mots. Acoustique vers token. C'est ce que fait bien l'ASR.
  2. Comprendre l'intention. Nettoyer les hésitations, les faux départs, les corrections en milieu de phrase. Reconnaître les entités techniques. Décider ce que l'utilisateur voulait vraiment taper.
  3. Le placer correctement. Formater la sortie pour l'app de destination. Faire que la même intention ressorte en code dans VS Code, en message Slack dans Slack, en prompt structuré dans Cursor.

Un modèle end-to-end unique qui essaie de faire les trois a un problème structurel difficile : la surface de loss pour « transcrire les phonèmes » et la surface de loss pour « formater pour Slack » pointent dans des directions différentes. L'entraînement conjoint compromet les deux. On peut le confirmer avec nos propres ablations — on a essayé d'unifier les couches 2 et 3 dans un seul transformer entraîné sur des données multi-tâches, et la précision a chuté des deux côtés.

Séparer en trois laisse chaque modèle bien faire un job. Le coût, c'est un peu de latence pour le passage entre couches, qu'on récupère en faisant tout tourner sur le Neural Engine dans un seul pipeline. C'est le cœur de l'architecture de dictée vocale de Loqua : pas un transcripteur monolithique unique, mais un petit dictation model stack où chaque couche est entraînée à dessein pour sa part du problème.

Couche 1 : reconnaissance vocale

Entrée acoustique → séquence de tokens. C'est un recognizer vocal spécifique à la tâche plutôt qu'un wrapper direct de Whisper. Les choix d'architecture ont été dictés par trois contraintes sur lesquelles on ne voulait pas céder :

  • Streaming d'abord. La sortie commence avant que le locuteur ait fini l'énonciation. Cela exclut l'attention non causale sur toute la séquence audio par défaut — on utilise une variante adaptée au streaming.
  • Compatibilité Neural Engine on-device. La taille du modèle et la sélection des opérateurs sont contraintes par ce qui tourne efficacement sur Core ML d'Apple via le Neural Engine. C'est une vraie contrainte — des opérateurs qui ont l'air bien dans un papier peuvent sortir du chemin Neural Engine et devenir CPU-bound.
  • Robustesse à faible amplitude. Nos données d'entraînement incluent délibérément des entrées au volume du chuchotement (des gens qui dictent doucement dans des cafés, tard le soir). La plupart des données d'entraînement ASR généraliste sont à volume normal ; le volume chuchotement demande une couverture explicite.

La sortie de cette couche est une séquence de tokens avec timing et scores de confiance. Ce n'est pas ton texte final — c'est la reconnaissance brute que la couche suivante nettoie.

Couche 2 : intelligence linguistique

Séquence de tokens → texte nettoyé, intention résolue. C'est là qu'on fait l'investissement de recherche le plus gros, parce que c'est là que vit l'essentiel de la qualité visible par l'utilisateur.

Le job de cette couche : prendre ce que le modèle vocal a entendu et produire ce que l'utilisateur voulait dire. Trois choses se passent en parallèle :

  • Suppression des hésitations et faux départs. "Um, so, basically, we should — actually wait, let me start over — we should cache this" devient "We should cache this." La correction en milieu de phrase est honorée ; les raclements de gorge disparaissent.
  • NER pour le vocabulaire technique. La couche apprend les noms des bibliothèques courantes, frameworks, familles de modèles, extensions de fichiers, commandes terminal et surfaces d'API idiomatiques. "React query" devient @tanstack/react-query quand le contexte environnant est un fichier JavaScript. Notre cible interne est une reconnaissance au-dessus de 95 % sur du vocabulaire technique in-domain trié, pour que les identifiants sortent justes sans forcer un dictionnaire personnel pour chaque terme commun.
  • Mise en forme structurelle. Que la sortie soit une phrase, une liste à puces, un tableau markdown ou un commentaire de code est décidé ici, à partir de ce que la couche suivante (contexte multimodal) dit de la destination.

Cette couche est le plus petit modèle du stack en nombre de paramètres, mais celle qui a le plus d'impact sur l'expérience utilisateur. On y a passé plus d'heures-équipe que sur les deux autres réunies.

Couche 3 : contexte multimodal

État de l'app + écran + curseur → directive de format. C'est la couche omni-modale — et c'est la raison pour laquelle on n'appelle pas ça juste « une app de dictée ». Le job de Loqua n'est pas de transcrire ; c'est d'écrire ce que tu voulais dire là où tu voulais le dire.

La couche de contexte lit l'app active via l'Accessibilité macOS, le texte sélectionné (s'il y en a), le texte adjacent visible, et les indices structurels de la destination (rédaction Gmail vs fil Slack vs fichier Python dans VS Code vs panneau de chat Cursor). Elle produit une directive de format que la couche linguistique utilise pour façonner le texte final.

L'intuition plus profonde — pourquoi les architectures omni-modales changent la dictée vocale plutôt que de simplement l'améliorer — est son propre article. Voir voice meets vision : comment les modèles omni-modaux débloquent la dictée consciente du contexte si tu veux ce fil.

Les chiffres qu'on suit

MétriqueCible interne actuellePourquoi ça compte
Latence end-to-endClasse 200 ms sur Apple SiliconSous le seuil à partir duquel la dictée commence à donner l'impression d'attendre
Time-to-first-token (TTFT)Sous 200 ms dans les cas streaming courantsLes premiers mots apparaissent pendant que les énonciations plus longues sont encore en cours
Précision NERAu-dessus de 95 % sur le vocabulaire technique in-domain triéLes identifiants, bibliothèques et noms de modèles doivent sortir justes
WER multilingueQuelques pourcents dans les conditions de test supportéesL'anglais/chinois mixte et l'anglais accentué doivent marcher dans les vrais workflows

Ce sont des chiffres de benchmark interne et de dogfooding, pas une suite de benchmarks tiers. Les jeux de test incluent notre propre vocabulaire technique, des exemples de code-switching, des échantillons d'anglais accentué et des environnements bruyants du quotidien. La prochaine étape côté contenu devrait être une page de méthodologie publique, pour que ces chiffres puissent être cités sans agiter les mains.

Ce qu'on en fait nous-mêmes

La chose la plus importante que je puisse dire de ce stack, c'est qu'on l'a écrit parce qu'on l'utilise. Chaque membre de l'équipe dicte au quotidien — pour les commits, les PR, les Slack internes, l'écriture technique plus longue, et (dans mon cas) la majeure partie de la prose des billets de blog comme celui-ci. Les décisions qui ont façonné l'architecture viennent d'un usage réel, pas d'une spec produit.

Trois endroits où la distinction architecture-vs-fonctionnalité ressort dans l'usage quotidien :

  • Dictée de code. La qualité du NER fait la différence entre la voix comme interface viable pour le code et la voix comme jouet. Voir le guide de dictée de code pour ce que ça débloque.
  • Code-switching multilingue. La moitié de notre Slack interne mélange mandarin et anglais. La couche linguistique est entraînée sur des données code-switched, pas autour ; les bascules en milieu de phrase marchent sans toggle de mode.
  • Formatage conscient de l'app. La même phrase vocale qui devient un commentaire de code dans VS Code et une description de PR structurée sur GitHub, c'est la différence entre la dictée vocale et un produit utile.

On est une petite équipe. La version honnête de cette histoire, c'est qu'on n'a pas les ressources pour maintenir un produit wrap-Whisper ET un stack de recherche à trois modèles. On a misé sur la voie structurelle parce qu'on avait besoin de la qualité pour nous-mêmes, et on préfère expédier quelque chose de plus restreint avec de la qualité que quelque chose de plus large sans.

Comment chaque couche a été reconstruite en 2026

Couche 1 — reconnaissance vocale. Le recognizer a été resserré autour du streaming, de la parole à faible amplitude et du vocabulaire technique ; la visite guidée plus profonde est dans à l'intérieur de notre voice stack omni-modal et les détails de post-entraînement dans le RL dans notre voice stack.

Couche 2 — intelligence linguistique. La couche linguistique traite désormais le nettoyage, la préservation des entités et la structure consciente de l'app comme un seul dictation model stack au lieu de post-processeurs séparés. C'est là que l'apprentissage par renforcement aide le plus : choisir la sortie que les utilisateurs éditent le moins.

Couche 3 — contexte multimodal. La couche de contexte a été reconstruite autour des indices d'écran locaux : app active, texte sélectionné, identifiants visibles et entourage du curseur. Voir construire un listener qui voit ce que tu vois pour l'architecture.

La prochaine frontière, c'est l'audio non verbal : AED et audio captioning comme contexte optionnel et local-first. On couvre ce travail à l'état de prototype dans des sons porteurs de sens.

Lectures complémentaires

Si tu veux aller plus loin que ce traitement de niveau blog :

Si tu as des questions sur l'architecture ou si tu veux creuser n'importe laquelle de ces couches plus en détail, écris-nous. On est une petite équipe et on lit chaque email.

Questions fréquentes

Pourquoi trois modèles plutôt qu'un gros LLM ?
Un modèle end-to-end unique a un problème structurel : la surface de loss pour "transcrire les phonèmes correctement" pointe dans une direction différente de la surface de loss pour "formater la sortie pour Slack". On a essayé d'unifier les couches 2 et 3 dans un seul transformer entraîné sur des données multi-tâches, et la précision a chuté des deux côtés. Trois modèles entraînés à dessein, chacun faisant bien un seul job, battent un modèle qui essaie d'en faire trois.
Pourquoi ne pas juste wrapper Whisper ?
Whisper est un excellent modèle ASR mais ce n'est pas un produit de dictée. L'approche wrap-Whisper échoue sur le vocabulaire technique (pas de NER en contexte), le formatage conscient de l'app (demande un post-processeur lourd) et le streaming on-device (Whisper est optimisé pour le batch). On avait besoin des trois pour notre propre usage quotidien.
Avez-vous entraîné les modèles à partir de zéro ?
Oui, pour les couches reconnaissance vocale et intelligence linguistique. La couche de contexte multimodal s'appuie sur des patterns de recherche omni-modaux (voir le post de blog omni-modal pour la filiation), avec nos propres données d'entraînement et un fine-tuning spécifique à la tâche de dictée.
Quelle est la taille de chaque modèle ?
On ne publie pas les nombres exacts de paramètres — ils sont calibrés pour tenir dans le budget du Neural Engine tout en atteignant nos cibles de latence et de précision. Les trois couches ensemble tournent dans la portion on-device du stack. Le cloud est réservé à des cas spécifiques (réécritures longues, certaines traductions) et est activable/désactivable par l'utilisateur.
Comment évaluez-vous la précision ?
Avec des suites de benchmark internes pour chaque couche, plus du dogfooding quotidien en équipe. La reconnaissance vocale est mesurée avec des protocoles type WER dans les conditions supportées. Le NER est mesuré sur du vocabulaire technique trié. On devrait publier la méthodologie avant de traiter un chiffre comme un benchmark externe.
Allez-vous open-sourcer une partie de tout ça ?
Pas prévu actuellement pour le stack de production — l'équipe est petite et maintenir une release publique propre à côté du produit nous ralentirait. On publie des notes comme celle-ci quand il y a quelque chose à dire. Si tu veux collaborer en recherche, envoie-nous un email.

Essaie Loqua aujourd'hui

Gratuit pour commencer. Natif Mac. Construit par des chercheurs en algorithmes qui s'en servent tous les jours.

Télécharger pour Mac

D'autres articles du blog Loqua

ingénierie
Dictée vocale omni-modale : compréhension multimodale, MoE et sortie texte en streaming
ingénierie
Dictée vocale et apprentissage par renforcement : GRPO, DPO et on-policy distillation dans notre voice stack
ingénierie
Reconnaissance vocale multimodale : construire un listener qui voit ce que tu vois
productivité
Voice productivity stack : 9 outils qu'on utilise vraiment pour écrire, livrer et penser
tutoriel
Notes de réunion à la voix sur Mac : de la voix au fait, avec notes et action items