⚠ Tradução desatualizada

Consulte a versão em inglês para obter as informações mais recentes sobre chamadas de ferramentas e desempenho do VoiceChat.

Modelos de fala para fala

A Soniqo oferece duas famílias de modelos MLX nativos para conversa de fala para fala: PersonaPlex 7B e VoiceChat 11B. Ambos recebem fala e geram diretamente o áudio do modelo, mas usam arquiteturas duplex e contratos de runtime diferentes.

Escolha um modelo

ModeloModelo de conversaMais indicado para
PersonaPlex 7BFull duplex simultâneo com Moshi, Depformer e MimiEscuta e fala sobrepostas, com 18 vozes selecionáveis
VoiceChat 11BDuplex contínuo e sincronizado por frame com FastConformer, Nemotron-H, EAR-TTS e codec neuralFrames de streaming de 80 ms, eventos de texto/função e voz nativa do checkpoint

Estes são modelos conversacionais de fala para fala. O Hibiki também transforma fala diretamente em fala, mas sua tarefa é tradução de fala em streaming e está documentada separadamente.

PersonaPlex 7B

Modelo de dialogo fala para fala full-duplex baseado na arquitetura Moshi (Kyutai). PersonaPlex 7B gera respostas faladas diretamente de entrada falada — sem pipeline intermediario de texto. O modelo vem com 18 presets de voz e esta disponivel em quantizacao 8-bit (recomendado) e 4-bit. 8-bit e o padrao — e 30% mais rapido e produz respostas coerentes, enquanto 4-bit degrada a qualidade de saida.

VoiceChat 11B

Referência de speech-to-speech duplex: SALM-Duplex: Efficient and Direct Duplex Modeling for Speech-to-Speech Language Model (Interspeech 2025), citada pelo model card upstream da NVIDIA.

M5 Pro, 48 GB, Release, cadência live de 80 ms, INT5 com heads protegidos: três execuções controladas mediram RTF do pipeline completo de 0.94, 0.92 e 0.92, com cerca de 8.70 GB de RSS de pico. O primeiro texto falado chegou em 44.3–46.7 ms e o primeiro áudio reproduzível em 74.0–76.4 ms. São valores de computação aquecida, não latência de troca de turno aprendida; o desempenho INT8 otimizado ainda não foi medido novamente.

VoiceChat é o outro runtime nativo MLX de fala para fala do speech-swift. Ele combina percepção FastConformer causal, canais duplex de texto/função Nemotron-H, EAR-TTS independente condicionado por texto e codec neural de 22,05 kHz. A sessão recebe continuamente áudio mono de 16 kHz e retorna eventos de texto e um quadro de saída de 1.764 amostras a cada 80 ms.

Carregue com VoiceChatModel.load(from:), inicie uma VoiceChatSession e envie áudio com pushAudio. Um bundle completo precisa conter encoder/, llm/ e tts/; bundles antigos apenas de compreensão são rejeitados.

Desempenho atual

INT5 mantém throughput agregado abaixo de RTF 1 no M5 Pro testado. As três execuções também mantiveram o p95 total por quadro abaixo de 80 ms, entre 76.2 e 78.6 ms; portanto, a margem de prazo continua estreita. Meça separadamente o início do turno do modelo e a latência de computação do hardware.

CLI VoiceChat ao vivo

Converse com a Soniqo usando o bundle INT5 completo com protected head, baseado no NVIDIA Nemotron VoiceChat 11B. O comando mantém captura e reprodução em um único AVAudioEngine para o AEC da Apple, transmite as legendas RNN-T do usuário incluídas no bundle e reproduz áudio do modelo a 22,05 kHz enquanto continua ouvindo.

O prompt padrão da Soniqo deixa os limites claros: este CLI pode conversar e responder perguntas, mas não acessa calendários, lembretes, aplicativos, contas, dispositivos nem serviços externos. Nesses pedidos, a Soniqo informa claramente que não pode executar a ação, não pede uma confirmação que não poderá cumprir e sugere brevemente o que o usuário pode fazer.

speech voice-chat

speech voice-chat --input question.wav --output response.wav

speech voice-chat \\
  --mcp-config Examples/VoiceChatMCP/apple-reminders.json

Ferramentas MCP configuráveis

O adaptador incluído apple-reminders-eventkit agora expõe ao modelo apenas list_reminders, create_reminder e update_reminder. A descoberta de listas fica privada no adaptador. Para atualizar, o usuário diz o nome do lembrete e o runtime o resolve para um ID EventKit privado e confiável, que nunca aparece no resultado visível ao modelo. A criação e o vencimento continuam sendo confirmados em uma única gravação EventKit. O primeiro início recebe pelo menos 60 segundos e as chamadas normais mantêm 15 segundos por padrão.

