نماذج الكلام إلى الكلام
تدعم Soniqo عائلتين أصليتين عبر MLX للمحادثة من الكلام إلى الكلام: PersonaPlex 7B وVoiceChat 11B. يستقبل كلاهما الكلام ويولّد صوت النموذج مباشرةً، لكنهما يستخدمان بنيتين مختلفتين للازدواج وعقدين مختلفين للتشغيل.
اختر نموذجًا
| النموذج | نمط المحادثة | الأنسب لـ |
|---|---|---|
| PersonaPlex 7B | ازدواج كامل متزامن باستخدام Moshi وDepformer وMimi | تداخل الاستماع والكلام و18 صوتًا قابلاً للاختيار |
| VoiceChat 11B | ازدواج مستمر متزامن مع الإطارات باستخدام FastConformer وNemotron-H وEAR-TTS ومرمّز عصبي | إطارات بث 80 ms وأحداث نص/وظيفة وصوت النموذج الأصلي |
هذه نماذج محادثة من الكلام إلى الكلام. يحوّل Hibiki الكلام إلى كلام مباشرةً أيضًا، لكن مهمته هي ترجمة الكلام المتدفقة، لذلك له دليل منفصل.
PersonaPlex 7B
نموذج حوار كلام إلى كلام ثنائي الاتجاه كامل مبني على بنية Moshi (Kyutai). يولّد PersonaPlex 7B ردودًا منطوقة مباشرةً من المدخل المنطوق — دون خط أنابيب نصّي وسيط. يأتي النموذج مع 18 إعدادًا صوتيًا مسبقًا، ومتوفّر بتكميم 8-bit (المُوصى به) و4-bit. إعداد 8-bit هو الافتراضي — فهو أسرع بنسبة 30 % وينتج ردودًا متماسكة، بينما يُضعف 4-bit جودة المخرجات.
VoiceChat 11B
مرجع speech-to-speech ثنائي الاتجاه: SALM-Duplex: Efficient and Direct Duplex Modeling for Speech-to-Speech Language Model (Interspeech 2025)، كما تستشهد به بطاقة VoiceChat الأصلية من NVIDIA.
M5 Pro، ذاكرة 48 GB، Release، إيقاع حي 80 ms، INT5 برؤوس محمية: قاست ثلاثة تشغيلات مضبوطة RTF للمسار الكامل قدره 0.94 و0.92 و0.92، مع ذروة RSS نحو 8.70 GB. تراوح وصول أول نص منطوق بين 44.3 و46.7 ms، وأول صوت قابل للتشغيل بين 74.0 و76.4 ms. هذه أرقام حساب بعد الإحماء، وليست زمن تبديل الأدوار المتعلّم؛ ولم يُعد قياس أداء INT8 المحسّن بعد.
VoiceChat هو runtime الآخر الأصلي عبر MLX لتحويل الكلام إلى كلام في speech-swift. يجمع إدراك FastConformer السببي، وقناتي النص/الوظيفة ثنائيتي الاتجاه في Nemotron-H، وEAR-TTS مستقلاً مشروطاً بالنص، ومرمّزاً عصبياً بتردد 22.05 kHz. تستقبل الجلسة باستمرار صوتاً أحادياً 16 kHz وتعيد أحداث النص وإطار خرج من 1,764 عينة كل 80 ms.
استخدم VoiceChatModel.load(from:)، وابدأ VoiceChatSession، ثم مرّر الصوت عبر pushAudio. يجب أن يحتوي bundle الكامل على encoder/ وllm/ وtts/؛ وتُرفض bundles القديمة الخاصة بالفهم فقط.
يحافظ INT5 على معدل إجمالي أقل من RTF 1 على M5 Pro المختبَر. وبقي p95 الكلي للإطار في التشغيلات الثلاثة دون 80 ms، بين 76.2 و78.6 ms؛ لذلك يبقى هامش الموعد ضيقاً. قِس بداية دور النموذج وزمن حساب العتاد كلّاً على حدة.
بنية PersonaPlex واستخدامه
PersonaPlex نموذج انحداري ذاتي متعدّد المسارات يضمّ ثلاثة مكوّنات أساسية:
| المكوّن | التفاصيل |
|---|---|
| Temporal Transformer | 32 طبقة، dim=4096، 32 رأسًا، SwiGLU (hidden_scale=4.125)، RoPE، مكمَّم إلى 8-bit (افتراضيًا) |
| Depformer | 6 طبقات، dim=1024، 16 رأسًا، MultiLinear (weights_per_step=true)، dep_q=16 |
| Mimi Codec | 16 codebook، معدّل إطارات 12.5 Hz، مخرج صوتي بتردّد 24 kHz |
يعالج النموذج 17 مسارًا في آنٍ واحد: مسار نصّي واحد + 8 مسارات صوتية للمستخدم + 8 مسارات صوتية للوكيل. تتيح هذه البنية محادثة ثنائية الاتجاه كاملة يمكن فيها للنموذج أن يستمع ويتحدّث في الوقت ذاته.
الإعدادات الصوتية المسبقة
يحتوي PersonaPlex على 18 إعدادًا صوتيًا مدمجًا بأساليب طبيعية ومتنوّعة:
| الفئة | الإعدادات |
|---|---|
| أنثوي طبيعي | NATF0, NATF1, NATF2, NATF3 |
| ذكوري طبيعي | NATM0, NATM1, NATM2, NATM3 |
| أنثوي متنوّع | VARF0, VARF1, VARF2, VARF3, VARF4 |
| ذكوري متنوّع | VARM0, VARM1, VARM2, VARM3, VARM4 |
المونولوج الداخلي
يولّد PersonaPlex مسارين متوازيين في كلّ خطوة: 8 رموز codebook صوتية لـ Mimi codec ورمز نصّي واحد للمونولوج الداخلي للنموذج. مسار النصّ هو ما “يفكّر” به النموذج أثناء حديثه — قد يتباعد قليلًا عن الصوت النهائي، لكنّه في الواقع يعكس الردّ المنطوق بدقّة كافية لاستخدامه تفريغًا مباشرًا.
تعود رموز النصّ على هيئة معرّفات قِطَع SentencePiece خام. فكّكها باستخدام 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
في وضع البثّ، يُصدر respondStream أجزاء textTokens فور إنتاجها — فكّكها تدريجيًا لتغذية عرض ترجمات حيّ بينما لا يزال الصوت يتولّد. يفعل خيار --transcript في الـ CLI هذا تمامًا خلف الكواليس.
لماذا هذا مهمّ: يُبنى SentencePieceDecoder على قارئ protobuf المشترك AudioCommon.SentencePieceModel، لذلك يفكّك PersonaPlex وOmnilingualASR وأيّ نموذج مستقبلي مبني على SentencePiece عبر نفس تنفيذ المُرمِّز. راجع مرجع SentencePieceModel.
تلقينات النظام
مرّر أيّ تلقين نظام مخصّص كسلسلة نصّية بسيطة — دون حاجة إلى ترميز خارجي:
let response = model.respond(
userAudio: audio,
voice: .NATM0,
systemPrompt: "You enjoy having a good conversation."
)
أو استخدم إعدادًا مدمجًا:
assistant— مساعد متعدّد الأغراض ومفيد (الافتراضي)focused— ردود موجزة ومباشرةcustomer-service— وكيل دعم مهذّب ومُوجَّه نحو الحلولteacher— أسلوب تعليمي صبور وتوضيحي
استخدام 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 | إعداد تلقين النظام: assistant، focused، customer-service، teacher |
--system-prompt-text | نصّ تلقين نظام مخصّص (يطغى على --system-prompt) |
--max-steps | الحدّ الأقصى لخطوات التوليد |
--stream | إصدار أجزاء صوتية أثناء التوليد |
--compile | استخدام استدلال MLX المُصرَّف لتوليد أسرع |
--transcript | إخراج التفريغ النصّي بجوار الصوت |
--json | إخراج JSON مع بيانات وصفية |
يمكن أيضًا تعديل معاملات الاعتيان:
| الخيار | الافتراضي | الوصف |
|---|---|---|
--audio-temp | 0.8 | درجة حرارة اعتيان رموز الصوت |
--audio-top-k | 250 | اعتيان top-k لرموز الصوت |
--text-temp | 0.7 | درجة حرارة اعتيان رموز النصّ |
--text-top-k | 25 | اعتيان top-k لرموز النصّ |
البثّ
يُفعِّل خيار --stream إخراج الصوت في الزمن الحقيقي. تُصدَر أجزاء الصوت بمجرّد توليدها، فيمكن بدء التشغيل قبل اكتمال الردّ كاملًا. وهذا مفيد بشكل خاصّ للتطبيقات التفاعلية التي يهمّ فيها الزمن المنخفض.
الأداء
| المقياس | القيمة |
|---|---|
| عامل الزمن الحقيقي (RTF) | ~1.4 (8-bit، قريب من الزمن الحقيقي) |
| زمن الخطوة | ~112 ms/خطوة على M2 Max (8-bit) |
| حجم النموذج (8-bit) | ~9.1 GB |
| ذروة الذاكرة (8-bit) | ~11 GB |
| حجم النموذج (4-bit) | ~4.9 GB |
| ذروة الذاكرة (4-bit) | ~7 GB |
يتطلّب PersonaPlex 7B (8-bit) ما لا يقلّ عن 24 GB من الذاكرة. تتّسع نسخة 4-bit للأجهزة ذات 16 GB لكنّها تنتج مخرجات أدنى جودة. على الأجهزة ذات 8 GB لن تتّسع أيّ نسخة. استخدم --compile للحصول على أفضل أداء على العتاد المدعوم.
متغيّرات النموذج
| النموذج | الحجم | HuggingFace |
|---|---|---|
| PersonaPlex-7B (8-bit) مُوصى به | 9.1 GB | aufklarer/PersonaPlex-7B-MLX-8bit |
| PersonaPlex-7B (4-bit) | 4.9 GB | aufklarer/PersonaPlex-7B-MLX-4bit |
واجهة 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"))