يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.
قبل فترة، كنا شغالين على مشروع كبير شوي لأحد العملاء المهمين. الفكرة كانت بناء مساعد ذكي (Chatbot) يساعد موظفي شركة قانونية ضخمة في البحث السريع عن معلومات في آلاف الصفحات من المستندات والعقود والقضايا السابقة. الحماس كان “فوّل”، والفريق كله متفائل. استخدمنا واحد من أقوى النماذج اللغوية الكبيرة (LLM) الموجودة في السوق، ودربناه بشكل مبدئي، والنتائج الأولية كانت مبهرة… مبهرة زيادة عن اللزوم.
في يوم من الأيام، وأنا بعمل مراجعة لبعض إجابات النظام، سألته سؤال بسيط عن قضية معينة: “ما هو البند رقم 7.3 في عقد الشراكة مع شركة X؟”. النموذج جاوب بكل ثقة وبسرعة خيالية، وسرد لي بند كامل مفصل بلغة قانونية سليمة. للحظة انبسطت وحكيت لحالي: “ما شاء الله، شغل مرتب!”. بس إشي جواتي خلاني أشك، شعور المبرمج اللي “متعود على المصايب”. رجعت للمستند الأصلي… ويا ريتني ما رجعت. لقيت إنه البند 7.3 بحكي عن إشي مختلف تماماً! النموذج مش بس أخطأ، لأ، هو “ألّف” بند كامل من خياله الواسع، لكن بصياغة واثقة ومقنعة لدرجة بتخدع أكبر محامي.
هنا أنا صفنت وحكيت: “شو هالحكي يا زلمة؟ هاي ورطة!”. تخيلوا لو محامي اعتمد على هاي المعلومة الغلط في قضية مصيرية؟ الكارثة كانت حتكون كبيرة. وقتها، أدركنا إننا بنواجه وحش اسمه “الهلوسة” (Hallucination)، وإن النماذج اللغوية، على كل قوتها، ممكن تكون كذابة محترفة. ومن هنا بدأت رحلتنا للبحث عن حل، رحلة انتهت بتقنية غيرت كل قواعد اللعبة: تقنية الاسترجاع المعزز للتوليد (RAG).
ما هي “هلوسة” النماذج اللغوية؟ (وما هيّش خرافات جنّ وعفاريت!)
قبل ما نغوص في الحل، خلينا نفهم المشكلة صح. “الهلوسة” في عالم الذكاء الاصطناعي مش معناها إن النموذج بشوف أشباح. ببساطة، هي ظاهرة تحدث عندما يقوم النموذج اللغوي بتوليد معلومات غير صحيحة، غير دقيقة، أو لا أساس لها من الصحة، ولكنه يقدمها كحقيقة مطلقة وبكل ثقة.
ليش بصير هيك؟
- طبيعة النموذج الإحصائية: النموذج اللغوي في جوهره هو آلة توقع إحصائية عملاقة. هو لا “يفهم” العالم مثلنا، بل تعلم توقع الكلمة التالية الأكثر احتمالاً في جملة بناءً على مليارات النصوص التي تدرب عليها. أحياناً، التسلسل الأكثر احتمالاً إحصائياً ينتج عنه معلومة خاطئة لكنها “تبدو” صحيحة.
- المعرفة المحدودة: معرفة النموذج محصورة بالبيانات التي تدرب عليها، وهذه البيانات لها تاريخ انتهاء صلاحية. لو سألته عن حدث صار مبارح، ممكن ما يعرف عنه ويحاول “يخترع” إجابة.
- الغموض في السؤال: إذا كان السؤال غامضاً، قد يميل النموذج إلى سد الفجوات بمعلومات من عنده.
باختصار، النموذج مبرمج ليكون مفيداً ويقدم إجابة، حتى لو اضطر لاختلاقها. وهذا بالضبط ما يجعله خطيراً في التطبيقات الحساسة.
الحل الذي أنقذ الموقف: تقنية الاسترجاع المعزز للتوليد (RAG)
بعد ما فهمنا حجم الورطة اللي كنا فيها، بدأت أنا وفريقي البحث عن حلول. جربنا نعمل Fine-tuning (ضبط دقيق) للنموذج على بياناتنا، وهذا حسن الوضع شوي، لكنه ما قضى على الهلوسة تماماً، وكان مكلف وبطيء. لحد ما وصلنا لتقنية RAG، اللي كانت زي طوق النجاة.
RAG هي اختصار لـ Retrieval-Augmented Generation. الفكرة عبقرية وبسيطة في نفس الوقت: بدلاً من أن نطلب من النموذج الإجابة من “ذاكرته” الداخلية فقط (اللي ممكن تكون غلط أو قديمة)، إحنا بنجبره يجاوب بناءً على مصدر معلومات خارجي وموثوق احنا بنحدده.
تخيلها زي امتحان “الكتاب المفتوح” (Open-book exam). الطالب (النموذج اللغوي) مش مطلوب منه يحفظ كل الكتاب (كل معلومات العالم)، لكن مطلوب منه يعرف كيف يبحث في الكتاب (قاعدة بياناتنا الموثوقة) عن المعلومة الصحيحة لما ينسأل سؤال، وبعدين يستخدم ذكاءه عشان يصيغ إجابة حلوة ومرتبة من الفقرات اللي لقاها.
كيف تعمل تقنية RAG خطوة بخطوة؟ (فكفكة الميكانيكية)
العملية بتمر بثلاث مراحل رئيسية. خلينا نفصلها عشان تكون واضحة للجميع، من المبتدئ للمحترف.
المرحلة الأولى: التجهيز والفهرسة (بناء مكتبتنا الخاصة)
هاي المرحلة بتصير مرة واحدة في البداية (أو كلما احتجنا نحدّث معلوماتنا). الهدف هو تحويل مستنداتنا (PDFs, Word, TXT, …) لمكتبة قابلة للبحث السريع.
- تحميل وتقطيع البيانات (Loading & Chunking): أول إشي، بنحمّل كل المستندات اللي بدنا النموذج يجاوب منها. ولأنه ما بنقدر نعطي النموذج مستند من 1000 صفحة مرة واحدة، بنقوم بتقطيع هاي المستندات لـ “قطع” أو “شقف” (Chunks) أصغر، مثلاً كل قطعة عبارة عن فقرة أو فقرتين.
- إنشاء التضمينات (Embeddings): هاي هي الخطوة السحرية. بناخذ كل “قطعة” نصية وبنحولها لمجموعة من الأرقام اسمها “متجه” أو Vector. هذا المتجه بمثل “معنى” القطعة النصية رياضياً. القطع اللي الها معاني متشابهة بتكون متجهاتِها “قريبة” من بعض في الفضاء الرياضي. بنستخدم نماذج خاصة اسمها Embedding Models لعمل هاي الشغلة.
- التخزين في قاعدة بيانات متجهات (Vector Database): بعد ما حولنا كل قطع النصوص لمتجهات، بنخزنها في قاعدة بيانات متخصصة اسمها “قاعدة بيانات المتجهات” (Vector Database). أمثلة عليها: FAISS, ChromaDB, Pinecone. هاي القاعدة مصممة خصيصاً عشان تبحث عن المتجهات المتشابهة بسرعة البرق.
المرحلة الثانية: الاسترجاع (صيد المعلومة الدقيقة)
هاي المرحلة بتصير كل مرة المستخدم بسأل سؤال.
- تحويل سؤال المستخدم لمتجه: لما المستخدم يسأل سؤال، مثلاً “ما هو البند 7.3 في عقد الشراكة؟”، بناخذ سؤاله وبنحوله هو كمان لمتجه بنفس الطريقة اللي عملناها في المرحلة الأولى.
- البحث عن التشابه: الآن، بنروح على قاعدة بيانات المتجهات وبنحكيلها: “يا قاعدة البيانات، أعطيني أكثر 5 قطع نصية متجهاتِها قريبة من متجه السؤال هاد”. العملية هاي اسمها “بحث التشابه” (Similarity Search).
- الحصول على السياق: قاعدة البيانات بترجعلنا القطع النصية الأكثر صلة بالسؤال من مستنداتنا الأصلية. هاي القطع هي “السياق” (Context) اللي النموذج راح يستخدمه.
المرحلة الثالثة: التعزيز والتوليد (صياغة الإجابة الذكية)
هنا يأتي دور النموذج اللغوي الكبير (LLM) ليقوم بالإجابة.
- بناء الموجه المعزز (Augmented Prompt): بدل ما نسأل النموذج سؤال المستخدم مباشرة، بنبني له “موجه” (Prompt) جديد ومحسن. هذا الموجه يحتوي على شيئين:
- السياق الذي استرجعناه في المرحلة الثانية.
- سؤال المستخدم الأصلي.
وبنحكيله بوضوح: “أجب على السؤال التالي بناءً على السياق المرفق فقط. إذا كانت الإجابة غير موجودة في السياق، قل أنك لا تعرف.”
- توليد الإجابة النهائية: النموذج اللغوي الآن يقرأ السياق والسؤال، ويولد إجابة دقيقة مبنية على المعلومات الحقيقية اللي أعطيناه إياها. وبهيك، بنكون “أرضينا” (Grounded) النموذج ومنعناه من الهلوسة.
مثال عملي بالكود: بناء نظام RAG بسيط باستخدام Python
الحكي النظري حلو، بس خلينا نشوف كود حقيقي. راح نستخدم مكتبة LangChain المشهورة عشان نسهل على حالنا الشغل.
المتطلبات الأساسية
لازم تكون منزل هاي المكتبات عندك. بتقدر تنزلها باستخدام pip:
pip install langchain openai faiss-cpu pypdf tiktoken
وطبعاً، لازم يكون عندك مفتاح API من OpenAI.
الكود خطوة بخطوة
تخيل عنا ملف PDF اسمه “my_document.pdf” فيه معلومات بدنا نسأل عنها.
import os
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
# الخطوة 0: إعداد مفتاح OpenAI
os.environ["OPENAI_API_KEY"] = "sk-YOUR_API_KEY_HERE"
# --- المرحلة الأولى: التجهيز والفهرسة ---
# 1. تحميل المستند
loader = PyPDFLoader("my_document.pdf")
documents = loader.load()
# 2. تقطيع المستند إلى قطع صغيرة
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=150)
chunks = text_splitter.split_documents(documents)
print(f"تم تقطيع المستند إلى {len(chunks)} قطعة.")
# 3. إنشاء التضمينات وتخزينها في قاعدة بيانات متجهات (FAISS)
embeddings = OpenAIEmbeddings()
# سيقوم FAISS بإنشاء قاعدة بيانات محلية في الذاكرة
vector_store = FAISS.from_documents(chunks, embeddings)
print("تم إنشاء قاعدة بيانات المتجهات بنجاح.")
# --- المرحلتان الثانية والثالثة: الاسترجاع والتوليد ---
# 1. إعداد النموذج اللغوي
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
# 2. بناء سلسلة RAG (RetrievalQA chain)
# هاي السلسلة بتدمج كل الخطوات مع بعض بشكل تلقائي
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # "stuff" تعني وضع كل القطع المسترجعة في السياق
retriever=vector_store.as_retriever()
)
# 3. طرح سؤال والحصول على إجابة مدعومة بالمصادر
question = "ما هي أهم النقاط المذكورة في الفصل الثالث؟"
response = qa_chain.run(question)
print("n--- السؤال ---")
print(question)
print("n--- الإجابة ---")
print(response)
الكود أعلاه يقوم بكل العملية التي شرحناها: يقرأ ملف PDF، يقطعه، يحوله لمتجهات، يخزنه، ثم يبني سلسلة كاملة للإجابة على الأسئلة. الإجابة التي ستحصل عليها ستكون مبنية حصراً على محتوى ملف “my_document.pdf”. لو سألته عن شيء غير موجود، سيقول لك أنه لا يعرف (حسب تصميم الموجه داخل السلسلة).
نصائح من خبرة أبو عمر: كيف تبني نظام RAG احترافي؟
بناء نظام RAG بسيط هو البداية فقط. لتحصل على نتائج احترافية، خذ هاي النصائح من أخوك:
نصيحة 1: جودة البيانات هي الأساس (الزبالة بتدخل، زبالة بتطلع)
هذه هي القاعدة الذهبية. قبل ما ترمي مستنداتك في النظام، نظفها. أزل المعلومات غير المهمة، الصفحات الفارغة، الترويسات والتذييلات المتكررة. كلما كانت بياناتك أنظف وأكثر تركيزاً، كانت نتائج البحث أفضل.
نصيحة 2: التقطيع الذكي (مش كل تقطيع زي بعضه)
طريقة تقطيعك للنصوص (Chunking) تؤثر بشكل كبير على جودة السياق المسترجع. لا تقطع الكلمات في منتصف الجملة. جرب استراتيجيات تقطيع مختلفة مثل `RecursiveCharacterTextSplitter` الذي يحاول الحفاظ على الفقرات والجمل متماسكة. في بعض الحالات المتقدمة، قد تحتاج لكتابة منطق تقطيع خاص بك يفهم طبيعة مستنداتك (مثلاً، التقطيع بناءً على العناوين والفقرات).
نصيحة 3: اختر نموذج التضمين (Embedding Model) المناسب
مش كل نماذج التضمين زي بعض. هناك نماذج أفضل للغات معينة (مثل العربية)، ونماذج أفضل للمجالات المتخصصة (مثل الطب أو القانون). إذا كانت بياناتك بالعربية، ابحث عن نماذج تضمين تم تدريبها بشكل مكثف على اللغة العربية لتحصل على أفضل فهم للمعنى.
نصيحة 4: لا تهمل إعادة الترتيب (Re-ranking)
في الأنظمة المتقدمة، بعد مرحلة الاسترجاع الأولية (اللي بترجعلك مثلاً أفضل 20 قطعة)، يمكنك استخدام مرحلة إضافية. في هذه المرحلة، تقوم بتمرير هذه الـ 20 قطعة على نموذج آخر (أصغر وأسرع) وظيفته فقط إعادة ترتيبها حسب مدى صلتها الدقيقة بالسؤال. ثم تأخذ أفضل 3-5 قطع من الترتيب الجديد وتعطيها للنموذج اللغوي الكبير. هذا يحسن دقة السياق بشكل ملحوظ.
الخلاصة: من هلوسة إلى حقيقة.. بفضل RAG 💡
في النهاية، تقنية RAG كانت هي المنقذ لمشروعنا. حولت نظامنا من “كذاب محترف” إلى “باحث خبير”. صحيح أنها ليست حلاً سحرياً لكل مشاكل الذكاء الاصطناعي، لكنها خطوة عملية وجبارة نحو بناء أنظمة ذكاء اصطناعي أكثر موثوقية وأماناً.
الجميل في RAG أنها تمنحنا السيطرة. لم نعد تحت رحمة “المعرفة المظلمة” داخل النموذج، بل أصبحنا نحن من يزوده بالمعرفة التي نريدها، ونراقبها، ونحدّثها. وهذا يفتح الباب لتطبيقات لا حصر لها في كل المجالات تقريباً، من خدمة العملاء والطب إلى القانون والتعليم.
نصيحتي لكل مطور ومبرمج مهتم بالذكاء الاصطناعي: لا تخف من هلوسة النماذج. افهم المشكلة، وتعلم أدوات مثل RAG. ابدأ اليوم ببناء نظامك البسيط، جرب، واغلط، وتعلم. المستقبل ليس فقط في بناء نماذج أكبر، بل في بناء أنظمة أذكى وأكثر ارتباطاً بالواقع.
أتمنى تكونوا استفدتوا. والله يعطيكم العافية.