spinny:~/writing $ less rag-langchain-deep-dive.md
12النماذج اللغوية الكبيرة (LLMs) مثل GPT-4 و Claude قوية بشكل استثنائي، لكنها تعاني من قيد أساسي: معرفتها مجمدة في وقت التدريب. لا يمكنها الوصول إلى مستنداتك الداخلية أو قاعدة بياناتك أو المعلومات في الوقت الفعلي. **التوليد المعزز بالاسترجاع (RAG)** يحل هذه المشكلة تحديداً من خلال الجمع بين القوة التوليدية لنماذج LLM والقدرة على استرجاع المعلومات من مصادر خارجية.34## المشكلة: حدود نماذج LLM56قبل الحديث عن RAG، من المهم فهم لماذا نحتاجها.781. **المعرفة الثابتة**: نموذج LLM يعرف فقط ما رآه أثناء التدريب. إذا سألته عن حدث وقع بعد تاريخ قطعه، لا يمكنه الإجابة.92. **الهلوسات**: عندما لا يعرف نموذج LLM الإجابة، يميل إلى اختلاقها، مولداً معلومات تبدو معقولة لكنها خاطئة تماماً.103. **عدم الوصول للبيانات الخاصة**: نموذج LLM العام لا يمكنه الوصول إلى وثائق شركتك الداخلية أو التذاكر أو قاعدة الكود.1112يعالج RAG هذه المشاكل الثلاث من خلال تزويد النموذج بـ**سياق ذي صلة** مسترجع من مصادر خارجية في وقت الاستعلام.1314## ما هو RAG؟1516التوليد المعزز بالاسترجاع هو بنية تثري الموجه المرسل إلى LLM بمعلومات مسترجعة من قاعدة معرفة خارجية. بدلاً من الاعتماد فقط على المعرفة البارامترية للنموذج، يقوم RAG أولاً بـ**البحث** عن المعلومات ذات الصلة ثم **حقنها** في الموجه، مما يمكّن النموذج من توليد إجابات دقيقة ومؤسسة.1718```mermaid19graph LR20 User["المستخدم"] -- "سؤال" --> Retriever21 Retriever -- "بحث عن مستندات\nذات صلة" --> VectorStore["مخزن المتجهات"]22 VectorStore -- "مستندات\nذات صلة" --> Retriever23 Retriever -- "سياق + سؤال" --> LLM24 LLM -- "إجابة\nمؤسسة" --> User25```2627## كيف يعمل RAG بالتفصيل2829تتكون بنية RAG من مرحلتين رئيسيتين: **الفهرسة** (غير متصلة) و**الاسترجاع + التوليد** (متصلة).3031### المرحلة 1: الفهرسة (استيعاب المستندات)3233تُعد مرحلة الفهرسة مستنداتك للبحث الدلالي. تتكون من أربع خطوات.3435```mermaid36graph TD37 A["المستندات\n(PDF, HTML, MD, DB)"] --> B["محمّل المستندات"]38 B --> C["مقسّم النصوص"]39 C --> D["أجزاء نصية"]40 D --> E["نموذج التضمين"]41 E --> F["متجهات رقمية"]42 F --> G["مخزن المتجهات\n(ChromaDB, Pinecone, FAISS)"]43```4445#### 1. تحميل المستندات4647يمكن أن تأتي المستندات من أي مصدر: ملفات PDF، صفحات ويب، قواعد بيانات، ملفات Markdown، واجهات برمجة التطبيقات. يقوم **محمّل المستندات** بقراءة هذه المستندات وتحويلها إلى نص منظم.4849#### 2. تقسيم النص (Chunking)5051نماذج LLM لديها نافذة سياق محدودة، والمستندات يمكن أن تكون طويلة جداً. يقسم **مقسّم النصوص** المستندات إلى أجزاء أصغر تسمى *chunks*. جودة التقسيم حاسمة: الأجزاء الصغيرة جداً تفقد السياق، والأجزاء الكبيرة جداً تخفف الصلة.5253الاستراتيجيات الأكثر شيوعاً هي:54- **التقسيم التكراري بالأحرف**: يقسم النص بشكل تكراري باستخدام فواصل مثل `\n\n`، `\n`، `. `، مع احترام بنية المستند.55- **التقسيم الدلالي**: يستخدم التضمينات لإيجاد نقاط الانقطاع الطبيعية في النص.56- **تداخل الأجزاء**: يتضمن تداخلاً بين الأجزاء المتتالية للحفاظ على السياق عند الحدود.5758#### 3. التضمين5960يتم تحويل كل جزء إلى **متجه رقمي** (تضمين) عبر نموذج تضمين (مثل `text-embedding-3-small` من OpenAI). تلتقط هذه المتجهات المعنى الدلالي للنص: الجمل ذات المعاني المتشابهة سيكون لها متجهات قريبة في الفضاء متعدد الأبعاد.6162#### 4. مخزن المتجهات6364تُحفظ المتجهات في **مخزن متجهات** (أو قاعدة بيانات متجهية)، مثل ChromaDB أو Pinecone أو Weaviate أو FAISS. قاعدة البيانات هذه محسّنة لـ**البحث بالتشابه**: بإعطاء استعلام، تجد المتجهات الأكثر تشابهاً (وبالتالي أجزاء النص الأكثر صلة).6566### المرحلة 2: الاسترجاع + التوليد6768عندما يطرح المستخدم سؤالاً:69701. يتم تحويل السؤال إلى تضمين باستخدام نفس نموذج التضمين.712. يجد مخزن المتجهات الأجزاء الأكثر تشابهاً عبر **البحث بالتشابه** (عادةً تشابه جيب التمام أو المسافة الإقليدية).723. يتم إدراج الأجزاء المسترجعة في الموجه كسياق.734. يولد نموذج LLM الإجابة بناءً على السياق المقدم.7475## بناء أنبوب RAG باستخدام LangChain7677**LangChain** هو إطار عمل Python (و JavaScript) الأكثر شعبية لبناء تطبيقات تعتمد على LLM. يوفر تجريدات عالية المستوى لكل مكون من مكونات أنبوب RAG.7879### التثبيت8081```bash82pip install langchain langchain-openai langchain-community chromadb83```8485### الخطوة 1: تحميل المستندات8687يوفر LangChain عشرات محمّلات المستندات لمصادر بيانات مختلفة.8889```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)9697# تحميل ملف PDF98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100101# تحميل صفحة ويب102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104105# تحميل جميع ملفات .md من مجلد106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108109all_docs = pdf_docs + web_docs + md_docs110```111112### الخطوة 2: تقسيم المستندات إلى أجزاء113114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122123chunks = text_splitter.split_documents(all_docs)124print(f"المستندات الأصلية: {len(all_docs)}, الأجزاء: {len(chunks)}")125```126127معامل `chunk_overlap` حاسم: يُنشئ تداخلاً بين الأجزاء المتتالية حتى لا يُفقد السياق عند الحدود.128129### الخطوة 3: إنشاء التضمينات ومخزن المتجهات130131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143144### الخطوة 4: إنشاء المسترجع145146المسترجع هو المكون الذي، بإعطاء استعلام، يسترجع الأجزاء الأكثر صلة من مخزن المتجهات.147148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153154relevant_docs = retriever.invoke("كيف يعمل المصادقة؟")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159160### الخطوة 5: بناء سلسلة RAG161162الآن نجمع كل شيء مع LLM وقالب موجه.163164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169170llm = ChatOpenAI(model="gpt-4o", temperature=0)171172prompt = ChatPromptTemplate.from_template("""173أجب على السؤال بناءً على السياق المقدم فقط.174إذا لم يحتوِ السياق على معلومات كافية، قل أنك لا تعرف.175176السياق:177{context}178179السؤال: {question}180181الإجابة:182""")183184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193194response = rag_chain.invoke("كيف تعمل المصادقة في النظام؟")195print(response)196```197198## تقنيات RAG المتقدمة199200يعمل الأنبوب الأساسي بشكل جيد، لكن هناك عدة تقنيات لتحسين جودة الإجابات بشكل كبير.201202### الاسترجاع متعدد الاستعلامات203204أحياناً يكون استعلام المستخدم غامضاً أو غير متوافق مع اللغة المستخدمة في المستندات. **المسترجع متعدد الاستعلامات** يولد تلقائياً متغيرات من السؤال الأصلي لالتقاط وجهات نظر متعددة.205206```python207from langchain.retrievers import MultiQueryRetriever208209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213214docs = multi_retriever.invoke("ما هي أفضل الممارسات الأمنية؟")215```216217### الضغط السياقي218219ليس كل محتوى الجزء ذا صلة بالاستعلام. **مسترجع الضغط السياقي** يستخدم LLM لاستخراج الأجزاء ذات الصلة فقط من كل جزء مسترجع.220221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231232### البحث الهجين233234البحث الدلالي البحت ليس دائماً الأمثل. **البحث الهجين** يجمع بين البحث الدلالي (التضمينات) والبحث المعجمي (BM25، مطابقة الكلمات المفتاحية) للحصول على نتائج أفضل.235236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250251### RAG المحادثي (مع الذاكرة)252253لبناء روبوت محادثة RAG يتذكر سياق المحادثة، تحتاج إلى إضافة ذاكرة تعيد صياغة أسئلة المستخدم مع مراعاة سجل المحادثة.254255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "بالنظر إلى سجل المحادثة وآخر سؤال للمستخدم، "261 "أعد صياغة السؤال بحيث يكون مفهوماً بدون السجل."),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270271## أفضل الممارسات2722731. **اختر حجم الجزء المناسب**: جرب أحجاماً مختلفة (500-1500 رمز). أجزاء أصغر للإجابات الدقيقة، أكبر للسياق الواسع.2742. **استخدم بيانات وصفية للمستندات**: أضف المصدر والتاريخ والفئة كبيانات وصفية للأجزاء. هذا يسمح بتصفية النتائج أثناء الاسترجاع.2753. **قيّم الجودة**: استخدم أطر عمل مثل [RAGAS](https://docs.ragas.io/) لقياس مقاييس مثل *الأمانة*، *الصلة* و*دقة السياق*.2764. **أدر تحديثات المستندات**: نفّذ أنبوب إعادة استيعاب للحفاظ على مخزن المتجهات متزامناً مع مصادر بياناتك.2775. **أضف مُعيد ترتيب**: بعد الاسترجاع الأولي، استخدم نموذج إعادة ترتيب (مثل Cohere Rerank) لإعادة ترتيب النتائج بناءً على الصلة الفعلية.278279## الخلاصة280281أصبح RAG البنية المعيارية لبناء تطبيقات الذكاء الاصطناعي التي تحتاج إلى الوصول لمعرفة محددة ومحدثة. يُبسط LangChain التنفيذ بشكل كبير من خلال توفير تجريدات لكل مكون من مكونات الأنبوب.282283**الخطوات التالية:**284- **جرب محلياً**: ابدأ مع ChromaDB وبضعة مستندات للتعرف على الأنبوب.285- **استكشف LangSmith**: استخدم [LangSmith](https://smith.langchain.com/) لمراقبة وتصحيح سلاسلك في الإنتاج.286- **جرب نماذج تضمين مختلفة**: قارن نماذج مثل `text-embedding-3-small` و `text-embedding-3-large` ونماذج مفتوحة المصدر من Sentence Transformers.287- **راجع التوثيق**: [توثيق LangChain](https://python.langchain.com/docs/) مورد ممتاز ومحدث باستمرار.288
:RAG و LangChain: دليل شامل للتوليد المعزز بالاسترجاعlines 1-288 (END) — press q to close