PersonaPlex

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

สถาปัตยกรรม

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

ในโหมด 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

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

# speech-to-speech พื้นฐาน
.build/release/speech respond --input question.wav

# เลือกพรีเซ็ตเสียง
.build/release/speech respond --input question.wav --voice NATM0

# สตรีมเอาต์พุตเสียงระหว่างการสร้าง
.build/release/speech respond --input question.wav --stream

# ข้อความ system prompt แบบกำหนดเอง
.build/release/speech respond --input question.wav --system-prompt-text "You enjoy having a good conversation."

# ใช้พรีเซ็ต system prompt
.build/release/speech respond --input question.wav --system-prompt customer-service

# รับบทถอดความพร้อมกับเสียง
.build/release/speech respond --input question.wav --transcript

# เอาต์พุต JSON พร้อม 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

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