O que EdgeVox — Regras do Claude Code faz?
Framework de agente de voz offline para robôs. Pacote puro Python, sem dependências de nuvem, funciona em CPU/CUDA/Metal.
Estrutura do projeto
edgevox/
├── edgevox/ # Pacote fonte
│ ├── audio/ # VAD, captura de microfone, reprodução
│ ├── stt/ # Backends STT (faster-whisper, sherpa-onnx)
│ ├── llm/ # Integração llama.cpp / Gemma
│ ├── tts/ # Backends TTS (Kokoro, Piper, Supertonic, PyThaiTTS)
│ ├── core/ # Orquestração do pipeline
│ ├── cli/ # Pontos de entrada CLI
│ ├── ui/ # Widgets TUI
│ ├── integrations/ # Ponte ROS2, etc.
│ ├── tui.py # Aplicativo TUI principal
│ └── setup_models.py # Download de modelos
│ ├── server/ # Web UI FastAPI + servidor WebSocket
├── webui/ # Frontend React (Vite + Tailwind)
├── scripts/ # Scripts utilitários (upload de modelo, etc.)
├── voices/ # Arquivos de configuração de voz
├── docs/ # Documentação do projeto
├── website/ # Site VitePress
└── pyproject.toml
Pontos de entrada (veja pyproject.toml):
edgevox→edgevox.tui:main(padrão TUI,--web-uipara web,--simple-uipara CLI)edgevox-cli→edgevox.cli.main:mainedgevox-setup→edgevox.setup_models:main
Idiomas e backends suportados
| Idioma | STT | TTS |
|---|---|---|
| Inglês, Francês, Espanhol, etc. | faster-whisper | Kokoro |
| Vietnamita | sherpa-onnx (zipformer) | Piper |
| Alemão, Russo, Árabe, Indonésio | faster-whisper | Piper |
| Coreano | faster-whisper | Supertonic |
| Tailandês | faster-whisper | PyThaiTTS |
Os modelos estão hospedados em nrl-ai/edgevox-models (HuggingFace) com fallback para repositórios upstream.
Princípios de arquitetura
- Plug-and-play, customizável por padrão. Todo componente — backend STT, backend TTS, LLM, VAD, comportamento do loop do agente, estágio do pipeline, ferramenta, habilidade, hook — deve ser intercambiável sem editar o código principal. Prefira Protocols, registries e decoradores em vez de caminhos fixos. Novo comportamento chega como um novo plugin/hook/backend, não como um patch em um módulo existente. Se você se pegar adicionando uma condicional no núcleo para um caso de uso específico, dê um passo para trás e extraia-a para um ponto de injeção.
Arquitetura do harness do agente
O harness do agente (edgevox/agents/ + edgevox/llm/hooks_slm.py + edgevox/llm/tool_parsers/) está totalmente documentado em docs/documentation/:
agent-loop.md— o loop de seis pontos de fogo, despacho paralelo, curto-circuito de handoff.hooks.md— contrato de autoria de hooks, hooks embutidos, regras de ordenação.memory.md—MemoryStore/SessionStore/NotesFile/Compactor.interrupt.md— sinais de barge-in + fiação de cancelamento.multiagent.md— Blackboard, BackgroundAgent, AgentPool.tool-calling.md— cadeia de parsers + roteiro para decodificação com restrição gramatical.
Regras do harness
- Campos
AgentContexttipados (ctx.tool_registry,ctx.llm,ctx.interrupt,ctx.memory,ctx.artifacts,ctx.blackboard) são a superfície pública de conexão.ctx.stateé área de rascunho apenas para o usuário — o código do framework não deve escrever chaves mágicas ali. - Estado pertencente a hooks vive em
ctx.hook_state[id(self)]. A chaveid(self)garante que duas instâncias da mesma classe de hook não compartilham estado. - Barge-in é aplicável, não opcional. Cada chamada
LLM.completeencaminhactx.interrupt.cancel_tokenviastop_event=…para questopping_criteriado llama-cpp realmente pare a geração dentro de uma etapa de decodificação. - Contagens de tokens exatas do tokenizador.
estimate_tokens(messages, llm)eLLM.count_tokenssubstituem a heurísticachars // 4quando um LLM está disponível. Necessário para decisões corretas de janela de contexto para CJK / Vietnamita / Tailandês. - Parsing de chamada de ferramenta executa primeiro no raw.
parse_tool_calls_from_contenttenta detectores contra o conteúdo bruto antes de remover blocosthinking— Qwen3 emite chamadas de ferramenta dentro de blocos de raciocínio (veja llama.cpp#20837). - Parsers predefinidos são validados no carregamento.
resolve_preset(slug)afirma que cada nome emtool_call_parsers=(...)é um detector registrado; um erro de digitação falha ruidosamente em vez de desabilitar a detecção silenciosamente. - IDs de chamada de ferramenta emitidos pelo modelo fazem round-trip. O formato
[TOOL_CALLS]do Mistral carrega um id de 9 caracteres que a mensagemrole="tool"de acompanhamento deve reutilizar.ToolCallItem.idcanaliza isso através da cadeia de parsers e do loop do agente.
Superfícies de importação preferidas
- Framework do agente:
from edgevox.agents import LLMAgent, AgentContext, Session, Handoff, ... - Hooks embutidos:
from edgevox.agents.hooks_builtin import MemoryInjectionHook, TokenBudgetHook, ... - Fortalecimento SLM:
from edgevox.llm.hooks_slm import default_slm_hooks - Memória:
from edgevox.agents.memory import JSONMemoryStore, NotesFile, Compactor, estimate_tokens - Multiagente:
from edgevox.agents.multiagent import Blackboard, BackgroundAgent, AgentPool - Interrupção:
from edgevox.agents.interrupt import InterruptController, InterruptPolicy, EnergyBargeInWatcher
Evite acessar módulos privados ou _agent_harness.py diretamente.
Regras de codificação
- Python ≥ 3.10. Use sintaxe moderna (
X | Yunions,match,dict[str, int]). - Formate e faça lint com ruff. Comprimento de linha 120. Execute
ruff formate depoisruff check --fix. - Sem resumos finais em comentários de código. Comente o porquê, não o o quê.
- Dicas de tipo em funções públicas. Helpers internos podem omiti-las quando óbvio.
- Sem prints no código de biblioteca. Use
rich/textualpara saída voltada ao usuário,loggingpara diagnósticos. - Imports vão no topo do arquivo. Apenas mova um import para dentro de uma função quando algo concreto o forçar — quebra de importação circular, dependência opcional/pesada atrás de uma verificação de capacidade, ou carregamento lento para reduzir latência de inicialização da CLI. Conveniência ou "só é usado em um lugar" não é motivo; mova para cima.
- Sem novas dependências de alto nível sem motivo. Prefira a stdlib. Se precisar adicionar uma, atualize
pyproject.toml. - Caminhos de código conscientes de hardware devem degradar graciosamente — fallbacks CUDA/Metal/CPU, nunca trave em acelerador ausente.
- Nunca envie arquivos de modelo (
.gguf,.onnx,.bin, pesos). Eles ficam emmodels/, que está no.gitignore.
Convenções de áudio / modelo
- Taxa de amostragem: 16 kHz mono int16 para captura e entrada STT.
- Saída TTS: reamostrada para a taxa do dispositivo via
sounddevice. - Tamanho do quadro VAD: 32 ms (512 amostras @ 16 kHz).
- Orçamento de latência: STT < 0,5 s, primeiro token LLM < 0,4 s, primeiro trecho TTS < 0,1 s em RTX 3080.
- Trate o pipeline de streaming como o contrato: não introduza chamadas bloqueantes que seguram o event loop.
Ferramentas
- uv para gerenciamento de pacotes. Use
uv pip install/uv venvem vez depip/python -m venvpuro. Veja https://docs.astral.sh/uv/. - pre-commit executa ruff (lint + format), gitleaks e hooks de higiene padrão. Instale uma vez com
pre-commit install. - gitleaks escaneia segredos em cada commit. Se um achado for um falso positivo, coloque-o na lista de permissões em
.gitleaks.tomlcom um comentário explicando o porquê — não delete o achado. - pytest para testes (
pytest, modo asyncio = auto). Testes ficam emtests/.
Expectativas de fluxo de trabalho
- Leia arquivos antes de editá-los. Não proponha mudanças no código que você não olhou.
- Execute
ruff format+ruff check --fixantes de declarar uma tarefa concluída. - Não pule hooks (
--no-verify) — corrija o problema subjacente. - Não se adicione ou adicione Claude como autor/coautor de commit. Especificamente: sem trailer
Co-Authored-By: Claude …, sem rodapé🤖 Generated with Claude Code— mensagens de commit terminam após o corpo, nada mais. - Prefira editar arquivos existentes em vez de criar novos; não crie arquivos README/docs a menos que solicitado.
- Se uma mudança tocar o pipeline de streaming, anote manualmente o impacto na latência na descrição do PR.
- Prefira diagramas Mermaid em vez de arte ASCII em qualquer documento markdown (
docs/,website/,README.md, descrições de PR). GitHub e VitePress renderizam blocosmermaidnativamente; ASCII desenhado à mão com caixas e linhas é mais difícil de ler, impossível de editar limpos e quebra com mudanças de fonte monoespaçada. Os únicos diagramas ASCII aceitáveis são árvores de diretório (├──/└──) — esses permanecem como estão.
Escrevendo docs (docs/, README.md, website/)
- Sempre cite rótulos Mermaid que contenham
(,),@,:,,,&,<br/>, ou pontuação diferente de letras, dígitos,_e-. Use["foo(args)"]para nós e-->|"@tool call"|para rótulos de aresta. Parênteses não citados dentro de[...]ou|...|quebram o parser de flowchart silenciosamente — a página ainda carrega, mas o diagrama desaparece. Depois de editar qualquer bloco mermaid, abra a página localmente (npm run docs:dev) e confirme zero entradasParse error on line Nno console do navegador. A quebra é invisível na revisão do código fonte. - Teste docs no servidor de desenvolvimento antes de declarar concluído.
npm run docs:dev(VitePress em:5173) captura erros de parse do mermaid, links quebrados e âncoras mortas que a fonte markdown esconde. Para mudanças visuais (novos diagramas, layout, hero, sidebar), capture uma captura de tela Playwright da página afetada para que o revisor possa detectar regressões sem iniciar o servidor. - Links entre páginas são absolutos pela raiz, sem extensão —
/documentation/hooks, nunca./hooks.mdouhooks(o último resolve errado sobcleanUrls: true). Alvos de clique em nós Mermaid seguem a mesma convenção. - Registro na sidebar é obrigatório. Uma nova página em
docs/documentation/é invisível até ser adicionada ao blocosidebaremdocs/.vitepress/config.ts. Atualize ambos ao mesmo tempo.
O que NÃO fazer
- Não adicione chamadas de API de nuvem ou telemetria. EdgeVox é offline-first.
- Não introduza dependências licenciadas sob GPL (o projeto é MIT).
- Verificação de licença é obrigatória antes de adicionar qualquer nova dependência. Verifique a licença real do pacote (PyPI/GitHub/documentação — não apenas um palpite) e registre o resultado inline em
pyproject.tomlao lado da dependência (ex.:# MIT/# LGPL-3 — linked dinamicamente, compatível). Licenças copyleft a recusar: GPL-2/3, AGPL, SSPL. LGPL é aceitável apenas para bibliotecas de ligação dinâmica pura (PySide6, Qt Multimedia, rlottie). CC-BY-SA é aceitável para assets (conjuntos de peças SVG etc.), nunca para dependências em nível de código fonte. Em caso de dúvida, escolha uma alternativa permissiva ou sinalize para discussão. - Não envie
dist/,build/,*.egg-info/, pesos de modelo ou gravações. - Não adicione abstrações especulativas ou "proteção para o futuro" além do que a tarefa requer.