⚠ Übersetzung veraltet

Die neuesten Informationen zu VoiceChat-Werkzeugaufrufen und Leistung finden Sie in der englischen Version.

Speech-to-Speech-Modelle

Soniqo unterstützt zwei native MLX-Modellfamilien für Sprachdialoge: PersonaPlex 7B und VoiceChat 11B. Beide nehmen Sprache auf und erzeugen Modell-Audio direkt, verwenden aber unterschiedliche Duplex-Architekturen und Laufzeitverträge.

Modell auswählen

ModellDialogmodellAm besten für
PersonaPlex 7BGleichzeitiges Vollduplex mit Moshi, Depformer und MimiÜberlappendes Hören/Sprechen und 18 wählbare Stimmen
VoiceChat 11BKontinuierliches, frame-synchrones Duplex mit FastConformer, Nemotron-H, EAR-TTS und neuronalem Codec80-ms-Streamingframes, Text-/Funktionsereignisse und checkpoint-native Sprache

Dies sind dialogorientierte Speech-to-Speech-Modelle. Hibiki bildet Sprache ebenfalls direkt auf Sprache ab, ist aber für Streaming-Sprachübersetzung bestimmt und daher separat dokumentiert.

PersonaPlex 7B

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.

VoiceChat 11B

Duplex-Speech-to-Speech-Referenz: SALM-Duplex: Efficient and Direct Duplex Modeling for Speech-to-Speech Language Model (Interspeech 2025), zitiert in NVIDIAs Upstream-VoiceChat-Modellkarte.

M5 Pro, 48 GB, Release, 80-ms-Live-Takt, INT5 mit geschützten Heads: Drei kontrollierte Läufe maßen für den Gesamtpfad RTF 0.94, 0.92 und 0.92 bei etwa 8.70 GB Peak-RSS. Der erste gesprochene Text erschien nach 44.3–46.7 ms, das erste abspielbare Audio nach 74.0–76.4 ms. Das sind Rechenwerte im Warmzustand, keine erlernte Turn-Taking-Latenz; die optimierte INT8-Leistung wurde noch nicht neu gemessen.

VoiceChat ist die zweite native MLX-Sprache-zu-Sprache-Laufzeit in speech-swift. Sie kombiniert kausale FastConformer-Wahrnehmung, Nemotron-H-Duplex-Text-/Funktionskanäle, eigenständiges textkonditioniertes EAR-TTS und einen neuronalen 22,05-kHz-Codec. Eine Sitzung nimmt fortlaufend 16-kHz-Mono-Audio an und liefert alle 80 ms Textereignisse sowie einen Ausgabeframe mit 1.764 Samples.

Lade mit VoiceChatModel.load(from:), starte eine VoiceChatSession und übergib Audio mit pushAudio. Ein vollständiges Bundle muss encoder/, llm/ und tts/ enthalten; ältere reine Verständnis-Bundles werden abgelehnt.

Aktuelle Leistung

INT5 hält auf dem getesteten M5 Pro den Gesamtdurchsatz unter RTF 1. In allen drei Läufen blieb auch das Gesamt-p95 pro Frame mit 76.2–78.6 ms unter 80 ms; der Deadline-Spielraum bleibt daher knapp. Modell-Turnbeginn und Hardware-Rechenlatenz sind getrennt zu messen.

Live-VoiceChat-CLI

Sprich mit Soniqo über das vollständige protected-head INT5-Bundle auf Basis von NVIDIA Nemotron VoiceChat 11B. Der Befehl hält Aufnahme und Wiedergabe für Apple AEC in einer AVAudioEngine, streamt die bundle-eigenen RNN-T-Untertitel des Nutzers und spielt 22,05-kHz-Modellaudio ab, während er weiter zuhört.

Der Standardprompt von Soniqo grenzt die Fähigkeiten klar ab: Diese CLI kann sich unterhalten und Fragen beantworten, hat aber keinen Zugriff auf Kalender, Erinnerungen, Apps, Konten, Geräte oder externe Dienste. Bei solchen Anfragen sagt Soniqo deutlich, dass die Aktion nicht ausgeführt werden kann, verlangt keine unerfüllbare Bestätigung und schlägt kurz vor, was der Nutzer selbst tun kann.

speech voice-chat

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

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

Konfigurierbare MCP-Werkzeuge

Der mitgelieferte Adapter apple-reminders-eventkit stellt dem Modell jetzt nur list_reminders, create_reminder und update_reminder bereit. Die Listenermittlung bleibt intern im Adapter. Für eine Aktualisierung nennt der Benutzer den Erinnerungsnamen; die Laufzeit löst ihn in eine vertrauenswürdige private EventKit-ID auf, die nie im modellsichtbaren Ergebnis erscheint. Erstellung und Fälligkeit werden weiterhin mit einem EventKit-Speichervorgang festgeschrieben. Der erste Serverstart erhält mindestens 60 Sekunden, normale Aufrufe standardmäßig 15 Sekunden.

