الفصل 3: نظرة عامة على معمارية RAG
مقدمة
يتكوّن نظام RAG من ثلاث مراحل رئيسية: الفهرسة، والاسترجاع، والتوليد. فهم كل مرحلة ضروري لبناء تطبيقات RAG فعّالة.
المراحل الثلاث
المرحلة 1: الفهرسة (دون اتصال)
تهيّئ مرحلة الفهرسة وثائقك للاسترجاع الفعّال. تحدث هذه المرحلة قبل أي استعلام من المستخدم.
الخطوة 1: تحميل الوثائق
حمّل الوثائق من مصادر متنوعة:
from langchain.document_loaders import (
PyPDFLoader,
TextLoader,
CSVLoader,
WebBaseLoader
)
# Load a PDF
pdf_loader = PyPDFLoader("company_handbook.pdf")
pdf_docs = pdf_loader.load()
# Load from web
web_loader = WebBaseLoader("https://example.com/docs")
web_docs = web_loader.load()
# Combine all documents
all_docs = pdf_docs + web_docs
الخطوة 2: تقسيم النص (Chunking)
تُقسَّم الوثائق إلى chunks أصغر لاسترجاع أفضل:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # Characters per chunk
chunk_overlap=200, # Overlap between chunks
separators=["\n\n", "\n", ".", " "]
)
chunks = splitter.split_documents(all_docs)
# Result: List of smaller text chunks
لماذا نستخدم Chunking؟
- وحدات نص أصغر = استرجاع أدق
- تجنّب استرجاع محتوى غير ذي صلة
- يتناسب مع نوافذ سياق LLM
الخطوة 3: توليد Embeddings
حوّل chunks النص إلى متجهات رقمية:
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# Each chunk becomes a vector
# "The return policy states..." → [0.023, -0.891, 0.156, ...]
ما هي embeddings؟
- تمثيلات متجهية كثيفة للنص
- تلتقط المعنى الدلالي
- النصوص المتشابهة لها متجهات متشابهة
- عادة بين 384 و3072 بُعدًا
الخطوة 4: التخزين المتجهي
خزّن المتجهات في قاعدة بيانات متخصّصة:
from langchain.vectorstores import Chroma
# Create vector store
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# Now we have an indexed, searchable knowledge base
ملخص خط أنابيب الفهرسة
Raw Documents
↓ (Load)
Document Objects
↓ (Split)
Text Chunks
↓ (Embed)
Vectors
↓ (Store)
Vector Database ✓
المرحلة 2: الاسترجاع (متصل)
عندما يطرح المستخدم سؤالًا، نسترجع الـ chunks ذات الصلة من قاعدة المعرفة المفهرسة.
الخطوة 1: Embedding للاستعلام
يُحوَّل استعلام المستخدم إلى فضاء المتجهات نفسه:
user_query = "What is the return policy?"
# Convert to embedding using same model
query_vector = embeddings.embed_query(user_query)
# Result: [0.034, -0.782, 0.234, ...]
الخطوة 2: البحث بالتشابه
ابحث عن chunks ذات متجهات مشابهة للاستعلام:
# Search for top 5 most similar chunks
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 5}
)
relevant_chunks = retriever.get_relevant_documents(user_query)
كيف يعمل التشابه:
Query Vector: [0.034, -0.782, 0.234, ...]
Chunk 1: [0.038, -0.791, 0.228, ...] → 0.98 similarity ✓
Chunk 2: [0.890, 0.123, -0.456, ...] → 0.23 similarity ✗
Chunk 3: [0.041, -0.768, 0.241, ...] → 0.95 similarity ✓
استراتيجيات الاسترجاع
| الاستراتيجية | الوصف | حالة الاستخدام |
|---|---|---|
| Similarity | تشابه متجهي خالص | غرض عام |
| MMR | Maximum Marginal Relevance (تنوّع) | تجنّب التكرار |
| Hybrid | دمج الكلمات المفتاحية والدلالة | استدعاء أفضل |
# MMR - balances relevance and diversity
retriever_mmr = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={
"k": 5,
"fetch_k": 20,
"lambda_mult": 0.7
}
)
المرحلة 3: التوليد (متصل)
اجمع استعلام المستخدم مع السياق المسترجع لتوليد إجابة.
الخطوة 1: بناء الـ Prompt
ابنِ prompt يتضمّن السياق:
def build_prompt(query, retrieved_chunks):
context = "\n\n".join([
chunk.page_content for chunk in retrieved_chunks
])
prompt = f"""You are a helpful assistant. Use the following context
to answer the question. If you don't know the answer based on the
context, say "I don't have enough information."
Context:
{context}
Question: {query}
Answer:"""
return prompt
الخطوة 2: توليد LLM
أرسل الـ prompt إلى نموذج LLM:
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(model="gpt-4", temperature=0)
prompt = build_prompt(user_query, relevant_chunks)
response = llm.invoke(prompt)
print(response.content)
الخطوة 3: تسليم الإجابة
الإجابة النهائية مثبتة في الوثائق المسترجعة:
User: "What is the return policy?"
Retrieved Context:
"Our return policy allows returns within 30 days of purchase.
Items must be unused and in original packaging. Refunds are
processed within 5-7 business days."
Generated Response:
"According to the company policy, you can return items within
30 days of purchase, provided they are unused and in their
original packaging. Refunds typically take 5-7 business days
to process."
شيفرة خط أنابيب RAG الكامل
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
# Phase 1: Indexing
loader = PyPDFLoader("documents/handbook.pdf")
documents = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
chunks = splitter.split_documents(documents)
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(chunks, embeddings)
# Phase 2 & 3: Retrieval + Generation
llm = ChatOpenAI(model="gpt-4")
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever()
)
# Query the system
response = qa_chain.invoke("What is the return policy?")
print(response["result"])
مخطط المعمارية
المقاييس الأساسية
| المرحلة | المقياس | الهدف |
|---|---|---|
| الفهرسة | الإنتاجية | 100+ وثيقة/دقيقة |
| الفهرسة | حجم الـ chunk | 500-1500 حرف |
| الاسترجاع | زمن الاستجابة | <100ms |
| الاسترجاع | Top-K | 3-10 chunks |
| التوليد | زمن الاستجابة | <3s |
| التوليد | ملاءمة السياق | <75% من الحد |
ملخص
| المرحلة | متى | ماذا | الناتج |
|---|---|---|---|
| الفهرسة | دون اتصال (مرة واحدة) | معالجة الوثائق وتخزينها | قاعدة بيانات متجهية |
| الاسترجاع | متصل (لكل استعلام) | إيجاد الـ chunks ذات الصلة | وثائق السياق |
| التوليد | متصل (لكل استعلام) | إنشاء الإجابة | الجواب النهائي |
النقاط الأساسية
- ثلاث مراحل متميزة بتوقيت وأغراض مختلفة
- الفهرسة دون اتصال - تُنفَّذ مرة واحدة أو دوريًا
- الاسترجاع والتوليد متصلان - يحدثان مع كل استعلام
- حجم الـ chunk مهم - يؤثّر في جودة الاسترجاع والتكلفة
- الخط وحدات مستقلة - يمكن تحسين كل مكوّن على حدة
الخطوات التالية
اختبر معرفتك عبر اختبار الوحدة 1!