O MCP só é ativado quando --mcp-config é fornecido. O JSON independente de fornecedor pode iniciar qualquer servidor MCP stdio e escolhe explicitamente até cinco ferramentas por enabledTools. Toda ferramenta fora de readOnlyTools é tratada como escrita. A política padrão é confirm: a primeira chamada fica pendente; uma confirmação determinística da Soniqo repete os argumentos interpretados e informa que nada foi alterado. Só uma nova confirmação afirmativa do usuário pode executá-la uma vez. Uma chamada idêntica não pode ser executada duas vezes sem nova fala do usuário, mesmo com allow. Também há deny e a autorização explícita allow. O resultado real retorna pelo canal de funções do modelo e o sucesso exige uma resposta ok.

Os argumentos de escrita são comparados à transcrição do usuário antes da confirmação. Datas relativas usam a data local atual; nomes de listas inventados, datas obsoletas, prioridades e outros valores não verificados nunca são executados. Nova fala RNN-T seguida do fim da enunciação resolve a confirmação mesmo após reiniciar a transcrição. A Soniqo só anuncia sucesso quando o servidor MCP realmente retorna ok: true; caso contrário, informa que não conseguiu confirmar a execução.

“Quais lembretes eu tenho?” faz uma única chamada plana a list_reminders que cobre todas as listas EventKit. Lista, vencimento, estado de conclusão e o ID estável reservado ao runtime ficam em cache, portanto perguntas de detalhe e atualizações seguras não releem cada lista. O painel mostra separadamente o tempo, os passos de token e os token/s da decodificação de função; o RTF de áudio continua medindo apenas os frames de áudio de 80 ms em primeiro plano.

A confirmação falada pergunta se o usuário deseja “prosseguir” e evita de propósito todas as frases afirmativas permitidas, para que o eco da reprodução não possa autorizar a ação pendente.

Os servidores MCP filhos herdam apenas PATH, HOME, o diretório temporário e variáveis de localidade. Credenciais devem ser passadas explicitamente no mapa env do servidor; outros segredos do processo pai não são encaminhados.

A projeção de funções de 8 bits ocupa cerca de 518 MB. Durante a fala normal, o VoiceChat avalia apenas uma sonda em cache de 36 KB para PAD e início de ferramenta. A cabeça completa de 131.072 linhas só roda quando o início de ferramenta primeiro supera PAD, verifica então o argmax global e permanece ativa enquanto uma chamada JSON aberta é gerada. Quando o resultado MCP já é conhecido, o VoiceChat o reproduz em blocos de prefill causal limitados a 16 tokens, preservando o feedback de função treinado e o estado dos caches de linguagem e fala sem executar um decode de 11B para cada token do resultado. Assim, a conversa MCP continua em tempo real sem enfraquecer as decisões de início de ferramenta.

A reprodução pré-carrega por padrão três frames de 80 ms (240 ms). Após uma falha, ela aguarda uma reserva de recuperação de oito frames. Após o primeiro callback de 88 ms ou três frames na fila, o refinamento cai de oito para duas etapas; com 120 ms, seis frames ou uma resincronização, cai diretamente para uma etapa. Se a fila ainda chegar a oito frames (640 ms), somente o mínimo de áudio antigo necessário para aceitar a nova entrada é descartado. Uma sobrecarga contínua é tratada como um único episódio de recuperação, e um turno já confirmado pelo RNN-T permanece ativo em vez de ser apagado por reinicializações repetidas. Palavras ignoradas continuam marcadas e talvez precisem ser repetidas.

As métricas descrevem a experiência: behind 0.3 s é o áudio do microfone em espera, RTF 0.93× é o tempo de cálculo dividido pelo tempo de áudio (abaixo de 1 acompanha; acima de 1 atrasa), e last 74 ms for 80 ms audio compara o último tempo de cálculo com os 80 ms de áudio representados por cada frame. A média móvel usa os últimos 120 callbacks do microfone; eventos de replay não contam como tempo de áudio adicional.

O modo microfone usa por padrão o controle de turnos RNN-T da NVIDIA. As decisões BOS/EOS aprendidas pelo modelo continuam sendo o caminho normal de baixa latência. A primeira previsão de cada frame de 80 ms é tratada como blank ou non-blank; após a confirmação da fala do usuário, 40 frames blank consecutivos (3,2 s) servem apenas como fallback de segurança para iniciar a resposta, e 40 novos frames non-blank fornecem o fallback correspondente de interrupção. O contador de fala é zerado quando a Soniqo começa, portanto a atividade do turno concluído do usuário não pode cortar a resposta imediatamente. Todos os frames de áudio continuam chegando ao modelo duplex; --no-rnnt-turn-taking desativa esses fallbacks e deixa BOS/EOS inteiramente a cargo do cabeçote de linguagem aprendido. O painel informa barge-ins RNN-T separadamente das falhas de reprodução.

