دوز مباشرة للمحتوى الرئيسي
دروس فالذكاء الاصطناعي

شغّل Qwen3.8 Flash Next 176B فوق جوج ديال DGX Spark

هاد الدليل كيبين كيفاش تستعمل الوصفة اللي فملف واحد فالمستودع باش تهبط، تصلّح، وتشغّل Qwen3.8-Flash-Next-NVFP4 فوق جوج ديال عقد NVIDIA DGX Spark، مع احترام حدود الذاكرة الموحّدة ديال GB10 ومتطلبات الانتباه المتناثر SM121 ديال الموديل.

شغّل Qwen3.8 Flash Next 176B فوق جوج DGX Spark

نظرة عامة

هاد المشروع هو وصفة نشر مهيّأة للاستعمال فالإنتاج باش تخدم RadixArk/Qwen3.8-Flash-Next-NVFP4 باستعمال SGLang. الموديل فيه تقريباً 176 مليار بارامتر، الحجم ديالو 135 GB، وهو موديل mixture-of-experts مكمّم بـ NVFP4.

السكربت start.sh المرفق كيوزّع الموديل على جوج ديال أنظمة NVIDIA DGX Spark باستعمال tensor parallelism مع TP=2. النودات كيتواصلو عبر وصلة ConnectX-7 مباشرة بسرعة 200 Gb باستعمال RoCEv2. منين كيخدم، النود الرئيسي كيعرض API متوافق مع OpenAI على 0.0.0.0:8888.

هاد الوصفة ماشي غير launcher. كتهبط الأوزان وكتتحقق منها، وكتزامنها مع worker، وكتبني صورة container معدّلة فالجوج نودات، وكتشعل الكلاستر، وكتبقى كتسنى حتى يكون الـ API واجد. مصممة باش تكون idempotent، يعني إلا عاودتي شغلتيها تقدر تكمل التحميلات اللي تقطعات، وتعاود تستعمل الـ caches، وتحيد الـ containers القديمة وتعوّضها.

شنو كتوفّر هاد الوصفة

  • خدمة tensor parallel على جوج نودات: واحد DGX Spark كيشغّل rank 0 وSGLang router، والثاني كيشغّل rank 1.
  • نقط نهاية متوافقة مع OpenAI: الـ API كيدعم chat، وcompletions، وإخراج reasoning، وتحليل tool calls، وإدخال النص مع الصور.
  • سياق طويل: الطول الافتراضي للسياق هو 900000 token، باستعمال توسيع YaRN انطلاقاً من سياق الموديل الأصلي اللي فيه 262,144 token.
  • فك ترميز speculative بـ NEXTN: الإعدادات الافتراضية ديال speculative هي 3/1/4، مع CUDA-graph decode فالجوج نودات.
  • التوافق مع SM121: عملية البناء كتطبّق Triton fallback لمسار Qwen Sparse Attention اللي كيسالي بالفشل فالتجميع على DGX Spark.
  • أوامر التشغيل: السكربت فيه فحوصات قبل التشغيل، والتحميل، والإقلاع، والإيقاف، والحالة، والسجلات، واختبار smoke.

الـ README كيسجّل 64.4 token فالثانية لستريم واحد ديال decode، و116.8 token فالثانية كمعدل إجمالي بجوج streams فالكلاستر المجرّب المكوّن من جوج نودات. ومع أربعة streams، كان الـ throughput الإجمالي 114.1 token فالثانية.

علاش patch ديال SM121 Kernel ضروري

معمارية Qwen4Exp كتوجّه attention عبر backend ديال Qwen Sparse Attention. الـ resolver ديال flash-attention كيفضّل FA2 الكلاسيكي، وإلا ما كانش كييستعمل واجهة flash-attn-4 CuTe DSL. فمعمارية SM121 ديال DGX Spark، هاد الـ fallback كيفشل وقت التجميع بسبب خطأ MLIR متعلق بتوافق الـ layout.

