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-queryet 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 :
- Entendre les mots. Acoustique vers token. C'est ce que fait bien l'ASR.
- 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.
- 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-queryquand 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étrique | Cible interne actuelle | Pourquoi ça compte |
|---|---|---|
| Latence end-to-end | Classe 200 ms sur Apple Silicon | Sous 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 courants | Les premiers mots apparaissent pendant que les énonciations plus longues sont encore en cours |
| Précision NER | Au-dessus de 95 % sur le vocabulaire technique in-domain trié | Les identifiants, bibliothèques et noms de modèles doivent sortir justes |
| WER multilingue | Quelques pourcents dans les conditions de test supportées | L'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 :
- Le papier Whisper (Radford et al., 2022) — pour le paradigme d'entraînement audio faiblement supervisé dont on s'est inspiré pour la couche 1.
- La documentation Apple Core ML — pour voir à quoi ressemble le déploiement Neural Engine en pratique.
- Notre note compagnon sur voice meets vision : la dictée omni-modale pour le raisonnement de la couche 3.
- Notre note sur la confidentialité by design avec une architecture hybride pour ce qui passe sur le réseau et ce qui n'y passe pas.
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
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