No modo normal, o BOS nativo do modelo é suprimido até a fala do usuário ser confirmada, portanto a Soniqo não fala primeiro. --greet permite explicitamente a saudação inicial. Após o conteúdo audível, os PADs blank permanecem no turno aberto por pelo menos 16 frames ou por três frames por token de conteúdo, o que for maior. O primeiro PAD blank além desse orçamento fecha o turno lógico. Essa cauda proporcional ao conteúdo preserva palavras tardias que um corte fixo em 1,28 segundo pode silenciar.

O modo interativo usa um painel fixo na tela alternativa do terminal; as atualizações são redesenhadas no lugar sem preencher o histórico. Use --plain para saída cumulativa.

Para diagnóstico local, --debug-timeline marca cada frase do usuário e da Soniqo e insere na mesma cronologia os argumentos da chamada nativa analisada, o início/fim do MCP e a sincronização do cache de resultados. Também marca pronunciation ended na última janela PCM gerada audível; esse é o tempo de saída do modelo, não o término da reprodução no dispositivo. A demo omite os blocos históricos de tempo; métricas detalhadas de decodificação nativa, provedor, cache e pressão do microfone ficam no JSON do voicechat-bench. Ele pode expor argumentos de ferramentas, portanto não o use em logs compartilhados. Os temporizadores de fase são reiniciados entre a decodificação nativa e a espera do provedor.

As sessões ao vivo mantêm o prompt de voz imutável de 37 frames mais 20 segundos de histórico EAR-TTS recente; o Nemotron-H mantém separadamente o contexto semântico da conversa. Os PADs dentro da cauda acústica proporcional ao conteúdo são gerados normalmente; só os PADs silenciosos posteriores avançam causalmente em lotes de oito. Assim a resposta falada não é cortada e a atenção TTS e o trabalho ocioso continuam limitados. --live-speech-context-seconds 0 restaura todo o histórico TTS e o modo de arquivo continua exato e completo por padrão.

Em um perfil de 63,6 segundos no M5 Pro com a cauda segura, o caminho ao vivo mediu RTF agregado 0,87, RTF 0,71 na janela final, síntese final de 4,1 ms/frame, RSS máximo de 8,72 GB e footprint físico máximo de 23,67 GB. As janelas ativas de oito etapas chegaram a RTF 1,05 e 1,12, por isso o CLI interativo mantém a redução reversível para duas etapas e um modo de emergência de uma etapa quando a entrada se acumula. A similaridade cosseno mínima entre os estados de cache ocioso sequencial e em lote foi 0,9999999.

O detector de estalo inicial do voicechat-bench examina a entrada gerada e os primeiros 50 ms de fala sustentada. Ele só marca saltos acima de 0,05 de amplitude e seis vezes a base p99 da fala estável; um cenário pode definir maximum_suspect_onset_transients como zero.

Medido com legendas ao vivo

Três execuções Release em um M5 Pro de 48 GB mediram RTF 0,92, p95 do frame total de 74,8–75,8 ms, primeiro áudio reproduzível em 74,2–74,9 ms e cerca de 8,74 GB de RSS máximo. Transcrição e resposta foram idênticas.

Arquitetura e uso do PersonaPlex

PersonaPlex e um modelo autoregressivo multi-stream com tres componentes principais:

ComponenteDetalhes
Temporal Transformer32 camadas, dim=4096, 32 heads, SwiGLU (hidden_scale=4.125), RoPE, quantizado em 8-bit (padrao)
Depformer6 camadas, dim=1024, 16 heads, MultiLinear (weights_per_step=true), dep_q=16
Codec Mimi16 codebooks, frame rate de 12.5 Hz, saida de audio a 24 kHz

O modelo processa 17 streams simultaneamente: 1 stream de texto + 8 streams de audio do usuario + 8 streams de audio do agente. Esta arquitetura permite conversa full-duplex onde o modelo pode ouvir e falar ao mesmo tempo.

Presets de voz

PersonaPlex inclui 18 presets de voz integrados em estilos naturais e variados:

CategoriaPresets
Feminino naturalNATF0, NATF1, NATF2, NATF3
Masculino naturalNATM0, NATM1, NATM2, NATM3
Feminino variadoVARF0, VARF1, VARF2, VARF3, VARF4
Masculino variadoVARM0, VARM1, VARM2, VARM3, VARM4

Monologo interno

PersonaPlex gera dois streams paralelos a cada passo: 8 tokens de codebook de audio para o codec Mimi e um token de texto para o monologo interno do modelo. O stream de texto e o que o modelo esta “pensando” enquanto fala — ele pode divergir ligeiramente do audio final, mas na pratica espelha a resposta falada de perto o suficiente para usar como transcricao ao vivo.

