PersonaPlex

Modèle de dialogue parole-à-parole full-duplex basé sur l'architecture Moshi (Kyutai). PersonaPlex 7B génère des réponses parlées directement à partir d'une entrée parlée — sans pipeline texte intermédiaire. Le modèle est livré avec 18 préréglages de voix et est disponible en quantification 8 bits (recommandée) et 4 bits. Le 8 bits est la valeur par défaut — il est 30 % plus rapide et produit des réponses cohérentes, tandis que le 4 bits dégrade la qualité de sortie.

Architecture

PersonaPlex est un modèle autorégressif multi-flux avec trois composants principaux :

ComposantDétails
Temporal Transformer32 couches, dim=4096, 32 têtes, SwiGLU (hidden_scale=4.125), RoPE, quantifié 8 bits (par défaut)
Depformer6 couches, dim=1024, 16 têtes, MultiLinear (weights_per_step=true), dep_q=16
Mimi Codec16 codebooks, taux de trame 12,5 Hz, sortie audio 24 kHz

Le modèle traite 17 flux simultanément : 1 flux texte + 8 flux audio utilisateur + 8 flux audio agent. Cette architecture permet une conversation full-duplex où le modèle peut écouter et parler en même temps.

Préréglages de voix

PersonaPlex inclut 18 préréglages de voix intégrés dans des styles naturels et variés :

CatégoriePréréglages
Femme naturelleNATF0, NATF1, NATF2, NATF3
Homme naturelNATM0, NATM1, NATM2, NATM3
Femme variéeVARF0, VARF1, VARF2, VARF3, VARF4
Homme variéVARM0, VARM1, VARM2, VARM3, VARM4

Monologue intérieur

PersonaPlex génère deux flux parallèles à chaque étape : 8 tokens de codebook audio pour le codec Mimi et un token de texte pour le monologue intérieur du modèle. Le flux de texte est ce que le modèle « pense » pendant qu'il parle — il peut diverger légèrement de l'audio final, mais en pratique il reflète suffisamment la réponse parlée pour être utilisé comme transcription en direct.

Les tokens de texte reviennent sous forme d'IDs de pièces SentencePiece bruts. Décodez-les avec le SentencePieceDecoder fourni par PersonaPlex :

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)  // Float32 mono 24 kHz

En mode streaming, respondStream émet des chunks textTokens à mesure qu'ils sont produits — décodez-les de façon incrémentale pour piloter une vue de sous-titres en direct pendant que l'audio est encore en génération. L'option CLI --transcript fait exactement cela en coulisses.

Pourquoi c'est important : SentencePieceDecoder est construit sur le lecteur protobuf partagé AudioCommon.SentencePieceModel, donc PersonaPlex, OmnilingualASR et tout futur modèle basé sur SentencePiece décodent via la même implémentation de tokenizer. Voir la référence SentencePieceModel.

Prompts système

Passez n'importe quel prompt système personnalisé sous forme de chaîne simple — aucune tokenisation externe n'est nécessaire :

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

Ou utilisez un préréglage intégré :

Utilisation en CLI

Générer une réponse parlée à partir d'une entrée audio :

# Parole-à-parole basique
.build/release/audio respond --input question.wav

# Choisir un préréglage de voix
.build/release/audio respond --input question.wav --voice NATM0

# Streamer la sortie audio pendant la génération
.build/release/audio respond --input question.wav --stream

# Texte de prompt système personnalisé
.build/release/audio respond --input question.wav --system-prompt-text "You enjoy having a good conversation."

# Utiliser un prompt système préréglé
.build/release/audio respond --input question.wav --system-prompt customer-service

# Obtenir la transcription avec l'audio
.build/release/audio respond --input question.wav --transcript

# Sortie JSON avec métadonnées
.build/release/audio respond --input question.wav --json

Options

OptionDescription
--inputFichier audio d'entrée (WAV, requis)
--voiceNom de préréglage de voix (ex. NATM0, VARF2)
--system-promptPréréglage de prompt système : assistant, focused, customer-service, teacher
--system-prompt-textTexte de prompt système personnalisé (prime sur --system-prompt)
--max-stepsÉtapes de génération maximum
--streamÉmettre des chunks audio pendant la génération
--compileUtiliser l'inférence compilée MLX pour une génération plus rapide
--transcriptAfficher la transcription textuelle avec l'audio
--jsonSortie JSON avec métadonnées

Les paramètres d'échantillonnage peuvent également être surchargés :

OptionPar défautDescription
--audio-temp0,8Température d'échantillonnage des tokens audio
--audio-top-k250Échantillonnage top-k des tokens audio
--text-temp0,7Température d'échantillonnage des tokens texte
--text-top-k25Échantillonnage top-k des tokens texte

Streaming

L'option --stream active la sortie audio en temps réel. Les chunks audio sont émis à mesure qu'ils sont générés, donc la lecture peut commencer avant que la réponse complète ne soit terminée. C'est particulièrement utile pour les applications interactives où une faible latence compte.

Performance

MétriqueValeur
Facteur temps réel (RTF)~1,4 (8 bits, proche du temps réel)
Latence par étape~112 ms/étape sur M2 Max (8 bits)
Taille du modèle (8 bits)~9,1 Go
RAM en pic (8 bits)~11 Go
Taille du modèle (4 bits)~4,9 Go
RAM en pic (4 bits)~7 Go
Important

PersonaPlex 7B (8 bits) requiert au moins 24 Go de RAM. La variante 4 bits tient sur des appareils 16 Go mais produit une sortie dégradée. Sur des appareils 8 Go, aucune variante ne tiendra. Utilisez --compile pour de meilleures performances sur le matériel pris en charge.

Variantes du modèle

ModèleTailleHuggingFace
PersonaPlex-7B (8 bits) recommandé9,1 Goaufklarer/PersonaPlex-7B-MLX-8bit
PersonaPlex-7B (4 bits)4,9 Goaufklarer/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")