المستودع كيحل هاد المشكل اللي كيمنع الإقلاع ببناء صورة SGLang مشتقة فيها Triton attention fallback متوافق مع SM121.

سياق البناء المولّد .patch/ فيه qsa_fa_fallback.py، وهو kernel متغيّر الطول على طريقة FlashDecoding ومهيّأ لعقد الاستدعاء ديال QSA فالموديل. كيدعم grouped-query attention، وأبعاد الرؤوس حتى لـ 256، وonline softmax. وكيسرا حتى cu_seqlens من الجهاز، وهاد الشي كيخلي إعادة تشغيل CUDA graph صالحة منين الـ backend كيعاود يكتب جدول التسلسلات ديالو.

أثناء بناء Docker، الوصفة كترقّع qwen_sparse_attn_backend.py باش تختار Triton fallback كلما كانت is_sm100_supported() كترجع false. وبالتالي المسار العادي كيبقى خدام فبطاقات B100 وB200 المدعومة ديال مراكز البيانات.

العتاد والبرمجيات المطلوبة

عتاد الكلاستر

  • جوج ديال أنظمة NVIDIA DGX Spark بمعالجات GB10، ومعمارية SM121، و128 GB ديال الذاكرة الموحّدة لكل واحد.
  • منافذ ConnectX-7 مربوطين مباشرة بين الجهازين بلا switch.
  • واجهات RoCE المعنية خدامة فالجوج نودات، بحال rocep1s0f1/enp1s0f1np1 فواحد النود وrocep1s0f0/enp1s0f0np0 فالنود الآخر.
  • مساحة تخزين كافية للـ checkpoint اللي حجمو تقريباً 135 GB، وصور الـ containers، والـ caches، وبيانات البناء المؤقتة.

برمجيات النود الرئيسي

  • Docker.
  • أداة hf ديال Hugging Face CLI.
  • Token ديال Hugging Face فبيئة shell إلا كان الولوج للـ checkpoint كيتطلب المصادقة.
  • ولوج SSH بلا كلمة سر من النود الرئيسي للـ worker.

ثبّت Hugging Face CLI فالنود الرئيسي بهاد الأمر:

pip install hf

بشكل افتراضي، الـ worker كيتوصل ليه عبر SSH alias سميتو spark2. تسجيل الدخول للـ worker كيبقى افتراضياً بنفس اسم المستخدم اللي كتشغّل به start.sh فالنود الرئيسي. تقدر تبدّل هاد الافتراضات فـ .env.

إعداد الكلاستر بجوج نودات

دير clone للمستودع فالنود الرئيسي، دخل للمجلد ديالو، وصايب ملف الإعداد المحلي:

git clone <this-repo>
cd <this-repo>
cp .env.example .env

عدّل .env وتأكد على الأقل من IP ديال RoCE فالرئيسي، وIP ديال RoCE فالـ worker، ووجهة SSH. الإعدادات الافتراضية كتتوقع عناوين الشبكة الخاصة التالية:

HEAD_CX7_IP=10.0.22.1
WORKER_CX7_IP=10.0.22.2
WORKER_HOST=spark2

إلا كان الـ worker كيستعمل حساب مختلف، عيّن WORKER_USER. وباش تتحكم بشكل كامل فوجهة SSH، عيّن WORKER_SSH لقيمة بحال user@host. متغيرات shell كتاخذ الأولوية على القيم الموجودة فـ .env.

الطوبولوجيا المتوقعة

العملاء كيتاصلو بالـ API ديال النود الرئيسي عبر المنفذ 8888. الرئيسي كيشغّل rank 0، وسيرفر SGLang، والـ router. rank 1 كيتشغّل فالـ worker. حركة NCCL كتمر عبر وصلة ConnectX-7 RoCEv2 المباشرة بين الجهازين.

