DisPatch: تطبيق دردشة ذاتي الاستضافة لوكلاء الذكاء الاصطناعي المحليين
- تاريخ النشر
- 11 يوليو 2026
- تاريخ التحديث
- 11 يوليو 2026
- بقلم
- جاكوب لويد — كُتب بمساعدة الذكاء الاصطناعي بعد إنجاز المشروع
- مدة القراءة
- قراءة 1 دقيقة
بعبارة مبسطة: DisPatch هو ثلاثة أشياء في واحد، مستضاف بالكامل على جهازك الخاص: تطبيق دردشة للتحدث مع مساعدين ذكاء اصطناعي يعملون هم أيضًا محليًا، وخادم ملفات، وطرفية (terminal) — متاح على كل أجهزتك في كل وقت، دون أن يذهب أي شيء إلى خدمة سحابية. خادم الملفات فيه ينقل الملفات من أي جهاز إلى الحاسوب الذي تعمل عليه المساعدات، حتى تستطيع العمل بما ترفعه. على الأجهزة المشتركة، تتوفر مجموعة آمنة من المساعدين بدون رمز PIN؛ وإدخال الرمز يفتح المساعدين كاملي الصلاحيات.
DisPatch هو التطبيق الذي بنيته لأتوقف عن تشغيل تثبيت Rocket.Chat المكوّن من 30 حاوية، فقط لأتمكن من مراسلة وكلاء الذكاء الاصطناعي الخاصين بي. انتهى الأمر بثلاثة أشياء في واحد — تطبيق دردشة بنموذج لغوي مستضاف محليًا، وخادم ملفات، وطرفية — متاحة على كل جهاز أملكه، في كل وقت. حجمه حوالي 720 كيلوبايت كملف مضغوط، وهو مجاني، وهو الآن واجهة الدردشة الوحيدة التي أستخدمها.
خلاصة سريعة
- ما هو: ثلاثة أشياء في واحد، ذاتية الاستضافة: دردشة ويب لمجموعة من وكلاء الذكاء الاصطناعي المحليين (بوابة OpenClaw)، وخادم ملفات بالسحب والإفلات، وطرفية حقيقية — على كل جهاز في شبكتك المحلية أو شبكة Tailscale، في كل وقت.
- التكلفة: صفر. لا اشتراك SaaS ولا حساب.
- ما تحتاجه: لينكس، Python 3.12 أو أحدث، وأداة
uv. بوابة OpenClaw إذا أردت ردودًا مكتوبة من الوكلاء — كل شيء آخر يعمل بدونها. - ما تحصل عليه في النهاية: سلاسل محادثات لكل بوت، Markdown متدفق، خادم ملفات بالسحب والإفلات، طرفية وكيل برمجة داخل التطبيق، "وضع آمن" قابل للقفل بـPIN، واجهة برمجية للدفع لمهام cron، و187 اختبارًا تنجح مباشرة من الملف المضغوط.
ما تحصل عليه في النهاية
قبل الحديث عن الإعداد، إليك كيف يبدو وهو يؤدي عمله. الصور مأخوذة من نسخة معزولة (sandbox) ببيانات وهمية — لا أحد يحتاج لرؤية سجلات البوتات الفعلية لدي.



