يا جماعة الخير، السلام عليكم ورحمة الله. معكم أخوكم أبو عمر.
خلوني أحكيلكم قصة صارت معي ومع فريقي قبل كم شهر، قصة فيها شوية توتر، وشوية قهوة زيادة عن اللزوم، وفيها درس تعلمناه بالطريقة الصعبة. كنا وقتها شغالين على مشروع كبير لجامعة مرموقة، بدهم يعملوا مساعد ذكي (Chatbot) للطلاب، يجاوب على كل أسئلتهم عن المواد، والتسجيل، والرسوم، وكل وثائق الجامعة الداخلية اللي عددها بالآلاف.
الأمور كانت ماشية تمام. أخذنا نموذج لغوي كبير (LLM) من أحدث الموديلات، وعملناله Fine-Tuning على بيانات الجامعة. بالبداية، النتائج كانت مبهرة، النموذج صار يحكي “بلهجة” الجامعة، وفاهم مصطلحاتهم. الفرحة ما كانت سايعتنا، وقلنا خلص، المشروع “في الجيبة”.
لكن المصيبة بلشت تظهر وقت الاختبارات المكثفة. مرة بنسأل النموذج: “ما هي شروط التحويل لقسم الهندسة؟”، فبيجاوب إجابة صحيحة. بنرجع نسأله نفس السؤال بصيغة مختلفة، فجأة بيخترع شرط جديد مش موجود بالمرة! مرة ثانية، سألناه عن رسوم مادة معينة، فأعطانا رقم صحيح، لكن لما سألناه عن رسوم مادة ثانية، أعطانا نفس الرقم الأول! النموذج بلش “يهلوس” (Hallucinate). كان بيعطينا إجابات بثقة عمياء، لكنها غلط. وصلنا إيميل من مسؤول المشروع في الجامعة، كلماته كانت واضحة زي الشمس:
“يا جماعة، النظام هذا لو طلع للطلاب رح يعمل كارثة. إجاباته مش دقيقة بالمرة. إذا طالب اعتمد على معلومة غلط من الشات بوت وخسر فصل دراسي، مين المسؤول؟”
وقتها حسيت بالدم بغلي في عروقي. كل الشغل والتعب ممكن يروح على الفاضي. قعدنا في المكتب لآخر الليل، بنحلل المشكلة. المشكلة ما كانت في قوة النموذج، المشكلة كانت في طبيعته. النماذج اللغوية الكبيرة، بطبيعتها، هي آلات “توقع” للكلمة التالية، مش قواعد بيانات. هي مش “حافظة” للمعلومات، هي “فاهمة” للأنماط اللغوية. وهنا كانت نقطة التحول… لما واحد من الشباب بالفريق حكى: “يا جماعة، ليش ما نخليه يشتغل والكتاب مفتوح قدامه؟ ليش ما نستخدم RAG؟”.
شو قصة “الهلوسة” هاي أصلاً؟
قبل ما ندخل في الحل، خلينا نفهم أصل المشكلة. “الهلوسة” (Hallucination) في عالم النماذج اللغوية هي لما النموذج يولد معلومات تبدو منطقية ومقنعة، لكنها غير صحيحة أو غير موجودة في بيانات التدريب الأصلية. هو مش بيكذب عن قصد، هو ببساطة بيحاول يكمل الفراغ بأكثر تسلسل كلمات محتمل إحصائياً.
تخيلها زي الطالب الشاطر اللي قرأ كل كتب المكتبة، بس دخل الامتحان بدون ولا كتاب. لما تسأله سؤال، بيجاوب من “ذاكرته” العامة وفهمه العميق. لو السؤال عن فكرة عامة، رح يبدع. بس لو سألته عن رقم صفحة معين أو اقتباس دقيق، ممكن “يألّف” إجابة تبدو صحيحة لأنه نسي التفصيل الدقيق. هاي هي الهلوسة.
الحل التقليدي (اللي ما نفع معنا): الضبط الدقيق (Fine-Tuning)
أول إشي بيخطر على بال أي مطور هو الـ Fine-Tuning. وهو إنك تاخد نموذج مدرب مسبقاً وتعمله “تدريب إضافي” على مجموعة بياناتك الخاصة. هذا الأسلوب ممتاز لـ:
- تعليم النموذج أسلوب معين في الكتابة أو الكلام (مثلاً، اللهجة الرسمية لشركة).
- تعليم النموذج مهمة جديدة (مثلاً، تصنيف النصوص).
- توسيع معرفته بمجال معين بشكل عام.
لكن مشكلته الأساسية إنه مكلف جداً، بيحتاج بيانات ضخمة وعتاد قوي، والأهم من هيك، هو لا يمنع الهلوسة بشكل كامل. النموذج رح يظل يعتمد على ذاكرته الداخلية “المضغوطة”، وممكن يخلط بين المعلومات القديمة والجديدة.
البطل المنقذ: نمط الجلب المعزز للتوليد (RAG)
هنا يأتي دور المنقذ، الـ RAG أو Retrieval-Augmented Generation. الفكرة عبقرية وبسيطة بنفس الوقت. بدل ما نطلب من النموذج يجاوب من ذاكرته، بنقول له: “اسمع يا كبير، هي السؤال، وهي مجموعة وثائق الها علاقة بالسؤال. اقرأها كويس، وبعدين جاوبني فقط من المعلومات اللي أعطيتك إياها”.
صرنا زي اللي بيعطي الطالب “كتاب مفتوح” (Open Book) في الامتحان. هيك بنضمن إنه الإجابة مش من وحي خياله، بل مبنية على مصدر حقيقي وموثوق.
كيف بيشتغل الـ RAG خطوة بخطوة؟
العملية بتتقسم لمرحلتين رئيسيتين: مرحلة التجهيز (Indexing) ومرحلة التشغيل (Retrieval & Generation).
- مرحلة التجهيز (Indexing): بتصير مرة واحدة (أو كل ما تحدثت البيانات).
- تقطيع المستندات (Chunking): بنمسك كل وثائقنا (ملفات PDF, Word, HTML, …) وبنقسمها لقطع صغيرة ومنطقية (Chunks).
- التضمين (Embedding): بنحول كل قطعة نصية لـ “متّجه” (Vector)، وهو عبارة عن مجموعة أرقام بتمثل المعنى الدلالي للنص. النصوص اللي معانيها قريبة، بتكون متّجهاتها قريبة من بعض في الفضاء الرياضي.
- التخزين (Storing): بنخزن هاي المتجهات مع النصوص الأصلية في قاعدة بيانات متخصصة اسمها “قاعدة بيانات المتجهات” (Vector Database) زي FAISS, Pinecone, ChromaDB.
- مرحلة التشغيل (عندما يسأل المستخدم):
- جلب المستندات (Retrieval): لما المستخدم يسأل سؤال، بنعمل Embedding لنص السؤال بنفس الطريقة.
- البحث عن التشابه (Similarity Search): بنروح على قاعدة البيانات وبنبحث عن أكثر القطع النصية (Chunks) اللي متجهاتها قريبة من متجه السؤال. هاي هي القطع الأكثر صلة بالسؤال.
- التعزيز والتوليد (Augmentation & Generation): بنعمل “Prompt” جديد للنموذج اللغوي. هذا الـ Prompt بيحتوي على:
- السياق (Context): وهو النصوص اللي جبناها من قاعدة البيانات.
- السؤال الأصلي للمستخدم.
وبنطلب من النموذج يجاوب على السؤال بناءً على السياق المقدم.
بهالطريقة، إحنا “أرضينا” النموذج (Grounding)، يعني ربطناه بالواقع ومصدر للمعلومات، ومنعناه يسبح في بحر خياله الواسع.
خلونا نشتغل عملي: مثال بسيط بلغة Python
الحكي النظري حلو، بس فش إشي بيثبت المعلومة قد كود شغال. خلينا نعمل مثال بسيط باستخدام مكتبة LangChain المشهورة.
الخطوة 1: تجهيز البيئة
أول شي، لازم ننزل المكتبات اللازمة. افتح الطرفية (Terminal) واكتب:
pip install langchain openai faiss-cpu tiktoken
الخطوة 2: تحضير البيانات وإنشاء قاعدة المتجهات
لنفترض عنا شوية نصوص داخلية بدنا النموذج يجاوب منها. رح نستخدم LangChain عشان يسهل علينا العملية.
import os
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
# ضروري تحط مفتاح OpenAI API الخاص فيك هنا
# os.environ["OPENAI_API_KEY"] = "sk-..."
# هاي النصوص اللي بدنا نبني عليها قاعدة المعرفة
# تخيل هاي آلاف الصفحات من وثائق جامعتك
internal_documents = [
"شروط التحويل لقسم الهندسة تتطلب معدل تراكمي لا يقل عن 85% وإتمام مساقي فيزياء 1 ورياضيات 1 بنجاح.",
"رسوم التسجيل للفصل الدراسي الواحد هي 500 دينار، بالإضافة إلى رسوم الساعات المعتمدة.",
"سعر الساعة المعتمدة لكلية الهندسة هو 120 دينار.",
"سعر الساعة المعتمدة لكلية الآداب هو 90 دينار.",
"للتواصل مع قسم القبول والتسجيل، يرجى إرسال بريد إلكتروني إلى admission@university.edu."
]
# 1. تقطيع النصوص لقطع أصغر
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=20)
docs = text_splitter.create_documents(internal_documents)
print(f"تم تقطيع النصوص إلى {len(docs)} قطعة.")
# 2. إنشاء المتجهات (Embeddings)
embeddings = OpenAIEmbeddings()
# 3. بناء وتخزين قاعدة بيانات المتجهات في الذاكرة باستخدام FAISS
# في المشاريع الحقيقية، ممكن تحفظها على القرص عشان ما تعيد بنائها كل مرة
print("جاري بناء قاعدة بيانات المتجهات...")
vector_db = FAISS.from_documents(docs, embeddings)
print("تم بناء قاعدة البيانات بنجاح!")
الخطوة 3: بناء سلسلة RAG والسؤال
الآن بعد ما صارت قاعدة المعرفة جاهزة، بنبني السلسلة اللي رح تربط كل المكونات مع بعض.
# 4. بناء سلسلة الجلب المعزز للتوليد (RAG Chain)
# هاي السلسلة رح تعمل كل الشغل: تجيب المعلومة من قاعدة البيانات وتعطيها للنموذج عشان يجاوب
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(temperature=0), # temperature=0 عشان نحد من إبداع النموذج ونخليه يلتزم بالنص
chain_type="stuff", # "stuff" تعني إنه رح يحط كل القطع المسترجعة في الـ Prompt
retriever=vector_db.as_retriever() # هون بنحدد أداة الجلب (Retriever)
)
# 5. نسأل النموذج سؤال!
question1 = "كم لازم يكون معدلي عشان أحول على كلية الهندسة؟"
answer1 = qa_chain.run(question1)
print(f"nسؤال: {question1}")
print(f"جواب: {answer1}")
question2 = "كم سعر الساعة في كلية الهندسة؟"
answer2 = qa_chain.run(question2)
print(f"nسؤال: {question2}")
print(f"جواب: {answer2}")
# سؤال مركب شوي
question3 = "بدي أحول هندسة ومعدلي 86%، بس ما أخذت فيزياء 1، شو أعمل؟ وكيف أتواصل مع القبول؟"
answer3 = qa_chain.run(question3)
print(f"nسؤال: {question3}")
print(f"جواب: {answer3}")
لو شغلت الكود هذا، رح تلاحظ إنه الإجابات دقيقة جداً ومأخوذة حرفياً من النصوص اللي زودناه فيها. النموذج ما رح يخترع أي معلومة من عنده. لو سألته عن إشي مش موجود (مثلاً، سعر الساعة في كلية الطب)، رح يحكيلك إنه ما بيعرف الإجابة، وهذا بحد ذاته نجاح هائل!
نصائح أبو عمر (من قلب المعركة)
شغل الـ RAG مش بس كود وبس، في شوية “صنعة” بتفرق معك في النتائج:
- فن التقطيع (Chunking): طريقة تقسيمك للنصوص هي أهم خطوة. لا تقطع في نص جملة. استخدم مقطّعات نصوص ذكية ( زي RecursiveCharacterTextSplitter) بتحاول تحافظ على السياق. حجم القطعة بفرق: قطع صغيرة جداً بتضيع السياق، وقطع كبيرة جداً بتعمل ضوضاء في الـ Prompt. جرب وأوزن.
- نموذج التضمين (Embedding Model): مش كل نماذج الـ Embedding زي بعض. في نماذج أفضل للغات معينة أو لمجالات متخصصة. استثمر وقت في اختيار النموذج المناسب لبياناتك.
- الـ Prompt هو الملك: حتى مع RAG، الـ Prompt اللي بتعطيه للنموذج النهائي مهم جداً. ممكن تضيف عليه تعليمات واضحة مثل: “أجب على السؤال التالي بناءً على السياق المرفق فقط. إذا كانت الإجابة غير موجودة في السياق، قل بوضوح أنك لا تعرف.”
- RAG مش بديل كامل للـ Fine-Tuning: أحياناً، أفضل النتائج بتيجي من دمج الاثنين. ممكن تعمل Fine-Tuning خفيف عشان تعلم النموذج “لهجة” معينة، وبعدين تستخدم RAG عشان تضمن دقة الحقائق. هذا اسمه RAG-Finetuning Hybrid. شغل مرتب على الآخر.
الخلاصة: من الآخر
النماذج اللغوية الكبيرة أداة جبارة، لكنها بدون توجيه وترويض ممكن تكون مصدر للكوارث. نمط RAG مش مجرد تقنية جديدة، هو نقلة نوعية في طريقة تفكيرنا ببناء تطبيقات ذكاء اصطناعي موثوقة. هو الجسر اللي بيربط القدرة التوليدية الهائلة للنماذج اللغوية مع أرض الواقع الصلبة المتمثلة في بياناتنا ومصادرنا المعرفية.
فالمرة الجاي اللي بتلاقي فيها نموذجك “بيخبص” وبيحكي كلام من المريخ، تذكر قصة أبو عمر وفريقه، وتذكر إنه الحل ممكن يكون بسيط زي إنك تعطيه كتاب مفتوح يقرأ منه. 🤓
يلا، شدّوا حيلكم، وخلينا نبني أنظمة ذكاء اصطناعي بنقدر نوثق فيها.