الخطوة الثانية في RAG: تحويل قطع النصوص إلى Vectors وتخزينها في قاعدة البيانات

دقائق القراءة: 5

الخطوة الثانية في RAG: تحويل قطع النصوص إلى Vectors وتخزينها في قاعدة البيانات

بعد أن أنجزنا استخراج النصوص من الملفات في درس بناء الخطوة الأولى في RAG: استخراج النصوص من ملفات PDF برمجياً، وبعدها قمنا بمرحلة تقطيع النصوص الضخمة إلى أجزاء صغيرة تتناسب مع حجم الـ LLMs، نصل الآن إلى قلب البنية الدلالية لأي نظام استرجاع احترافي: تحويل كل chunk نصي إلى تمثيل عددي قابل للمقارنة الرياضية، ثم حفظ هذا التمثيل داخل قاعدة بيانات متجهة.

هذه المرحلة ليست مجرد خطوة وسيطة، بل هي الطبقة التي تجعل النظام قادراً على تنفيذ semantic retrieval بدلاً من البحث الحرفي. فعندما يسأل المستخدم سؤالاً بصياغة مختلفة عن النص الأصلي، لا يمكن الاعتماد على التطابق النصي فقط، بل يجب تحويل السؤال والمستندات معاً إلى فضاء عددي موحد، حيث تصبح “درجة القرب” بين المعاني قابلة للحساب.

كيف تعمل هذه المرحلة داخل معمارية RAG؟

في الأنظمة الهندسية الحديثة، يتكون خط التدفق عادة من أربع طبقات مترابطة:

  • طبقة Ingestion: استقبال البيانات الخام مثل PDF أو HTML.
  • طبقة Chunking: تقسيم النص إلى أجزاء صغيرة متماسكة دلالياً.
  • طبقة Embedding + Indexing: تحويل الأجزاء إلى متجهات وتخزينها مع بياناتها الوصفية.
  • طبقة Retrieval + Generation: استرجاع الأجزاء الأقرب لسؤال المستخدم ثم تمريرها إلى النموذج التوليدي.

الدرس الحالي يركّز على الطبقة الثالثة. هنا نأخذ كل قطعة نصية، نرسلها إلى نموذج Embeddings، فنحصل على مصفوفة أرقام تمثل موقع المعنى داخل فضاء متعدد الأبعاد. ثم نخزّن هذا المتجه مع النص الأصلي وبيانات مثل اسم الملف، رقم الصفحة، عنوان القسم، ومعرّف فريد لكل قطعة.

لماذا لا نكتفي بحفظ النصوص فقط؟

لو خزّنت النصوص بشكل عادي في SQLite أو حتى في ملف JSON، فستتمكن من البحث بالكلمات المباشرة فقط. لكن في RAG نحتاج إلى مطابقة “المعنى” وليس “الحروف”. لهذا السبب تُستخدم المتجهات مع خوارزميات مثل cosine similarity أو dot product لقياس قرب السؤال من المحتوى.

تقنياً، قاعدة البيانات المتجهة لا تخزن النص فقط، بل تبني أيضاً index مخصصاً للبحث في فضاءات عالية الأبعاد بسرعة كبيرة. وهذا ما يجعلها مختلفة عن قواعد البيانات التقليدية.

ما الذي نخزّنه مع كل Vector؟

البنية الصحيحة لكل سجل داخل قاعدة البيانات يجب أن تشمل أكثر من مجرد الأرقام:

  • id فريد لكل قطعة نصية.
  • النص الأصلي داخل الحقل document.
  • المتجه العددي الناتج عن نموذج embedding.
  • بيانات وصفية metadata مثل المصدر، الصفحة، الفئة، أو اللغة.

هذه metadata تمنحك قوة هندسية كبيرة لاحقاً، لأنك تستطيع تنفيذ استرجاع مفلتر، مثل جلب النتائج من دليل معين فقط أو من مستندات تعود لعميل محدد في بيئة multi-tenant.

تنفيذ عملي باستخدام LangChain و ChromaDB

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

import os
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain.schema import Document

os.environ["OPENAI_API_KEY"] = "YOUR_API_KEY"

chunks = [
    {
        "id": "doc_001_chunk_001",
        "text": "RAG systems rely on embeddings to convert text into numerical vectors.",
        "metadata": {"source": "rag_guide.pdf", "page": 1, "section": "intro"}
    },
    {
        "id": "doc_001_chunk_002",
        "text": "Vector databases enable semantic search using similarity metrics.",
        "metadata": {"source": "rag_guide.pdf", "page": 2, "section": "database"}
    }
]

