PersonaPlex
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.
Arquitetura
PersonaPlex e um modelo autoregressivo multi-stream com tres componentes principais:
| Componente | Detalhes |
|---|---|
| Temporal Transformer | 32 camadas, dim=4096, 32 heads, SwiGLU (hidden_scale=4.125), RoPE, quantizado em 8-bit (padrao) |
| Depformer | 6 camadas, dim=1024, 16 heads, MultiLinear (weights_per_step=true), dep_q=16 |
| Codec Mimi | 16 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:
| Categoria | Presets |
|---|---|
| Feminino natural | NATF0, NATF1, NATF2, NATF3 |
| Masculino natural | NATM0, NATM1, NATM2, NATM3 |
| Feminino variado | VARF0, VARF1, VARF2, VARF3, VARF4 |
| Masculino variado | VARM0, 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 = try model.makeTextDecoder() // SentencePieceDecoder
let result = model.respondWithTranscript(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:
assistant— Assistente de proposito geral (padrao)focused— Respostas concisas e diretascustomer-service— Agente de suporte educado e orientado a solucoesteacher— Estilo de ensino paciente e explicativo
Uso do CLI
Gerar uma resposta falada a partir de uma entrada de audio:
# Basic speech-to-speech
.build/release/audio respond --input question.wav
# Choose a voice preset
.build/release/audio respond --input question.wav --voice NATM0
# Stream audio output during generation
.build/release/audio respond --input question.wav --stream
# Custom system prompt text
.build/release/audio respond --input question.wav --system-prompt-text "You enjoy having a good conversation."
# Use a preset system prompt
.build/release/audio respond --input question.wav --system-prompt customer-service
# Get transcript alongside audio
.build/release/audio respond --input question.wav --transcript
# JSON output with metadata
.build/release/audio respond --input question.wav --json
Opcoes
| Opcao | Descricao |
|---|---|
--input | Arquivo de audio de entrada (WAV, obrigatorio) |
--voice | Nome do preset de voz (por exemplo, NATM0, VARF2) |
--system-prompt | Preset de system prompt: assistant, focused, customer-service, teacher |
--system-prompt-text | Texto de system prompt customizado (sobrescreve --system-prompt) |
--max-steps | Maximo de passos de geracao |
--stream | Emitir chunks de audio durante a geracao |
--compile | Usar inferencia compilada MLX para geracao mais rapida |
--transcript | Emitir a transcricao de texto junto com o audio |
--json | Saida JSON com metadados |
Parametros de sampling tambem podem ser sobrescritos:
| Opcao | Padrao | Descricao |
|---|---|---|
--audio-temp | 0.8 | Temperatura de sampling de tokens de audio |
--audio-top-k | 250 | Sampling top-k de tokens de audio |
--text-temp | 0.7 | Temperatura de sampling de tokens de texto |
--text-top-k | 25 | Sampling 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
| Metrica | Valor |
|---|---|
| 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 |
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
| Modelo | Tamanho | HuggingFace |
|---|---|---|
| PersonaPlex-7B (8-bit) recomendado | 9.1 GB | aufklarer/PersonaPlex-7B-MLX-8bit |
| PersonaPlex-7B (4-bit) | 4.9 GB | aufklarer/PersonaPlex-7B-MLX-4bit |
API Swift
import PersonaPlex
let model = try await PersonaPlexModel.loadFromHub()
let response = try await model.respond(
audioFile: "question.wav",
voice: .NATM0,
systemPrompt: .assistant
)
try response.audio.write(to: "answer.wav")