Ensinar o Mac a escrever bem: ditado com Whisper e glossário
O ditado do macOS não aprende palavras. O Glossa põe o Whisper na tecla de ditado, com o teu glossário e o contexto do cursor, e os nomes saem certos.
O ditado da Apple transforma "vLLM na megamaschine" em "VLLM na Megamachine" todas as vezes, e não há maneira de lhe ensinar uma palavra. O Whisper aprende: recebe um pequeno prompt antes do áudio, e o que estiver nesse prompt define como escreve o que vem a seguir. Construí o Glossa, uma app de barra de menus para macOS que toma conta da tecla de ditado, envia a tua voz para um servidor Whisper com o teu glossário e o texto antes do cursor como prompt, e escreve o resultado. Do primeiro benchmark a uma release pública notarizada foi cerca de meia hora, numa só sessão de Claude Code.
Dito muito. Prompts para o Claude Code, mensagens no Slack, um email ou outro. Quase tudo cheio de nomes que só existem no meu mundo: hostnames, nomes de produtos, o modelo por trás de um alias.
O ditado do macOS trata bem do inglês. Só não consegue escrever nenhuma das minhas palavras.
Pedi ao Claude Code para explorar uma alternativa, e a exploração virou uma release antes do jantar.
Índice
- O problema não é a precisão
- O Whisper ouve um prompt
- O que o Glossa faz
- A tecla de ditado não é o que eu pensava
- Meia hora do benchmark à release
- O que correu mal
- Instalar
O problema não é a precisão
Diz esta frase ao ditado do macOS: restart the vLLM container on megamaschine and point llm-router at sparkmaschine, then check the Tailscale MagicDNS entry for supermaschine.
Recebes de volta: Please restart the VLLM container on Megamachine and point the LLM router at Spark Machine. Then check the TailScale Magic DNS entry for SuperMachine.
Cada palavra é um palpite razoável. Cada uma está errada. Foneticamente a transcrição é perfeita, e mesmo assim tenho de corrigir sete coisas antes de poder enviar.
O ditado da Apple não tem vocabulário configurável. Apps como o Superwhisper e o VoiceInk têm um dicionário, o que ajuda, mas nenhuma sabe em que frase eu ia a meio.
O Whisper ouve um prompt
O Whisper da OpenAI tem uma funcionalidade que as apps de ditado usam quase só para uma coisa: um prompt inicial. É um pequeno texto que o modelo vê antes do áudio, como se fosse a transcrição até ali. O modelo continua-o.
Põe vocabulário no prompt e as palavras saem escritas assim. Põe a frase anterior e o modelo mantém o registo, a pontuação e as maiúsculas do que estavas a escrever.
Passei a mesma frase sintética pelo whisper.cpp no meu MacBook, com o modelo large-v3-turbo, antes de construir o que quer que fosse:
Sem prompt: Please restart the VLLM container on Megamachine and point the LLM router at Spark Machine. Then check the TailScale Magic DNS entry for SuperMachine.
Com prompt: Please restart the vLLM container on megamaschine and point the LLM router at sparkmaschine. Then check the Tailscale MagicDNS entry for supermaschine.
O prompt era uma linha: Terms: vLLM, megamaschine, sparkmaschine, supermaschine, llm-router, Tailscale, MagicDNS. Todos os termos saíram exatamente certos, e a chamada demorou cerca de um segundo só no portátil. Ficou decidido se valia a pena construir.
O que o Glossa faz

O Glossa é uma app de barra de menus, cerca de 600 linhas de Swift, sem projeto Xcode.
- Carrega na tecla do microfone do teclado para começar, carrega outra vez para parar, ou mantém-na premida para falar. Duplo toque no Fn e Fn premido também funcionam. Nada muda na forma como ditas.
- Antes de gravar, lê o teu glossário, um ficheiro de texto com um termo por linha, e pede à app onde estás o texto antes do cursor através da API de Acessibilidade. Os dois vão para o prompt do Whisper, glossário primeiro, contexto no fim, para que o contexto sobreviva quando o prompt é demasiado longo.
- O áudio vai para um servidor Whisper num único pedido HTTP. Por omissão é o
whisper-serverdo whisper.cpp no teu próprio Mac, mantido quente como launch agent. Pode ser qualquer endpoint de transcrição compatível com a API da OpenAI na tua rede. - O resultado é colado onde estavas, e o que tinhas na área de transferência volta ao sítio.
- Se estiver música a tocar quando começas, o volume desce dez pontos enquanto falas e volta quando paras.
Nada sai da máquina a não ser que o apontes para outro lado. O servidor é um ficheiro de modelo com 550 MB e um processo.
O Whisper não faz streaming, por isso não há texto ao vivo enquanto falas. Todas as apps Whisper "em tempo real" voltam a transcrever o áudio acumulado a cada segundo ou dois e mostram texto provisório numa janela flutuante. Está na lista; a colagem no fim ficaria igual.
A tecla de ditado não é o que eu pensava
A única coisa que eu queria era manter a tecla em que já carrego. Num MacBook é a tecla do microfone, F5, e o macOS reclama-a: com o Ditado desligado, carregar nela mostra um diálogo a perguntar se queres ligar o Ditado.

Assumi que a tecla era uma tecla multimédia, como o volume e o brilho, que chegam como eventos definidos pelo sistema. Não é. Chega a um CGEvent tap como uma tecla normal, código virtual 176, com o modificador Fn ativo. Nada documenta isto; a app registou todos os eventos e eu carreguei na tecla.
Um event tap ativo pode engolir um evento devolvendo nada. O Glossa engole essa tecla, por isso o macOS nunca a vê e o diálogo nunca aparece. Carregar e largar alterna, manter premido é push-to-talk, e como o tap vê a tecla a descer e a subir, os dois saem do mesmo handler.
A permissão de Acessibilidade que torna o tap possível é a mesma que deixa o Glossa ler o texto antes do cursor e colar o resultado. Uma permissão, três usos.
Meia hora do benchmark à release
A sessão começou com "vamos explorar este pequeno projeto". Acabou com um build universal notarizado no GitHub.
Pelo meio, numa só sessão de Claude Code: o benchmark acima, um levantamento curto das apps existentes, a app Swift com o event tap, o gravador, o construtor do prompt e a colagem, um launch agent para o servidor, um bundle assinado para que as permissões do macOS sobrevivam a rebuilds, um README, changelog e pipeline de release iguais aos do Smart Terminal, e o repositório, a release e a entrada na página de ferramentas. A versão 0.2.0 veio vinte minutos depois, com a música a baixar e um cask do Homebrew.
A minha parte foi a mesma que no wims e no Smart Terminal: dizer o que devia ser, carregar nas teclas e relatar o que aconteceu. O agente não consegue carregar na minha tecla do microfone. Todos os testes do atalho fui eu, e o log que ele escrevia era a forma de descobrir o que eu tinha visto.
O que correu mal
O pedido de Acessibilidade não é um pedido. Disse ao Claude que tinha dado a permissão. Tinha dado o microfone. A Acessibilidade é um interruptor nas Definições do Sistema para onde o diálogo só aponta, e a app ficou à espera até o agente ler a base de dados de permissões e reparar.
Mudar o nome da app revogou tudo. Dictate passou a Glossa, o que significou um novo bundle identifier, que o macOS trata como uma app nova. O meu relatório de bug ditado, "we lost the shortcut override when updating to Glossa", está no log, transcrito sem erros.
O log de debug gravou o que eu escrevia. Registar todos os eventos de teclado para encontrar a tecla do microfone registou também os códigos da mensagem que escrevi a seguir. O log passou a guardar só teclas com código 96 ou superior, as teclas F e as especiais, e o primeiro log foi apagado.
Detetar a língua custa meio segundo. Os primeiros ditados a sério demoraram 1,5 a 1,9 segundos para clips de dois segundos, mais lentos do que o benchmark. A deteção de língua é uma passagem extra pelo encoder. Fixar em inglês trouxe uma frase para cerca de 0,6 segundos, e o prompt com o glossário não custa quase nada.
O Homebrew pede para confiar numa tap. O primeiro brew install a partir da tap nova parou num prompt que o filtro de output escondeu, e passei um minuto a perguntar-me porque é que nada tinha sido instalado.
Instalar
brew install --cask pluginslab/tap/glossa
glossa-server
O cask instala a app e o whisper.cpp; o glossa-server descarrega o modelo e arranca o launch agent. Há também um DMG notarizado para macOS 14 ou posterior, com o mesmo script do servidor dentro da app.
Depois dá as permissões de Acessibilidade e Microfone, e em Definições do Sistema → Teclado põe "Premir a tecla Globo para" em Não fazer nada e o atalho do Ditado em Desligado, para que o ditado da Apple deixe de responder à tecla.
O teu glossário está em ~/.config/glossa/glossary.txt. Põe lá o que o Whisper escrever mal, e edita-o sempre que apanhares uma palavra nova; é lido em cada ditado.
O Glossa é MIT e o código está no GitHub. Faz um pedido de rede por ditado, ao servidor que configuraste.
Se o teu Mac anda há um ano a escrever "Megamachine", a solução é esta.
Links: Glossa no GitHub · whisper.cpp · Prompting no Whisper · Smart Terminal · wims
Tags: macOS, Whisper, Ditado, ferramentas de developer