Os tokens de texto voltam como IDs brutos de pieces do SentencePiece. Decodifique-os com o SentencePieceDecoder que o PersonaPlex fornece:

import PersonaPlex
import AudioCommon

let model = try await PersonaPlexModel.fromPretrained()
let decoder = model.tokenizer  // SentencePieceDecoder?

let result = model.respond(userAudio: userSamples, voice: .NATM0)
let transcript = decoder?.decode(result.textTokens) ?? ""
print(transcript)        // "Sure, I can help with that..."
playAudio(result.audio)  // 24 kHz mono Float32

No modo streaming, respondStream emite chunks textTokens conforme sao produzidos — decodifique-os incrementalmente para alimentar uma visualizacao de legendas ao vivo enquanto o audio ainda esta sendo gerado. A flag CLI --transcript faz exatamente isso por baixo dos panos.

Por que importa: SentencePieceDecoder e construido sobre o leitor protobuf compartilhado AudioCommon.SentencePieceModel, entao PersonaPlex, OmnilingualASR e qualquer futuro modelo baseado em SentencePiece decodificam pela mesma implementacao de tokenizer. Veja a referencia do SentencePieceModel.

System prompts

Passe qualquer system prompt customizado como uma string simples — sem tokenizacao externa necessaria:

let response = model.respond(
    userAudio: audio,
    voice: .NATM0,
    systemPrompt: "You enjoy having a good conversation."
)

Ou use um preset integrado:

Uso do CLI

Gerar uma resposta falada a partir de uma entrada de audio:

# Basic speech-to-speech
.build/release/speech respond --input question.wav

# Choose a voice preset
.build/release/speech respond --input question.wav --voice NATM0

# Stream audio output during generation
.build/release/speech respond --input question.wav --stream

# Custom system prompt text
.build/release/speech respond --input question.wav --system-prompt-text "You enjoy having a good conversation."

# Use a preset system prompt
.build/release/speech respond --input question.wav --system-prompt customer-service

# Get transcript alongside audio
.build/release/speech respond --input question.wav --transcript

# JSON output with metadata
.build/release/speech respond --input question.wav --json

Opcoes

OpcaoDescricao
--inputArquivo de audio de entrada (WAV, obrigatorio)
--voiceNome do preset de voz (por exemplo, NATM0, VARF2)
--system-promptPreset de system prompt: assistant, focused, customer-service, teacher
--system-prompt-textTexto de system prompt customizado (sobrescreve --system-prompt)
--max-stepsMaximo de passos de geracao
--streamEmitir chunks de audio durante a geracao
--compileUsar inferencia compilada MLX para geracao mais rapida
--transcriptEmitir a transcricao de texto junto com o audio
--jsonSaida JSON com metadados

Parametros de sampling tambem podem ser sobrescritos:

OpcaoPadraoDescricao
--audio-temp0.8Temperatura de sampling de tokens de audio
--audio-top-k250Sampling top-k de tokens de audio
--text-temp0.7Temperatura de sampling de tokens de texto
--text-top-k25Sampling top-k de tokens de texto

Streaming

A flag --stream habilita saida de audio em tempo real. Os chunks de audio sao emitidos conforme sao gerados, entao a reproducao pode comecar antes da resposta completa terminar. Isso e particularmente util para aplicacoes interativas onde baixa latencia importa.

Desempenho

MetricaValor
Fator de tempo real (RTF)~1.4 (8-bit, proximo de tempo real)
Latencia por passo~112 ms/passo em M2 Max (8-bit)
Tamanho do modelo (8-bit)~9.1 GB
RAM de pico (8-bit)~11 GB
Tamanho do modelo (4-bit)~4.9 GB
RAM de pico (4-bit)~7 GB
Importante

PersonaPlex 7B (8-bit) requer pelo menos 24 GB de RAM. A variante 4-bit cabe em dispositivos de 16 GB mas produz saida degradada. Em dispositivos de 8 GB, nenhuma das variantes cabera. Use --compile para melhor desempenho em hardware suportado.

Variantes do modelo

ModeloTamanhoHuggingFace
PersonaPlex-7B (8-bit) recomendado9.1 GBaufklarer/PersonaPlex-7B-MLX-8bit
PersonaPlex-7B (4-bit)4.9 GBaufklarer/PersonaPlex-7B-MLX-4bit

API Swift

import PersonaPlex
import AudioCommon

let model = try await PersonaPlexModel.fromPretrained()
let response = model.respond(
    userAudio: userSamples,
    voice: .NATM0,
    systemPrompt: "You are a helpful assistant."
)
try WAVWriter.write(samples: response.audio, sampleRate: 24000, to: URL(filePath: "answer.wav"))