MCP wird nur mit --mcp-config aktiviert. Die anbieterneutrale JSON-Datei kann jeden stdio-MCP-Server starten und wählt über enabledTools ausdrücklich höchstens fünf Werkzeuge aus. Jedes Werkzeug außerhalb von readOnlyTools gilt als Schreibzugriff. Standard ist confirm: Der erste Aufruf bleibt ausstehend; eine deterministische Soniqo-Bestätigung wiederholt die erkannten Argumente und stellt klar, dass noch nichts geändert wurde. Erst eine neue bestätigende Nutzeräußerung führt ihn genau einmal aus. Ohne neue Nutzersprache kann ein identischer Aufruf selbst unter allow nicht zweimal ausgeführt werden. Alternativ stehen deny und die ausdrückliche Sitzungsfreigabe allow bereit. Das echte Ergebnis gelangt über den Funktionskanal zurück; eine Erfolgsmeldung erfordert eine ok-Antwort.

Schreibargumente werden vor der Bestätigung mit dem Nutzertranskript abgeglichen. Relative Daten verwenden das aktuelle lokale Datum; erfundene Listennamen, veraltete Daten, Prioritäten und andere nicht belegte Werte werden nie ausgeführt. Frische RNN-T-Sprache plus Äußerungsende löst die Bestätigung auch nach einem Transkript-Neustart aus. Soniqo meldet Erfolg erst, wenn der MCP-Server tatsächlich ok: true zurückgibt; andernfalls erklärt es, dass die Ausführung nicht bestätigt werden konnte.

„Welche Erinnerungen habe ich?“ führt genau einen flachen list_reminders-Aufruf über alle EventKit-Listen aus. Liste, Fälligkeit, Abschlussstatus und die nur intern verwendete stabile ID werden zwischengespeichert, sodass Detailfragen und sichere Aktualisierungen keine einzelnen Listen erneut lesen. Das Dashboard zeigt Dauer, Token-Schritte und Token/s der Funktionsdekodierung getrennt vom Audio-RTF; der RTF misst weiterhin nur die 80-ms-Audioframes im Vordergrund.

Die gesprochene Bestätigung fragt nach dem „Fortfahren“ und vermeidet bewusst alle positiven Freigabeformulierungen, damit ein Wiedergabeecho die ausstehende Aktion nicht selbst autorisieren kann.

Untergeordnete MCP-Server erben nur PATH, HOME, Variablen für das temporäre Verzeichnis und die Spracheinstellung. Zugangsdaten müssen ausdrücklich in der env-Map des Servers stehen; andere Geheimnisse des Elternprozesses werden nicht weitergegeben.

Die 8-Bit-Funktionsprojektion ist etwa 518 MB groß. Während normaler Sprache wertet VoiceChat nur einen zwischengespeicherten 36-KB-Test für PAD und Werkzeugstart aus. Der vollständige Kopf mit 131.072 Zeilen läuft erst, wenn Werkzeugstart PAD übertrifft, prüft anschließend das globale Argmax und bleibt während der Erzeugung eines offenen JSON-Aufrufs aktiv. Sobald das MCP-Ergebnis bekannt ist, spielt VoiceChat es in begrenzten kausalen Prefill-Blöcken mit 16 Tokens ein. Dabei bleiben das trainierte Funktionsfeedback sowie der Sprach- und Audiocache erhalten, ohne für jedes Ergebnis-Token einen 11B-Decode auszuführen. So bleibt die MCP-Unterhaltung echtzeitfähig, ohne Entscheidungen zum Werkzeugstart abzuschwächen.

Vor der Wiedergabe werden standardmäßig drei 80-ms-Frames (240 ms) gepuffert. Nach einer Lücke wartet die Wiedergabe vor dem Neustart auf acht Recovery-Frames. Nach dem ersten 88-ms-Callback oder drei wartenden Frames sinkt die Sprachverfeinerung von acht auf zwei Schritte; ab 120 ms, sechs Frames oder einer Eingabe-Neusynchronisierung direkt auf einen Schritt. Erreicht die Warteschlange trotzdem acht Frames (640 ms), wird nur so viel des ältesten Mikrofonaudios verworfen, wie für neue Eingabe nötig ist. Dauerhafte Überlast wird als eine Recovery-Phase behandelt, und ein bereits von RNN-T bestätigter Nutzerzug bleibt aktiv, statt durch wiederholte Resets gelöscht zu werden. Übersprungene Wörter bleiben markiert und müssen eventuell wiederholt werden.

Die Metriken beschreiben die Nutzererfahrung: behind 0.3 s ist wartendes Mikrofonaudio, RTF 0.93× ist Rechenzeit geteilt durch Audiozeit (unter 1 hält mit, über 1 fällt zurück), und last 74 ms for 80 ms audio vergleicht die letzte Rechenzeit mit den 80 ms Audio eines Modell-Frames. Der gleitende Mittelwert umfasst die letzten 120 Mikrofon-Callbacks; Replay-Ereignisse zählen nicht als zusätzliche Audiozeit.

