StudioForge: خادم LLM يعمل على GPU فقط، استبدل LM Studio على جهازي ويديره وكلائي عن بُعد
- تاريخ النشر
- 23 أغسطس 2026
- بقلم
- جاكوب لويد — كُتب بمساعدة الذكاء الاصطناعي بعد إنجاز المشروع
- مدة القراءة
- قراءة 13 دقيقة
بعبارة مبسطة: هذا برنامج يعمل على الحاسوب الذي يحتوي على بطاقات الرسوميات. تطلب منه التطبيقات إجابة من نموذج ذكاء اصطناعي، فيحسب على أي البطاقات يتّسع ذلك النموذج، ويشغّله، ويبقيه دافئًا أثناء استخدامه، ثم يطفئه من جديد عندما يهدأ. القاعدة التي لا يكسرها أبدًا هي أن النموذج إما يتّسع بالكامل على بطاقات الرسوميات أو يُرفض — فلن يشغّل نصفه بهدوء على المعالج البطيء ويتركك تتساءل لماذا صار كل شيء زاحفًا. المأزق: يحتاج إلى بطاقات NVIDIA، وأفضل اختبار له على Windows، ولم أختر له ترخيصًا بعد.
StudioForge هو خادم النماذج المحلي الذي كتبته ليحل محل LM Studio على جهاز GPU الخاص بي: مشرفٌ على llama.cpp مع واجهة برمجية متوافقة مع OpenAI على المنفذ 1234 — منفذ LM Studio، عمدًا — ولوحة تحكم في المتصفح، ومرافق استرداد (sidecar) يجيب عندما تعجز العملية الرئيسية عن الإجابة، ومستوى إدارة منشور عبر MCP ليديره وكيلٌ على جهاز آخر دون الحاجة إلى طرفية سطر أوامر.
وُجد بسبب سجل حوادث توقّف عن كونه مضحكًا. ردّ 200 لا يثبت شيئًا: يجيب LM Studio عن المسارات غير الموجّهة بحالة نجاح مع جسم خطأ، وسجلّه الخاص يقرأ Returning 200 anyway. قائمة /v1/models تعرض كل ما تم تنزيله بدلًا مما تم تحميله، فلا إجابة عن «ما المقيم فعليًا؟». نماذج الاستدلال تتجاوز نافذة 8,192 رمزًا، لأن السياق الذي ضبطته في إعدادات العميل لم يكن هو السياق الذي استخدمه التحميل — في LM Studio تكون النافذة مثبتة عند التحميل، ولم يخبرني أحد. والأغلى منها: نموذج لم يتّسع تم اقتطاعه بدلًا من رفضه — السلوك الموثّق هو أنه «سيقلّل تلقائيًا حجم التحميل على GPU … والباقي في ذاكرة النظام»، ما يستبدل سرعة GPU بسرعة ذاكرة النظام ويُبلغ بالنجاح في الحالتين.
إذا قرأت مقال dsh فقد قابلت هذا الخادم من قبل: كتلة المزوّد المسماة «StudioForge (GPU rig)» هي هو. إنه ما تصل إليه منظومة الوكلاء الثمانية عبر الشبكة من أجل العمل الثقيل، وما تتحدث إليه DisPatch، وما يطلب منه OpenClaw Email نموذج دردشة ونموذج تضمين. وقد سمّى مقال OpenClaw لدي المشكلة — «دقائق من وقت تحميل النموذج، وتنقّل VRAM بين الخدمات» — وما يلي هو الجواب، وكل قاعدة تحمل القياس الذي فرضها.
خلاصة سريعة
- ما هو: خادم LLM يعمل على GPU فقط ومتوافق مع OpenAI، مبني على
llama-serverمن llama.cpp، ومُقاس على الإصدارb10425(CUDA 13.3). بوابة على1234، ولوحة تحكم على8080، ومراقب على1235، وعملية فرعية واحدة لكل نموذج محمّل على18100–18200. - ماذا يفعل: يحمّل النموذج عند أول استخدام، مخطّطًا سياقه ونوع ذاكرة KV المؤقتة وموضع GPU وعدد الفتحات (slots) مقابل ذاكرة VRAM المتاحة في تلك اللحظة؛ ويُخمِده بعد الخمول؛ ويُبقي النماذج المثبّتة مقيمة؛ ويمنح بطاقات كاملة لنموذج واحد عند الطلب؛ وينشر 29 أداة عبر MCP — 19 للإدارة و10 للاسترداد.
- ما لا يفعله أبدًا: التسريب إلى المعالج المركزي (النموذج يتّسع بالكامل في VRAM أو يُرفض مع الأرقام)، أو الاتصال بالخارج من تلقاء نفسه (الاتصالات الصادرة الوحيدة هي Hugging Face لجلب النماذج، وGitHub لإصدار
llama-serverالمثبّت وفحص تحديثه، وفحص إصدار StudioForge الاختياري، وروابط الصور التي يسمّيها الطلب)، أو تنفيذ الاستدلال عبر MCP — فمستوى التحكم لا يملك أداة إكمال ويقول ذلك بأحرف كبيرة. - ما تحتاجه: بطاقات NVIDIA على تعريف من سلسلة 580 أو أحدث، وPython 3.12+، وuv، ومجلد ملفات GGUF. لم تشغّل نموذجًا محليًا من قبل؟ ابدأ من هنا بدلًا من ذلك.
- ما تنتهي إليه: رابط أساسي واحد يستخدمه كل عميل OpenAI على شبكتك دون تغيير، ولوحة تسمّي ما يحتل كل غيغابايت على كل بطاقة، ومساعد يستطيع أن يقول «حمّل النموذج 27B بسياق 128k على بطاقتي 5090» فيتحقق ذلك.
- الجزء الصريح: Windows هو المنصة المرجعية، وNVIDIA فقط، ولا يوجد ملف ترخيص بعد — اقرأ احصل عليه أولًا.
ماذا يفعل، في صورة واحدة
ما ليس هو:
- ليس نموذجًا. يشغّل ملفات GGUF التي تملكها، ويجلب المزيد إلى نفس المجلد وبنفس التخطيط الذي يستخدمه LM Studio.
- ليس محرك استدلال. llama.cpp هو من يقوم بالحسابات؛ وهذا يقرر أي عملية تعمل بأي معاملات وعلى أي بطاقات.
- ليس تطبيق دردشة. يوجد تبويب دردشة (Chat)، وهو موجود لإثبات أن مسار الطلب الحقيقي يعمل.
- ليس عنقودًا (cluster). جهاز واحد وبطاقاته الخاصة. واجهة RPC الخلفية في llama.cpp موجودة لكنها غير موصولة.
- ليس خادم استدلال على المعالج المركزي. لا توجد قيمة لـ
--n-gpu-layersغير999في أي مكان من الكود. - ليس سطح واجهة برمجية ثانيًا. لا يوجد
/api/generateالخاص بـOllama ولا واجهة KoboldCpp — فقط سطح OpenAI، ومرآة/api/v0بنكهة LM Studio، وواجهة الإدارة REST عبر/api، وهذا كل شيء.
ماذا يعمل وأين
| العنصر | أين | ما الغرض منه |
|---|---|---|
| البوابة | مضيف GPU، عملية واحدة، 1234 | /v1 و/mcp (19 أداة) و/api. تحوي السجل والمخطّط والمشرف. |
| لوحة التحكم | العملية نفسها، خادم uvicorn ثانٍ، 8080 | اللوحة الرئيسية، والإعداد، والنماذج، والتنزيل، والدردشة، والخادم، والسجلات. |
| المراقب | عملية منفصلة، 1235 | خادم MCP خاص به، و10 أدوات استرداد. يعيش أطول مما يشرف عليه. |
عمليات llama-server الفرعية | 18100–18200، على الحلقة المحلية فقط | واحدة لكل نموذج محمّل؛ الانهيار يُسقط نموذجًا واحدًا، لا البوابة أبدًا. |
رفيق sfctl | جهاز الوكيل | عميل HTTP خالص — Python 3.11+، دون CUDA ودون اعتماد على الخادم. وهو أيضًا جسر stdio الخاص بـMCP. |
| مكتبة GGUF | models.dir، حيث هي بالفعل | مفهرسة في مكانها؛ لا شيء يُنسخ. يواصل LM Studio استخدام المجلد نفسه. |
ما يقوله الجدول:
- مضيف GPU وحده يثبّت أي شيء. يحتاج العميل إلى رابط أساسي؛ ويحتاج الوكيل إلى حزمة Python صغيرة واحدة، ولأدوات الإدارة فقط.
- العمليات الفرعية غير مرئية من الخارج. ترتبط بـ
127.0.0.1ولا شيء غيره، فالبوابة هي السطح العام الوحيد — وهو ما يجعل مفتاح API واحدًا حدًا حقيقيًا.
كيف يسري الطلب
كل ما يمكن أن يخطئ فيه العميل يُفحص قبل أول بايت، لأن الطلب السيئ يجب أن يحصل على 4xx حقيقي (404 لمعرّف نموذج غير موجود) مع جسم JSON بدلًا من إطار خطأ مدفون داخل تدفق SSE برمز 200 — العملاء يتعاملون مع الأول جيدًا ويخطئون بشكل متكرر في الثاني. ما يقوله GET /health على جهازي:
{"status": "ok", "version": "1.26-08-23", "uptime_s": 12690.6,
"loaded_models": ["ggml-org/SmolVLM-256M-Instruct-GGUF/SmolVLM-256M-Instruct-Q8_0"],
"busy": {"active_requests": 0, "busy_models": [], "loading": [], "testing": null},
"draining": false, "instance": "primary",
"boot": {"phase": "ready", "ready": true, "elapsed_s": 0.2, "error": null},
"engine": {"ok": true, "tag": "b10425", "variant": "cuda", "smoke_tested": true},
"gpu_count": 4, "models_indexed": 34, "can_serve": true}
can_serveهو جواب مشكلة «الـ200 الذي لا يثبت شيئًا». يكون false أثناء تشغيل أول فحص للمكتبة، بينما يبقىstatusعلىokلأن العملية حية وهذا ما يسأله عنه فاحص النبض (liveness poller). ينفّذGET /health?deep=trueإكمالًا حقيقيًا من 8 رموز (واستدعاء تضمين لنماذج التضمين) على كل نموذج محمّل — ومع عدم وجود أي نموذج محمّل يجيب بـno_models_loadedبدلًا من النجاح، لأن المسبار الذي لا يمكن أن يفشل أسوأ من غياب المسبار.- الاسم المستعار
local-modelيُحل. كلاً منlocal-modelوdefaultوautoوcurrentيُوجَّه إلىmodels.default_model. يعود عملاء LM Studio إلى تلك السلسلة الحرفية، فإرجاع 404 لها يكسرهم بلا داعٍ. - النموذج البارد لا يبدو كأنه معلّق. يحمل التدفق المفتوح
: loading <model id> (5s)كل خمس ثوانٍ، ثم: prefilling <model id> (Ns)حتى أول رمز حقيقي — أسطر تعليق SSE يتجاهلها كل محلل. المقبس الصامت لهذه المدة يطلق مهلة قراءة، والعميل الذي يعيد المحاولة يكدّس مزيدًا من التعبئة المسبقة (prefill) على دفعة مشبعة. - تحميل واحد في كل مرة، على مستوى الجهاز كله. خُطّط نموذجان باردان ذات مرة في نفس الوقت على البطاقات نفسها؛ مات أحدهما بخطأ
CUDA error: out of memory، وأخلت إعادة محاولته النموذج الجديد الآخر، فاصطدم طلب ذلك العميل بعملية فرعية ميتة. - مدة
ttlعلى مستوى الطلب تحرّك مؤقت الخمول ولا شيء غير ذلك. إنttl: 0هو الصيغة الشبكية للتثبيت في كل مكان هنا، لذا يُتجاهل بدلًا من أن يُنفَّذ — فعميل يرسل{"ttl": 60}كان يفك تثبيت ما ثبّته صاحبه.
الحقيقة حول VRAM: المخطِّط
هذا هو الجزء الذي لا يفعله أحد غيري، لذا إليك بالتفصيل بدلًا من الصفات. إن core/planner.py بطول 3,188 سطرًا يجيب عن سؤال واحد قبل أن يبدأ النموذج: بالنظر إلى ذاكرة VRAM الحرة فعلًا على كل بطاقة الآن، ما أفضل نافذة وجودة ذاكرة مؤقتة وعدد فتحات يمكن أن يحصل عليها هذا النموذج — وإن كانت الإجابة «لا شيء»، فماذا يجب أن أخبرك؟
السلّم، وما يرفض المقايضة عليه
الدرجات المرسومة هناك توضيحية وليست الإعدادات الافتراضية الموزّعة: الآلية دقيقة، والأرقام مجرد مثال. ما يوزّعه config.example.yaml هو target_ctx: 1048576 كهدف وdefault_ctx: 8192 كأرضية، مع قصّ الهدف على نافذة النموذج المدرَّبة أولًا — لكن عند أول تشغيل يكتب tune_for_hardware القيمة default_ctx: 16384 عندما تملك أصغر بطاقة 24 GiB أو أكثر، و8192 اعتبارًا من 12 GiB فما فوق؛ وجهازي يعمل بأرضية 128000. تجاوز النافذة المدرَّبة يتطلب تحجيم RoPE ويُضعف الجودة، لذا لا تُعرض أبدًا درجة فوقها. أما ctx_size الصريح فسلّم بدرجة واحدة.
تمريرتان، الثانية فقط عندما تفشل الأولى في كل مكان. السبب تحميل حصل في الساعة 12:03 من ظهيرة أحد الأيام: 79,832 ميجابايت حرة، و19,423 ميجابايت أخرى قابلة للاسترداد من نموذج خامل واحد. عند تلك الميزانية، اتّسع 262144 على ذاكرة مؤقتة q4_0 بحجم 96,004 ميجابايت، واتّسع 65536 على f16 كامل بحجم 95,236 ميجابايت. ما حُمّل فعلًا هو 8192/f16 بحجم 89,860 ميجابايت — دفع النموذج الثمن الكامل للإخلاء وحصل على أصغر نافذة في السلّم.
ذاكرة KV ليست رقمًا واحدًا لكل نموذج
تقريبًا كل حاسبة VRAM على الإنترنت تحسب KV على أنها طبقات × رؤوس × بُعد الرأس × 2 × بايتات × سياق. صحيحة لنموذج Llama، لكنها خاطئة بشدة لعائلتين يشغّلهما الناس فعلًا: Gemma 3 و4 تتداخلان بخمس طبقات نافذة منزلقة لكل طبقة كاملة عند نصف بُعد الرأس، وQwen3.5 و3.6 و3.8 تعلن full_attention_interval = 4، لذا توجد ذاكرة KV مؤقتة في كل طبقة رابعة والباقي طبقات متكررة Gated-DeltaNet بحالة ثابتة لكل تسلسل.
كلفة ارتكاب هذا الخطأ: نموذج Gemma-4 31B طُلِب له 262,144 رمزًا فقُدّرت ذاكرة KV بـ480 GiB وحُدّ عند 65,536، بينما كان سجل المعايرة يسجّل predicted_mb=95615 actual_mb=40037 لأسابيع. بعد إصلاح الهندسة، يهبط المتوقع والفعلي معًا إلى 38 GiB عبر بطاقتي 5090 عند n_ctx=262144 — فتحٌ بمقدار 4× جرّ معه أسطول Gemma-4 كله. أما تحميل ذاكرة KV على كل طبقة من Qwen3.5 فكان الخطأ نفسه بقبعة مختلفة: مغالاة مستقيمة بمقدار 4×.
تفصيلان يستحقان الاستعارة. يجب أن يطابق عدّ خلايا النافذة المنزلقة llama.cpp تمامًا؛ المضاعف الثابت 1.25× كان خطأً في الاتجاه الخطير، أقل بـ3.6× عند أربع فتحات. وattention_kind يُشتق من هندسة الطبقات بدلًا من general.architecture — وحيثما يتعذر اشتقاقه يُبلغ unknown، التي تعني «لا تثق بأي رقم KV هنا»، لا «افترض الحالة الرخيصة» أبدًا.
تُختار جودة الذاكرة المؤقتة داخل كل درجة بدلًا من مقايضتها بنافذة أوسع: f16/f16 → q8_0/q8_0 → q8_0 K + q4_0 V، مع إزالة q4_0 المتماثل من كل مسار تلقائي. ليس ذوقًا — فمع ذاكرة K مؤقتة q4_0، يعيد نموذج Qwen2.5-7B إنتاج 11.7% فقط من الرموز التي تنتجها نسخته f16، بينما يستقر الزوج المتطابق q8_0/q8_0 عند تباعد KL مقداره 0.0018.
أربع بطاقات، وجيلان
الجهاز يتألف من بطاقتي RTX 5090 وبطاقتي RTX 3090 — بطاقات تبلغ اسميًا 32 GB و24 GB، وتحسبها اللوحة 31.84 GiB و24.0 GiB، بإجمالي 111.7 GiB — على تعريف 610.88 ومشغل CUDA 13.3. البطاقة الواحدة أولًا، دائمًا — فالنموذج المقسّم على PCIe دون NVLink أبطأ بشكل ملموس — ولا يتجاوز المخطّط ذلك إلا عندما يكون موضع البطاقة الواحدة متضورًا عند فتحة واحدة، ويضاعف التقسيم الفتحات على الأقل، وتكون كل بطاقة مضافة بنفس القدرة على الأقل، وتكون قد تركت عدد الفتحات على الوضع التلقائي. الشرط الأخير ليس مجاملة: التقسيم يعمل بوتيرة أبطأ أعضائه.
| الموضع (1.5B Q4_K_M، بطاقتا 3090، سياق 8k) | التوليد | معالجة الموجه |
|---|---|---|
| بطاقة 3090 واحدة | 352.5 tok/s | 2803.6 tok/s |
اثنتان، -sm layer | 344.4 tok/s | 2722.5 tok/s |
اثنتان، -sm tensor | 294.3 tok/s | 1182.0 tok/s |
اثنتان، -sm row | يفشل: error loading model: device CUDA2 does not support split buffers | |
ما يقوله الجدول:
- بطاقة واحدة تفوقت على اثنتين في المحورين. يكلف تقسيم الطبقات نحو 2% من التوليد؛ ويكلف تقسيم الموترات 17% من التوليد و58% من معالجة الموجه — لذا وضع الموترات اختياري، ولا يجوز إلا للقياس أن يختاره.
-sm rowميت على CUDA. يقبله المحلل ثم يفشل التحميل، لذا يُحجب قبل إنشاء العملية الفرعية بدلًا من بعدها.
تفصيلان في الموضع لا يظهران إلا عندما تقيس لكل بطاقة. تُحمَّل طبقة الإخراج على آخر جهاز، لأن أدوات التكميم تُبقي موترات التضمين والإخراج عند Q6_K أو Q8_0 حتى داخل ملف Q4: فنموذج 27B خُطّط له --device CUDA1,CUDA0 --tensor-split 0.5079,0.4921 انتهى إلى 15.52 GiB على CUDA0 — آخر جهاز، وهو الذي أعطاه التقسيم أقل — مقابل 14.48 على CUDA1. ويفتح llama.cpp سياق CUDA على كل جهاز مرئي — نحو 0.22 GiB على 3090، و0.43 GiB على 5090 — ولهذا يملك عمود الموضع أرضية 512 MiB.
كم محادثة يستحقها الموضع
إن --ctx-size في llama.cpp هو إجمالي ميزانية KV المشتركة عبر الفتحات، وليس النافذة لكل فتحة — معلَم يُساء قراءته على نطاق واسع، ولا يوضحه README الخاص بالمشروع الأصلي. فالتحميل بـ--ctx-size 4096 دون --parallel يُبلغ total_slots: 4: أي 1,024 رمزًا لكل محادثة. يُطلق StudioForge بـctx_per_slot × parallel. ثم: كم عدد الفتحات التي تستحق الوجود، وهنا توقفت عن الثقة بحساباتي.
| متزامن | لكل تدفق | إجمالي | p50 | p95 | الدفعة المحققة |
|---|---|---|---|---|---|
| 1 | 302.8 tok/s | 302.8 tok/s | 0.41 ث | 0.41 ث | 1.00 |
| 2 | 225.3 tok/s | 425.3 tok/s | 0.46 ث | 0.49 ث | 1.84 |
| 4 | 134.5 tok/s | 436.0 tok/s | 0.83 ث | 1.00 ث | 3.46 |
| 8 | 83.3 tok/s | 576.9 tok/s | 1.57 ث | 1.77 ث | 6.03 |
نموذج Qwen2.5-1.5B-Instruct-Q4_K_M، بطاقة RTX 3090 واحدة، 8,192 رمزًا لكل فتحة، ذاكرة KV من نوع f16، ثماني فتحات مُطلقة، موجهات من 512 رمزًا، و192 رمزًا مولّدًا لكل منها.
ما يقوله الجدول:
- قال المقدِّر 8. وقال القياس 2. عند أربع فتحات يهبط كل تدفق إلى 44% من سرعة الانفراد، تحت أرضية 65%؛ تأخذ القاعدة أكبر عدد من 1/2/4/8 يتجاوز تلك الأرضية مع اكتساب 15% من الإجمالي عن المستوى الأدنى.
- الإجمالي لا يتوقف عن الصعود — ثماني فتحات تنقل 1.9× من الرموز التي تنقلها فتحة واحدة — بينما تنهار المحادثة الواحدة إلى 27%. قاعدة تعظّم الإجمالي كانت ستختار 8، وسيعيش كل مستخدم نموذجًا أبطأ بثلاث مرات مما تستطيع البطاقة تشغيله به.
- التجميع (batching) حقيقي وليس طابور انتظار. صعود الدفعة المحققة 1.00 ← 1.84 ← 3.46 ← 6.03 يثبت خطوات فك ترميز مشتركة؛ وقد أُعيد إنتاجه عبر ثلاث جولات ضمن 2%.
جولة ثانية على بطاقتي 3090 عند 32,768 لكل فتحة سارت من 301.7 إلى 230.5 لكل تدفق وأجابت 2 مرة أخرى. وبالصدفة كان الفهرس قد تنبأ بـ308.0 tok/s لذلك الموضع بينما قاست الجولة 301.7 — انحراف 2%، أفضل مما توقعت. تحمل الصفوف الآن max_parallel (كم يتّسع) بجانب recommended_parallel (كم يستحق التشغيل).
علمان قستهما قبل أن أثق بهما
فك الترميز التخميني (speculative decoding) ربح للتدفق الواحد. نموذج Qwen3.8-27B Q5_K_S برأس MTP، وبطاقة 3090 واحدة، وأربعة موجهات متباينة من 256 رمزًا، مع إيقاف ذاكرة الموجه المؤقتة: دون تخمين 37.75 tok/s؛ وdraft-mtp عند عمق 3 أعطى 50.70 tok/s، أي +34.3%، عند قبول 0.528. يهبط العمق 4 إلى 47.48، لأن القبول ينخفض إلى 0.446 وكل رمز مرفوض إضافي تم التحقق منه بلا فائدة. أما ngram-mod فحقق +0.4% ولم يصدر أي مسودات إطلاقًا.
وهنا الفخ: الموجّه نفسه ثلاث مرات قاس +751% على ذلك النموذج 27B. كرّر موجهًا واحدًا وستكون تقيس ذاكرة الموجه المؤقتة وتسميها تخمينًا. فوق أربع فتحات يعيد auto الآن none ويقول السبب — «التخمين ربح للتدفق الواحد ويضر الدفعة المشبعة» — بعد جولة حمّلت نموذج 27B عند --parallel 8 وما زال auto يختار draft-mtp، لرؤيته رأس MTP دون عدد الفتحات.
الدفعة الصغرى (micro-batch) تشتري التعبئة المسبقة مقابل VRAM. نفس النموذج 1.5B، وموجّه من 5,166 رمزًا: أعطى -ub 512 (افتراضي المحرك) 15,232 tok/s عند 1492 MiB؛ و-ub 1024، 17,307 tok/s (+13.6%) عند 1562 MiB؛ و-ub 2048، 18,061 tok/s (+18.6%) عند 1702 MiB. كان متوقفًا لوقت طويل لأن مخزن الحساب ينمو مع -ub، ولم يكن المخطّط يحاكيه، والمخزن غير المحاكى يحوّل التلاؤم إلى نفاد ذاكرة. يخصمه المخطّط الآن، مقرّبًا إلى الأعلى كي يميل نحو الرفض، ويرفع الدفعة الصغرى تلقائيًا فوق أربع فتحات فقط.
الرفض، مع الأرقام
كل إطلاق يمرر --fit off و--n-gpu-layers 999، والثاني ثابت وليس إعدادًا. وهذا أهم مما كان عليه: الإصدار المثبّت b10425 يوزّع -fit, --fit [on|off] — «هل تُعدَّل الوسائط غير المعيّنة لتتلاءم مع ذاكرة الجهاز» — مفعّلًا افتراضيًا، إلى جانب --n-gpu-layers auto؛ وكلاهما وصل إلى المشروع الأصلي في PR #16653 في ديسمبر 2025. هذا الزوج هو بالضبط مسار تفريغ جزئي صامت: افتراض معقول لخادم متعدد الأغراض، والسلوك نفسه الذي وُجد هذا المشروع لرفضه. عندما لا يتّسع شيء، يكون الجواب HTTP 507 مع الحساب في الجسم، مختصرًا هنا:
HTTP 507 {"error": {"code": "insufficient_vram", "type": "server_error", "message":
"Cannot load 'lmstudio-community/gemma-4-31B-it-QAT-GGUF/gemma-4-31B-it-QAT-Q4_0'
entirely in VRAM: needs 29.09 GiB, 20.90 GiB usable. largest single GPU offers
20.90 GiB usable (headroom 10% reserved). Suggestions: set KV cache type to q8_0
(roughly halves KV cache VRAM for a small quality cost); VRAM is held by other
processes: 5.87 GiB held by python.exe (pid 45072) on CUDA1; 0.83 GiB held by
dwm.exe (pid 2468) on CUDA0; …; clear the per-model device override so the
planner can use other GPUs",
"studioforge": {
"required_bytes": 31235974510, "available_bytes": 22438368871,
"per_gpu_free": {"3": 22438368871},
"max_ctx_that_fits": null, "max_parallel_that_fits": null,
"suggestions": ["set KV cache type to q8_0 (roughly halves KV cache VRAM
for a small quality cost)",
"VRAM is held by other processes: …",
"clear the per-model device override so the planner can use
other GPUs"],
"notes": ["wanted up to 262144 tokens of context but not even the 128000 floor
fits in the VRAM available right now",
"device placement forced by per-model device_override"],
"estimate_mb": {"weights_bytes": 16818.2, "kv_bytes": 8575.0,
"compute_bytes": 2438.6, "mmproj_bytes": 1145.1,
"mmproj_compute_bytes": 512.0, "cuda_context_bytes": 300.0,
…, "total": 29788.9},
"vram_holders": [ … one entry per process, per card … ],
"busy_models": [], "retry_after_s": null }}}
هذا رفض حقيقي، التقطته أثناء كتابة هذا المقال: النموذج 31B طُلب تحميله على بطاقة RTX 3090 واحدة. يسمّي النص النقص والمحتجزين؛ ويحمل error.studioforge الفشل نفسه كبيانات — البايتات المطلوبة والمتاحة، وذاكرة VRAM الحرة لكل بطاقة، والتقدير مقسّمًا إلى أوزان وKV ومخزن حساب ونموذج إسقاط وسياق CUDA، وكل عملية تحجز VRAM، وnotes توضح على أي درجة من السلّم كان واقفًا عندما استسلم. وعندما تتّسع نافذة أصغر، يسمّيها max_ctx_that_fits، محسوبة على هندسة الطبقات ليكون العرض عرضًا يقبله التحميل التالي؛ هنا لم تتّسع حتى الأرضية، لذا القيمة null بدلًا من رقم سيفشل. والرفض الذي لا يتعلق بنموذج مشغول لا يحمل retry_after_s، لأن «حاول لاحقًا» نصيحة سيئة عندما لا شيء سيتغير.
التثبيتات ومدد TTL والاستئجار وأداة إعادة التوازن
التثبيت حالة مرغوبة، وليس إعفاءً. كان يعني سابقًا مدة TTL صفرًا، والاستبعاد من كل سلّم إخلاء، وتسخينًا واحدًا عند الإقلاع — وليس الشيء الرابع: النموذج المثبّت لم يكن يُعاد تحميله أبدًا، فطفل دار في حلقة انهيار متجاوزًا max_restarts الخاص بنموذجه بقي عند state="failed" دون أن يحمل شيئًا. يركب الموفّق الآن دورة الـ15 ثانية، متراجعًا من 60 ثانية إلى سقف 900 ثانية. والشيء الوحيد الذي يغلب التثبيت هو الإنسان: إلغاء التحميل الصريح يعلّم المعرّف كمكبوت ويبقى متوقفًا.
الاستئجار بطاقة تنتمي لنموذج واحد. البطاقة المستأجرة غائبة عن رؤية GPU لكل نموذج آخر — ليست في المرتبة الأخيرة وليست خيارًا — ويُجبر المالك على تلك البطاقات بالضبط، بحجم يحدده المقدّر، وفي وضع التقسيم الذي قاسه معياره الخاص أسرعَ هناك. البطاقة المستأجرة مسبقًا تعارض 409، وليست استيلاءً أبدًا. والاستئجار دون نموذج يحجز بطاقات لشيء خارج الخادم: reserve_gpus(devices=[3], reason="ComfyUI render") هو سبب توقف توليد الصور لدي ونماذج اللغة عن التصارع.
أداة إعادة التوازن تصحح قرار الأمس الجيد. عند 13:42 خُطّط نموذج 27B على البطاقتين [1, 3] — تقسيم عبر فئتين يتشارك GPU1 — لأن نموذج 31B كان يحتل [1, 0, 2] وتطبيق توليد صور يحتل 7.5 GiB من GPU2. عند 13:53 انكمش النموذج 31B إلى [0, 1]؛ ومنذ ذلك الحين بقيت [2, 3] حرة وأفضل بشكل صارم، وبقي النموذج 27B مكانه. لذا يُنقل النموذج الخامل الآن عندما تُنزله خطة بلا إخلاء عند إعداداته الحالية بالضبط خارج كل بطاقة مشتركة. والرموز المقدرة في الثانية لا تبرر نقلًا أبدًا.
تنظر مرة كل دقيقة، وفقط عندما يتغير العالم: فقط على جهاز هادئ، وفقط لنموذج خامل خمس دقائق، ونقل واحد لكل نموذج كل 30 دقيقة — لأن النقل إعادة تحميل، وإعادة التحميل تُسقط ذاكرة الموجه المؤقتة، وفي حمولة المحادثات الطويلة على هذا الجهاز كانت تلك الذاكرة 93% من موجه من 98 ألف رمز. للإخلاء ثلاث قواعد صارمة إضافة إلى ذلك — لا نموذج مثبّت أبدًا، ولا نموذج في منتصف طلب، ولا نسخة قيد التحميل — والبطاقة المستأجرة ليست في رؤية المخطّط من الأساس. والتحميل الفوري (just-in-time) لا يستطيع أبدًا ضبط force.
عندما تتعطل الأمور
تموت VRAM مع العملية التي أخذتها. على Windows، يعيش الأطفال في كائن مهمة مجهول أُنشئ بـJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE، فتقتل النواة كل عضو عندما يُغلق آخر مقبض — مهما كانت نهاية الأصل. مجهول، لأن المهمة المسماة ستُشارك مع أي شيء يخمّن الاسم. يحصل Linux على مُحوّل PR_SET_PDEATHSIG، يغطي kill -9 للبوابة؛ وهو جهد أفضل وليس ضمانة نواة، لذا تلتقط دورة الإقلاع وreclaim_orphan_engines ما يتسلل.
ولهذا توجد دورة إقلاع. في 18 أغسطس 2026، كان نحو 10 GiB على GPU0 ونحو 15.6 GiB على GPU1 غير متاحة مع «توقف كل شيء». كان المحتجزون ثلاثة أطفال llama-server.exe لعملية python -m pytest tests -q بدأها وكيل برمجة ثم خرج منذ ذلك الحين. يُصنَّف كل محتجز الآن — ours وchild-of-live-process وorphan وother-instance وforeign — ولا يُقتل أبدًا إلا orphan، آمن بالبناء لأن لا شيء آخر يطلق ثنائيات من شجرة محركاتنا.
تسمية من يحجز ماذا استغرقت محاولتين. تُبلغ NVML عن صفر من الذاكرة المستخدمة لكل عملية تحت Windows، لذا تأتي الأحجام من العداد الذي يقرأه عمود «Dedicated GPU memory» في إدارة المهام — لكنه إجمالي لكل عملية عبر المحولات، لذا كان عمود الجهاز خاطئًا: عملية مُبلغ عنها على CUDA0,1,2,3 كانت تحمل فعلًا 15.52 GiB على CUDA0 ولا شيء على بطاقتي 3090. الإصلاح يربط LUID المحول بعنوان ناقل PCI، وفخ واحد يستحق الفقرة: رقم الناقل في busId الخاص بـNVML نظام عدّ ست عشري. "00000000:42:00.0" هو الناقل 66 وليس 42 — إجابة خاطئة واثقة، لا يمكن تمييزها عن الإجابة الصحيحة.
إلغاء التحميل يُتحقق منه، لا يُعلن. يعيد المشرف فحص معرّف العملية (pid) مع حارس زمن الإنشاء ضد إعادة استخدام المعرّف، ويصعّد الناجي إلى قتل شجري قسري، ويسجّل VRAM قبل وبعد؛ والناجي يرفع خطأ 500 يطلب منك قتله يدويًا. إلغاء تحميل يُبلغ بالنجاح بينما تبقى العملية مقيمة هو أغلى كذبة يستطيع هذا النظام قولها، لأن كل تحميل لاحق سيُخطَّط حينها مقابل VRAM غير حرة.
رموز الخروج مفردات لغوية. 2 خطأ إعدادات يسمّي المفتاح. و3 تعارض منفذ، ولا تعيد الأيقونة الإحياء أبدًا على المنفذ المتعارض — بل تنتظر أن يجيب المحتجز على /health كخادم StudioForge وتلتحق به بدلًا من ذلك. و75 تعني «إعادة تشغيل مطلوبة»: يصرّف الخادم الطلبات ويضبط الرمز ويغلق بأمان — 1.0 ثانية من الطلب إلى الخروج، مقاسة — وتعيد الأيقونة الإحياء دون صرف محاولة انهيار. وُجد هذا التمييز لأن إعادة تشغيل من الواجهة أنتجت ذات مرة خادمين يتسابقان على 1234، وبعد ثلاثة انهيارات معدودة جلست الأيقونة على Crashed — see the logs folder بجانب خادم سليم لم تعد تستطيع إيقافه.
عندما تكون البوابة عالقة بدلًا من ميتة، تخاطب المراقب: عملية منفصلة تعمل دائمًا على 1235 مبنية من argparse وتسجيل stdlib، فيبدأ حتى عندما يكون config.yaml هو الشيء المكسور. أدواته العشر هي health وget_config وset_config وrestart_server وkill_model وnuke_all_models وreclaim_orphan_engines وtail_logs وgpu_status وrollback_update. يعيد تنفيذ قاعدة اليتيم محليًا بدلًا من استيراد الوحدة التي تملكها — يجب ألا تستورد عملية الاسترداد المنظومة التي تصلحها.
لوحة التحكم
اللوحة على 8080 هي خادم uvicorn ثانٍ داخل العملية نفسها، يشارك مخطط الكائنات الخاص بالبوابة بالمرجعية — لا توجد فيها روابط مطلقة إطلاقًا، وهو ما يجعلها تعمل بشكل متطابق عبر HTTP عادي على شبكة VPN شبكية وخلف واجهة HTTPS أمامية.
9.7 tok/s هو حساب هذا التبويب الخاص — أجزاء متدفقة مقسومة على وقت الساعة منذ لحظة مغادرة الطلب، بما في ذلك التعبئة المسبقة — وليس توقيت توليد المحرك، الذي يظهر 58.77 tok/s على بطاقة النموذج. الدردشة الناجحة هنا دليل على أن العميل سيعمل، وليست مسارًا وهميًا يمكن أن ينحرف.
sfctl كعملية خارجية، لا يضطر رمز PIN للاقتران أن يظهر في ملف إعدادات إطلاقًا.
أربعة أسطر في أسفل المخزن نفسه هي ما أقرؤه عندما يفاجئني تحميل — سطر الأوامر الذي بناه، والعملية التي أجابت، والمخطّط وهو يصحح واجبه بنفسه (مع قصّ الطوابع الزمنية وأسماء المسجّلين):
model_spawn argv='C:\Users\<you>\…\engines\b10425\llama-server.exe
--model E:\LLM\Models\…\gemma-4-31B-it-QAT-Q4_0.gguf --host 127.0.0.1 --port 18100
--n-gpu-layers 999 --ctx-size 262144 --parallel 1 --device CUDA1,CUDA0
--tensor-split 0.5368,0.4632 --split-mode layer --cache-type-k q8_0 --cache-type-v q8_0
--flash-attn on --fit off --cache-reuse 256 --cache-ram 32603 --reasoning-format deepseek
--mmproj …' port=18100 source=jit:/v1/chat/completions
model_ready pid=34732 port=18100 source=jit:/v1/chat/completions
load observation actual_mb=37046 predicted_mb=33031 ratio=1.122 ctx=262144
devices=[1, 0] per_device_mb={'0': 18908, '1': 18138}
[warning] a device holds more than its planned share devices=[1, 0]
overruns={'CUDA0': {'planned_mb': 15886, 'actual_mb': 18908}}
detail='llama.cpp places the output layer on the last device of the list; the planner
now charges it there, so a persistent overrun means the charge is too small for this model'
ذلك هو المخطّط وهو يضبط نفسه متجاوزًا بـ3 GB على بطاقة واحدة، و4 GB عبر الزوج، ويقول أي بطاقة ولماذا — طبقة الإخراج استقرت على آخر جهاز، بالضبط حيث يخصم تكلفتها، ومع ذلك كان الخصم ناقصًا.
استخدامه كخلفية لأداة التشغيل (harness)
ستة عملاء على شبكتي يتحدثون إلى هذا الشيء، وواحد فقط منهم يعرف أنه StudioForge. وهذه هي النقطة. خُمسة منهم مرسومون أدناه؛ وOpenClaw Email هو السادس.
أي عميل OpenAI
متغيرا بيئة اثنان. يكون server.api_key هو null افتراضيًا، لذا تعمل أي سلسلة غير فارغة — معظم عملاء OpenAI يرفضون البدء بسلسلة فارغة.
export OPENAI_BASE_URL=http://my-gpu-rig:1234/v1
export OPENAI_API_KEY=not-required # any non-empty string while server.api_key is unset
curl http://my-gpu-rig:1234/v1/chat/completions -H "Content-Type: application/json" \
-d '{"model": "<id from /v1/models>", "messages": [{"role": "user", "content": "hello"}]}'
from openai import OpenAI
client = OpenAI(base_url="http://my-gpu-rig:1234/v1", api_key="none") # any non-empty string, until you set one
print(client.models.list()) # every downloaded model; naming an unloaded one loads it on demand
يسرد GET /v1/models كل ما تم تنزيله، على طريقة LM Studio، مضيفًا state و—عندما يكون مقيمًا— ctx_per_slot وmax_parallel وparallel_limited_by، لأن طول السياق وحده يصبح ملتبسًا بمجرد أن يعمل النموذج بأكثر من فتحة واحدة. تعود المعرّفات كما هي: معرّف publisher/repo/file الكامل، أو اسم ملف عارٍ، أو publisher/name، دون حساسية لحالة الأحرف. لم يحتج DisPatch سوى رابط أساسي جديد؛ أما OpenClaw Email فعميل أكثر تطلبًا، يريد نموذج دردشة ونموذج تضمين ويستدعي /v1/models عند الإقلاع ليسأل عمّا يُقدَّم فعلًا.
OpenClaw، على جهاز آخر
يثبّت صندوق الوكيل حزمة صغيرة واحدة: sfctl، التي تستهدف Python 3.11 بدلًا من 3.12 الخاصة بالخادم لأن الجهاز الذي يشغّل الوكيل غالبًا ما يتخلف عن جهاز التشغيل، والتي لا تعتمد عمدًا على حزمة الخادم — لا CUDA ولا مخطّط ولا سجل.
sfctl servers add rig http://my-gpu-rig:1234 --api-key <PIN> --use
openclaw mcp add studioforge --command sfctl --arg mcp
أو يدويًا — التفصيل الذي يكلّف الناس ظهيرة كاملة. مفتاح OpenClaw هو mcp.servers، متداخل تحت mcp: خريطة mcpServers المسطحة صحيحة لـClaude Code وCline وLibreChat، لكنها ليست مفتاحًا يعرفه مخطط OpenClaw. أما الاستدلال فمسار منفصل، تحت models.providers — لاحظ baseUrl بحرفي rl صغيرين:
// ~/.openclaw/openclaw.json
{ "mcp": { "servers": {
"studioforge": { "command": "sfctl", "args": ["mcp"] } } },
"models": { "providers": {
"studioforge": {
"baseUrl": "http://my-gpu-rig:1234/v1",
"apiKey": "not-required",
"api": "openai-completions",
"models": [ { "id": "<id from /v1/models>", "name": "Rig 27B", "contextWindow": 131072 } ] } } } }
ما يحصل عليه الوكيل هو قائمة أدوات مدمجة واحدة من 29: 19 الخاصة بالبوابة زائد 10 الخاصة بالمراقب، ثلاثة منها أُعيدت تسميتها بصيغة recovery_* — get_config وset_config لأنهما يتصادمان مع أدوات البوابة، وhealth للتناظر. ويحتفظ restart_server باسمه المجرد، لأنه الاسم الذي تخبر رسالة الخطأ الخاصة بأداة إدارة ميتة الوكيلَ باستدعائه. وعندما يكون الخادم الرئيسي متوقفًا، ما يزال الجسر يعرض كل أدوات الإدارة الـ19 مع ملاحظة ملحقة — فالوكيل الذي لا يرى load_model لا يعرف أن القدرة موجودة. الحلقة التي يديرها:
list_models(limit=N)— الفهرس، الأحدث تنزيلًا أولًا. اقرأ الصف الموصى به.load_model(**row["load_args"])— مرّره كما هو دون تغيير؛ الوكيل الذي اختار صفًا انتهى من الاختيار.load_recommended(model_id, ctx_size=N)عندما يكون ما تعرفه هو السياق الذي تحتاجه — مسار التحميل الوحيد الذي يرفض بدلًا من الانكماش.- الاستدلال عبر HTTP، لا MCP. تسمية نموذج غير محمّل تحمّله، بالإعدادات الافتراضية للمخطّط بدلًا من الصف الذي كنت تقرؤه.
model_options(model_id)عندما لا يكفي الصف الموصى به: كل درجات السياق، مع السرعات.search_models←repo_details←download_modelللحصول على شيء جديد.pin_modelللنموذج الذي يجب أن يجيب دائمًا؛ وreserve_gpus/release_gpusلبطاقات خاصة به.server_statusوconnection_info— ما المقيم، ومن يحجز VRAM، وكل عنوان يجيب عليه.
والفقرة التي أفتخر بها أكثر من غيرها، والتي تُقدَّم لكل عميل عند الاتصال:
INFERENCE IS NOT HERE. This server exposes no chat/completion/generation tool
by design. To actually run a prompt, use the OpenAI-compatible HTTP API on the
gateway port (POST /v1/chat/completions, /v1/embeddings; GET /v1/models).
Naming an unloaded model in a request just-in-time loads it, so you usually do
not need load_model at all -- reach for it only to pre-warm a model or to load
one with non-default context/quantization settings.
dsh وClaude Code وخمسة أوامر من سطر واحد
يبدّل dsh (DeepSeek Harness) النماذج بتعديل ملف YAML واحد، يُعاد تحميله فورًا للطلب التالي. كتلة المزوّد بصيغة camelCase — baseURL وapiKeyEnv:
# ~/.dsh/settings.yaml
llm-pi-ai:
providers:
gpu-rig:
displayName: StudioForge (GPU rig)
apiKeyEnv: STUDIOFORGE_PLACEHOLDER_KEY # a reference, not a value
api: openai-completions
baseURL: http://my-gpu-rig:1234/v1
defaultContextWindow: 131072
compat:
supportsDeveloperRole: false # many local servers reject role: "developer"
maxTokensField: max_tokens
models:
- id: <id from /v1/models>
apiKeyEnv هو مرجع، لا قيمة حرفية أبدًا — ولا يزال الخادم المحلي دون مفتاح يحتاج إلى الإشارة إلى مؤهل ما، لأن العميل المتوافق مع OpenAI يُصرّ على رمز حامل (bearer token). أما Claude Code، على النقيض، فلا يستطيع توجيه استدلاله الخاص إلى هنا إطلاقًا: مرجع بروتوكول بوابته يسرد Anthropic Messages وBedrock وVertex، ولا أحد منها هو /v1/chat/completions. لذا فـStudioForge هو أداته، لا دماغه — claude mcp add studioforge -- sfctl mcp (علامة -- إلزامية) يسلّمه كل الـ29.
أنتج bench-llm أرقام الجهاز التي يقتبسها الناس لي — Gemma 4 26B-A4B (QAT, Q4) عند 229.0 tok/s، و110 مللي ثانية حتى أول رمز، مقيسة عبر LM Studio آنذاك — وهو عميل OpenAI عادي، فلا يحتاج إلا رابطًا أساسيًا؛ لكنه يشغّل pkill -f llama-server بين القياسات، ما يقتل كل خلفية StudioForge على الصندوق. أما البقية فسطر واحد لكل منها: Open WebUI، OPENAI_API_BASE_URL؛ وLibreChat، نقطة نهاية custom مع baseURL وmodels.fetch: true؛ وaider، OPENAI_API_BASE ثم --model openai/<id>؛ وContinue، provider: openai زائد apiBase. يُكتب مفتاح الرابط الأساسي بشكل مختلف في كل واحد منها، وهو المصدر الأكثر موثوقية لأمسيات ضائعة في هذا النظام البيئي.
ممّا يختار منه الوكيل
يجعل الفهرس اختيار النموذج بحثًا بدلًا من تخمين: مرتّب بالأحدث تنزيلًا أولًا، وصف واحد لكل درجة سياق، كل صف يحمل fits مقابل VRAM الحرة الحية، وdevices، وأنواع KV، وmax_parallel، وrecommended_parallel، وconfidence، وعمود if_gpus_idle، وload_args. الصف الواحد هو استدعاء تخطيط حقيقي واحد، لذا لا يستطيع أن يَعِد بما سيرفضه التحميل — وif_gpus_idle هو الفرق بين وكيل يستسلم ووكيل يستدعي unload_model.
وrepo_details هي الجديرة بالتسمية: تقرأ ترويسة GGUF عن بُعد، عبر طلبات Range من HTTP — 2–15 ميجابايت، معظمها مصفوفات النصوص بطول مسبوق في أداة التجزئة، مخزّنة مؤقتًا على القرص — بدلًا من تنزيل 20 جيجابايت لمعرفة ما إذا كان يتّسع، وأي CDN يجيب عن طلب نطاق بـ200 والجسم كاملًا يُكتشف ويُرفض. وما يعود هو مصفوفة context_fit من المخطّط نفسه الذي يستخدمه التحميل الحقيقي:
| التكميم | 1× RTX 5090 | 2× RTX 5090 | البطاقات الأربع |
|---|---|---|---|
| BF16 (51.8 GiB) | — الأوزان وحدها لا تتّسع | 32k عند q8_0 | 256k |
| Q8_0 (27.9 GiB) | — | 256k | 256k |
| Q5_K_M (19.3 GiB) | 128k عند q8_0 | 256k | 256k |
| IQ2_M (10.5 GiB) | 256k | 256k | 256k |
من دليل OpenClaw الخاص بالمستودع، محسوب على هذا الجهاز، لـunsloth/Qwen3.8-27B-GGUF. إن max_ctx هو أكبر نافذة عند ذاكرة f16 كاملة الجودة؛ ولا يظهر رقم q8_0 إلا حيث يصل أبعد.
ما يقوله الجدول: التكميم هو من يقرر النافذة، لا عدد البطاقات — تحويل BF16 إلى Q5_K_M يقلب «لا يتّسع إطلاقًا» إلى 128k على بطاقة واحدة — وهو جواب المخطّط نفسه، لذا حيثما لا تُبلغ درجة إلا بتكميم الذاكرة المؤقتة تقول المصفوفة «عند q8_0» بدلًا من اعتبارها فوزًا. للأرقام الحقيقية، الخطة ثلاث خطوات: قياس موضع (كل وضع GPU تحت استئجاره الخاص، والإنتاجية من توقيتات llama-server نفسها)، ثم benchmark_parallel على الفائز، ثم reserve_gpus لتثبيته. لا تقس أبدًا نموذجًا يكون أحدهم في منتصف محادثة معه.
بديل مباشر عن LM Studio
كان التوافق هو قيد التصميم، ويسرد المستودع ما اقتُرض حتى لا يضطر أحد للتخمين: المنفذ 1234؛ و/v1/models الذي يسرد المنزّل بدلًا من المحمّل؛ والتحميل الفوري؛ ومدة الخمول TTL؛ وttl لكل طلب؛ وتخطيط publisher/repo/ المستخدم في مكانه، فلا توجد خطوة استيراد ويشترك البرنامجان في مكتبة واحدة؛ ومرآة /api/v0/models؛ والرابط العميق lmstudio://open_from_hf. وترحيل عميل ما هو تغيير للمضيف، لا تغيير للمضيف والمنفذ معًا.
| LM Studio 0.4.21 | StudioForge 1.26-08-23 | |
|---|---|---|
| المحرك | إصدارات llama.cpp خاصة به بالإضافة إلى MLX على Apple | llama-server الأصلي، إصدار مثبّت واحد (b10425، CUDA 13.3)، مختبر تجريبيًا قبل التفعيل |
| المسارات غير الموجّهة | 200 مع جسم خطأ — يقول سجله Returning 200 anyway | 404 مع غلاف JSON، وJSON على كل حالة |
| الأخطاء | نثر غير منظم يطابقه العملاء بتعبيرات نمطية | error.code ثابت، وتشخيصات تحت error.studioforge |
| إعداد التحميل | context_length متجاهَل في أحد مساري التحميل؛ وrepetition_penalty متجاهَل بصمت | مسار تحميل واحد، كل حقل مُنفَّذ، والقيم الفعلية تُردَّد؛ وأسماء مستعارة لأداة المعاينة مقبولة |
| عندما لا يتّسع | «سيقلّل تلقائيًا حجم التحميل على GPU … والباقي في ذاكرة النظام» — تسريب صامت إلى المعالج المركزي، بالتصميم | 507 insufficient_vram مع البايتات المطلوبة والمتاحة، والحر لكل GPU، وأكبر سياق قد يتّسع، واقتراحات مرتّبة |
| تعدد GPU | تقسيم بالأولوية أو متساوٍ، ومفاتيح لكل GPU، وموازاة موترات منذ 0.4.15 | مخطّط يحدد حجم السياق ونوع KV والفتحات لكل موضع، ويميل كسور التقسيم لصالح طبقة الإخراج |
| مدة الخمول TTL | 60 دقيقة؛ والإخلاء التلقائي يُبقي نموذجًا واحدًا محمّلًا فوريًا على الأكثر | 1,800 ثانية موزّعة (15 دقيقة على جهازي)، تُمسح كل 15 ثانية؛ وبقدر ما يتّسع، مع التثبيتات والاستئجار يقرران من يبقى |
| الإدارة عن بُعد | /api/v1 للتحميل/الإلغاء/التنزيل؛ وLM Link في المعاينة وسيُحوَّل إلى خدمة مدفوعة، والاكتشاف عبر مركز LM Studio | /api REST، وsfctl، و29 أداة MCP؛ والوصول عبر شبكتك المحلية أو شبكة VPN الشبكية الخاصة بك |
| MCP | مضيف فقط — فهو يستهلك خوادم MCP | خادم MCP لإدارته الخاصة، بالإضافة إلى واحد على المراقب |
| المصدر | مغلق؛ مجاني للاستخدام الشخصي والتجاري الداخلي | المصدر في ملف zip، ولم يُختر ترخيص بعد |
تم التحقق مقابل سجل تغييرات LM Studio ووثائقه ومتعقّب الأخطاء في 2026-08-23، الإصدار 0.4.21 (صدر في 12 أغسطس 2026). الصف 2 موجود في متعقّب الأخطاء العام لديه؛ والصفان 3–4 هما ما اضطر عميلي الخاص للالتفاف حوله على واجهة 0.3.x. لم أعد اختبار أي منها مقابل 0.4.21، لذا اقرأها على أنها «موثقة في مرحلة ما»، لا «مكسورة اليوم».
ما يقوله الجدول: إنه تطبيق سطح مكتب أفضل مما سيكون عليه هذا أبدًا — واجهة مصقولة، وMLX على Apple Silicon، ونقطة نهاية متوافقة مع Anthropic، ورفيق للهاتف، وفريق يصدر كل أسبوعين — والانقسام فلسفي وليس في الميزات. الوضع الافتراضي لـLM Studio هو بذل أقصى جهد: اجعله يعمل بأي طريقة. أما وضعي فهو الرفض والتفسير. وحيث يقرر وكيل ما الذي يُحمَّل، فإن بذل أقصى جهد هو الافتراضي الخاطئ، لأن لا شيء في المراحل التالية يستطيع تمييز السريع من البطيء دون قياس.
كيف يقارن بالآخرين
| الخادم · المحرك · الترخيص | التبديل الفوري / مدة الخمول TTL | الموضع متعدد GPU | يرفض التسريب إلى المعالج المركزي | الإدارة عن بُعد / MCP |
|---|---|---|---|---|
| StudioForge 1.26-08-23 llama.cpp، إصدار مثبّت واحد · الترخيص: لا شيء بعد | ✅ تحميل فوري · مدة TTL 1,800 ثانية، ومسح كل 15 ثانية | مخطَّط لكل نموذج، بطاقات مختلطة، تثبيتات + استئجار | ✅ -ngl 999 + --fit off، و507 مع الأرقام | ✅ REST + 29 أداة MCP |
| LM Studio 0.4.21 llama.cpp خاص به + MLX · مغلق | ✅ تحميل فوري · 60 دقيقة، إخلاء تلقائي حتى 1 | تقسيم بالأولوية/متساوٍ، وموازاة موترات | ❌ يقلل التحميل، والباقي في الذاكرة | REST؛ وMCP مضيف فقط |
| Ollama 0.32.15 llama.cpp/GGML؛ وMLX على Apple · MIT | ✅ keep_alive 5 دقائق · 3 مقيمون لكل GPU | توزيع تلقائي عبر البطاقات | ❌ يسرّب، ويعرض نسبة المعالج المركزي في ollama ps | /api/* غني؛ دون خادم MCP |
| موجّه llama-server (b105xx، أغسطس 2026) هو llama.cpp · MIT | ✅ عملية فرعية لكل نموذج · --sleep-idle-seconds، وLRU بالعدد | يدوي -sm / -ts / -dev | ❌ --fit on يقلّص خطتك | /models/load|unload |
| llama-swap v251 وكيل ينشئ غيره · MIT | ✅ المنتج كله · ttl لكل نموذج/مجموعة | ❌ ما يقوله cmd لديك | غير قابل للتطبيق — وكيل فقط | /ui + مسارات المنبع |
| vLLM 0.27.1 خاص به (PagedAttention) · Apache-2.0 | ❌ نموذج واحد لكل عملية | موازاة موترات/خطوط/خبراء | جزئي — لا مسار تفريغ طبقات | LoRA فقط، «تطوير محلي» |
| KoboldCpp 1.119 تفرّع llama.cpp + صور/صوت · AGPL-3.0 | ✅ --admin + --routermode | يدوي --tensor_split | ❌ يسرّب | /api/admin/*؛ عميل MCP فقط |
| TextGen (oobabooga سابقًا) 4.9 5 محمّلات منها ExLlamaV3 وTRT-LLM · AGPL-3.0 | ✅ تبديل دون إعادة تشغيل · TTL؟ | يدوي --tensor-split | ❌ يسرّب | /v1/internal/model/*؛ عميل MCP |
| TabbyAPI (متحرك) ExLlamaV3 فقط — لا GGUF · AGPL-3.0 | ✅ إدارة + تحميل مضمّن · TTL؟ | gpu_split_auto مفعّل افتراضيًا | ✅ فعليًا — لا مسار معالج مركزي في ExLlama | /v1/model/load بمفتاح إدارة |
| Jan 0.8.4 وضع موجّه llama.cpp · Apache-2.0 | ✅ عبر الموجّه · TTL؟ | موروث من llama.cpp | ❌ يسرّب | /v1/orchestrations؛ عميل MCP |
| LocalAI 4.9.0 أكثر من 60 خلفية كصور حاويات · MIT | ✅ عند الطلب · WATCHDOG_IDLE_TIMEOUT | «ملاءمة نماذج تلقائية لـGPU» | ❌ «لا حاجة إلى GPU» | REST + واجهة؛ عميل MCP فقط |
| GPUStack 2.2.3 vLLM وSGLang وMindIE وVoxBox · Apache-2.0 | جزئي — نشر عنقودي | Spread/Binpack تلقائي، متعدد العقد | ؟ | واجهة برمجية كاملة لإدارة العنقود |
تم التحقق من المصادر الأولية في 2026-08-23. علامة الاستفهام تعني غير معروف، لا «لا». وعمّال GPUStack لنظام Linux فقط؛ وvLLM بلا دعم أصلي لنظام Windows (WSL أو التفرعات فقط).
ما يقوله الجدول:
- التحميل الفوري ومدة الخمول TTL ليسا جديدين، وأنا لا أدّعيهما. كل من LM Studio وOllama وllama-swap وLocalAI يفعل الأمرين، ومفاتيح
swap/exclusive/persistentلكل مجموعة في llama-swap محرك سياسات أنيق فعلًا. - ثلاثة أشياء نادرة هنا: الرفض بدلًا من التسريب إلى المعالج المركزي (TabbyAPI وحده يقترب، وفقط لأن ExLlama بلا مسار معالج مركزي)؛ ومخطّط يحدد حجم السياق والفتحات على البطاقات التي وجدها فعلًا؛ والإدارة المنشورة كأدوات MCP، التي لا يفعلها أي شيء آخر في الفئة على حد علمي.
- أقرب شيء في المنبع هو موجّه llama.cpp نفسه، منافسة عادلة: نماذج متعددة مع عزل عمليات، مجاني، في الثنائي الذي تملكه. ما ينقصه هو الإخلاء القائم على الذاكرة بدلًا من العدد، والتثبيتات، والاستئجار، ورفض يحمل أرقامًا؛ لكن لديه نوم الخمول (
--sleep-idle-seconds)، رغم أن استطلاع/metricsيوقظه.
استُعيرت عدة أفكار جيدة، ويسرد المستودع أيّها. أصبح Modelfile الخاص بـOllama نماذج افتراضية، فيتشاطر شخصان فوق قاعدة واحدة llama-server واحدة، وأصبح keep_alive لديه هو ttl لكل طلب. واعتُمد سطح الإعدادات ثلاثي الطبقات الخاص بـTextGen مباشرة، بما في ذلك «المعاملات الإضافية» الخام، مع التحقق من المعاملات مقابل --help الخاص بالمحرك المثبّت عند الحفظ. وفلسفة الأثر الواحد لدى KoboldCpp هي سبب عيش المحركات في أدلة مرقّمة بالإصدارات.
التثبيت
Windows، المنصة المرجعية، في أربع خطوات: ثبّت Git وPython 3.12+ وuv وتعريف NVIDIA حديثًا؛ وانسخ المستودع أو فكّ ضغط ملف التنزيل؛ وانقر نقرًا مزدوجًا على launchers\Update StudioForge.bat، الذي رغم اسمه هو خطوة التشغيل الأول — يبني البيئة الافتراضية، ويثبّت أحدث إصدار llama.cpp يملك إصدارًا لتعريفك، ويختبره تجريبيًا ويثبّته (b10425 هو الإصدار الذي قيست عليه هذه المقالة، وليس الإصدار الذي ستحصل عليه)؛ ثم launchers\Start StudioForge.bat، أو launchers\StudioForge Tray.bat إذا أردته في منطقة الإشعارات، وتفتح اللوحة على http://127.0.0.1:8080 في تبويب الإعداد.
Linux، أربعة أسطر — بالإضافة إلى cmake ومجموعة أدوات CUDA يوافق nvcc فيها تعريفك، لأن المشروع الأصلي لا ينشر أرشيف CUDA لنظام Linux عند أي وسم، والمحرك يُبنى من المصدر مرة لكل إصدار:
git clone https://github.com/LaserLloyd/StudioForge.git && cd StudioForge
uv venv --python 3.12 .venv
uv pip install --python .venv/bin/python -e ".[dev]"
.venv/bin/studioforge serve --open # first run builds the engine
للجهاز دون شاشة، يحمل deploy/ وحدتي systemd مستخدم — وحدات مستخدم عمدًا، لأن العملية يجب أن تعمل كمستخدم الدخول الذي يملك مكتبة النماذج والبيئة الافتراضية وعقد أجهزة GPU. والمراقب عمدًا ليس BindsTo= للبوابة ويستخدم Restart=always: فهو موجود ليكون قيد التشغيل عندما لا تكون البوابة كذلك. ثم sudo loginctl enable-linger "$USER"، النمط نفسه كما في مقال ComfyUI دون شاشة. يفتح التشغيل الأول على تبويب الإعداد، حيث يفحص Detect LM Studio library قيمة downloadsFolder في ~/.lmstudio/settings.json أولًا.
| الخدمة | المنفذ الافتراضي | مفتاح الإعداد |
|---|---|---|
البوابة — /v1 و/api و/mcp | 1234 | server.port |
| لوحة التحكم على الويب | 8080 | gui.port |
| مراقب الاسترداد | 1235 | watchdog.port |
عمليات llama-server الفرعية (الحلقة المحلية فقط) | 18100–18200 | gateway.child_port_start / _end |
ما يقوله الجدول: المنفذ 1234 يعني «خادم النماذج المحلي» على جهازيّ، وليسا الشيء نفسه — يشغّل صندوق الوكيل منفذه الخاص على 127.0.0.1:1234، على الحلقة المحلية، بينما يخدم جهاز التشغيل my-gpu-rig:1234 عبر شبكة VPN الشبكية — وثلاثة منافذ فقط يمكن الوصول إليها أبدًا، مع رفض التحقق من الإعدادات لأي تصادم بين أي منفذ خدمة ومدى العمليات الفرعية عند التحميل.
قاعدة دليل البيانات هي SF_DATA_DIR أولًا، ثم مجلد ملف --config، ثم <repo>/data في نسخة منسوخة — الترتيب الكامل، وسبب عدم كتابة data_dir أبدًا مرة أخرى في config.yaml، موجود في docs/SETUP.md. تملك نسخة واحدة دليل بيانات واحدًا، بفرض قفل نظام تشغيل حصري؛ والنسخة الثانية للقراءة فقط.
الأمان، بصراحة
قاعدة واحدة تحتها كلها: تظل القراءة والاستدلال والإقامة مفتوحة؛ أما تغيير الصندوق فلا. مع عدم ضبط server.api_key، لا يُقبل طلب مغيّر لمسار يغيّر الصندوق إلا من مستدعٍ على هذا الجهاز، أو مع إرسال رمز PIN الخاص بـMCP كـX-MCP-Pin أو كرمز حامل — وأي شيء آخر يحصل على 403 remote_admin_requires_credential. المجموعة المحجوبة هي الإعدادات وإعادة التشغيل والمحركات والتحديثات واسترداد VRAM والتنزيلات والاستئجار والحذف وكتابتا النموذج اللتان تعيشان بعد النسخة. المشكلة التي أصلحها كانت لي: أي شخص على الشبكة المحلية كان يستطيع PATCH /api/config وضبط server.api_key بنفسه ويحبسني خارجًا — بينما أداة MCP set_config، بالقدرة نفسها وفي العملية نفسها، كانت تطالب برمز PIN.
- رمز PIN يحمي MCP فقط. إنه رمز اقتران تقرؤه من لافتة الإقلاع، ومحدود النطاق بأدوات الإدارة. وهو ليس مفتاح API.
server.api_keyهو المؤهل الحقيقي وهوnullافتراضيًا. اضبطه فيغطي/v1و/apiو/mcpوالمراقب؛ ويظل رمز PIN يعمل على نقطتي MCP إلى جانبه.- الربط الموزّع هو
0.0.0.0على المستمعين الثلاثة. يتحول صف Network exposure في تبويب الإعداد إلى كهرماني ويلزم فورًا بمجرد كشف أي مستمع دون مفتاح — بفحص الثلاثة، لأنserver.hostعلى الحلقة المحلية معgui.hostعلى0.0.0.0كان يقرأ أخضر بينما اللوحة مفتوحة على مصراعيها. - طلب المتصفح عبر الأصول ليس «هذا الجهاز»، حتى على الحلقة المحلية. مع
cors_origins: ["*"]، يمكن لأي صفحة تزورها أن تستعلم مسبقًاPATCH /api/configعند127.0.0.1:1234وتصل وتبدو محلية — لذا تتضمن مقارنة الأصل المنفذ، ويُعتبرOrigin: nullأجنبيًا. تنظم CORS ما يجوز للصفحة قراءته، ولا تحدد أبدًا من يثق به الخادم. وللـwebsocket الخاص باللوحة نسخة مقتصرة على المضيف من البوابة نفسها، لأن اللوحة يُوصَل إليها عبر المنفذ الذي قُدّمت منه. - المتصفح البعيد على تثبيت دون مفتاح يحصل على القراءة والاستدلال، و403 على تغييرات الصندوق، ويُحجب عنه رمز PIN — وإلا لاستطاع أي شيء على الشبكة المحلية قراءة رمز PIN من نقطة نهاية مفتوحة واستخدامه. كان رمز PIN مسرحية تمامًا في اللحظة التي كان يهم فيها.
- تُجلب الصور تحت حارس SSRF يحجب الحلقة المحلية والرابط المحلي والخاص وULA ونطاق CGNAT — مدى
100.64/10، حيث يعيش كل نظير في شبكة VPN الشبكية — ويحل مرة واحدة، ويتصل بالعنوان المدقق معHostوSNI الأصليين. - لا شيء يغادر الصندوق دون طلب. الاتصالات الصادرة الوحيدة هي Hugging Face للنماذج، وGitHub لإصدار
llama-serverالمثبّت وفحص تحديثه، وفحص إصدار StudioForge الاختياري، وروابط الصور التي يسمّيها الطلب؛ ويُبلغ التحديث الذاتي عن «غير مُهيأ» دون اتصال شبكي حتى تضبطupdate.repo، واختبار وحدة يثبّت ذلك.
حدّان يُذكران بدلًا من إخفائهما: فحص عنوان النظير يثق بأي شيء على الحلقة المحلية، وخلف الوكيل العكسي يكون الوكيل هو ذلك الشيء، لذا ضع الوكيل خلف server.api_key؛ ولا توجد مصادقة تتجاوز مفتاحًا مشتركًا واحدًا — لا حسابات ولا تحديد معدل. وما تزال القاعدة المنزلية من مقال OpenClaw سارية: الحلقة المحلية مع وكيل مصادق، أو شبكة VPN شبكية، لا الإنترنت الخام أبدًا.
الجزء الصريح
مكتوب بوضوح كي تقرر قبل أن تثبّته:
- Windows هو المنصة المرجعية — فهو ما بُنيت عليه الأيقونة وحارس VRAM بكائن المهمة وعدادات GPU لكل عملية. وLinux مدعوم ويعمل CI على كليهما، لكنه أقل اختبارًا في الميدان؛ مسار البناء من المصدر اختبار بناؤه للأوامر لكنه لم يُختبر من البداية إلى النهاية هنا أبدًا. وmacOS غير مدعوم: لا CUDA.
- NVIDIA فقط. يقرأ المخطّط NVML، والمحرك إصدار CUDA، وتُعبَّر ألفة التكميم بقدرات الحوسبة.
- تقدير VRAM هو تقدير. تستقر الأوزان ضمن 2% من حجم الملف وKV دقيق من هندسة الطبقات، لكن مخزن الحساب كسر معاير، يُضبط مرة عند الإقلاع، ويُقيَّد بين 0.03–0.15 ويُحفظ في الذاكرة فقط — لذا يُلغى أي ضبط سيئ بإعادة تشغيل. تاريخا معايرة على هذا الصندوق تلوثا وأصبحا متجاهلين بالكامل.
- التقسيم متعدد GPU نسبي، وليس مقيسًا. لا يحاكي عرض نطاق الربط البيني، وخلط الأجيال يعمل بوتيرة البطاقة الأبطأ.
- تقديرات التزامن حسابية. يفترض المقدّر أن الفتحات نصف ممتلئة ويخفّض نماذج MoE بنصف ثابت، و
--ctx-checkpointsغير محاكى إطلاقًا. يشير الخطآن نحو فتحات أقل، وهو الاتجاه الآمن — لكن شغّل قياس التوازي قبل أن تثق بالرقم 8. - تستخدم تقديرات السرعة أرقام البائعين الاسمية — 5090 عند 1792 GB/s و209 تيرافلوب fp16، و3090 عند 936 و71، ولا شيء منها مقيس هنا. يوجد مرساة معايرة اثنتان بالضبط، كلتاهما على هذا الجهاز وكلتاهما عند فتحة واحدة: نموذج كثيف 31B قيس 39.4 tok/s مقابل تقدير 36.1، ونموذج 122B MoE قيس 37.3 مقابل 47.4. لا شيء موثق عند أربع أو ثماني فتحات، ولا على نموذج كثيف فوق 31B.
- حجز GPU لبرنامج آخر يقيّد مخطّطي فقط. لا شيء يفرضه على البرنامج الآخر، ولا شيء يمنعه من أخذ الذاكرة أولًا.
- نسخة واحدة لكل دليل بيانات، ويغطي القفل دليل البيانات بدلًا من مكتبة النماذج — نسختان بدليلي بيانات مختلفين فوق مكتبة واحدة تظلان كاتبين اثنين.
- لم يُختر ترخيص. لا يوجد ملف
LICENSEعمدًا، وpyproject.tomlيقول ذلك في تعليق — وفي احصل عليه ما يعنيه ذلك عمليًا. - لا يوجد تدقيق أمني من طرف ثالث. الادعاءات أعلاه تصف ما يفعله الكود؛ وقد كتبت كليهما. اقرأ المصدر — ولهذا هو تنزيل وليس خدمة.
2,503 اختبار وحدة ينجح و17 يتخطى، في 327 ثانية على هذا الصندوق؛ ويشغّل CI الحزمة نفسها على Windows وUbuntu مع إجبار مسبار GPU على خلفية null. وحزمة ثانية تحمّل أوزانًا حقيقية على بطاقات GPU حقيقية وهي غير محددة افتراضيًا ومحجوبة خلف متغير بيئة — حزام وحمّالة، بعد حادثة اليتيم أعلاه. ما لم يُنجز، أو لم يُفعَّل: اختبار A/B للدفعة الصغرى عند 8 فتحات، وإعدادات المعاينة المسماة، وmypy في CI؛ والتحديث الذاتي للتطبيق مكتوب لكنه يظل متوقفًا حتى تضبط update.repo بنفسك.
مطبات يجب الانتباه لها
القائمة الصريحة — أشياء عضّت فعلًا، بترتيب تقريبي حسب كمية الوقت التي كلفتها:
- يجب إغلاق LM Studio على 1234 أولًا. لا يستطيع كلاهما احتلال المنفذ؛ يسمّي الفحص المسبق المحتجز بدلًا من طباعة أثر ربط، و
server.portينقله. أما المكتبة فلا بأس في مشاركتها. - معاملان في llama.cpp لا يعنيان ما يبدوان عليه.
--ctx-sizeهو الميزانية عبر كل الفتحات، لا لكل فتحة؛ و--fitمفعّل افتراضيًا في المنبع، إلى جانب--n-gpu-layers auto— كلاهما أعلاه، تحت المخطّط. - نماذج الاستدلال تعيد ردًا فارغًا تحت
--reasoning-format auto. نفس الموجه، وتغيّر المعامل فقط:contentبطول 0 حرف وreasoning_contentبطول 316 تحتauto؛ وcontentبطول 323 تحتnone. وreasoning_contentليس في مخطط OpenAI، فيقرأ العميل القياسي سلسلة فارغة ويستنتج أن النموذج لم يقل شيئًا. أشغّل النموذج 31B علىdeepseekلأن عملائي يقرؤون ذلك الحقل؛ وكل شيء آخر يحصل علىnone. - إصدارات
vX.Y.Zالتجريبية من llama.cpp لا تحمل أصل Windows CUDA. إصدار موسومv0.1.2جلس فوق إصدارين عاديينbNNNNدون أي أرشيفات مبنية مسبقًا إطلاقًا، فعرض تبويب الخادم تحديثًا خلف زر لا يمكن إلا أن يفشل. تُفلتر الوسوم إلى^b\d+$الآن. - لا تقتل أبدًا جذر الأيقونة قتلًا شجريًا مع فتح تبويب متصفح على اللوحة. تحت كعب مشغّل البيئة الافتراضية يكون المراقب حفيدًا للخادم، وانتهت إعادة تشغيل ذات مرة بخادم ميت ومراقب ميت ولا شيء أُنشئ. استخدم قائمة الأيقونة، أو اللوحة، أو
sfctl recover --restart. pkill -f llama-serverمن أداة أخرى يقتل خلفياتك. يفعل bench-llm ذلك بالضبط بين الجولات — أعلاه، تحت عملاء أداة التشغيل.- رمز PIN الخاص بـMCP ليس مفتاح API، ولا يزال الخادم دون مفتاح يحتاج مؤهلًا نائبًا في بعض العملاء — أعلاه، تحت dsh.
- مفتاح MCP في OpenClaw هو
mcp.servers، متداخل تحتmcp. خريطةmcpServersالمسطحة ليست مفتاحًا يعرفه مخططه — أعلاه، تحت OpenClaw. - اختصارا قياس سيكذبان عليك. تكرار موجه واحد في قياس تخميني يوقّت ذاكرة الموجه المؤقتة (+751% مقابل +0.4%)، وصيغة KV بالطبقات × الرؤوس × السياق تخطئ بمقدار 4× على Qwen3.5 — كلاهما أعلاه، تحت المخطّط. ويُبلغ
/propsأيضًا عنspeculative.types: "none"أثناء التخمين، لذا اقرأtimings.draft_nمن إكمال حقيقي. - نماذج الرؤية لا تستفيد من ذاكرة الموجه المؤقتة. يعطّل llama.cpp إعادة استخدام الذاكرة المؤقتة للنماذج متعددة الوسائط بنفسه، وتُقدَّر كل صورة بـ1,024 رمزًا ما لم تقل بيانات mmproj الوصفية غير ذلك — بما يكفي لتكون نافذة 8k في معظمها صورًا.
احصل عليه
ملف zip أدناه هو كل شيء: شجرة المصدر الموسومة، والاختبارات، والوثائق، والمشغلات، ووحدات systemd، بالإضافة إلى مجلد dist/ يحمل الحزمتين (wheels) وsdist حتى تستطيع التثبيت دون خطوة بناء. studioforge-2026-08.zip — الإصدار v1.26-08-23، بحجم 4,238,728 بايت (4.04 MiB)، وبصمة SHA-256:
84f4f828b5c75206236890f28e8651c96146a7bb39c14e21b13a0922a13f7d3f studioforge-2026-08.zip
226 مدخلًا تحت دليل علوي واحد، مبني بأمر git archive من الوسم المشروح v1.26-08-23 عند الالتزام 0610446، فلا يمكن أن يحتوي إلا ملفات متتبعة — لا config.yaml ولا data/ ولا تجاوزات محلية. والمصدر موجود أيضًا على github.com/LaserLloyd/StudioForge. يحتاج إلى Python 3.12+ وuv وتعريف NVIDIA من سلسلة 580 فما فوق ومجلد ملفات GGUF. الترخيص: لم يُختر بعد، لذا كل الحقوق محفوظة رسميًا — وعمليًا، عامله كما تعامل بقية تنزيلات هذا الموقع: مجاني للاستخدام الشخصي، وإن أردته تجاريًا فاسألني.
إذا تعطل شيء ما، راسلني بالبريد الإلكتروني — العنوان في صفحة حول — وأرسل شكل العطل بدلًا من إعداداتك: error.code، والأرقام من رد 507، وآخر عشرين سطرًا من logs/models/<model>.log. لا ترسل أبدًا رمز PIN ولا المفتاح. وإن بنيت مخطّط التعبئة المشتركة (bin-packing)، أو إعدادات المعاينة المسماة، أو مسار AMD يعمل فعلًا قبلي، فأنا أفضل دمج نسختك على كتابة نسختي.
أين يتركني هذا
ما لم أتوقعه هو كم تحوّل من هذا إلى قياس بدلًا من كود. المخطّط حساب يستطيع أي شخص كتابته؛ ما جعله جديرًا بالثقة هو قراءة هندسة KV الخاصة بـllama.cpp نفسها بدلًا من صيغة، ثم قياس منحنى الفتحات عند 2 بينما قال المقدّر 8. تقريبًا كل قرار في ذلك السجل بدأ رقمًا يخالف اعتقادًا. إذا كنت تشغّل LM Studio بالفعل على 1234، فالتجربة كلها نسخة، وملف دفعي واحد، وتوجيه models.dir إلى المجلد الذي تملكه بالفعل — وإن لم يكسب مكانه في ظهيرة واحدة، فإعدادك القديم لم يُمس.
مقالات ذات صلة: DeepSeek Harness (dsh) (حيث يظهر هذا الخادم أول مرة، ككتلة مزوّد غير مفسرة)، وbench-llm (حيث تأتي أرقام الرموز في الثانية للجهاز، والأداة التي ستقتل خلفياتك)، وإعداد OpenClaw لدي (المقال الذي سمّى المشكلة التي يحلها هذا)، وDisPatch (تطبيق الدردشة أمامه).
التنزيلات
مجاني للاستخدام الشخصي. إذا وفّر لك عصرًا كاملًا من الوقت، فزر القهوة قريب منك.