الوصفة كتستعمل NCCL_NET=IB، وكتفعّل نقل RoCE المتوافق مع InfiniBand، وكتعطّل NVLS وCUMEM فهاد الإعداد. وتقدر حتى تحمّل مسبقاً build مرحّلاً ديال NCCL 2.30.7 فالمضيف. هاد السلوك مفعّل افتراضياً عبر USE_HOST_NCCL=1.

التحقق من البيئة

دير الـ preflight المدموج قبل ما تحمّل الموديل:

./start.sh doctor

الأمر doctor كيتحقق من الـ fabric، والـ GPUs، والـ RAM، والسعة ديال الديسك، والاتصال عبر SSH، والبورتات، وقيود النشر. صلّح أي مشاكل مبلّغ عليها فالشبكة، أو المصادقة، أو التخزين، أو الموارد قبل ما تبدا التحميل الكبير وبناء الـ image.

حمّل الموديل وسانكرونيزيو

حمّل الـ checkpoint للـ head node، تأكد منو، ومن بعد سانكرونيزيو مع الـ worker:

./start.sh download

الإعداد الافتراضي DOWNLOAD_MODE=rsync كيخلي الـ head يحمّل الملفات ومن بعد ينسخهم للـ worker. العملية كتقدر تكمل منين توقفات. إلا بغيتي كل machine تجبد الملفات بوحدها مباشرة من Hugging Face، عيّن:

DOWNLOAD_MODE=direct

ما تجرّبش هاد الموديل بخيار --load-format dummy ديال SGLang فـ GB10. حسب تحليل الانهيار فـ repository، الـ dummy initializer كيحوّل مؤقتاً معاملات fp8 لـ fp16. جدول PLE n-gram ديال الموديل، اللي الحجم ديالو 51.2 GB بصيغة fp8، يقدر بالتالي يخلق طلب مؤقت على الذاكرة كيتجاوز 150 GB، ويجمّد system كامل عندو تقريباً 121.7 GB من unified DRAM القابلة للاستعمال.

بني الـ Image المرقّعة وبدا الخدمة

طلّق النشر ديال جوج nodes بهاد الأمر:

./start.sh serve

هاد الأمر كيدير الـ preflight، وكيأكد باللي الـ weights والـ images موجودين، وكيبني الـ image المرقّعة بـ SM121 فالجوج ديال الـ nodes، وكيطلّق cluster ديال TP2، وكيبقى كيتسنى حتى يولي الـ API واجد. الـ image المحلية اللي كتتولّد سميتها qwen38-flashnext-dspark:local.

الإقلاع الأول كياخذ تقريباً عشرة دقايق حيث كيشمل تحميل 135 GB ديال الـ weights، وJIT compilation، وCUDA-graph capture. السكريبت كيسمح افتراضياً بمدة انتظار حتى لـ WAIT_TIMEOUT_MIN=90.

باش تدير أول تشغيل بشكل أوتوماتيكي كامل، استعمل:

./start.sh

إلا ما عطيت حتى subcommand، السكريبت كينفّذ doctor، ومن بعد download، ومن بعد serve بالترتيب.

صيفط طلب Chat بسيط

من بعد ما يولي الـ server واجد، عيّط للـ chat endpoint المتوافق مع OpenAI:

curl http://127.0.0.1:8888/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "Qwen3.8-Flash-Next-NVFP4",
    "messages": [
      {
        "role": "user",
        "content": "Explain RoCEv2 in one paragraph."
      }
    ]
  }'

الاستدلال مفعّل افتراضياً، ومحتوى الاستدلال كيتبث بشكل منفصل. باش تعطل التفكير فطلب واحد، زيد chat_template_kwargs للـ JSON body:

{
  "model": "Qwen3.8-Flash-Next-NVFP4",
  "messages": [
    {"role": "user", "content": "Summarize tensor parallelism."}
  ],
  "chat_template_kwargs": {
    "enable_thinking": false
  }
}

