PersonaPlex

Vollduplex-Sprache-zu-Sprache-Dialogmodell basierend auf der Moshi-Architektur (Kyutai). PersonaPlex 7B erzeugt gesprochene Antworten direkt aus gesprochener Eingabe — keine zwischengeschaltete Text-Pipeline nötig. Das Modell wird mit 18 Stimmvoreinstellungen ausgeliefert und ist in 8-Bit (empfohlen) und 4-Bit-Quantisierung verfügbar. 8-Bit ist der Standard — es ist 30 % schneller und erzeugt kohärente Antworten, während 4-Bit die Ausgabequalität verschlechtert.

Architektur

PersonaPlex ist ein autoregressives Multi-Stream-Modell mit drei Kernkomponenten:

KomponenteDetails
Temporal-Transformer32 Schichten, dim=4096, 32 Heads, SwiGLU (hidden_scale=4.125), RoPE, 8-Bit-quantisiert (Standard)
Depformer6 Schichten, dim=1024, 16 Heads, MultiLinear (weights_per_step=true), dep_q=16
Mimi-Codec16 Codebooks, 12,5 Hz Frame-Rate, 24 kHz Audio-Ausgabe

Das Modell verarbeitet 17 Streams gleichzeitig: 1 Text-Stream + 8 Benutzer-Audio-Streams + 8 Agent-Audio-Streams. Diese Architektur ermöglicht Vollduplex-Gespräche, bei denen das Modell gleichzeitig zuhören und sprechen kann.

Stimmvoreinstellungen

PersonaPlex enthält 18 eingebaute Stimmvoreinstellungen in natürlichen und abwechslungsreichen Stilen:

KategorieVoreinstellungen
Natürlich weiblichNATF0, NATF1, NATF2, NATF3
Natürlich männlichNATM0, NATM1, NATM2, NATM3
Abwechslungsreich weiblichVARF0, VARF1, VARF2, VARF3, VARF4
Abwechslungsreich männlichVARM0, VARM1, VARM2, VARM3, VARM4

Inner Monologue

PersonaPlex erzeugt bei jedem Schritt zwei parallele Streams: 8 Audio-Codebook-Tokens für den Mimi-Codec und ein Text-Token für den inneren Monolog des Modells. Der Text-Stream ist das, was das Modell „denkt“, während es spricht — er kann leicht vom finalen Audio abweichen, spiegelt in der Praxis jedoch die gesprochene Antwort eng genug wider, um als Live-Transkript verwendet zu werden.

Die Text-Tokens kommen als rohe SentencePiece-Piece-IDs zurück. Dekodiere sie mit dem SentencePieceDecoder, den PersonaPlex mitliefert:

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

Im Streaming-Modus emittiert respondStream textTokens-Chunks, sobald sie erzeugt werden — dekodiere sie inkrementell, um eine Live-Untertitelansicht zu treiben, während das Audio noch generiert wird. Das CLI-Flag --transcript macht hinter den Kulissen genau das.

Warum das wichtig ist: SentencePieceDecoder baut auf dem gemeinsamen Protobuf-Reader AudioCommon.SentencePieceModel auf, sodass PersonaPlex, OmnilingualASR und jedes künftige SentencePiece-basierte Modell über dieselbe Tokenizer-Implementierung dekodieren. Siehe die SentencePieceModel-Referenz.

System-Prompts

Gib einen beliebigen benutzerdefinierten System-Prompt als reinen String weiter — keine externe Tokenisierung nötig:

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

Oder verwende eine eingebaute Voreinstellung:

CLI-Verwendung

Erzeuge eine gesprochene Antwort aus einer Audioeingabe:

# Einfache Sprache-zu-Sprache
.build/release/audio respond --input question.wav

# Stimmvoreinstellung wählen
.build/release/audio respond --input question.wav --voice NATM0

# Audio-Ausgabe während der Generierung streamen
.build/release/audio respond --input question.wav --stream

# Benutzerdefinierter System-Prompt-Text
.build/release/audio respond --input question.wav --system-prompt-text "You enjoy having a good conversation."

# Voreinstellungs-System-Prompt verwenden
.build/release/audio respond --input question.wav --system-prompt customer-service

# Transkript neben Audio erhalten
.build/release/audio respond --input question.wav --transcript

# JSON-Ausgabe mit Metadaten
.build/release/audio respond --input question.wav --json

Optionen

OptionBeschreibung
--inputEingabe-Audiodatei (WAV, erforderlich)
--voiceName der Stimmvoreinstellung (z. B. NATM0, VARF2)
--system-promptSystem-Prompt-Voreinstellung: assistant, focused, customer-service, teacher
--system-prompt-textBenutzerdefinierter System-Prompt-Text (überschreibt --system-prompt)
--max-stepsMaximale Generierungsschritte
--streamGibt Audio-Chunks während der Generierung aus
--compileVerwendet MLX-kompilierte Inferenz für schnellere Generierung
--transcriptGibt das Text-Transkript neben dem Audio aus
--jsonJSON-Ausgabe mit Metadaten

Sampling-Parameter können ebenfalls überschrieben werden:

OptionStandardBeschreibung
--audio-temp0,8Sampling-Temperatur für Audio-Tokens
--audio-top-k250Top-K-Sampling für Audio-Tokens
--text-temp0,7Sampling-Temperatur für Text-Tokens
--text-top-k25Top-K-Sampling für Text-Tokens

Streaming

Das Flag --stream aktiviert Echtzeit-Audio-Ausgabe. Audio-Chunks werden ausgegeben, sobald sie generiert werden, sodass die Wiedergabe beginnen kann, bevor die vollständige Antwort fertig ist. Das ist besonders nützlich für interaktive Anwendungen, bei denen niedrige Latenz zählt.

Leistung

MetrikWert
Echtzeitfaktor (RTF)~1,4 (8-Bit, nahe Echtzeit)
Schritt-Latenz~112 ms/Schritt auf M2 Max (8-Bit)
Modellgröße (8-Bit)~9,1 GB
Spitzen-RAM (8-Bit)~11 GB
Modellgröße (4-Bit)~4,9 GB
Spitzen-RAM (4-Bit)~7 GB
Wichtig

PersonaPlex 7B (8-Bit) benötigt mindestens 24 GB RAM. Die 4-Bit-Variante passt auf 16-GB-Geräte, erzeugt jedoch verschlechterte Ausgabe. Auf 8-GB-Geräten passt keine der beiden Varianten. Verwende --compile für die beste Leistung auf unterstützter Hardware.

Modellvarianten

ModellGrößeHuggingFace
PersonaPlex-7B (8-Bit) empfohlen9,1 GBaufklarer/PersonaPlex-7B-MLX-8bit
PersonaPlex-7B (4-Bit)4,9 GBaufklarer/PersonaPlex-7B-MLX-4bit

Swift-API

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")