يا الله شو بتذكر هداك اليوم… كنا قاعدين في المكتب، مبسوطين على حالنا، وبنحتفل بإطلاق مساعد ذكي جديد لشركة محاماة كبيرة. النموذج كان المفروض يساعد المحامين الجدد في البحث السريع عن القوانين وقضايا سابقة. الأمور كانت ماشية زي الحلاوة في مرحلة الاختبار، والنموذج كان “فهلوي” وبجاوب على كل شي.
بعد أسبوع من الإطلاق، بيجيني إيميل من مدير قسم التقنية عند العميل، عنوانه بس كلمتين: “مشكلة عاجلة”. قلبي نقزني. بفتح الإيميل بلاقي سكرين شوت لمحادثة مع المساعد الذكي تبعنا. محامي جديد سأل عن مادة معينة في قانون الشركات، والنموذج جاوبه بثقة عمياء… بمعلومات خاطئة تماماً! مش بس خاطئة، بل اختلق رقم مادة قانونية غير موجودة أصلاً واستشهد فيها. يا ساتر! الموقف كان كارثي، ولو اعتمد المحامي على هاي المعلومة كانت ممكن تسبب ضرر كبير.
يومها، حسيت إنه كل شغلنا راح على الفاضي. النموذج اللي صرفنا عليه شهور من التدريب والضبط (Fine-tuning) طلع “بهلوس” أو زي ما بنحكي بالعامية “بخبّص”. كان بيألف كلام من عنده لكن بأسلوب واثق ومقنع. هنا كانت بداية رحلتنا لفهم هاي الظاهرة، والبحث عن حل جذري أنقذ مشروعنا، وكان اسمه: الاسترجاع المعزز للتوليد (Retrieval-Augmented Generation) أو اختصاراً RAG.
ما هي “هلوسة” الذكاء الاصطناعي وليش بتصير؟
قبل ما نغوص في الحل، خلينا نفهم المشكلة بالزبط. “هلوسة” الذكاء الاصطناعي (AI Hallucination) هي لما نموذج اللغة الكبير (LLM) يولد معلومات تبدو منطقية ومقنعة، لكنها في الحقيقة غير صحيحة، أو لا تستند إلى أي بيانات واقعية في مصدر معلوماته.
تخيلها مثل طالب ذكي جداً قرأ آلاف الكتب، لكنه لا يملك ذاكرة فوتوغرافية. لما تسأله سؤال دقيق، بدلاً من أن يقول “لا أعرف”، يحاول عقله أن يربط كل المعلومات اللي قرأها ليخلق إجابة تبدو هي الأكثر احتمالاً. هو لا يكذب عن قصد، بل “يتوقع” الكلمة التالية الأكثر منطقية بناءً على سياق مليارات الكلمات التي تدرب عليها. وهذه هي طبيعته الإحصائية.
الأسباب الرئيسية للهلوسة:
- طبيعة النماذج الاحتمالية: النماذج اللغوية الكبيرة هي عبارة عن محركات تنبؤ بالكلمة التالية. هي لا “تفهم” الحقيقة، بل تتنبأ بما يجب أن يأتي بعد ذلك بناءً على الأنماط اللغوية التي تعلمتها.
- معلومات قديمة: النموذج محدود بالبيانات التي تدرب عليها. إذا كانت بياناته تتوقف عند عام 2022، وسألته عن حدث وقع في 2024، سيحاول أن “يؤلف” إجابة.
- نقص في المعرفة المتخصصة: عندما تسأل النموذج عن مجال دقيق جداً (مثل قوانين شركة معينة أو تفاصيل منتج داخلي)، فمن المحتمل ألا تكون هذه المعلومات موجودة في بيانات تدريبه العامة، فيبدأ بالهلوسة.
محاولاتنا الأولى الفاشلة لكبح جماح الهلوسة
قبل ما نوصل للـ RAG، جربنا حلول تانية، زي أي مطور شاطر بحاول يحل مشكلته بالأدوات اللي بيعرفها.
المحاولة الأولى: هندسة الأوامر (Prompt Engineering)
أول شي عملناه هو محاولة “ترويض” النموذج بالأوامر. صرنا نكتب له في بداية كل محادثة تعليمات صارمة مثل:
“أنت مساعد خبير. أجب فقط بناءً على المعلومات التي تعرفها. إذا لم تكن متأكداً من الإجابة، قل بوضوح ‘أنا لا أملك هذه المعلومة’.”
هذا الأسلوب ساعد شوي، لكنه ما كان كافي. النموذج كان لا يزال يجد طرقاً للالتفاف على التعليمات ويقدم إجابات مختلقة، خصوصاً مع الأسئلة المعقدة. كانت الشغلة مثل محاولة إمساك الماء باليدين.
المحاولة الثانية: الضبط الدقيق (Fine-Tuning)
قلنا لحالنا: “الحل إنه نعلّم النموذج مجالنا الخاص!”. جمعنا كل الوثائق القانونية للعميل، مئات الملفات، وعملنا عملية ضبط دقيق (Fine-tuning) لنموذج لغوي كبير. الفكرة كانت رائعة نظرياً: سنجعل النموذج خبيراً في هذه القوانين تحديداً.
لكن النتائج كانت محبطة لعدة أسباب:
- التكلفة والوقت: العملية كانت مكلفة جداً وتتطلب قوة حوسبة هائلة، وأخذت أسابيع.
- النسيان الكارثي (Catastrophic Forgetting): لاحظنا أن النموذج بعد الضبط الدقيق صار ممتازاً في قوانين العميل، لكنه بدأ ينسى قدراته العامة الأخرى وأصبح أداؤه ضعيفاً في المحادثات العادية.
- لم يحل المشكلة جذرياً: حتى بعد الضبط الدقيق، كانت الهلوسة لا تزال تحدث وإن بشكل أقل. النموذج لا يزال يعتمد على “ذاكرته” الداخلية التي قد تخونه.
هنا وصلنا لنقطة يأس، وبدأنا نفكر أن فكرة المشروع كله كانت خاطئة. إلى أن قرأنا ورقة بحثية غيّرت كل شيء.
وهنا أشرق نور “الاسترجاع المعزز للتوليد” (RAG)
الـ RAG هو الحل العبقري الذي يجمع بين عالمين: قوة البحث الدقيق (مثل جوجل) وقوة التوليد اللغوي للنماذج الكبيرة.
الفكرة بسيطة بشكل يخدع العقل: بدلاً من أن تسأل النموذج سؤالاً وتتركه يجيب من ذاكرته، أعطه “كتاباً مفتوحاً” للإجابة منه.
بمعنى آخر، عندما يسأل المستخدم سؤالاً، يقوم نظام RAG بالخطوات التالية:
- الاسترجاع (Retrieval): يبحث أولاً في قاعدة بياناتك الخاصة (مستنداتك، ملفاتك، موقعك الإلكتروني) عن الأجزاء الأكثر صلة بالسؤال.
- التعزيز (Augmentation): يأخذ هذه الأجزاء التي وجدها ويضيفها إلى سؤال المستخدم الأصلي كسياق.
- التوليد (Generation): يعطي هذا “الطلب المعزز” (السؤال + السياق) للنموذج اللغوي الكبير ويطلب منه الإجابة بناءً على السياق المقدم فقط.
بهذه الطريقة، نحن لا نطلب من النموذج أن “يتذكر”، بل أن “يقرأ ويفهم ويلخص”. لقد حولنا النموذج من “طالب يعتمد على ذاكرته” إلى “باحث يستخدم المصادر المتاحة أمامه”. وهذا فرق هائل!
كيف يعمل الـ RAG خطوة بخطوة؟
لنفصل العملية تقنياً أكثر. تنقسم بنية RAG إلى مرحلتين رئيسيتين: مرحلة الإعداد (Indexing) ومرحلة التشغيل (Runtime).
المرحلة الأولى: الإعداد (Indexing) – بناء قاعدة المعرفة
هذه المرحلة تقوم بها مرة واحدة (أو كلما تحدث بياناتك). الهدف هو تحويل مستنداتك إلى صيغة قابلة للبحث السريع.
- تحميل وتقسيم البيانات (Loading & Chunking): نقوم بتحميل جميع مستنداتك (PDFs, Docs, Txts, HTML). ثم نقسم هذه المستندات الكبيرة إلى أجزاء صغيرة (chunks). ليش؟ لأن النماذج اللغوية لها نافذة سياق محدودة، ولا يمكننا إرسال كتاب كامل لها مرة واحدة. التقسيم الجيد هو مفتاح النجاح.
- إنشاء التضمينات (Embeddings): هنا السحر الحقيقي. كل “جزء” من النص يتم تحويله إلى متجه رقمي (vector) باستخدام نموذج تضمين متخصص (مثل
text-embedding-3-smallمن OpenAI أو نماذج مفتوحة المصدر). هذا المتجه هو تمثيل رياضي لمعنى النص. النصوص ذات المعاني المتقاربة يكون لها متجهات متقاربة في الفضاء الرقمي. - التخزين والفهرسة (Storing & Indexing): نقوم بتخزين هذه المتجهات (مع النص الأصلي المرتبط بها) في قاعدة بيانات متخصصة تسمى قاعدة بيانات المتجهات (Vector Database). أمثلة عليها: Pinecone, ChromaDB, FAISS. هذه القاعدة مصممة خصيصاً للبحث عن “أقرب الجيران” أو المتجهات الأكثر تشابهاً بسرعة فائقة.
المرحلة الثانية: وقت التشغيل (Runtime) – الإجابة على سؤال المستخدم
هذا ما يحدث في كل مرة يسأل فيها المستخدم سؤالاً.
- سؤال المستخدم: المستخدم يكتب سؤاله، مثلاً: “ما هي سياسة الإجازات السنوية؟”.
- تضمين السؤال: نقوم بتحويل سؤال المستخدم إلى متجه رقمي باستخدام نفس نموذج التضمين الذي استخدمناه في المرحلة الأولى.
- البحث عن التشابه (Similarity Search): نأخذ متجه السؤال ونبحث في قاعدة بيانات المتجهات عن “أقرب” المتجهات إليه (مثلاً، أقرب 5 متجهات). هذه المتجهات تمثل أجزاء النص الأكثر صلة بسؤال المستخدم من وثائقنا الأصلية. هذا هو جزء الاسترجاع (Retrieval).
- تعزيز الطلب (Prompt Augmentation): الآن، نبني طلباً جديداً للنموذج اللغوي الكبير. هذا الطلب يحتوي على:
- السياق: أجزاء النص التي استرجعناها من قاعدة البيانات.
- السؤال: سؤال المستخدم الأصلي.
- التعليمات: “أجب على السؤال التالي بناءً على السياق المرفق فقط.”
- توليد الإجابة (Generation): نرسل هذا الطلب المعزز إلى النموذج اللغوي (مثل GPT-4). النموذج الآن لديه كل ما يحتاجه ليجيب بدقة. سيقرأ السياق ويصيغ إجابة تستند إلى الحقائق الموجودة فيه، مما يقلل من احتمالية الهلوسة بشكل جذري.
مثال عملي بسيط باستخدام Python و LangChain
هذا مثال بسيط يوضح الفكرة باستخدام مكتبة LangChain الشهيرة. الكود يقوم بإعداد نظام RAG بسيط على نص معين.
# أولاً، نقوم بتثبيت المكتبات اللازمة
# pip install langchain langchain-openai beautifulsoup4 chromadb faiss-cpu
import os
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain.chains import create_retrieval_chain
# تأكد من وضع مفتاح OpenAI API الخاص بك كمتغير بيئة
# os.environ["OPENAI_API_KEY"] = "sk-..."
# 1. تحميل البيانات (مثلاً، من مقالة ويكيبيديا عن الذكاء الاصطناعي)
loader = WebBaseLoader("https://ar.wikipedia.org/wiki/%D8%B0%D9%83%D8%A7%D8%A1_%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A")
docs = loader.load()
# 2. تقسيم النص إلى أجزاء (chunks)
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
# 3. إنشاء التضمينات وتخزينها في قاعدة بيانات متجهات (ChromaDB)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings)
# 4. إعداد المسترجع (Retriever)
retriever = vectorstore.as_retriever()
# 5. إعداد سلسلة الإجابة (RAG Chain)
# أولاً، نحدد قالب الطلب (Prompt Template)
prompt_template = """أجب على السؤال التالي بناءً على السياق المقدم فقط.
إذا كانت الإجابة غير موجودة في السياق، قل بوضوح "لا أملك معلومات كافية للإجابة".
{context}
Question: {input}"""
prompt = ChatPromptTemplate.from_template(prompt_template)
# نختار النموذج اللغوي الكبير
llm = ChatOpenAI(model="gpt-4o")
# ننشئ السلسلة التي تربط كل شيء ببعضه
# هذه السلسلة تأخذ السياق والسؤال وتمررهما للنموذج
document_chain = create_stuff_documents_chain(llm, prompt)
# الآن ننشئ السلسلة الكاملة التي تبدأ بالاسترجاع ثم التوليد
retrieval_chain = create_retrieval_chain(retriever, document_chain)
# 6. طرح سؤال
question = "من هو جون مكارثي وماذا فعل؟"
response = retrieval_chain.invoke({"input": question})
print(response["answer"])
# مثال آخر لسؤال قد لا تكون إجابته موجودة
question_2 = "ما هو سعر سهم شركة أبل اليوم؟"
response_2 = retrieval_chain.invoke({"input": question_2})
print(response_2["answer"])
نصائح من خبرة أبو عمر
بعد ما اشتغلنا على أنظمة RAG كثير، تعلمت كم شغلة على الطريق الصعب. خذوا هاي النصائح من أخوكم:
- جودة التقسيم (Chunking) هي كل شيء: لا تستهينوا بهذه الخطوة. التقسيم العشوائي قد يقطع جملة مهمة في المنتصف. جربوا التقسيم الدلالي (Semantic Chunking) أو التقسيم حسب العناوين والفقرات. جودة الأجزاء تحدد جودة البحث.
- المسترجع (Retriever) هو بطلك الخارق: أداء نظامك يعتمد بشكل كبير على جودة المسترجع. لا تكتفِ بالبحث عن التشابه البسيط. جرب تقنيات متقدمة مثل Maximum Marginal Relevance (MMR) لتضمن تنوع النتائج، وجرب تغيير عدد المستندات التي يتم استرجاعها (قيمة k).
- لا تهمل النموذج اللغوي (LLM): نموذج قوي مثل GPT-4o أو Claude 3 Opus سيكون أفضل في فهم السياق المعقد وتلخيصه ودمج المعلومات من عدة أجزاء. نموذج أضعف قد يميل إلى نسخ ولصق أجزاء من السياق.
- فكر في البحث الهجين (Hybrid Search): أحياناً، البحث عن الكلمات المفتاحية التقليدي (Keyword Search) يكون أفضل لبعض الأسئلة (مثل البحث عن اسم منتج معين). دمج البحث بالمتجهات مع البحث بالكلمات المفتاحية غالباً ما يعطي أفضل النتائج.
- التقييم ثم التقييم ثم التقييم: كيف تعرف أن نظامك يعمل بشكل جيد؟ لا تعتمد على إحساسك. استخدم أطراً للتقييم مثل RAGAS أو ARES لقياس دقة الإجابات (Faithfulness) وارتباطها بالسؤال (Answer Relevancy).
الخلاصة: RAG ليس عصا سحرية، ولكنه أفضل ما لدينا 🚀
في النهاية يا جماعة، تقنية RAG لم تكن مجرد حل تقني لمشكلتنا، بل كانت نقلة نوعية في طريقة تفكيرنا ببناء تطبيقات الذكاء الاصطناعي. لقد حولت النماذج اللغوية من “مبدعين قصصيين” لا يمكن الوثوق بهم دائماً، إلى “مساعدين باحثين” يستندون إلى حقائق ومصادر يمكن التحقق منها.
هل الـ RAG مثالي؟ لا طبعاً. لا يزال هناك تحديات في الاسترجاع والتقييم. لكنه اليوم، وبدون أي شك، هو أقوى سلاح في ترسانتنا لمحاربة هلوسة الذكاء الاصطناعي وبناء تطبيقات موثوقة وقوية وقادرة على استخدام معرفة خاصة ومحدثة.
نصيحتي الأخيرة لكم: ما تخافوا تجربوا. ابدأوا بمثال بسيط مثل الذي عرضته، وابنوا عليه. كل وثيقة وكل قاعدة بيانات معرفية هي فرصة لبناء مساعد ذكي مذهل. الفشل هو أول خطوة للنجاح، وكل كود بتكتبوه هو درس جديد. يلا شدوا حيلكم!