إدخال الصور مدعوم عبر جزء المحتوى المعياري image_url المتوافق مع OpenAI. الـ README كيأكد باللي الجمع بين النص والصورة خدام، ولكن ما كيعطيش مثال كامل على طلب فيه صورة.

ربط Client متوافق مع OpenAI

التطبيقات اللي كتدعم OpenAI endpoints مخصّصة تقدر تستعمل الـ base URL التالي:

http://127.0.0.1:8888/v1

المعرّف ديال الموديل هو:

Qwen3.8-Flash-Next-NVFP4

إلا كان الـ client كيتطلب خانة ديال API key حتى إلا كان الـ endpoint المحلي ما كيديرش المصادقة على الطلبات، المثال ديال الـ repository كيستعمل dummy وكيعطل authentication header. إعدادات القدرات والتوافق المهمة ديال الموديل كتشمل:

  • الاستدلال مفعّل.
  • إدخال النص والصورة.
  • نافذة سياق بحجم 900000 token.
  • حتى لـ 32768 token ديال الإخراج فإعدادات الـ client النموذجية.
  • max_tokens هو الحقل ديال tokenات الإخراج.
  • ما كاينش دعم لـ developer role فإعدادات التوافق النموذجية.
  • ما كاينش دعم لـ reasoning-effort parameter.

سيّر الـ Server

الـ launcher الوحيد كيوفّر الأوامر الإدارية الرئيسية:

  • ./start.sh serve كيدير فحوصات preflight، وكيضمن وجود الـ images والـ weights، وكيطلق TP2، وكيبقى كيتسنى الجاهزية.
  • ./start.sh download كيحمّل وكيأكد وكيزامن الـ weights بلا ما يطلق الـ server.
  • ./start.sh stop كيحيد الجوج ديال الـ containers وكيوقف log followers.
  • ./start.sh status كيعرض حالة الـ containers فالجوج ديال الـ nodes.
  • ./start.sh logs [N] كيعرض آخر N سطر من logs ديال الـ head والـ worker.
  • ./start.sh smoke كيبعث completion سريع greedy للـ API اللي خدام.
  • ./start.sh doctor كيعيد preflight كامل ديال البيئة والـ recipe.

ملي كتقلب على مشاكل الإقلاع، شوف .serve.log، و.sglang.log، و.sglang-worker.log. الأول كيسجّل مخرجات الـ launcher، والجوج الآخرين فيهم logs ديال containers ديال الـ head والـ worker.

فهم ميزانية الذاكرة ديال GB10

كل DGX Spark كيوفّر pool موحّدة ديال الذاكرة الفيزيائية، كيتقاسموها CUDA allocations، وpinned host memory، وDocker، وsystem التشغيل. لذلك الـ recipe كيستعمل نسبة ثابتة محافظة من الذاكرة، عوض ما يعتبر ذاكرة الـ host وذاكرة الـ GPU موارد منفصلة.

التوزيع المبلّغ عليه لكل node كيشمل تقريباً 62.5 GB لـ NVFP4 expert وdense وMTP وvision weights؛ وحوالي 11 GB من pinned host memory لجدول fp8 PLE؛ و9 GB ديال bf16 KV cache كيشد تقريباً 956,800 token؛ وحوالي 3.6 GB لحالة Mamba/GDN؛ وزيد تقريباً 8 GB لـ CUDA graphs وNCCL ولا cuBLAS workspaces.

التخصيص الناتج اللي باين لـ CUDA كيوصل تقريباً لـ 95.6 GB، ومعاه حوالي 17.5 GB متاحة لخدمة إضافية. جدول PLE المثبّت ما كيبانش كتخصيص ديال الجهاز لكل عملية فـ nvidia-smi، ولكن باقي كياكل نفس الذاكرة الفيزيائية DRAM. راقب القيمة ديال available_gpu_mem فـ logs ديال SGLang ولا العمود ديال available من free -h ملي كتقيّم الهامش الحقيقي المتبقي.

خلي تفريغ PLE مفعّل

