โมเดล Speech-to-Speech

Soniqo รองรับโมเดล native MLX สำหรับการสนทนา speech-to-speech สองตระกูล ได้แก่ PersonaPlex 7B และ VoiceChat 11B ทั้งสองรับเสียงพูดและสร้างเสียงของโมเดลโดยตรง แต่ใช้สถาปัตยกรรม duplex และสัญญา runtime ต่างกัน

เลือกโมเดล

โมเดลรูปแบบการสนทนาเหมาะที่สุดสำหรับ
PersonaPlex 7BFull duplex พร้อมกันด้วย Moshi, Depformer และ Mimiการฟังและพูดที่ซ้อนกัน พร้อมเสียงให้เลือก 18 แบบ
VoiceChat 11BDuplex ต่อเนื่องแบบ frame-synchronous ด้วย FastConformer, Nemotron-H, EAR-TTS และ neural codecStreaming frame 80 ms, event ข้อความ/ฟังก์ชัน และเสียง native ของ checkpoint

โมเดลเหล่านี้เป็น speech-to-speech สำหรับการสนทนา Hibiki ก็แปลงเสียงพูดเป็นเสียงพูดโดยตรง แต่มีหน้าที่แปลเสียงพูดแบบ streaming จึงแยกเอกสารไว้ต่างหาก

PersonaPlex 7B

โมเดล speech-to-speech บทสนทนาแบบฟูล-ดูเพล็กซ์ ที่ใช้สถาปัตยกรรม Moshi (Kyutai) PersonaPlex 7B สร้างคำตอบที่พูดได้โดยตรงจากอินพุตที่เป็นเสียงพูด — ไม่จำเป็นต้องมี pipeline ข้อความตรงกลาง โมเดลมาพร้อมพรีเซ็ตเสียง 18 ตัวและพร้อมใช้ในรูปแบบที่ลดความละเอียดเป็น 8-bit (แนะนำ) และ 4-bit 8-bit เป็นค่าเริ่มต้น — เร็วกว่า 30% และสร้างคำตอบที่สอดคล้องกัน ในขณะที่ 4-bit จะลดคุณภาพเอาต์พุต

VoiceChat 11B

เอกสารอ้างอิง duplex speech-to-speech: SALM-Duplex: Efficient and Direct Duplex Modeling for Speech-to-Speech Language Model (Interspeech 2025) ซึ่ง model card VoiceChat ต้นทางของ NVIDIA อ้างอิง

M5 Pro, 48 GB, Release, จังหวะ live 80 ms, INT5 แบบ protected-head: การรันแบบควบคุมสามครั้งวัด whole-pipeline RTF ได้ 0.94, 0.92 และ 0.92 โดยมี RSS สูงสุดประมาณ 8.70 GB ข้อความพูดแรกมาใน 44.3–46.7 ms และเสียงแรกที่เล่นได้มาใน 74.0–76.4 ms ตัวเลขเหล่านี้คือเวลา compute หลัง warm-up ไม่ใช่ latency การผลัดกันพูดที่เรียนรู้ และยังไม่ได้วัด performance ของ INT8 หลัง optimize ซ้ำ

VoiceChat คือ runtime เสียงพูดสู่เสียงพูดแบบ native MLX อีกตัวใน speech-swift โดยรวมการรับรู้ FastConformer แบบ causal, ช่องข้อความ/ฟังก์ชัน duplex ของ Nemotron-H, EAR-TTS แบบกำหนดเงื่อนไขด้วยข้อความที่แยกอิสระ และ neural codec 22.05 kHz Session รับเสียง mono 16 kHz อย่างต่อเนื่อง และส่ง text event พร้อม output frame 1,764 samples ทุก 80 ms

โหลดด้วย VoiceChatModel.load(from:) เริ่ม VoiceChatSession และป้อนเสียงผ่าน pushAudio Bundle แบบเต็มต้องมี encoder/, llm/ และ tts/; bundle รุ่นเก่าที่มีเฉพาะส่วนความเข้าใจจะถูกปฏิเสธ

ประสิทธิภาพปัจจุบัน

INT5 รักษา throughput รวมต่ำกว่า RTF 1 บน M5 Pro ที่ทดสอบ การรันทั้งสามยังรักษา total/frame p95 ต่ำกว่า 80 ms ที่ 76.2–78.6 ms แต่ deadline headroom ยังแคบ ควรวัดจังหวะเริ่มพูดของโมเดลและ latency การคำนวณของฮาร์ดแวร์แยกกัน

สถาปัตยกรรมและการใช้งาน PersonaPlex

PersonaPlex เป็นโมเดล autoregressive แบบหลายสตรีมที่มีส่วนประกอบหลัก 3 ส่วน:

ส่วนประกอบรายละเอียด
Temporal Transformer32 layers, dim=4096, 32 heads, SwiGLU (hidden_scale=4.125), RoPE, 8-bit quantized (default)
Depformer6 layers, dim=1024, 16 heads, MultiLinear (weights_per_step=true), dep_q=16
Mimi Codec16 codebooks, 12.5 Hz frame rate, 24 kHz audio output

โมเดลประมวลผล 17 สตรีม พร้อมกัน: 1 สตรีมข้อความ + 8 สตรีมเสียงผู้ใช้ + 8 สตรีมเสียงเอเจนต์ สถาปัตยกรรมนี้รองรับบทสนทนาแบบฟูล-ดูเพล็กซ์ที่โมเดลสามารถฟังและพูดได้ในเวลาเดียวกัน

