مرحباً أيها الخبراء! اليوم، سنغوص في عالم تقنية RAG (Retrieval Augmented Generation) المذهلة. سنتعلم كيف نُمكن نماذج اللغة الكبيرة (LLMs) من توليد نصوص ذكية ودقيقة بالاعتماد على بياناتنا الخاصة، بدلاً من الاقتصار على معلوماتها التدريبية العامة. تخيل أن تجعل الذكاء الاصطناعي يكتب تقارير، ملخصات، أو حتى إجابات لأسئلة معقدة، مستنداً بشكل حصري إلى مستنداتك السرية أو قواعد بياناتك الداخلية. هذا ما ستحققه تقنية RAG.
ماذا سنبني اليوم؟
سنقوم ببناء نظام بسيط لـ RAG باستخدام مكتبة LangChain و ChromaDB كمتجر للمتجهات، ونموذج لغة كبير محلي (مثل Llama2 عبر Ollama) أو سحابي (مثل GPT-3.5-turbo). هدفنا هو تحميل ملف PDF خاص بنا، تقسيمه، تخزين "ملخصاته" الرقمية في قاعدة بيانات متجهات، ثم استخدامها لاسترجاع المعلومات ذات الصلة عند طرح سؤال، وأخيراً، تمرير هذه المعلومات إلى LLM لتوليد إجابة دقيقة ومستنيرة.
المتطلبات الأساسية
- Python 3.8+
- Ollama (إذا كنت تفضل نماذج محلية) أو مفتاح API لـ OpenAI (إذا كنت تفضل نماذج سحابية)
الخطوات العملية
الخطوة 1: إعداد البيئة وتثبيت المكتبات
أولاً، نحتاج إلى تثبيت المكتبات اللازمة. إذا كنت تستخدم Ollama، تأكد من تثبيته وتشغيل نموذج مثل llama2 و nomic-embed-text. يمكنك تحميلهما عبر الأوامر: ollama run llama2 و ollama run nomic-embed-text.
pip install langchain langchain-community pypdf chromadb langchain-openai
# إذا كنت تستخدم Ollama بدلاً من OpenAI (وستحتاج إلى langchain-ollama أيضاً إذا أردت استخدام نماذج Ollama للغة)
# pip install langchain-ollama
ملاحظة تقنية:
langchain-communityتحتوي على العديد من عمليات الدمج (integrations) مع مصادر بيانات و LLMs مختلفة، بينماlangchain-openaiوlangchain-ollamaتوفران عمليات الدمج الخاصة بـ OpenAI و Ollama على التوالي.
الخطوة 2: تحميل ومعالجة المستندات
سنقوم بتحميل ملف PDF خاص بنا. يمكنك استبدال your_document.pdf بمسار ملفك. ثم سنقوم بتقسيم المستند إلى أجزاء (chunks) أصغر لكي يسهل على نموذج الاسترجاع التعامل معها.
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# تحديد مسار ملف PDF الخاص بك
pdf_path = "your_document.pdf" # استبدل هذا بمسار ملفك الحقيقي
# تحميل المستندات
loader = PyPDFLoader(pdf_path)
docs = loader.load()
# تقسيم المستندات إلى أجزاء أصغر
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
print(f"تم تحميل {len(docs)} مستندًا (صفحة).")
print(f"تم تقسيمها إلى {len(splits)} جزءًا.")
ملاحظة تقنية:
chunk_sizeوchunk_overlapقيمتان مهمتان تؤثران على جودة الاسترجاع.chunk_sizeتحدد حجم كل جزء، وchunk_overlapتحدد مقدار التداخل بين الأجزاء المتتالية للمحافظة على السياق.
الخطوة 3: إنشاء التضمينات (Embeddings) وتخزينها في قاعدة بيانات المتجهات
الآن، سنقوم بتحويل أجزاء النص إلى "تضمينات" (Embeddings)، وهي تمثيلات رقمية للنص يمكن مقارنتها رياضياً. ثم سنخزن هذه التضمينات في قاعدة بيانات متجهات (Vector Database) مثل ChromaDB، والتي تسمح بالبحث السريع عن المتجهات المتشابهة.
from langchain_community.vectorstores import Chroma
# اختر نموذج التضمين الخاص بك (Ollama أو OpenAI)
# ---------- خيار Ollama (نموذج محلي) ----------
from langchain_community.embeddings import OllamaEmbeddings
embeddings = OllamaEmbeddings(model="nomic-embed-text")
# ---------- خيار OpenAI (نموذج سحابي) ----------
# من الضروري تعيين مفتاح API الخاص بك كمتغير بيئة (OPENAI_API_KEY)
# import os
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
# from langchain_openai import OpenAIEmbeddings
# embeddings = OpenAIEmbeddings()
# إنشاء قاعدة بيانات المتجهات من الأجزاء والتضمينات
vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings, persist_directory="./chroma_db")
# إنشاء مسترجع (retriever) من قاعدة البيانات المتجهية
retriever = vectorstore.as_retriever()
print("تم إنشاء قاعدة بيانات المتجهات بنجاح.")
ملاحظة تقنية:
persist_directoryمهمة جداً! تسمح لك بحفظ قاعدة بيانات المتجهات على القرص، بحيث لا تحتاج إلى إعادة معالجة المستندات في كل مرة تشغل فيها السكريبت.
الخطوة 4: تعريف سلسلة RAG (RAG Chain)
هنا نربط الأجزاء معاً. سنستخدم نموذج لغة كبير (LLM) ومسترجعنا لإنشاء سلسلة RAG. هذه السلسلة ستتلقى السؤال، تسترجع الأجزاء ذات الصلة من قاعدة البيانات المتجهية، ثم تمرر هذه الأجزاء والسؤال إلى LLM لتوليد الإجابة.
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains import create_retrieval_chain
# اختر نموذج اللغة الكبير الخاص بك (Ollama أو OpenAI)
# ---------- خيار Ollama (نموذج محلي) ----------
from langchain_community.llms import Ollama
llm = Ollama(model="llama2")
# ---------- خيار OpenAI (نموذج سحابي) ----------
# من الضروري تعيين مفتاح API الخاص بك كمتغير بيئة (OPENAI_API_KEY)
# import os
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"
# from langchain_openai import ChatOpenAI
# llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
# تعريف قالب الوجه (prompt template)
prompt = ChatPromptTemplate.from_template("""
أنت مساعد ذكاء اصطناعي خبير. أجب على السؤال التالي بناءً على السياق المقدم فقط.
إذا لم تتمكن من العثور على الإجابة في السياق، فقل "لا أستطيع الإجابة على هذا السؤال بناءً على المعلومات المتوفرة."
السياق: {context}
السؤال: {input}
""")
# إنشاء سلسلة لدمج المستندات مع الوجه
document_chain = create_stuff_documents_chain(llm, prompt)
# إنشاء سلسلة الاسترجاع الكاملة
rag_chain = create_retrieval_chain(retriever, document_chain)
print("تم تعريف سلسلة RAG بنجاح.")
ملاحظة تقنية: قالب الوجه (prompt template) حاسم في توجيه سلوك LLM. لاحظ كيف نوجه LLM للإجابة بناءً على السياق فقط، والاعتراف بعدم القدرة على الإجابة إذا كانت المعلومات غير متوفرة.
الخطوة 5: الاستعلام عن نظام RAG
الآن، حان وقت التجربة! اطرح سؤالاً متعلقاً بالمحتوى داخل ملف PDF الخاص بك وشاهد كيف يجيب الذكاء الاصطناعي بدقة.
# الاستعلام عن نظام RAG
question = "ما هو الموضوع الرئيسي لهذا المستند؟" # استبدل بسؤالك
print(f"\nالسؤال: {question}")
response = rag_chain.invoke({"input": question})
print("\nالإجابة:")
print(response["answer"])
# يمكنك أيضاً رؤية المستندات المصدر التي استخدمها LLM (اختياري)
# print("\nالمستندات المصدر:")
# for doc in response["context"]:
# print(doc.page_content[:200] + "...") # عرض أول 200 حرف من كل مستند
# print(f"المصدر: {doc.metadata.get('source', 'غير معروف')}, الصفحة: {doc.metadata.get('page', 'غير معروف')}")
النتيجة النهائية المتوقعة
بعد تشغيل الكود، سيقوم البرنامج بتحميل ملف PDF الخاص بك، ومعالجته، وبناء قاعدة بيانات متجهات منه. عند طرح سؤال، سيتم استخدام هذه القاعدة لاسترجاع الأجزاء الأكثر صلة من مستندك. سيمرر الكود هذه الأجزاء مع سؤالك إلى نموذج اللغة الكبير (LLM)، والذي سيقوم بدوره بتوليد إجابة دقيقة ومستنيرة تستند بشكل مباشر إلى المحتوى الموجود في ملفك. ستظهر الإجابة في نافذة الكونسول.
على سبيل المثال، إذا كان ملفك يتحدث عن تاريخ الذكاء الاصطناعي وطرحت سؤالاً مثل "متى ظهر مصطلح الذكاء الاصطناعي لأول مرة؟"، سيبحث النظام في ملفك، يسترجع الفقرات ذات الصلة، ويقدم لك إجابة مستندة إلى تلك الفقرات.