راجع النسخة الإنجليزية للحصول على أحدث معلومات استدعاء الأدوات وأداء VoiceChat.
نماذج الكلام إلى الكلام
تدعم 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؛ لذلك يبقى هامش الموعد ضيقاً. قِس بداية دور النموذج وزمن حساب العتاد كلّاً على حدة.
واجهة VoiceChat الحية
تحدث مع Soniqo باستخدام حزمة INT5 الكاملة ذات protected head والمدعومة بـ NVIDIA Nemotron VoiceChat 11B. تجمع الأداة الالتقاط والتشغيل في AVAudioEngine واحد لاستخدام Apple AEC، وتبث عناوين المستخدم RNN-T من الحزمة، وتشغّل صوت النموذج بتردد 22.05 kHz مع استمرار الاستماع.
يوضح موجّه Soniqo الافتراضي حدود القدرات: تستطيع هذه الواجهة المحادثة والإجابة عن الأسئلة، لكنها لا تصل إلى التقويمات أو التذكيرات أو التطبيقات أو الحسابات أو الأجهزة أو الخدمات الخارجية. عند طلب ذلك، تقول Soniqo بوضوح إنها لا تستطيع تنفيذ الإجراء، ولا تطلب تأكيدًا لا يمكنها الوفاء به، وتقترح بإيجاز ما يمكن للمستخدم فعله بنفسه.
speech voice-chat
speech voice-chat --input question.wav --output response.wav
speech voice-chat \\
--mcp-config Examples/VoiceChatMCP/apple-reminders.json
أدوات MCP قابلة للتهيئة
يعرض محول apple-reminders-eventkit المرفق للنموذج الآن الأدوات list_reminders وcreate_reminder وupdate_reminder فقط. يبقى اكتشاف القوائم عملية داخلية خاصة بالمحول. عند التحديث ينطق المستخدم اسم التذكير، ثم يحوله وقت التشغيل إلى معرّف EventKit خاص وموثوق لا يظهر أبداً في نتيجة الأداة المرئية للنموذج. يُحفظ إنشاء التذكير وموعده في عملية EventKit واحدة. يُمنح بدء الخادم الأول 60 ثانية على الأقل، وتبقى المهلة الافتراضية للنداءات العادية 15 ثانية.
لا يُفعّل MCP إلا عند تمرير --mcp-config. يستطيع ملف JSON المحايد للمزوّد تشغيل أي خادم MCP عبر stdio، ويختار صراحةً حتى خمس أدوات بواسطة enabledTools. تُعامل كل أداة غير موجودة في readOnlyTools كعملية كتابة. السياسة الافتراضية هي confirm: يُعلّق الاستدعاء الأول، ويعيد تأكيد حتمي بصوت Soniqo المعاملات التي فُهمت ويوضح أن شيئًا لم يتغير. لا تنفذه إلا إجابة إيجابية جديدة من المستخدم ولمرة واحدة. ولا يمكن تنفيذ الاستدعاء المطابق مرتين من دون كلام جديد من المستخدم، حتى مع allow. تتوفر أيضًا deny والموافقة الصريحة allow. تعود النتيجة الحقيقية عبر قناة الوظائف في النموذج، ويتطلب إعلان النجاح استجابة ok.
تُطابق معاملات الكتابة مع نص المستخدم قبل طلب التأكيد. تُفسَّر التواريخ النسبية وفق التاريخ المحلي الحالي، ولا تُنفَّذ أسماء القوائم المختلقة أو التواريخ القديمة أو الأولويات أو أي قيم غير متحقق منها. يحسم كلام RNN-T الجديد متبوعًا بنهاية الجملة الرد الإيجابي حتى بعد إعادة بدء النص. ولا ينطق Soniqo بالنجاح إلا بعد أن يعيد خادم MCP فعليًا ok: true؛ وإلا يوضح أنه تعذر تأكيد التنفيذ.
ينفّذ سؤال «ما التذكيرات لدي؟» نداءً مسطحاً واحداً إلى list_reminders يشمل جميع قوائم EventKit. تُخزّن القائمة والموعد وحالة الاكتمال والمعرّف الثابت الخاص بوقت التشغيل، فلا تحتاج أسئلة التفاصيل والتحديثات الآمنة إلى قراءة كل قائمة من جديد. تعرض اللوحة مدة فك ترميز الدالة وخطوات token وtoken/s بصورة منفصلة عن RTF الصوتي، الذي يستمر في قياس إطارات الصوت الأمامية ذات 80 مللي ثانية فقط.
يسأل التأكيد المنطوق إن كان المستخدم يريد «المتابعة»، ويتجنب عمدًا كل عبارات قائمة الموافقة، بحيث لا يستطيع صدى صوت التشغيل تفويض الإجراء المعلّق.
لا ترث خوادم MCP الفرعية سوى متغيرات PATH وHOME والدليل المؤقت والإعدادات المحلية. يجب تمرير بيانات الاعتماد صراحةً في خريطة env الخاصة بالخادم؛ ولا تُمرر أسرار العملية الأم غير المرتبطة.
يبلغ حجم إسقاط الوظائف بدقة 8-bit نحو 518 MB. أثناء الكلام العادي، يقيّم VoiceChat مسبارًا مخزنًا بحجم 36 KB فقط لرمزي PAD وبدء الأداة. لا تعمل الرأس الكاملة ذات 131,072 صفًا إلا عندما يتجاوز رمز بدء الأداة PAD أولًا، ثم تتحقق من القيمة العظمى العامة وتبقى فعالة أثناء إنشاء استدعاء JSON مفتوح. عندما تصبح نتيجة MCP معروفة، يعيد VoiceChat تشغيلها في كتل prefill سببية محدودة من 16 token، مع الحفاظ على تغذية الوظيفة المدرّبة وحالة مخابئ اللغة والكلام من دون تنفيذ decode لنموذج 11B لكل token في النتيجة. يحافظ ذلك على محادثة MCP في الزمن الحقيقي من دون إضعاف قرارات بدء الأداة.
يُخزَّن افتراضيًا ثلاثة إطارات مدة كل منها 80 ms قبل التشغيل، أي 240 ms. بعد انقطاع ينتظر التشغيل مخزون استعادة من ثمانية إطارات. ينخفض تنقيح الصوت من ثماني خطوات إلى خطوتين بعد أول استدعاء يستغرق 88 ms أو عند تراكم ثلاثة إطارات، وينخفض مباشرةً إلى خطوة واحدة بعد 120 ms أو ستة إطارات أو إعادة مزامنة الإدخال. وإذا بلغ الطابور ثمانية إطارات (640 ms)، لا يُحذف إلا الحد الأدنى من أقدم صوت اللازم لقبول المدخل الجديد. ويُعامل الحمل الزائد المستمر كحادثة استعادة واحدة، وتبقى مداخلة المستخدم التي أكدها RNN-T مفعّلة بدل أن تمحوها عمليات إعادة الضبط المتكررة. تظل الكلمات المتخطاة معلَّمة وقد يلزم تكرارها.
تصف المقاييس تجربة المستخدم: behind 0.3 s هو صوت الميكروفون المنتظر، وRTF 0.93× هو وقت الحساب مقسومًا على وقت الصوت (أقل من 1 يواكب، وأكثر من 1 يتأخر)، وlast 74 ms for 80 ms audio يقارن وقت الحساب الأخير بمدة الصوت البالغة 80 ms في كل إطار. ويستخدم المتوسط المتحرك آخر 120 استدعاءً للميكروفون؛ ولا تُحسب أحداث إعادة التشغيل كزمن صوت إضافي.
يستخدم وضع الميكروفون افتراضيًا إدارة الأدوار RNN-T بأسلوب NVIDIA. تبقى إشارات BOS/EOS المتعلّمة في النموذج هي المسار المعتاد منخفض التأخير. يُعامل أول تنبؤ لكل إطار 80 ms على أنه blank أو non-blank؛ وبعد تأكيد كلام المستخدم، تعمل 40 إطارًا blank متتالية (3.2 ثانية) فقط كاحتياط أمان لبدء رد Soniqo، وتوفّر 40 إطارًا non-blank جديدة الاحتياط المطابق للمقاطعة. يُصفّر عداد الكلام عند بدء Soniqo، لذلك لا يمكن لنشاط دور المستخدم المكتمل أن يقطع الاستجابة فورًا. تستمر جميع إطارات الصوت بالوصول إلى النموذج ثنائي الاتجاه؛ استخدم --no-rnnt-turn-taking لتعطيل هذه الاحتياطات وترك قرارات BOS/EOS بالكامل لرأس اللغة المتعلّم. تعرض اللوحة مقاطعات RNN-T بشكل منفصل عن فجوات التشغيل.
في الوضع العادي، يُحجب BOS الأصلي من النموذج حتى يتأكد كلام المستخدم، لذلك لا تبدأ Soniqo بالكلام. يسمح --greet صراحةً بالتحية الافتتاحية. بعد محتوى الرد المسموع تبقى إطارات blank PAD داخل الدور المفتوح لمدة لا تقل عن 16 إطارًا أو ثلاثة إطارات لكل token محتوى، أيهما أطول. يغلق أول blank PAD يتجاوز هذه الميزانية الدور المنطقي. يحافظ هذا الذيل المتناسب مع المحتوى على الكلمات المتأخرة التي قد يكتمها قطع ثابت بعد 1.28 ثانية.
يستخدم الوضع التفاعلي لوحة ثابتة في الشاشة البديلة للطرفية، فتُعاد رسم تحديثات الحالة في مكانها بدلاً من ملء سجل التمرير؛ استخدم --plain لإخراج تراكمي.
للتشخيص المحلي، يضيف --debug-timeline طابعًا زمنيًا لكل عبارة للمستخدم وSoniqo، ويدرج في التسلسل نفسه معاملات الاستدعاء الأصلي بعد تحليلها، وبدء/اكتمال MCP، ومزامنة ذاكرة النتيجة. كما يضع pronunciation ended عند آخر نافذة PCM مسموعة مولّدة؛ وهذا وقت خرج النموذج لا وقت انتهاء تشغيل السماعة. يحذف العرض كتل توقيت المراحل السابقة عمدًا؛ وتبقى المقاييس التفصيلية لفك الترميز والمزوّد والذاكرة وضغط الميكروفون في JSON الخاص بـ voicechat-bench. قد يكشف معاملات الأدوات، لذلك لا تستخدمه في السجلات المشتركة. يُعاد ضبط مؤقت المرحلة بين فك الترميز الأصلي وانتظار المزوّد.
تحتفظ الجلسات الحية بموجّه المتحدث الثابت ذي 37 إطارًا إضافة إلى آخر 20 ثانية من سجل EAR-TTS، بينما يحتفظ Nemotron-H بسياق المحادثة الدلالي بشكل منفصل. تُولَّد إطارات PAD داخل الذيل الصوتي المتناسب مع المحتوى بصورة طبيعية؛ ولا تتقدم على دفعات سببية من ثمانية إلا إطارات PAD الصامتة اللاحقة. وهكذا لا يُقطع الرد المنطوق، مع بقاء انتباه TTS والعمل أثناء الخمول محدودين. تعيد --live-speech-context-seconds 0 سجل TTS الكامل، ويبقى وضع الملف دقيقًا وكاملاً افتراضيًا.
في اختبار مدته 63.6 ثانية على M5 Pro مع الذيل الصوتي الآمن، سجّل المسار الحي RTF إجماليًا 0.87 وRTF قدره 0.71 في النافذة الأخيرة، وتوليدًا قدره 4.1 ms لكل إطار في النافذة الأخيرة، وذروة RSS تبلغ 8.72 GB، وذروة بصمة فعلية تبلغ 23.67 GB. بلغت نوافذ الكلام النشطة ذات الخطوات الثماني RTF 1.05 و1.12، لذلك يواصل CLI التفاعلي الرجوع القابل للعكس إلى خطوتين مع وضع طوارئ بخطوة واحدة. بلغ الحد الأدنى لتشابه جيب التمام بين حالتي ذاكرة الخمول التسلسلية والمجمّعة 0.9999999.
يفحص كاشف الطقطقة الأولية في voicechat-bench مقدمة الصوت وأول 50 ms من الكلام المستمر. لا يعلّم إلا القفزات التي تتجاوز سعة 0.05 وستة أضعاف خط p99 للكلام المستقر؛ ويمكن ضبط maximum_suspect_onset_transients على صفر.
قِيسَت ثلاثة تشغيلات Release على M5 Pro بذاكرة 48 GB: RTF بقيمة 0.92، وp95 للإطار الكامل بين 74.8 و75.8 ms، وأول صوت قابل للتشغيل بين 74.2 و74.9 ms، وذروة RSS نحو 8.74 GB. تطابقت النسخة النصية والإجابة في التشغيلات الثلاثة.
بنية 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"))