พรีเซ็ตเสียง

PersonaPlex มาพร้อมพรีเซ็ตเสียง 18 ตัวในสไตล์ที่เป็นธรรมชาติและหลากหลาย:

หมวดหมู่พรีเซ็ต
หญิงธรรมชาติNATF0, NATF1, NATF2, NATF3
ชายธรรมชาติNATM0, NATM1, NATM2, NATM3
หญิงหลากหลายVARF0, VARF1, VARF2, VARF3, VARF4
ชายหลากหลายVARM0, VARM1, VARM2, VARM3, VARM4

ความคิดภายใน (Inner Monologue)

PersonaPlex สร้างสตรีมขนานสองสตรีมในทุกขั้นตอน: 8 codebook token เสียงสำหรับ codec Mimi และ 1 text token สำหรับ inner monologue ของโมเดล สตรีมข้อความคือสิ่งที่โมเดล “กำลังคิด” ขณะพูด — อาจเบี่ยงเบนเล็กน้อยจากเสียงสุดท้าย แต่ในทางปฏิบัติจะสะท้อนคำตอบที่พูดได้ใกล้พอที่จะใช้เป็นบทถอดความสด

Text token จะกลับมาเป็น raw SentencePiece piece ID ถอดรหัสด้วย SentencePieceDecoder ที่ PersonaPlex ปล่อยมาด้วย:

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

ในโหมด streaming, respondStream จะปล่อย textTokens chunk ทีละชุดเมื่อถูกสร้างขึ้น — ถอดรหัสทีละขั้นเพื่อขับเคลื่อนหน้า caption แบบสดในขณะที่เสียงยังคงสร้างอยู่ แฟล็ก --transcript CLI ทำสิ่งนี้เบื้องหลังพอดี

เหตุใดเรื่องนี้จึงสำคัญ: SentencePieceDecoder สร้างขึ้นบน protobuf reader ของ AudioCommon.SentencePieceModel ที่ใช้ร่วมกัน ดังนั้น PersonaPlex, OmnilingualASR และโมเดลที่ใช้ SentencePiece อนาคตจึงถอดรหัสผ่าน tokenizer แบบเดียวกัน ดู เอกสารอ้างอิง SentencePieceModel

System Prompt

ส่ง system prompt แบบกำหนดเองใด ๆ เป็นสตริงธรรมดา — ไม่จำเป็นต้องใช้ tokenization ภายนอก:

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

หรือใช้พรีเซ็ตในตัว:

การใช้งาน CLI

สร้างคำตอบเป็นเสียงพูดจากอินพุตเสียง:

# 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

ตัวเลือก

ตัวเลือกคำอธิบาย
--inputไฟล์เสียงอินพุต (WAV, ต้องระบุ)
--voiceชื่อพรีเซ็ตเสียง (เช่น NATM0, VARF2)
--system-promptพรีเซ็ต system prompt: assistant, focused, customer-service, teacher
--system-prompt-textข้อความ system prompt แบบกำหนดเอง (เขียนทับ --system-prompt)
--max-stepsขั้นตอนการสร้างสูงสุด
--streamปล่อย chunk เสียงระหว่างการสร้าง
--compileใช้ MLX compiled inference เพื่อการสร้างที่เร็วขึ้น
--transcriptเอาต์พุตบทถอดความข้อความพร้อมกับเสียง
--jsonเอาต์พุต JSON พร้อม metadata

พารามิเตอร์การสุ่มสามารถเขียนทับได้:

ตัวเลือกค่าเริ่มต้นคำอธิบาย
--audio-temp0.8อุณหภูมิการสุ่ม audio token
--audio-top-k250การสุ่ม top-k ของ audio token
--text-temp0.7อุณหภูมิการสุ่ม text token
--text-top-k25การสุ่ม top-k ของ text token

Streaming

แฟล็ก --stream เปิดใช้งานเอาต์พุตเสียงแบบเรียลไทม์ Chunk เสียงจะถูกปล่อยออกมาเมื่อถูกสร้างขึ้น ดังนั้นการเล่นจึงเริ่มต้นได้ก่อนที่คำตอบจะเสร็จสมบูรณ์ มีประโยชน์อย่างยิ่งสำหรับแอปพลิเคชันแบบโต้ตอบที่หน่วงต่ำเป็นสิ่งสำคัญ

ประสิทธิภาพ

เมตริกค่า
Real-time factor (RTF)~1.4 (8-bit, ใกล้เรียลไทม์)
หน่วงของขั้นตอน~112 ms/step บน M2 Max (8-bit)
ขนาดโมเดล (8-bit)~9.1 GB
RAM สูงสุด (8-bit)~11 GB
ขนาดโมเดล (4-bit)~4.9 GB
RAM สูงสุด (4-bit)~7 GB
สำคัญ

PersonaPlex 7B (8-bit) ต้องการ RAM อย่างน้อย 24 GB รุ่น 4-bit พอดีบนอุปกรณ์ 16 GB แต่ผลลัพธ์จะเสื่อมคุณภาพ บนอุปกรณ์ 8 GB จะไม่มีรุ่นใดพอดี ใช้ --compile เพื่อประสิทธิภาพที่ดีที่สุดบนฮาร์ดแวร์ที่รองรับ

รุ่นย่อยของโมเดล

โมเดลขนาดHuggingFace
PersonaPlex-7B (8-bit) แนะนำ9.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"))