ما الذي يفعله فعليًا
- سلاسل محادثات لكل بوت، في كل مكان. افتحه على هاتفك في منتصف محادثة وستجد نفس السلسلة، ونفس السجل، دون رقصة إعادة تسجيل دخول.
- خادم ملفات — سحب وإفلات للملفات بين كل جهاز والصندوق. الميزة التي أستخدمها أكثر من غيرها؛ ولها قسم خاص بها أدناه.
- طرفية (Terminex) — شل حقيقي داخل المتصفح يشغّل وكيل برمجة. ولها أيضًا قسم خاص بها أدناه.
- ردود Markdown متدفقة مع كود ملوّن نحويًا (وزر نسخ مضمّن)، صور وفيديوهات مع صندوق تكبير (lightbox)، وقابل للتثبيت كتطبيق ويب تقدمي (PWA).
- لوحة "قيد العمل" مباشرة. انقر على نقاط الكتابة لمشاهدة استدعاءات أدوات النموذج وتفكيره مباشرة — إنها تتابع (tail) نسخة المحادثة الفعلية للوكيل، وليست مؤشر تحميل وهميًا.
- قفل بطبقتين. يفصل رمز PIN الوكلاء كاملي الصلاحيات عن مجموعة فرعية آمنة للمعلومات العامة فقط — التفاصيل في قسم القفل بطبقتين أدناه.
- واجهة برمجية للدفع —
POST /api/inject— حتى تستطيع مهمة cron أو وكيل آخر إسقاط رسالة (أو صورة) في سلسلة محادثة. هكذا يصل الموجز اليومي إلى هاتفك دون أن تطلبه. - توليد صور محلي داخل المحادثة. اطلب من وكيل صورة، ويمكنه استدعاء نسخة ComfyUI محلية؛ وتصل النتيجة إلى الدردشة كأي وسائط أخرى، بصندوق تكبير وكل شيء. بلا واجهة برمجية للصور، وبلا رفع لأي جهة.
- اختياري، مطفأ افتراضيًا: لوحة ComfyUI لبدء أو إيقاف خدمة توليد الصور تلك من الدردشة.
خادم الملفات
من بين كل ما في DisPatch، خادم الملفات هو الجزء الذي أستخدمه أكثر من غيره. إنه منطقة سحب وإفلات للملفات تُقدَّم من نفس الجهاز: افتح DisPatch على أي جهاز، أسقط ملفًا عليه، ويصبح الملف على الخادم — مجمّعًا حسب اليوم، مع صور مصغّرة للصور والفيديوهات.

ما يجعله أكثر من مجرد أداة مريحة هو أين تحطّ الملفات: على نفس الجهاز الذي تعيش عليه الوكلاء. صورة تُرفع من هاتفي يمكن لأي وكيل قراءتها من القرص بعد ثوانٍ — ليصفها، أو يؤرشفها، أو يضعها على موقع ويب، أو يمررها لأدوات الصور. إنه الجسر بين "ملف على أي جهاز أحمله" و"ملف يستطيع وكلائي العمل عليه"، بلا محرك سحابي في المنتصف وبلا أسلاك.
الجوانب غير البرّاقة معالَجة: يُتحقق من حجم الملفات المرفوعة قبل تخزينها مؤقتًا (فلا يمكن لهاتف أن يُغرق الجهاز بذاكرة زائدة/OOM)، وهناك سقف إجمالي للتخزين، والكتابة ذرّية (atomic) حتى لا يترك رفع منقطع أبدًا ملفًا مكتوبًا جزئيًا، وتُنسخ مخزن الملفات احتياطيًا ضمن نفس نظام النسخ الاحتياطي الدوري المستخدم لسجل الدردشة. يمكن أيضًا لعلم إعداد (config flag) تحويل الأمر كله إلى صندوق إسقاط أحادي الاتجاه، للرفع فقط — بحيث تستطيع الأجهزة إضافة ملفات لكن لا تستطيع تصفحها — لإعدادات يمكن فيها لأشخاص أكثر الوصول إلى التطبيق مما ينبغي أن يقرأ أرشيفه.
Terminex: طرفية داخل الدردشة
الميزة الأخرى التي تثبت جدارتها يوميًا: يستطيع DisPatch وضع طرفية حقيقية داخل المتصفح. تظهر في شريط البوتات مثل أي بوت آخر، لكن فتحها يصلك بـPTY على جانب الخادم يُبَث عبر WebSocket إلى xterm.js — جلسة شل فعلية على الجهاز، وليست محاكاة.

