الخطوة 2 في RAG: التضمينات والمتجهات

قبل أن تبدأ البحث في المستندات، عليك أن تفهم كيف يتحول النص إلى أرقام.

ما هو التضمين؟

التضمين (embedding) قائمة من الأرقام (تُسمى «متجهًا») تلتقط معنى قطعة من النص.

"Cat"     → [0.21, -0.45, 0.83, ...]   (384 رقمًا)
"Kitten"  → [0.19, -0.42, 0.80, ...]   (شبيه جدًا بكلمة Cat!)
"Car"     → [0.55, 0.12, -0.33, ...]   (غير شبيه بكلمة Cat)

الكلمات المتشابهة لها متجهات متشابهة، ويتعلم النموذج ذلك من قراءة مليارات الجمل.

تصوّر التضمينات

تخيّل فضاءً ثلاثي الأبعاد تكون فيه كل كلمة نقطة:

                    "dog"
                      ●
                     /
                    /
        "cat" ●────● "kitten"
                   /
                  /
    "car" ●───────● "truck"

في الواقع، التضمينات ذات 384 بُعدًا (أو 768 أو 1024، إلخ) — يستحيل تصوّرها، لكن الرياضيات تعمل بالطريقة نفسها.

تشابه جيب التمام

لقياس مدى تشابه متجهين، نستخدم تشابه جيب التمام (cosine similarity):

similarity = cos(θ) = (A · B) / (||A|| × ||B||)
  • 1.0 = الاتجاه نفسه (المعنى نفسه)
  • 0.0 = متعامدان (لا علاقة بينهما)
  • -1.0 = اتجاهان متعاكسان (المعنى معاكس)
"Kitten" vs "Cat":     cosine = 0.92  (تشابه كبير)
"Kitten" vs "Car":     cosine = 0.15  (تشابه ضعيف)
"Kitten" vs "Phone":   cosine = 0.08  (تشابه ضعيف)

تضمينات الكلمات مقابل تضمينات الجمل

النوع نموذج مثال المدخل متجه المخرج
كلمة Word2Vec, GloVe كلمة واحدة متجه واحد لكل كلمة
جملة all-MiniLM-L6-v2 جملة أو فقرة متجه واحد للنص كله

في RAG نستخدم تضمينات الجمل — إذ تتحول الفقرة كاملة إلى متجه واحد.

النموذج الذي سنستخدمه: all-MiniLM-L6-v2

  • الحجم: 80MB (صغير، ويعمل على أي حاسوب محمول)
  • الأبعاد: 384 (توازن جيد بين السرعة والدقة)
  • السرعة: نحو 10,000 جملة في الثانية على معالج حاسوب محمول
  • مجاني: لا يحتاج إلى مفتاح API، ويعمل محليًا
  • الترخيص: MIT (مجاني لأي استخدام)

طوّرته مكتبة sentence-transformers من Hugging Face.

جرّبه

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("all-MiniLM-L6-v2")

sentences = [
    "Clark College offers free tutoring",
    "Students can get help with math",
    "I love pizza",
]

vectors = model.encode(sentences)
print(f"Shape: {vectors.shape}")   # (3, 384)
print(f"Vector 0: {vectors[0][:5]}...")  # أول 5 أرقام

المخرجات:

Shape: (3, 384)
Vector 0: [0.042, -0.133, 0.287, -0.091, 0.155]...

لماذا 384 بُعدًا؟

أبعاد أكثر = معنى أدق وأغنى، لكن بحث أبطأ وتخزين أكبر.

الأبعاد النموذج التخزين (مليون مستند) السرعة
384 all-MiniLM-L6-v2 ~1.5 GB سريع
768 all-mpnet-base-v2 ~3 GB متوسط
1536 OpenAI ada-002 ~6 GB بطيء (استدعاء API)

في هذا المشروع، تكفي 384 بُعدًا.

الخلاصة الأساسية

تحوّل التضمينات النص إلى إحداثيات في «فضاء المعنى». المعاني المتشابهة تكون إحداثياتها متقاربة. ويقيس تشابه جيب التمام المسافة بينها. وهكذا سيجد نظام RAG لديك المستندات ذات الصلة.