Der Mikrofonmodus verwendet standardmäßig NVIDIAs RNN-T-Turn-Taking. Die gelernten BOS/EOS-Entscheidungen des Modells bleiben der normale latenzarme Pfad. Die erste Vorhersage jedes 80-ms-Frames gilt als blank oder non-blank; nach bestätigter Nutzersprache dienen 40 aufeinanderfolgende blank-Frames (3,2 s) nur als Sicherheitsfallback für den Start der Antwort, und 40 neue non-blank-Frames bilden den entsprechenden Barge-in-Fallback. Der Sprachzähler wird beim Start von Soniqo zurückgesetzt, sodass Aktivität aus dem abgeschlossenen Nutzerturn die Antwort nicht sofort abschneiden kann. Alle Audioframes erreichen weiterhin das Duplexmodell; --no-rnnt-turn-taking deaktiviert diese Fallbacks und überlässt BOS/EOS vollständig dem trainierten Sprachkopf. Das Dashboard zeigt RNN-T-Barge-ins und Wiedergabelücken getrennt.

Im Normalmodus wird modellnatives BOS unterdrückt, bis Nutzersprache bestätigt ist, sodass Soniqo nicht zuerst spricht. --greet erlaubt die anfängliche Begrüßung ausdrücklich. Nach hörbarem Antwortinhalt bleiben blank-PAD-Frames mindestens 16 Frames oder drei Frames pro Inhaltstoken lang im offenen Turn, je nachdem, welcher Wert größer ist. Das erste blank-PAD danach schließt den logischen Turn. Dieser inhaltsabhängige Ausklang bewahrt verzögert erzeugte Wörter, die ein fester Abbruch nach 1,28 Sekunden stummschalten kann.

Im interaktiven Modus verwendet das Terminal ein festes Dashboard im alternativen Bildschirm. Statusaktualisierungen werden dort neu gezeichnet, statt den Scrollback zu füllen. Für fortlaufende Ausgabe --plain verwenden.

Für lokale Diagnosen versieht --debug-timeline jede Äußerung von Benutzer und Soniqo mit einem Zeitstempel und fügt geparste native Aufrufargumente, MCP-Start/-Abschluss und Ergebnis-Cache-Synchronisierung in dieselbe Chronologie ein. Es markiert außerdem pronunciation ended am letzten hörbaren erzeugten PCM-Fenster; dies ist die Modellausgabezeit, nicht das Ende der Gerätewiedergabe. Das Demo blendet historische Phasenzeiten aus; detaillierte Werte für native Dekodierung, Provider, Cache und Mikrofonlast bleiben im voicechat-bench-JSON. Tool-Argumente können sichtbar werden; daher nicht in gemeinsam genutzten Logs verwenden. Der Phasentimer wird zwischen nativer Dekodierung und Provider-Wartezeit zurückgesetzt.

Live-Sitzungen behalten den unveränderlichen 37-Frame-Sprecherprompt plus 20 Sekunden aktuelle EAR-TTS-Historie; Nemotron-H verwaltet den semantischen Gesprächsverlauf separat. PAD innerhalb des inhaltsabhängigen akustischen Ausklangs wird normal erzeugt; nur spätere stille PAD-Frames werden kausal in Achterblöcken fortgeschrieben. So wird die gesprochene Antwort nicht abgeschnitten und TTS-Attention sowie Leerlaufarbeit wachsen dennoch nicht unbegrenzt. --live-speech-context-seconds 0 stellt die vollständige TTS-Historie wieder her; der Dateimodus bleibt standardmäßig exakt und vollständig.

In einem 63,6-Sekunden-Profil auf einem M5 Pro mit sicherem Ausklang maß der Live-Pfad RTF 0,87 insgesamt, RTF 0,71 im letzten Fenster, 4,1 ms/Frame Synthese im letzten Fenster, 8,72 GB Spitzen-RSS und 23,67 GB maximale physische Speichernutzung. Aktive Acht-Schritt-Sprachfenster erreichten RTF 1,05 und 1,12; deshalb schaltet die interaktive CLI bei wachsender Eingabewarteschlange weiterhin reversibel auf zwei Schritte und im Notfall auf einen Schritt. Die minimale Kosinusähnlichkeit zwischen sequenziellem und gebündeltem Leerlauf-Cachezustand betrug 0,9999999.

Der Onset-Pop-Detektor von voicechat-bench untersucht den Vorlauf und die ersten 50 ms stabiler Sprache. Er markiert nur Sprünge über 0,05 Amplitude und dem Sechsfachen der p99-Basis stabiler Sprache; Szenarien können maximum_suspect_onset_transients auf null setzen.

Mit Live-Untertiteln gemessen

Drei Release-Läufe auf einem M5 Pro mit 48 GB ergaben jeweils RTF 0,92, Gesamtframe-p95 von 74,8–75,8 ms, erstes abspielbares Audio nach 74,2–74,9 ms und etwa 8,74 GB Spitzen-RSS. Transkript und Antwort waren in allen Läufen identisch.

PersonaPlex: Architektur und Verwendung

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 = 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

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:

# 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

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