Skip to content

Repository files navigation

Aiden

Assistente de voz em português que roda 100% offline — sem nuvem, sem API paga, sem GPU.

CI Licença: MIT Python 3.12 Plataforma: Windows

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.

Números medidos

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

Como funciona

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"]
Loading

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.

Decisões de engenharia

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.

Testes

pytest              :: o que vale em qualquer máquina — é o que a CI roda
pytest --maquina    :: inclui a verificação da instalação real

524 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.

Requisitos

  • Windows 10 ou 11
  • Python 3.12 — não 3.11 (o numpy fixado 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.

Por que Windows-only

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.

Instalação

:: 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.bat

O 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.txt

Perfil pessoal

O 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.

Wake word

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 classificador

Sem esse arquivo, a ativação por voz fica desligada — o atalho global e o Num Lock continuam funcionando normalmente.

Limitações conhecidas

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_gravacoes em 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 em test_a_chamada_mais_baixa_das_57_tambem_deveria_passar, um xfail(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.

Estrutura

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.

Licença

MIT.

About

Assistente de voz em portugues 100% offline, rodando em CPU-only. Python, FastAPI, faster-whisper, Piper e Ollama.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages