يا جماعة الخير، السلام عليكم ورحمة الله. معكم أخوكم أبو عمر.
قبل فترة، كنا شغالين على مشروع لشركة محترمة بتبيع منتجات تراثية فلسطينية… زيت زيتون أصلي، زعتر فاخر، ميرمية بتقعد بالراس. الفكرة كانت نعمل مساعد ذكي (Chatbot) على موقعهم، يرد على أسئلة الزباين عن المنتجات، تاريخها، وكيفية استخدامها. استخدمنا واحد من النماذج اللغوية الكبيرة (LLM) المشهورة، ودربناه تدريب خفيف، وكنا متحمسين جداً للنتيجة.
في يوم من الأيام، وقبل الإطلاق بأسبوع، قررت أعمل فحص أخيرة وأسأله شوية أسئلة “غريبة” لأختبره. سألته: “ما هي المكونات الدقيقة لخلطة الزعتر الملكي؟”. كنت أتوقع إجابة ذكية مثل “هذه خلطة سرية” أو “تتكون من زعتر وسمسم وسماق…”. لكن جوابه كان صاعقاً! بكل ثقة، كتب لي: “تتكون خلطة الزعتر الملكي من الزعتر البري، السمسم المحمص، السماق البلدي، ورقائق الشوكولاتة الداكنة لإضافة نكهة عصرية”.
رقائق شوكولاتة؟! في الزعتر؟! للحظة فكرت الشاشة فيها مشكلة. جربت سؤال ثاني: “من أين يأتي زيت الزيتون لديكم؟”. الجواب كان: “يتم استيراد زيت الزيتون الخاص بنا من أفضل المزارع في إقليم توسكانا الإيطالي لضمان الجودة الأوروبية”.
هنا أنا لطمت على راسي. المشروع اللي شغالين عليه شهور، كان على وشك إنه يدمر سمعة الشركة ويحولها لمسخرة. النموذج اللغوي كان “يهلوس” (Hallucinating)، يخترع حقائق من العدم بكل ثقة، ويقدمها كأنها حقيقة مطلقة. دخلنا في اللي بنسميه “جحيم الهلوسة”، وكنا على وشك نلغي فكرة المساعد الذكي كلها. لكن الحمد لله، كان هناك حل عبقري وبسيط نسبياً أنقذ الموقف: تقنية الـ RAG.
ما هي “هلوسة” الذكاء الاصطناعي ولماذا تحدث؟
قبل ما نحكي عن الحل، خلينا نفهم المشكلة. النماذج اللغوية الكبيرة مثل GPT-4 وغيرها، هي بالأساس محركات توليد نصوص احتمالية. يعني هي شاطرة جداً في تخمين الكلمة التالية في جملة بناءً على مليارات النصوص اللي تدربت عليها. هي ليست قاعدة بيانات أو موسوعة علمية دقيقة، بل هي “ببغاء إحصائي” متطور جداً.
الهلوسة تحدث للأسباب التالية:
- معلومات قديمة: النموذج لا يعرف أي شيء حدث بعد تاريخ قطع تدريبه. لو سألته عن حدث جديد، سيحاول “التأليف”.
- معلومات غير موجودة في بيانات التدريب: مثل خلطة الزعتر السرية لشركتنا. النموذج لا يعرفها، فبدلاً من أن يقول “لا أعرف”، يحاول أن “يرضيك” بإجابة تبدو منطقية من وجهة نظره.
- غموض السؤال: إذا كان السؤال غير واضح، قد يفسره النموذج بطريقة خاطئة ويذهب في اتجاه بعيد عن الحقيقة.
المشكلة إن الهلوسة هذه بتطلع بثقة عمياء، مما يجعل كشفها صعباً على المستخدم العادي. وهنا تكمن الخطورة.
المنقذ: تقنية استرجاع المعلومات المعززة (RAG)
RAG هي اختصار لـ Retrieval-Augmented Generation. الفكرة من الآخر بسيطة وعبقرية: بدلاً من أن نعتمد على ذاكرة النموذج اللغوي فقط (واللي ممكن تكون غلط أو قديمة)، سنجعله يبحث في مصدر معلومات موثوق “قبل” أن يجيب. بالضبط مثل الطالب اللي بتعطيه كتاب مفتوح في الامتحان.
بهذه الطريقة، نحن لا نغير النموذج اللغوي نفسه، بل نغير “عملية” توليد الإجابة. نحن “نعزز” قدرته على التوليد بمعلومات “نسترجعها” من مصادرنا الخاصة. وبهيك، بنجبر النموذج على استخدام حقائقنا بدلاً من هلوساته.
كيف يعمل الـ RAG خطوة بخطوة؟
الشغلانة بتتقسم لمرحلتين رئيسيتين: مرحلة التجهيز (بتصير مرة واحدة أو كل فترة)، ومرحلة الاستعلام (بتصير مع كل سؤال).
المرحلة الأولى: بناء قاعدة المعرفة (Indexing)
هنا نقوم بتجهيز “الكتاب المفتوح” الذي سيستخدمه النموذج. هذه العملية تحدث خلف الكواليس.
- جمع البيانات (Knowledge Base): نجمع كل المستندات والمعلومات التي نريد أن يجيب النموذج بناءً عليها. في قصة شركتنا، كانت هذه المعلومات هي ملفات PDF عن المنتجات، صفحات من الموقع، مقالات عن تاريخ الشركة، وصفات، إلخ.
- التقطيع (Chunking): لا يمكننا إعطاء النموذج مستندات طويلة مرة واحدة. نقوم بتقطيع هذه المستندات إلى أجزاء صغيرة (chunks) ذات معنى، مثلاً كل فقرة أو كل بضعة أسطر. هذا يسهل عملية البحث لاحقاً.
- إنشاء المتجهات (Embeddings): هذه هي الخطوة السحرية. نستخدم نموذجاً متخصصاً (Embedding Model) لتحويل كل “قطعة” نصية إلى مجموعة من الأرقام تسمى “متجهاً” (Vector). هذا المتجه يمثل “معنى” النص رياضياً. النصوص ذات المعاني المتقاربة يكون لها متجهات متقاربة في الفضاء الرياضي.
- التخزين في قاعدة بيانات متجهة (Vector Database): نقوم بتخزين كل هذه المتجهات النصية في قاعدة بيانات متخصصة تسمى Vector Database (مثل ChromaDB, Pinecone, FAISS). وظيفتها هي البحث بسرعة فائقة عن المتجهات الأكثر تشابهاً لمتجه معين.
المرحلة الثانية: الاستعلام والتوليد (Retrieval & Generation)
هذه هي العملية التي تحدث في كل مرة يسأل فيها المستخدم سؤالاً.
- سؤال المستخدم (Query): المستخدم يكتب سؤاله، مثلاً: “ما هي مكونات زعتركم؟”.
- تحويل السؤال لمتجه: نستخدم نفس الـ Embedding Model لتحويل سؤال المستخدم إلى متجه.
- البحث عن السياق (Retrieval): نأخذ متجه السؤال ونبحث في قاعدة البيانات المتجهة عن أكثر القطع النصية (chunks) تشابهاً له في المعنى. قاعدة البيانات سترجع لنا مثلاً: “قطعة 1: خلطة الزعتر الملكي تتكون من زعتر بري مجفف، سمسم محمص، سماق بلدي، وملح بحري” و “قطعة 2: يتميز زعترنا بأنه لا يحتوي على أي إضافات صناعية”.
- تعزيز الطلب (Augmentation): الآن نقوم ببناء “طلب معزز” (Augmented Prompt) نرسله للنموذج اللغوي الكبير. هذا الطلب يبدو كالتالي:
أنت مساعد ذكي. أجب على سؤال المستخدم التالي بناءً على السياق المرفق فقط. إذا كانت الإجابة غير موجودة في السياق، قل “لا أملك معلومات كافية للإجابة”.
السياق:
– خلطة الزعتر الملكي تتكون من زعتر بري مجفف، سمسم محمص، سماق بلدي، وملح بحري.
– يتميز زعترنا بأنه لا يحتوي على أي إضافات صناعية.سؤال المستخدم:
ما هي مكونات زعتركم؟ - توليد الإجابة (Generation): النموذج اللغوي الآن يرى السياق الدقيق والصحيح. بدلاً من الهلوسة، سيستخدم هذا السياق ليصيغ إجابة دقيقة ومفيدة مثل: “مكونات الزعتر الملكي لدينا هي الزعتر البري المجفف، السمسم المحمص، السماق البلدي، والملح البحري، وهو خالٍ من أي إضافات صناعية”.
وبهذه الطريقة، حولنا النموذج من “مؤلف قصص” إلى “باحث دقيق”. 🎉
مثال عملي بالكود (Python و LangChain)
الحكي النظري حلو، بس خلينا نشوف كود حقيقي. سنستخدم مكتبة langchain الشهيرة لتبسيط العملية.
# أولاً، نقوم بتثبيت المكتبات اللازمة
# pip install langchain langchain-openai chromadb
import os
from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains import RetrievalQA
# ضع مفتاح OpenAI API الخاص بك هنا
os.environ["OPENAI_API_KEY"] = "sk-..."
# --- المرحلة الأولى: التجهيز (Indexing) ---
# 1. تحميل البيانات (سنستخدم ملف نصي بسيط كمثال)
# knowledge_base.txt يحتوي على:
# "خلطة الزعتر الملكي في شركتنا تتكون من زعتر بري مجفف، سمسم محمص، سماق بلدي، وملح بحري. نحن لا نستخدم أي مواد حافظة."
# "زيت الزيتون لدينا يأتي من أشجار زيتون رومانية معمرة في منطقة الخليل، ويتم عصره على البارد."
loader = TextLoader('./knowledge_base.txt')
documents = loader.load()
# 2. تقطيع النص إلى أجزاء (chunks)
text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
chunks = text_splitter.split_documents(documents)
# 3. إنشاء المتجهات وتخزينها في قاعدة بيانات متجهة (ChromaDB)
# هنا نستخدم نماذج OpenAI لإنشاء المتجهات
embeddings = OpenAIEmbeddings()
# ChromaDB هي قاعدة بيانات متجهة تعمل في الذاكرة (للتجربة)
vector_db = Chroma.from_documents(documents=chunks, embedding=embeddings)
# --- المرحلة الثانية: الاستعلام (Retrieval & Generation) ---
# تعريف النموذج اللغوي الذي سيقوم بتوليد الإجابة النهائية
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
# إنشاء "سلسلة" RAG التي تربط كل شيء ببعضه
# retriever هو الجزء المسؤول عن البحث في قاعدة البيانات المتجهة
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # "stuff" تعني أخذ كل القطع المسترجعة ووضعها في الطلب
retriever=vector_db.as_retriever()
)
# 4. طرح السؤال!
query = "ما هي مكونات الزعتر عندكم؟"
response = qa_chain.invoke(query)
print("السؤال:", query)
print("الإجابة:", response['result'])
# جرب سؤالاً آخر
query_2 = "من أين يأتي زيت الزيتون؟"
response_2 = qa_chain.invoke(query_2)
print("nالسؤال:", query_2)
print("الإجابة:", response_2['result'])
عند تشغيل هذا الكود، ستكون الإجابات دقيقة 100% بناءً على ملف knowledge_base.txt، ولن يكون هناك أي ذكر لرقائق الشوكولاتة أو توسكانا الإيطالية. لقد قضينا على الهلوسة!
نصائح من خبرة أبو عمر
- جودة التقطيع (Chunking) أهم شيء: لا تقطع النصوص في منتصف الجملة. استخدم “قواطع” ذكية (RecursiveCharacterTextSplitter) تحترم بنية النص (الفقرات، الجمل). كلما كانت القطع ذات معنى مستقل، كانت نتائج البحث أفضل.
- لا تبخل على نموذج الـ Embedding: جودة المتجهات التي يتم إنشاؤها تعتمد على قوة نموذج الـ Embedding. النماذج الجيدة تفهم الفروق الدقيقة في المعنى بشكل أفضل، مما يحسن دقة البحث.
- هندسة الطلب (Prompt Engineering) لا تزال مهمة: الطلب الذي أرسلناه للنموذج اللغوي (LLM) في خطوة التعزيز مهم جداً. كن واضحاً في تعليماتك: “أجب بناءً على السياق فقط”، “لا تخترع معلومات”، “إذا كانت الإجابة غير موجودة قل لا أعرف”.
- فكر في ما بعد البحث (Re-ranking): أحياناً، البحث الأولي قد يأتي بقطع نصية غير دقيقة 100%. يمكنك إضافة خطوة تالية تسمى “إعادة الترتيب” (Re-ranking) حيث يقوم نموذج أصغر وأسرع بإعادة تقييم القطع المسترجعة واختيار الأفضل منها قبل إرسالها للنموذج الكبير.
الخلاصة: من الهلوسة إلى الثقة
تقنية RAG ليست مجرد حل تقني، بل هي نقلة نوعية في كيفية تفكيرنا وبنائنا للتطبيقات المعتمدة على الذكاء الاصطناعي. إنها تحول النماذج اللغوية من “عالم بكل شيء” قد يكذب أحياناً، إلى “باحث مجتهد” يتأكد من مصادره قبل أن يتكلم. وهذا بالضبط ما نحتاجه لبناء أنظمة ذكاء اصطناعي موثوقة وآمنة يمكننا الاعتماد عليها.
في مشروعنا، تطبيق RAG لم يكن مجرد إصلاح لمشكلة، بل كان سبباً في نجاح المشروع وإطلاقه بثقة. الزبائن الآن يحصلون على معلومات دقيقة، ونحن مطمئنون أن مساعدنا الذكي لن يقترح عليهم يوماً وضع الشوكولاتة على الزعتر. 😉
نصيحتي الأخيرة لكل مطور يعمل في هذا المجال: قبل أن تقع في “جحيم الهلوسة”، ابدأ بالبحث في RAG. قد تكون هي أبسط وأقوى أداة في ترسانتك لبناء الجيل القادم من التطبيقات الذكية. وكما يقول المثل الشعبي عندنا: “اللي بعرفش، بقول عدس”. مع RAG، نحن نضمن أن نماذجنا “تعرف” قبل أن تتكلم.