spinny:~/writing $ vim rag-langchain-deep-dive.md
1~2النماذج اللغوية الكبيرة (LLMs) مثل GPT-4 و Claude قوية بشكل استثنائي، لكنها تعاني من قيد أساسي: معرفتها مجمدة في وقت التدريب. لا يمكنها الوصول إلى مستنداتك الداخلية أو قاعدة بياناتك أو المعلومات في الوقت الفعلي. **التوليد المعزز بالاسترجاع (RAG)** يحل هذه المشكلة تحديداً من خلال الجمع بين القوة التوليدية لنماذج LLM والقدرة على استرجاع المعلومات من مصادر خارجية.3~4## المشكلة: حدود نماذج LLM5~6قبل الحديث عن RAG، من المهم فهم لماذا نحتاجها.7~81. **المعرفة الثابتة**: نموذج LLM يعرف فقط ما رآه أثناء التدريب. إذا سألته عن حدث وقع بعد تاريخ قطعه، لا يمكنه الإجابة.92. **الهلوسات**: عندما لا يعرف نموذج LLM الإجابة، يميل إلى اختلاقها، مولداً معلومات تبدو معقولة لكنها خاطئة تماماً.103. **عدم الوصول للبيانات الخاصة**: نموذج LLM العام لا يمكنه الوصول إلى وثائق شركتك الداخلية أو التذاكر أو قاعدة الكود.11~12يعالج RAG هذه المشاكل الثلاث من خلال تزويد النموذج بـ**سياق ذي صلة** مسترجع من مصادر خارجية في وقت الاستعلام.13~14## ما هو RAG؟15~16التوليد المعزز بالاسترجاع هو بنية تثري الموجه المرسل إلى LLM بمعلومات مسترجعة من قاعدة معرفة خارجية. بدلاً من الاعتماد فقط على المعرفة البارامترية للنموذج، يقوم RAG أولاً بـ**البحث** عن المعلومات ذات الصلة ثم **حقنها** في الموجه، مما يمكّن النموذج من توليد إجابات دقيقة ومؤسسة.17~18```mermaid19graph LR20 User["المستخدم"] -- "سؤال" --> Retriever21 Retriever -- "بحث عن مستندات\nذات صلة" --> VectorStore["مخزن المتجهات"]22 VectorStore -- "مستندات\nذات صلة" --> Retriever23 Retriever -- "سياق + سؤال" --> LLM24 LLM -- "إجابة\nمؤسسة" --> User25```26~27## كيف يعمل RAG بالتفصيل28~29تتكون بنية RAG من مرحلتين رئيسيتين: **الفهرسة** (غير متصلة) و**الاسترجاع + التوليد** (متصلة).30~31### المرحلة 1: الفهرسة (استيعاب المستندات)32~33تُعد مرحلة الفهرسة مستنداتك للبحث الدلالي. تتكون من أربع خطوات.34~35```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```44~45#### 1. تحميل المستندات46~47يمكن أن تأتي المستندات من أي مصدر: ملفات PDF، صفحات ويب، قواعد بيانات، ملفات Markdown، واجهات برمجة التطبيقات. يقوم **محمّل المستندات** بقراءة هذه المستندات وتحويلها إلى نص منظم.48~49#### 2. تقسيم النص (Chunking)50~51نماذج LLM لديها نافذة سياق محدودة، والمستندات يمكن أن تكون طويلة جداً. يقسم **مقسّم النصوص** المستندات إلى أجزاء أصغر تسمى *chunks*. جودة التقسيم حاسمة: الأجزاء الصغيرة جداً تفقد السياق، والأجزاء الكبيرة جداً تخفف الصلة.52~53الاستراتيجيات الأكثر شيوعاً هي:54- **التقسيم التكراري بالأحرف**: يقسم النص بشكل تكراري باستخدام فواصل مثل `\n\n`، `\n`، `. `، مع احترام بنية المستند.55- **التقسيم الدلالي**: يستخدم التضمينات لإيجاد نقاط الانقطاع الطبيعية في النص.56- **تداخل الأجزاء**: يتضمن تداخلاً بين الأجزاء المتتالية للحفاظ على السياق عند الحدود.57~58#### 3. التضمين59~60يتم تحويل كل جزء إلى **متجه رقمي** (تضمين) عبر نموذج تضمين (مثل `text-embedding-3-small` من OpenAI). تلتقط هذه المتجهات المعنى الدلالي للنص: الجمل ذات المعاني المتشابهة سيكون لها متجهات قريبة في الفضاء متعدد الأبعاد.61~62#### 4. مخزن المتجهات63~64تُحفظ المتجهات في **مخزن متجهات** (أو قاعدة بيانات متجهية)، مثل ChromaDB أو Pinecone أو Weaviate أو FAISS. قاعدة البيانات هذه محسّنة لـ**البحث بالتشابه**: بإعطاء استعلام، تجد المتجهات الأكثر تشابهاً (وبالتالي أجزاء النص الأكثر صلة).65~66### المرحلة 2: الاسترجاع + التوليد67~68عندما يطرح المستخدم سؤالاً:69~701. يتم تحويل السؤال إلى تضمين باستخدام نفس نموذج التضمين.712. يجد مخزن المتجهات الأجزاء الأكثر تشابهاً عبر **البحث بالتشابه** (عادةً تشابه جيب التمام أو المسافة الإقليدية).723. يتم إدراج الأجزاء المسترجعة في الموجه كسياق.734. يولد نموذج LLM الإجابة بناءً على السياق المقدم.74~75## بناء أنبوب RAG باستخدام LangChain76~77**LangChain** هو إطار عمل Python (و JavaScript) الأكثر شعبية لبناء تطبيقات تعتمد على LLM. يوفر تجريدات عالية المستوى لكل مكون من مكونات أنبوب RAG.78~79### التثبيت80~81```bash82pip install langchain langchain-openai langchain-community chromadb83```84~85### الخطوة 1: تحميل المستندات86~87يوفر LangChain عشرات محمّلات المستندات لمصادر بيانات مختلفة.88~89```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)96~97# تحميل ملف PDF98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100~101# تحميل صفحة ويب102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104~105# تحميل جميع ملفات .md من مجلد106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108~109all_docs = pdf_docs + web_docs + md_docs110```111~112### الخطوة 2: تقسيم المستندات إلى أجزاء113~114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116~117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122~123chunks = text_splitter.split_documents(all_docs)124print(f"المستندات الأصلية: {len(all_docs)}, الأجزاء: {len(chunks)}")125```126~127معامل `chunk_overlap` حاسم: يُنشئ تداخلاً بين الأجزاء المتتالية حتى لا يُفقد السياق عند الحدود.128~129### الخطوة 3: إنشاء التضمينات ومخزن المتجهات130~131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134~135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136~137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143~144### الخطوة 4: إنشاء المسترجع145~146المسترجع هو المكون الذي، بإعطاء استعلام، يسترجع الأجزاء الأكثر صلة من مخزن المتجهات.147~148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153~154relevant_docs = retriever.invoke("كيف يعمل المصادقة؟")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159~160### الخطوة 5: بناء سلسلة RAG161~162الآن نجمع كل شيء مع LLM وقالب موجه.163~164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169~170llm = ChatOpenAI(model="gpt-4o", temperature=0)171~172prompt = ChatPromptTemplate.from_template("""173أجب على السؤال بناءً على السياق المقدم فقط.174إذا لم يحتوِ السياق على معلومات كافية، قل أنك لا تعرف.175~176السياق:177{context}178~179السؤال: {question}180~181الإجابة:182""")183~184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186~187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193~194response = rag_chain.invoke("كيف تعمل المصادقة في النظام؟")195print(response)196```197~198## تقنيات RAG المتقدمة199~200يعمل الأنبوب الأساسي بشكل جيد، لكن هناك عدة تقنيات لتحسين جودة الإجابات بشكل كبير.201~202### الاسترجاع متعدد الاستعلامات203~204أحياناً يكون استعلام المستخدم غامضاً أو غير متوافق مع اللغة المستخدمة في المستندات. **المسترجع متعدد الاستعلامات** يولد تلقائياً متغيرات من السؤال الأصلي لالتقاط وجهات نظر متعددة.205~206```python207from langchain.retrievers import MultiQueryRetriever208~209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213~214docs = multi_retriever.invoke("ما هي أفضل الممارسات الأمنية؟")215```216~217### الضغط السياقي218~219ليس كل محتوى الجزء ذا صلة بالاستعلام. **مسترجع الضغط السياقي** يستخدم LLM لاستخراج الأجزاء ذات الصلة فقط من كل جزء مسترجع.220~221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224~225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231~232### البحث الهجين233~234البحث الدلالي البحت ليس دائماً الأمثل. **البحث الهجين** يجمع بين البحث الدلالي (التضمينات) والبحث المعجمي (BM25، مطابقة الكلمات المفتاحية) للحصول على نتائج أفضل.235~236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239~240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242~243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244~245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250~251### RAG المحادثي (مع الذاكرة)252~253لبناء روبوت محادثة RAG يتذكر سياق المحادثة، تحتاج إلى إضافة ذاكرة تعيد صياغة أسئلة المستخدم مع مراعاة سجل المحادثة.254~255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258~259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "بالنظر إلى سجل المحادثة وآخر سؤال للمستخدم، "261 "أعد صياغة السؤال بحيث يكون مفهوماً بدون السجل."),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265~266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270~271## أفضل الممارسات272~2731. **اختر حجم الجزء المناسب**: جرب أحجاماً مختلفة (500-1500 رمز). أجزاء أصغر للإجابات الدقيقة، أكبر للسياق الواسع.2742. **استخدم بيانات وصفية للمستندات**: أضف المصدر والتاريخ والفئة كبيانات وصفية للأجزاء. هذا يسمح بتصفية النتائج أثناء الاسترجاع.2753. **قيّم الجودة**: استخدم أطر عمل مثل [RAGAS](https://docs.ragas.io/) لقياس مقاييس مثل *الأمانة*، *الصلة* و*دقة السياق*.2764. **أدر تحديثات المستندات**: نفّذ أنبوب إعادة استيعاب للحفاظ على مخزن المتجهات متزامناً مع مصادر بياناتك.2775. **أضف مُعيد ترتيب**: بعد الاسترجاع الأولي، استخدم نموذج إعادة ترتيب (مثل Cohere Rerank) لإعادة ترتيب النتائج بناءً على الصلة الفعلية.278~279## الخلاصة280~281أصبح RAG البنية المعيارية لبناء تطبيقات الذكاء الاصطناعي التي تحتاج إلى الوصول لمعرفة محددة ومحدثة. يُبسط LangChain التنفيذ بشكل كبير من خلال توفير تجريدات لكل مكون من مكونات الأنبوب.282~283**الخطوات التالية:**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~
NORMAL · rag-langchain-deep-dive.md [readonly]288 lines · :q to close