عندي هو يفتح مباشرة على Reasonix، وكيل البرمجة على طراز Claude Code الذي أشغّله على DeepSeek — الإعداد الذي تسميه صفحة مركز المساعد Terminex. هذا يحوّل تبويب دردشة إلى كونسول كامل لوكيل برمجة: أعطه مهمة، وشاهده يعمل، من أي جهاز يستطيع فتح التطبيق. مقترنًا بخادم الملفات، يجعل الجهاز قابلًا للتشغيل الكامل من الهاتف — أسقط ملفًا من أينما كنت، افتح الطرفية، وأخبر الوكيل بما يفعله به.
- في وضع فتح القفل (Unlocked) فقط. لا وجود للطرفية في الوضع الآمن، وتبقى مطفأة تمامًا ما لم تفعّلها (
LOCAL_CHAT_TERMINAL). - الجلسة في الذاكرة فقط. ابدأ / أعد التشغيل / أوقف من التطبيق؛ إعادة تشغيل الخادم تُنهيها. لا يوجد شل دائم مختبئ في الخلفية.
لماذا هو موجود
لقد حلّ محل نشر كامل لـRocket.Chat — قاعدة بيانات وكومة من الحاويات فقط ليتحدث شخص واحد مع بعض النماذج المحلية. كان يعمل، لكنها بنية تحتية ضخمة مقابل "أرسل نصًا، احصل على رد، وزامنه في كل مكان".
أما الإجابة الأعمق عن "لماذا تستضيف تطبيق دردشة بنفسك أصلًا": بمجرد أن تعمل نماذجك محليًا، تصبح واجهة الدردشة آخر جزء لا يزال يغريك بالتوجه نحو خدمة سحابية. استضافتها بنفسك تعني أن أوامرك (prompts)، ومخرجات وكلائك، ورسائل عائلتك لا تغادر المنزل أبدًا، وأن الحلقة كاملة — الواجهة، البوابة، النماذج، توليد الصور — تستمر بالعمل حتى مع فصل الإنترنت. إذا كنت قد التزمت بالفعل بـنماذج لغوية محلية، فإن واجهة دردشة مستضافة سحابيًا هي الشاذة الوحيدة في الصورة.
قرار تصميمي يستحق الذكر: DisPatch لا يملك أي خطوة بناء (build)، عن قصد. الواجهة الأمامية هي جافاسكريبت خام (وحدات ES) وCSS/HTML عاديان. لا حزّام (bundler)، ولا npm run build. متطلبي الفعلي منذ البداية كان "سهل التعديل لاحقًا من قبل وكيل ذكاء اصطناعي". عدّل ملف جافاسكريبت، اضغط تحديث، وانتهى الأمر. سأتخذ نفس القرار مجددًا لو أُتيحت لي الفرصة.
المكونات التقنية
| الطبقة | ما هي |
|---|---|
| الخلفية (Backend) | Python 3.12+، FastAPI + uvicorn، aiosqlite (ملف SQLite واحد، بوضع WAL)، Pillow، تُدار عبر uv |
| حجم الخلفية | حوالي 7,200 سطر في backend/app/ (ملف main.py وحده حوالي 3,600 سطر)، بالإضافة إلى حوالي 2,700 سطر من الاختبارات |
| الواجهة الأمامية | جافاسكريبت خام بلا بناء + HTML/CSS — حوالي 4,900 سطر جافاسكريبت، وحوالي 2,100 سطر CSS/HTML/service-worker |
| مكتبات مضمّنة | marked وhighlight.js وDOMPurify وxterm.js — بلا أي استدعاء CDN |
| البيانات | خارج الكود، في ~/.local/share/local-chat/: قاعدة البيانات، config.yaml، security.yaml، media/، files/، نسخ احتياطية دورية لقاعدة البيانات |
الإعداد
ملخّص من README-SETUP.md داخل الملف المضغوط (صندوق التنزيل أدناه).
unzip dispatch-2026-07.zip
cd dispatch
./install.sh
./run.sh
هذا يشغّله على http://0.0.0.0:8765 ويطبع روابط الشبكة المحلية وTailscale، حتى تستطيع النقر على أحدها من هاتفك. من هناك:
- عدّل
~/.local/share/local-chat/config.yaml(يُنشأ تلقائيًا، وموثّق فيconfig.yaml.example) حتى تطابق معرّفات البوتاتidمعرّفات وكلاء OpenClaw لديك. يُعاد تحميله تلقائيًا (hot-reload). - اختياري:
./install.sh --systemdيولّد وحدة systemd لمستخدمك. - اختياري: عيّن رمز PIN من أيقونة الترس ← Security. يوثّق
security.yaml.exampleالملف، بما في ذلكapi_tokenلمن يستدعي/api/injectمن خارج الجهاز. - مقابض متغيرات البيئة:
LOCAL_CHAT_HOST/PORT/DATA_DIR/AGENT_TIMEOUT/MAX_CONCURRENCY، بالإضافة إلىLOCAL_CHAT_COMFY=0/LOCAL_CHAT_TERMINAL=0لإبقاء اللوحات الاختيارية مطفأة.
واجهة سطر الأوامر openclaw نفسها غير مرفقة — يفترض DisPatch وجود بوابة OpenClaw تعمل بالفعل في مكان يستطيع الوصول إليه. لا بوابة بعد؟ التطبيق ما زال يبدأ التشغيل، ويحذّرك في السجلات، وكل شيء عدا الردود المكتوبة من الوكلاء يعمل: السلاسل، واجهة inject البرمجية، خادم الملفات، PIN والوضع الآمن. هكذا اختبرت هذا الملف المضغوط بسرعة — فك ضغط جديد، uv sync، منفذ فارغ، بلا بوابة — وحزمة الاختبارات المرفقة نجحت بـ187 اختبارًا ناجحًا خلال 8.4 ثانية.
كيف تحدث رسالة مكتوبة فعليًا
تكتب شيئًا وتضغط إرسال. هذا هو المسار الذي يسلكه:
هذا هو المسار السعيد. أما الهندسة الحقيقية فذهبت إلى المسارات غير السعيدة: عندما تنتهي مهلة واجهة سطر الأوامر، أو تنهار، أو يجيب وكيل فرعي متأخرًا بعشر دقائق. أربعة مصادر مستقلة — المراقب الحي، ومحتوى JSON من واجهة سطر الأوامر، ومُوفِّق (reconciler) بعد انتهاء الدور يعيد مسح نسخة المحادثة، و"متابع" (follower) يواصل التتبع لنحو 30 دقيقة إضافية — تغذّي جميعها قمعًا واحدًا للتسليم مزودًا بإزالة تكرار مرتبطة بالدور (نص موحّد بالإضافة إلى بصمة الوسائط). أي مسار واحد قد يفشل ويظل الرد يصل مرة واحدة بالضبط. حتى عند خطأ في واجهة سطر الأوامر، يعمل المُوفِّق أولًا، لأن النموذج غالبًا ما يكون قد أفرغ إجابته في نسخة المحادثة قبل أن يتعطل.
القفل بطبقتين: الوضع العادي مقابل الوضع الآمن
هذا التمييز مهم لأن الجانب غير المقفل ليس لعبة. تلك الوكلاء تستطيع فعليًا فعل أشياء: تعديل الكود المصدري، دفع تغييرات إلى موقعي الحي، تشغيل أدوات، إدارة الجهاز الذي تعيش عليه. الجانب المقفل هو معلومات عامة فقط — لا يزال قابلًا للاستخدام فعليًا، يمكنك طرح أسئلة والدردشة والحصول على مساعدة حقيقية — لكنه مسيّج عمدًا عن كل تلك الصلاحيات. السبب بسيط: لدي طفلة عمرها ثلاث سنوات، وهاتف العائلة ليس دائمًا بين يدي شخص بالغ. تخيّل ما يمكن لطفلة بعمر ثلاث سنوات فعله بصلاحية كاملة من هاتفي — سيصبح الموقع بأكمله فيديوهات Paw Patrol. لذا فالوضع الآمن هو حماية للأطفال، مفروضة على مستوى الخادم. لمسة بسيطة لتحسين التجربة: تعرض الشاشة الرئيسية المقفلة الآن نفس الصور الرمزية للبوتات التي يعرضها الجانب غير المقفل، بدلًا من العناصر النائبة القديمة على شكل حروف. إليك ما يحدث عندما يصل طلب دون إدخال PIN بعد:
إذا أُقفِلت خارجًا عن حسابك، هناك ملف رمز استرجاع بالإضافة إلى YAML قابل للتعديل، وليس تذكرة دعم فني. وإذا تعطّل security.yaml أثناء الكتابة، فوضع الفشل هو "يبقى مقفلًا"، وليس "يقدّم كل شيء لأي شخص على الشبكة المحلية". كان ذلك قرارًا متعمدًا.
مطبّات شائعة
- حقل الواجهة البرمجية هو
content، وليسtext. يريدPOST /api/injectالشكل{"bot_id": "...", "content": "..."}. أكثر خطأ تكامل شيوعًا على الإطلاق، ومنها أخطاء ارتكبتها بنفسي. - لا تمرّر الرسائل الطويلة كوسيطة سطر أوامر. يحدّد النواة (kernel) طول argv بـ128 كيلوبايت (
MAX_ARG_STRLEN)؛ رد طويل يُسقط عملية الإطلاق بخطأ E2BIG. الكود يستخدم--message-file <tmpfile>— لا "تبسّطه" رجوعًا إلى وسيطة. - معرّفات البوتات حساسة لحالة الأحرف في نقطة واحدة تحديدًا. بوابة OpenClaw تحوّل مفاتيح الجلسات في فهرسها إلى أحرف صغيرة، لكن معرّفاتك مختلطة الحالة (
Doxy،DS_Brain). البحث الحساس لحالة الأحرف يقتل بصمت لوحة "قيد العمل" ومتابع الإجابات المتأخرة بينما يستمر كل شيء آخر بالعمل — أمر خبيث الملاحظة. المطابقة الدقيقة أولًا، ثم الأحرف الصغيرة كبديل احتياطي. - واجهة سطر الأوامر تُعيد الكتلة الأخيرة فقط. أي شيء يقوله الوكيل بين استدعاءات الأدوات لا يوجد إلا في نسخة محادثته، وليس في القيمة المُعادة من واجهة سطر الأوامر — من هنا الحاجة إلى المراقب والمُوفِّق، والمعالجة الخاصة لحالة "إليك ما وجدته حتى الآن" أثناء انتظار الوكيل لوكيل فرعي.
- اختطاف WebSocket عبر المواقع محجوب بفحص Origin. كلا مساري WS يرفضان أي مصافحة (handshake) لا يطابق فيها مضيف
OriginقيمةHost؛ العملاء من غير المتصفحات لا يرسلون Origin فيمرّون بلا مشكلة. - ملفات SVG غير قابلة للتقديم كوسائط، عن قصد. يتعامل المتصفح مع SVG المضمّن كسكربت من نفس الأصل — تقديمه كصورة هو ثغرة XSS مخزّنة. تحصل استجابات الوسائط بدلًا من ذلك على CSP للعزل (sandboxing) وnosniff.
- تحديثات عامل الخدمة (service worker) تترك تبويبات قديمة عالقة على جافاسكريبت قديم. يبدو تمامًا كتراجع (regression) حتى تدرك أن التبويب لم يلتقط العامل الجديد أبدًا. أُصلح بإعادة تحميل تلقائية عند
controllerchange.
محاكاة الفكرة بنفسك
حتى لو لم تشغّل ملفي المضغوط أبدًا، تستحق البنية المعمارية أن تُستعار. الأمر كله أربع قطع مستقلة، كل واحدة قابلة للاستبدال:
- خلفية دردشة تمتلك السجل: خادم ويب صغير واحد (هنا FastAPI + ملف SQLite واحد بوضع WAL) مع WebSocket للتحديثات الحية ونقطة نهاية HTTP عادية لدفع الرسائل. هذا هو كامل بديل "قاعدة البيانات وكومة الحاويات".
- بوابة وكلاء تستدعيها الخلفية عبر عملية فرعية أو عبر HTTP — هنا OpenClaw، لكن أي شيء يأخذ "معرّف جلسة + رسالة كمدخل، ونص كمخرج" يعمل. إبقاؤها عملية منفصلة يعني أن تطبيق الدردشة ينجو من انهيار حزمة الوكلاء، أو ترقيتها، أو استبدالها بالكامل.
- خوادم نماذج محلية خلف البوابة (LM Studio، vLLM، Ollama — مشكلة البوابة، لا مشكلة تطبيق الدردشة).
- خدمات وسائط اختيارية، مثل ComfyUI محلي لتوليد الصور، تستدعيها الوكلاء كأدوات وتخزّن الخلفية مخرجاتها كوسائط عادية فقط.
الدروس التي تنتقل بغض النظر عن حزمتك التقنية: افرض وضعك الآمن/المقفل على الخادم، أبدًا في CSS؛ تعامل مع "وصول رد النموذج إلى المستخدم مرة واحدة بالضبط" كمسألة أنظمة موزعة حقيقية، لا كافتراض للمسار السعيد؛ وأبقِ الواجهة الأمامية بلا خطوة بناء إذا كنت تتوقع أن يكون وكلاء الذكاء الاصطناعي هم من سيصونها.
التنزيلات
مجاني للاستخدام الشخصي. إذا وفّر لك عصرًا كاملًا من الوقت، فزر القهوة قريب منك.