documents = [
    Document(page_content=item["text"], metadata=item["metadata"])
    for item in chunks
]

embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")

vectorstore = Chroma.from_documents(
    documents=documents,
    embedding=embedding_model,
    persist_directory="./chroma_db",
    collection_name="rag_chunks"
)

print("Vector store created successfully.")

هذا السكربت ينفذ ثلاث عمليات مركزية:

  • يبني كائنات Document من النصوص المقطعة.
  • يستخدم نموذج OpenAIEmbeddings لتوليد المتجهات.
  • يخزن البيانات في Chroma مع خاصية persist_directory للاحتفاظ بالبيانات محلياً.

شكل البيانات قبل الإدخال

من المهم أن تكون بنية الإدخال واضحة ومنظمة قبل بدء عملية indexing. المثال التالي يوضح بنية منطقية مناسبة للتعامل مع خطوط ETL في مشاريع الذكاء الاصطناعي:

chunks = [
    {
        "id": "doc_001_chunk_001",
        "text": "Text content here...",
        "metadata": {
            "source": "manual.pdf",
            "page": 5,
            "title": "Embedding Pipeline",
            "language": "en"
        }
    }
]

كلما كانت metadata مدروسة منذ البداية، كان من السهل لاحقاً بناء استرجاع دقيق، لوحات مراقبة، أو حتى سياسات حذف وتحديث للمحتوى.

التخزين المحلي أم السحابي؟

إذا كنت تختبر على جهازك أو تطور نموذجاً أولياً prototype، فإن ChromaDB خيار ممتاز. أما إذا كنت تبني نظاماً إنتاجياً متعدد المستخدمين وبحجم فهارس كبير، فقد تحتاج إلى قاعدة سحابية مثل Pinecone.

الفرق الجوهري هنا ليس في فكرة المتجهات نفسها، بل في خصائص التشغيل مثل:

  • التحمل durability.
  • التوسع الأفقي scalability.
  • سرعة الاستعلام تحت الضغط.
  • دعم الفلاتر والنسخ المتعددة والبيئات المعزولة.

أفضل الممارسات الهندسية عند بناء طبقة Embedding Pipeline

  • ثبّت نموذج embedding نفسه بين الفهرسة والاستعلام، لأن تغيير النموذج يفسد قابلية المقارنة.
  • تجنب القطع الصغيرة جداً لأنها تفقد السياق، وتجنب القطع الطويلة جداً لأنها تضعف الدقة.
  • احفظ metadata غنية لكن منضبطة، ولا تكرر بيانات لا حاجة لها.
  • نفذ عمليات الإدخال على دفعات batching لتحسين الأداء وتقليل الضغط على API.
  • راقب التكلفة وحجم الرموز مستفيداً من درس حساب التكلفة وإدارة الرموز (Tokens)، لأن توليد المتجهات على آلاف الصفحات قد يصبح مكلفاً.

ماذا سيحدث لاحقاً عند وصول سؤال المستخدم؟

عندما يرسل المستخدم استفساراً، سيمر السؤال عبر نموذج embedding نفسه، ثم يُقارن متجه السؤال بالمتجهات المخزنة. عندها نسترجع أقرب top-k قطع، ونبني منها prompt موجهاً إلى النموذج اللغوي.

استخدم المقاطع المسترجعة التالية فقط للإجابة على سؤال المستخدم. إذا لم تجد المعلومة داخل السياق، صرّح بوضوح أنك لا تملك دليلاً كافياً من المستندات.

هذا الربط بين الاسترجاع والتوليد هو ما يمنح أنظمة RAG موثوقيتها العملية، ويقلل من الهلوسة عبر فرض الإجابة ضمن سياق فعلي مستخرج من قاعدة المعرفة.

الخلاصة الهندسية

الخطوة الثانية في RAG ليست مجرد “تحويل نص إلى أرقام”، بل هي بناء طبقة فهرسة دلالية كاملة. في هذه الطبقة يتحول المحتوى غير المنظم إلى أصل قابل للبحث الذكي، وتُربط كل قطعة نصية بتمثيل رياضي وبيانات وصفية تسهّل الاسترجاع، التصفية، والتوسع. وكلما صممت هذه المرحلة بعناية، حصلت لاحقاً على إجابات أدق، سياق أقوى، وتكلفة تشغيل أقل.

في الدرس التالي سننتقل من التخزين إلى التنفيذ العملي لمرحلة الاسترجاع نفسها: كيف نستدعي أقرب القطع من قاعدة البيانات ونمررها إلى النموذج لبناء إجابة نهائية عالية الجودة داخل خط أنابيب RAG متكامل.

4 comments

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *