Assistente de voz em português que roda 100% offline — sem nuvem, sem API paga, sem GPU.
Você fala "Aiden"; ele acorda, ouve, entende, decide se responde sozinho ou chama o modelo de linguagem, e fala de volta. Tudo dentro da sua máquina: nenhum byte de áudio sai do computador, e não há chave de API em lugar nenhum.
A máquina em que ele foi construído é um notebook sem GPU dedicada, com 15,7 GB de RAM e sem CUDA. Essa restrição não é um detalhe do ambiente — é o que decide cada escolha técnica do projeto, e é o que torna os números abaixo interessantes.
Medidos na mesma máquina, entre versões, sem trocar hardware:
| v1 | hoje | ||
|---|---|---|---|
| Tempo até a primeira resposta (TTFA) | 14,8 s | 8,5 s | −42% |
| Taxa de erro de transcrição (WER) | 31% | 24,7% | −6,3 p.p. |
| Testes automatizados | 37 | 524 | 44 arquivos |
| Skills | 0 | 27 | roteador de intenção |
| Chamadas a serviços em nuvem | 0 | 0 | por construção |
flowchart LR
MIC([microfone]) --> WW["wake word<br/>openWakeWord"]
WW --> VAD["VAD<br/>silero-vad"]
VAD --> STT["STT<br/>faster-whisper<br/>(cascata small→medium)"]
STT --> ROT{"roteador<br/>de intenção"}
ROT -->|casa com skill| SK["27 skills<br/>hora, tarefas, música,<br/>abrir programas…"]
ROT -->|conversa aberta| LLM["LLM local<br/>Ollama"]
SK --> TTS["TTS<br/>Piper pt_BR"]
LLM --> TTS
TTS --> SPK([alto-falante])
ROT -.eventos.-> HUD["HUD na tela<br/>FastAPI + WebSocket"]
Três formas de ativar: a wake word "Aiden", um atalho global do Windows
(RegisterHotKey, funciona com o Aiden em segundo plano) e a tecla Num Lock.
Para encerrar, basta dizer "boa noite, encerrar".
Cada camada — STT, TTS e o modelo de linguagem — é uma interface abstrata em
base.py. Trocar de motor é escrever um arquivo novo e mudar uma linha em
config/aiden.yaml. Nenhum valor mágico vive no código.
O arquivo docs/DECISOES.md tem 169 KB de registro: cada
decisão do projeto com as alternativas consideradas e o motivo da escolha. Quatro
exemplos do que está lá:
A cascata de STT existe porque medir venceu o palpite. O modelo small erra
mais que o medium, e o medium é lento demais em CPU. A solução foi rodar o
small e escalar para o medium só quando a transcrição sai com sinais de
desconfiança. O ganho de WER foi medido em bancada de 25 áudios, não estimado.
A contagem de tokens é real, não estimada. O orçamento de contexto usa
prompt_eval_count do próprio Ollama, com num_predict=1 para não gerar a
resposta inteira só para contar. A versão anterior estimava por caractere e
errava o suficiente para cortar a memória da sessão anterior.
O que se viola vira trava. A regra "padrão de ativação vem do log, nunca da
cabeça" foi violada quatro vezes — inclusive por mim, horas depois de escrevê-la.
Então virou teste: todo padrão de toda skill precisa casar com pelo menos uma fala
real registrada em data/corpus_ativacao.txt, senão o pytest reprova. Padrão
ainda sem fala real é permitido, mas só marcado com # manual: <data> — e o teste
conta quantos existem. Garantia estrutural nunca falhou neste projeto; garantia
documental falhou quatro vezes.
Capacidade não exercitada não conta como entregue. A versão 4 fechou com 428 testes e um catálogo de 60 programas descobertos sozinho — contra 16 turnos de uso real. O diagnóstico virou a regra que rege a versão atual: a v4.5 não acrescenta funcionalidade, ela existe para tornar o uso inevitável. É por isso que este README não promete nada que ainda não foi usado.
pytest :: o que vale em qualquer máquina — é o que a CI roda
pytest --maquina :: inclui a verificação da instalação real524 testes em 44 arquivos, rodando a cada push pelo GitHub Actions em
windows-latest com Python 3.12. A suíte não precisa de microfone, de GPU nem do
Ollama no ar: os motores de áudio e o modelo de linguagem entram por interface, e
os testes usam dublês.
Um teste que só passa na sua máquina é um teste quebrado. O marcador maquina
existe para separar as duas perguntas que a suíte fazia misturadas: "o código está
certo?", que vale em qualquer lugar, e "esta instalação aqui está montada?" — que
depende do microfone, do Menu Iniciar do Windows e do modelo da wake word treinado
localmente. Só a primeira roda por padrão. A segunda fica marcada e sai do caminho
da CI, porque um runner recém-criado reprovaria por ausência de máquina, não por
defeito — e CI que reprova por isso ensina a ignorar CI vermelho.
- Windows 10 ou 11
- Python 3.12 — não 3.11 (o
numpyfixado não tem distribuição para ela) e não 3.13 (ainda faltam wheels de áudio e IA) - Ollama instalado e rodando
- Microfone e fone de ouvido (fone evita eco na interrupção por voz)
- Internet apenas na primeira execução, para baixar o modelo do Whisper e as vozes do Piper. Depois disso, offline de verdade.
Não é preguiça de portar, é escopo declarado. O Aiden usa msvcrt para leitura de
tecla, RegisterHotKey da API do Windows para o atalho global e WebView2 (via
pywebview) para a janela do widget sempre-no-topo. Rodar em Linux ou macOS exigiria
substituir essas três camadas — perfeitamente possível, mas não é o que este projeto
se propõe a fazer.
:: 1. modelo de linguagem local
ollama pull gemma2:2b
:: 2. ambiente e dependências
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
pip install -e .
:: 3. rodar
run.batO pip install -e . instala o pacote aiden em modo editável — é o que faz
python -m aiden funcionar. Na primeira execução o modelo do Whisper
(models/faster-whisper/) e as vozes do Piper (models/piper/) são baixados
automaticamente.
Para rodar sem janela de console, use run_silencioso.bat. O único diagnóstico
passa a ser data/logs/aiden.log.
Ferramentas de treino e diagnóstico têm dependências próprias:
pip install -r requirements-dev.txtO Aiden carrega um perfil curto sobre quem o usa, para não perguntar as mesmas
coisas toda sessão. O repositório versiona apenas
docs/PERFIL_EXEMPLO.md; para usar o seu, copie-o para
docs/PERFIL.md — esse caminho está no .gitignore e nunca sai da sua máquina.
O modelo da wake word (models/wakeword/aiden.joblib) não vem no repositório:
ele foi treinado com gravações da minha voz, e publicar isso não faria sentido nem
para você nem para mim. Para treinar o seu:
python scripts/gerar_amostras_wakeword.py :: amostras sintéticas com as vozes do Piper
python scripts/gravar_amostras_wakeword.py :: suas próprias gravações
python scripts/treinar_wakeword_local.py :: treina e salva o classificadorSem esse arquivo, a ativação por voz fica desligada — o atalho global e o Num Lock continuam funcionando normalmente.
Este é um projeto pessoal em uso real, não um produto. O que ainda não está resolvido:
- A wake word pontua alto no silêncio. Um portão de volume contorna o problema; a correção de verdade é retreinar o modelo com negativos de silêncio. Está registrado como dívida, não como resolvido.
- O portão de volume não tem folga na ponta de baixo. Medidas as 57 gravações
de
data/wakeword/minhas_gravacoesem janelas de 80 ms — a unidade que o portão de fato compara —, a melhor janela de cada uma vai de 1250 a 5942, mediana 3147. Com o portão em 1400, a chamada mais baixa das 57 é engolida. Escolher um número novo exige medir gravações de ruído, que não estão no repositório; até lá a dívida fica travada emtest_a_chamada_mais_baixa_das_57_tambem_deveria_passar, umxfail(strict=True)que avisa no dia em que o portão descer. - 8,5 s até a primeira resposta ainda é lento para perguntas triviais. É o teto do que dá para fazer em CPU com o modelo atual, e é por isso que a versão em curso aposta em tarefas longas, onde três minutos de espera valem a pena.
- Cinco decisões estão bloqueadas esperando dados de uso real. Elas não fecham com opinião, e não vão ser fechadas no chute.
src/aiden/
├── audio/ captura, VAD, wake word e reprodução
├── stt/ transcrição (faster-whisper, com cascata)
├── tts/ síntese de voz (Piper)
├── brain/ persona, memória entre sessões e o provedor de LLM
├── core/ pipeline, roteador de intenção, estado e eventos
├── skills/ as 27 capacidades diretas, sem passar pelo LLM
└── web/ servidor FastAPI + WebSocket e o HUD
docs/ AIDEN.md (escopo), DECISOES.md (registro), roadmaps e relatórios
scripts/ bancadas de medição, treino da wake word e diagnóstico
tests/ 44 arquivos, 524 testes
O escopo completo, a arquitetura e o roadmap estão em AIDEN.md.
MIT.