ما تزيدش --no-ple-offload-embedding فـ GB10. القاعدة التلقائية ديال SGLang كتخلي جدول PLE ديال embeddings مفرّغ فـ CPU. تجاوز هاد القاعدة يقدر يحط تقريباً 26 GB لكل rank من ذاكرة host المثبّتة فالجهة ديال GPU، ويدير overcommit للماكينة.

حافظ على الهامش المؤقت ديال prefill

الـ QSA indexer كينشئ مساحة عمل ديال logits بـ fp32، والحجم ديالها كيكبر حسب chunk × history. مع حجم chunk ديال 4096 وتاريخ فيه 300,000 token، هاد المساحة المؤقتة تقدر تحتاج تقريباً من 8 حتى لـ 10 GB من بعد ما يتحسبو نسخ gather و top-k المرتابطة.

القيم المرفقة محافظة عن قصد:

MEM_FRACTION_STATIC=0.82
CHUNKED_PREFILL_SIZE=1024
MAMBA_FULL_MEMORY_RATIO=0.3

ما ترفعش CHUNKED_PREFILL_SIZE فوق 1024 مع context ديال 900,000 token بلا ما تدير profiling للذاكرة المؤقتة القصوى مقابل الميزانية المتاحة ديال الذاكرة الموحدة. المستودع كيشير بلي حجم chunk ديال 4096 مجموع مع MEM_FRACTION_STATIC=0.85 كان كافي باش يعلّق الماكينات.

تجنب تجارب allocator

الوصفة كتنصح بوضوح ما تضبطش PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True. هاد الإعداد مازال ما تجرّبش فهاد النشر، وما كانش موجود فـ وصفات DGX Spark المعروفة بالخدمة المزيانة اللي استعملوها المؤلفين.

عدّل الإعدادات المهمة

ضبط الإعدادات فـ .env ولا صدّرها فالـ shell. قيم الـ shell هي اللي كتغلب. الإعدادات اللي غالباً غتحتاج التعديل هي:

  • PORT: القيمة الافتراضية هي 8888 وكتربط الـ API مع جميع الواجهات.
  • CONTEXT_LENGTH: القيمة الافتراضية هي 900000.
  • MAX_RUNNING_REQUESTS: القيمة الافتراضية هي 16، ولكن إعداد Mamba كيحدّ التوازي العملي فـ 14 خانة مع النسبة الافتراضية.
  • SPEC_STEPS وSPEC_TOPK وSPEC_DRAFT: القيم الافتراضية ديالهم هي 3 و1 و4 بالنسبة لفك الترميز التخميني NEXTN.
  • KERNEL_PATCH: القيمة الافتراضية هي 1 باش تتبنى وتتستعمل صورة fallback ديال SM121.
  • IMAGE: كيستعمل الصورة المحددة مباشرة وكيقفز على patching.
  • CPUSET: القيمة الافتراضية هي 5-9,15-19 باش يستعمل cores الكبار ديال CPU فـ GB10. عيّنو بقيمة خاوية باش تحيد pinning.
  • EXTRA_ARGS: كيزيد arguments إضافية ديال SGLang فالآخر، وكيخلي override ديال آخر قيمة هو اللي يتطبق.

تعامل مع حلقات التفكير واستدعاء الأدوات

كاين مشكل معروف فـ SGLang يقدر يأثر على الجلسات اللي كاتجمع بين التفكير الافتراضي، وأدوات OpenAI، و parser ديال tool calls ديال qwen3_coder. السيرفر يقدر يبقى يخرج token ID 0 بشكل متكرر، والـ tokenizer كيعرضو على شكل !، حتى يوصل لـ max_tokens.

الحل المؤقت المفضّل هو تعطل التفكير غير فالطلبات اللي كتصيفط tools:

"chat_template_kwargs": {"enable_thinking": false}

باش تعطل التفكير على مستوى السيرفر كامل مع استعمال tool parser، وقف النشر وعاود شغلو بـ override:

