ما وراء الفهرسة: هندسة بحث متجهي محلي عالي الأداء باستخدام SQLite VSS و Expo
غوص عميق في تنفيذ بحث تشابه سريع يعتمد على مبدأ Offline-first على الأجهزة المحمولة باستخدام SQLite VSS. تعلم كيفية الربط بين Vector Embeddings عالية الأبعاد و React Native دون التضحية بالأداء.

ما وراء الفهرسة: هندسة بحث متجهي محلي عالي الأداء باستخدام SQLite VSS و Expo
لسنوات، ظل المعيار السائد في الصناعة للبحث المتجهي (Vector Search) مرادفاً للبنية التحتية من جانب الخادم (Server-side). لقد اعتدنا على الاعتقاد بأن البحث عن التشابه في الأبعاد العالية يتطلب مجموعة (Cluster) ضخمة من Pinecone أو Weaviate تقبع خلف واجهة برمجة تطبيقات (API).
لكن عندما بدأت في بناء تطبيقات تعتمد على مبدأ Offline-first باستخدام Expo، أدركت أن إرسال بيانات المستخدم إلى السحاب (Cloud) لمجرد الحصول على نتيجة تشابه (Similarity score) كان كابوساً من حيث التأخير (Latency) والخصوصية. كنت أرغب في سرعة التنفيذ المحلي.
في هذه المقالة، سأشارككم كيف نجحت في تنفيذ SQLite VSS (Vector Similarity Search) داخل بيئة Expo — ولماذا يغير هذا كل شيء بالنسبة لتقنية الـ RAG (Retrieval-Augmented Generation) على تطبيقات الجوال.
بنية البحث المحلي (The Architecture of Local Search)
التحدي في البحث المتجهي على الجوال لا يكمن فقط في تخزين البيانات، بل في الحسابات. فحساب جيب تمام التشابه (Cosine similarity) عبر آلاف المتجهات ذات الـ 768 بعداً في JavaScript سيؤدي إلى تجميد خيط الواجهة البرمجية (UI thread) أسرع مما يمكنك قول Array.prototype.reduce().
لحل هذه المشكلة، نقوم بنقل العمل الشاق إلى محرك SQLite باستخدام إضافة sqlite-vss. توفر هذه الإضافة قدرات شبيهة بـ FAISS (Facebook AI Similarity Search) مباشرة داخل طبقة قاعدة البيانات.
1. إرساء القواعد
أولاً، لا يمكنك استخدام نسخة expo-sqlite القياسية إذا كنت تريد إضافات مخصصة. ستحتاج إلى استخدام Development build والتأكد من أن مكتبة SQLite الأصلية (Native binary) مجمّعة مع دعم vss0 و vector0.
بمجرد تحميل الإضافة، سيبدو إنشاء جدول المتجهات بهذا الشكل:
2. مسار الإدخال (The Insertion Pipeline)
إحدى الاختراقات التي حققتها كانت تحسين تدفق الإدخال. عند التعامل مع المتجهات، فأنت تتعامل مع كتل كبيرة (Large blobs) من الأرقام العشرية (Floats). تمرير هذه الأرقام كسلاسل JSON نصية عبر جسر (Bridge) React Native يمثل عنق زجاجة (Bottleneck) هائل.
بدلاً من ذلك، أستخدم Float32Array وأقوم بتحويله إلى تنسيق ثنائي (Binary format) مضغوط قبل إرساله إلى استعلام SQL. هذا قلل من حركة المرور عبر الجسر بنسبة 60% تقريباً في اختباراتي.
3. الاستعلام بسرعة 60 إطاراً في الثانية
السحر الحقيقي يحدث أثناء الاسترجاع (Retrieval). من خلال الاستفادة من دالة vss_search نحن ننقل حسابات أقرب جار (Nearest-neighbor) إلى كود C++ محسن للغاية. إليكم كيف قمت بهيكلة استعلام البحث للحفاظ على استجابة التطبيق:
ضبط الأداء: الدروس المستفادة
بعد عدة محاولات، اكتشفت ثلاثة تحسينات رئيسية لا غنى عنها للبحث الاحترافي على الجوال:
- الكمية (Quantization) هي صديقك: المتجهات ذات الـ 768 بعداً ثقيلة. استخدام نماذج تنتج 384 بعداً (مثل
all-MiniLM-L6-v2) يوفر نقطة توازن مثالية بين الدقة وحجم الذاكرة (Memory footprint) على الأجهزة المحمولة. - مقايضة الفهرسة (The Indexing Trade-off): بينما يدعم
sqlite-vssالفهرسة، فإن بناء الفهرس على الجهاز يمكن أن يستهلك الكثير من المعالج (CPU). لمجموعات البيانات التي تقل عن 5,000 عنصر، يكون البحث المسطح "Flat Index" (القوة الغاشمة) سريعاً بشكل مذهل على أجهزة iPhone الحديثة ويتجنب عبء صيانة الفهرس. - المعالجة بالدفعات (Batch Processing): لا تقم أبداً بإدراج المتجهات واحداً تلو الآخر. قم بتجميع التضمينات (Embeddings) في دفعات داخل معاملة واحدة (Single transaction) لتقليل عبء الإدخال والإخراج على القرص (Disk I/O).
لماذا هذا الأمر مهم؟
من خلال نقل البحث المتجهي إلى الحافة (الجهاز)، نحقق بحثاً بزمن تأخير صفر (Zero-latency search). لا وجود لمؤشرات التحميل أثناء انتظار استجابة الخادم. لا توجد مخاوف تتعلق بالخصوصية بشأن مغادرة بيانات المستخدم للجهاز.
هندسة هذا الأمر لم تكن تتعلق فقط بجعله يعمل؛ بل بجعله غير مرئي. عندما يكون البحث محلياً، تشعر واجهة المستخدم وكأنها امتداد لعملية تفكير المستخدم.
إذا كنت تبني تطبيق Expo اليوم، توقف عن النظر إلى السحاب لكل مشكلة. قوة SQLite VSS تثبت أن الميزات الأكثر تطوراً يمكن —ويجب— أن تعيش محلياً.
هل جربتم استخدام التضمينات المحلية؟ أود سماع آرائكم حول استراتيجيات إدارة الذاكرة في التعليقات.