دمج نموذج دردشة ذري داخل تطبيق Flutter: دليل عملي للكمّ، التحويل، والتخزين المؤقت

٢٤ نوفمبر ٢٠٢٥
Woman in casual attire shopping online using a laptop and phone indoors.

مقدمة: لماذا تشغيل نموذج دردشة على الجهاز (on-device) داخل Flutter؟

تزايدت الحاجة إلى نماذج دردشة تعمل محليًا لأسباب عدة: تحسين الخصوصية، تقليل الاعتماد على الشبكة، وخفض التكلفة التشغيلية. تشغيل نسخة خفيفة من نموذج LLM على هاتف المستخدم أصبح ممكنًا بفضل مشاريع مثل llama.cpp وMLC-LLM التي تتيح استدلالًا فعّالًا على أجهزة iOS وAndroid عبر مكتبات C/C++ وML compilation.

هذه الإمكانيات تفتح الطريق لبناء واجهات دردشة تفاعلية داخل تطبيق Flutter دون إرسال كل الرسائل إلى خادم خارجي، مع المحافظة على تجربة مستخدم سريعة ومتجاوبة.

معمارية مقترحة لدمج نموذج ذري داخل تطبيق Flutter

النسخة المكثفة من المعمارية تتضمن ثلاثة مكونات رئيسية:

  • محرّك الاستدلال (Inference Engine): مكتبة native (مثل llama.cpp أو MLC runtime) مُمَثَّلة كمكتبة C/C++ أو Runtime مُهيَّأ للهواتف.
  • طبقة الربط (Bridge): استخدام dart:ffi أو قنوات منصّة (Platform Channels) لربط Flutter بالمكتبة الأصلية (native) وتشغيل الاستدلال دون تجمّد واجهة المستخدم.
  • التخزين المؤقت والإدارة: حفظ الأوزان المنقّصة (quantized weights)، embeddings، والسياق (context) على الجهاز باستخدام قاعدة بيانات محلية أو ملف نظام لتسريع التشغيل اللاحق.

في Flutter يُنصح بالاعتماد على dart:ffi عند وجود مكتبات C/C++ جاهزة لأداء أعلى والتحكم بالذاكرة، بينما تُستخدم العزلات (Isolates) أو Isolate.run لتنفيذ استدعاءات الاستدلال الثقيلة بعيدًا عن الخيط الرئيسي UI لتجنب حاجز الاستجابة.

تقنيات الكمّ (Quantization) وتحويل النماذج (Conversion)

لتشغيل نموذج على الجهاز بذاكرة وتخزين محدودين، تُستخدم تقنيات الكمّ لتقليل حجم الأوزان ودقّة الأعداد (مثلاً: 8-bit أو 4-bit وحتى صيغ متقدّمة مثل q4_* في بيئات ggml). مشاريع مثل llama.cpp تدعم تنسيقات متعددة للكمّ وتوفر أدوات لتحويل نماذج Hugging Face إلى صيغة ggml/quantized قابلة للتشغيل محليًا.

الخطوات العملية عادةً تشمل: تنزيل نسخة النموذج الكبيرة، تحويلها إلى صيغة كمّ مناسبة (باستخدام أدوات التحويل الخاصة بالمشروع)، ثم حزم الملف النهائي داخل التطبيق أو تنزيله عند التشغيل لأول مرة وتخزينه على قرص الجهاز. هذه العملية تقلل الذاكرة المطلوبة وتسرّع الاستدلال.

التخزين المؤقت وإدارة البيانات المحلية: أفضل الممارسات في Flutter

لتقليل زمن الاستجابة ومعدلات التحميل، نفّذ استراتيجيات تخزين مؤقت متعددة الطبقات:

  1. تخزين الأوزان والملفات الكبيرة: حفظ ملفات النموذج المنقّصة (quantized) في التخزين الداخلي/خارجي باستخدام package مثل path_provider ثم تحميلها إلى الذاكرة عند الحاجة.
  2. تخزين embeddings وذاكرة المحادثة: استخدم قواعد بيانات محلية سريعة مثل Hive أو sqflite لتخزين embeddings، ملخّص سياق المحادثة، وقطع المحادثة التي تستخدم لاحقًا في بحث محلي أو RAG خفيف.
  3. سياسة التحديث والتفريغ (Eviction): اعمل سياسة تتضمن حدّ حجم التخزين، أعمارًا زمنية (TTL) للملفات، وآلية تنظيف عندما ينخفض المساحة المتاحة.

توفر وثائق Flutter دليلًا عمليًا للاستراتيجية العامة للتخزين المؤقت المحلي، وتوضّح متى تستخدم shared_preferences أو ملفات النظام أو قواعد البيانات (sqflite/Hive) حسب حجم ونوع البيانات.

دمج عملي: خطوات مختصرة واعتبارات التنفيذ

خطوات شاملة للدمج داخل مشروع Flutter:

  • اختر محرك الاستدلال المناسب (مثلاً: llama.cpp، MLC-LLM إذا أردت دعمًا مُجمّعًا لكل المنصات).
  • حوّل النموذج إلى صيغة مدعومة وكمّها وفق قيود الذاكرة (قم بتجربة q8/q4 وتحقق من tradeoff بين الجودة والسرعة).
  • ادمج المكتبة الأصلية في المشروع (Android: .so وNDK، iOS: .framework أو .a) وعرّف واجهات FFI باستخدام dart:ffi.
  • نفّذ حلًا غير متزامن باستخدام Isolates أو background threads لاستدعاء الاستدلال وإرسال النتائج إلى واجهة Flutter عبر رسائل مصغّرة.
  • طبّق تخزينًا مؤقتًا متعدد الطبقات: ملفات للنموذج، قاعدة بيانات للـembeddings/ذاكرة، وcache الذاكرة لنتائج الاستدلال الأخيرة.

أمثلة جاهزة ومشاريع مرجعية (مثل حزم MLC وllama.cpp على GitHub) تظهر كيفية إعداد بيئات iOS وAndroid وبنائها داخل CI، لكن لاحظ أن بعض الخطوات تتطلب ضبط حقوق الذاكرة (مثل Increased Memory Limit على iOS) وتعديلات على إعدادات البناء.

قائمة فحص سريعة قبل النشر

  • اختبار الأداء على أجهزة منخفضة الذاكرة (1-4GB) والأكثر شيوعًا بين جمهورك.
  • تحديد سياسة تنزيل الأوزان: تضمين داخل الحزمة أم تنزيل عند التشغيل الأول مع عرض تقدم للمستخدم.
  • التعامل مع الأخطاء: حالات نفاد الذاكرة، استجابة بطيئة، وإعادة المحاولة.
  • ضمان الخصوصية: تخزين مشفّر (إن لزم) للبيانات الحسّاسة، وآليات حذف السجل بناءً على طلب المستخدم.
  • الميتريكس والمراقبة: تسجيل زمن الاستجابة وعدد الطلبات المحلية لمساعدة فرق الأداء في التحسين.

باتباع الخطوات أعلاه يمكن بناء تجربة دردشة مُحلية في تطبيق Flutter تجمع بين خصوصية المستخدم وسرعة الاستجابة مع قابلية التوسعة مستقبلاً نحو حلول RAG هجينة أو استدلال على الحافة.