./start.sh stop
EXTRA_ARGS='--tool-call-parser qwen3_coder --default-chat-template-kwargs {"enable_thinking":false}' ./start.sh serve

إلى كان EXTRA_ARGS مضبوط من قبل فـ .env، دمج الخيارات مع الإعداد الموجود. ما تعاودش تفعّل التفكير فنفس الطلب اللي فيه tools. بلا parser، التفكير يقدر يخدم، ولكن tool calls كيبانو على شكل XML ديال <tool_call> فالمحتوى العادي عوض message.tool_calls المهيكلة. الـ README كينصح حتى هو تخلي temperature ديال agent فـ 0.7 ولا أقل ملي كتستعمل هاد الحل المؤقت.

نصائح متقدمة للموثوقية والأداء

استعمل فك الترميز التخميني باش تزيد throughput

فك الترميز التخميني NEXTN هو العامل الرئيسي فالـ throughput فالإعداد اللي تقاس. مع السلسلة الافتراضية 3/1/4، كل خطوة ديال decode كتتحقق من أربعة tokens مسودات فـ forward pass واحد. الـ cluster اللي تجرّب وصل تقريباً لـ 117 token فالثانية كمجموع مع جوج streams، ومن بعد استقر ملي ولات streams إضافية كتتنافس على الذاكرة الباقية.

اختار الحتمية ملي تكون ضرورية

فك الترميز الجشع ماشي قابل لإعادة الإنتاج bit-for-bit فالإعداد الافتراضي. الـ tokens اللي قريبين فالتعادل يقدرو يتبدلو أحياناً بين التشغيلات، وعلى الأرجح بسبب اختلافات تقليص floating-point فـ NVFP4 MoE GEMMs على SM121. إلى بغيتي سلوك أبطأ ولكن حتمي، عاود شغّل بـ:

EXTRA_ARGS="--moe-runner-backend triton" ./start.sh serve

فسّر تحذير TileLang بالشكل الصحيح

أثناء JIT compilation، TileLang يقدر يقول بلي Logits(bx, position) مكتوب من طرف threads متعددة. المستودع كيعرف هاد الشي كـ false positive حيث المواقع المحسوبة ما كتتقاطعش، مع أن checker ما كيقدرش يثبت هاد الخاصية ملي كتكون group قيمة runtime. تقدر تسكتو بـ PassKey.TL_DISABLE_DATA_RACE_CHECK إلا كان ضروري.

حمي الولوج الخارجي

الـ API مربوط بـ 0.0.0.0، وهاد الشي كيخليه قابل للوصول عبر أي interface مسموح بها. الـ README كيشرح الولوج من clients ديال LAN ولا Tailscale، ولكن ما كيزيدش طبقة ديال authentication. طبّق ضوابط مناسبة ديال firewall ديال host، ولا private network، ولا proxy ملي ما خاصش endpoint يكون متاح بشكل عام.

الخلاصة

هاد المستودع كيحوّل نشر صعيب بجوج ديال العُقد لمسار خدمة قابل للتكرار. المساهمة الرئيسية ديالو هي الجمع بين حلّ احتياطي لـ Qwen Sparse Attention متوافق مع SM121، وإعدادات مضبوطة بعناية للذاكرة الموحّدة ديال GB10، وتوازي مباشر للتنسورات عبر RoCEv2، وسكريبت واحد للعمليات كيعاود يتنفّذ بلا ما يبدّل الحالة.

بدا بالإعدادات الافتراضية المرفقة، وشغّل ./start.sh doctor قبل النشر، وتفادى الأوزان الوهمية، وتعطيل تفريغ PLE، وقطع prefill كبار بزاف، وإعدادات المخصّص التجريبية. منين تكون الكلاستر واجدة، التطبيقات تقدر تستعمل السيرفر المحلي عبر واجهات الدردشة وإكمال النصوص المتوافقة مع OpenAI والمألوفة.