استراتيجيات التقطيع المتقدمة
التقطيع الهرمي والسياقي
3 دقيقة للقراءة
استراتيجيات التقطيع المتقدمة التي تحافظ على هيكل المستند وتضيف سياقاً لتحسين دقة الاسترجاع.
مشكلة السياق
القطع القياسية تفقد سياقها المحيط:
المستند: "الفصل 3: أفضل ممارسات الأمان
...
استخدم متغيرات البيئة للأسرار.
لا تضمّن مفاتيح API مباشرة أبداً.
..."
القطعة: "لا تضمّن مفاتيح API مباشرة أبداً."
الاستعلام: "كيف يجب أن أتعامل مع مفاتيح API؟"
المشكلة: القطعة تطابق لكن تفتقر للسياق حول لماذا
ولا تذكر أنها من فصل الأمان
التقطيع الهرمي
إنشاء علاقات الأب-الابن بين القطع:
from langchain.text_splitter import RecursiveCharacterTextSplitter
class HierarchicalChunker:
def __init__(self):
# قطع الأب - أكبر، للسياق
self.parent_splitter = RecursiveCharacterTextSplitter(
chunk_size=2000,
chunk_overlap=200
)
# قطع الابن - أصغر، للاسترجاع
self.child_splitter = RecursiveCharacterTextSplitter(
chunk_size=400,
chunk_overlap=50
)
def chunk(self, document: str) -> list[dict]:
parents = self.parent_splitter.split_text(document)
all_chunks = []
for parent_id, parent in enumerate(parents):
children = self.child_splitter.split_text(parent)
for child_id, child in enumerate(children):
all_chunks.append({
"id": f"p{parent_id}_c{child_id}",
"content": child,
"parent_id": f"p{parent_id}",
"parent_content": parent,
"metadata": {
"parent_id": f"p{parent_id}",
"child_index": child_id
}
})
return all_chunks
استرجاع مستند الأب
استرجع الابن، أرجع الأب للسياق الكامل:
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
# مخزن لمستندات الأب
docstore = InMemoryStore()
# إنشاء المسترجع
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=docstore,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
# إضافة مستندات - يخزن الآباء في docstore، الأبناء في vectorstore
retriever.add_documents(documents)
# البحث - يجد الأبناء ذوي الصلة، يرجع مستندات الأب
results = retriever.get_relevant_documents("التعامل مع مفاتيح API")
# يرجع قطع الأب الكاملة مع السياق الكامل
كيف يعمل:
- فهرسة قطع الابن الصغيرة للاسترجاع الدقيق
- ربط كل ابن بأبيه
- عند الاسترجاع، إرجاع الأب (سياق أكبر)
التقطيع السياقي
إضافة سياق لكل قطعة قبل التضمين:
class ContextualChunker:
"""إضافة السياق المحيط لكل قطعة."""
def __init__(self, llm):
self.llm = llm
self.splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=50
)
def chunk_with_context(self, document: str) -> list[dict]:
chunks = self.splitter.split_text(document)
contextualized = []
# الحصول على ملخص المستند للسياق العام
doc_summary = self._summarize(document[:3000])
for i, chunk in enumerate(chunks):
# توليد السياق لهذه القطعة
context = self._generate_context(
chunk=chunk,
prev_chunk=chunks[i-1] if i > 0 else None,
next_chunk=chunks[i+1] if i < len(chunks)-1 else None,
doc_summary=doc_summary
)
contextualized.append({
"original": chunk,
"contextualized": f"{context}\n\n{chunk}",
"context": context
})
return contextualized
def _generate_context(self, chunk, prev_chunk, next_chunk, doc_summary):
prompt = f"""بالنظر لهذه القطعة من مستند، اكتب سياقاً موجزاً
(1-2 جمل) يضع هذه القطعة ضمن المستند.
ملخص المستند: {doc_summary}
القطعة السابقة: {prev_chunk[:200] if prev_chunk else 'غير متاح'}
القطعة الحالية: {chunk}
القطعة التالية: {next_chunk[:200] if next_chunk else 'غير متاح'}
السياق لهذه القطعة:"""
return self.llm.invoke(prompt).content
مثال على الإخراج:
القطعة الأصلية:
"لا تضمّن مفاتيح API مباشرة في كود المصدر الخاص بك."
القطعة مع السياق:
"هذا القسم يناقش أفضل ممارسات الأمان للتعامل مع
بيانات الاعتماد الحساسة في تطبيقات الإنتاج.
لا تضمّن مفاتيح API مباشرة في كود المصدر الخاص بك."
التسلسل الهرمي للمستند
للمستندات المهيكلة، حافظ على التسلسل الهرمي:
class DocumentHierarchyChunker:
"""الحفاظ على هيكل المستند في القطع."""
def chunk_with_hierarchy(self, document: str) -> list[dict]:
chunks = []
current_hierarchy = []
for line in document.split('\n'):
# اكتشاف العناوين
if line.startswith('# '):
current_hierarchy = [line[2:]]
elif line.startswith('## '):
current_hierarchy = current_hierarchy[:1] + [line[3:]]
elif line.startswith('### '):
current_hierarchy = current_hierarchy[:2] + [line[4:]]
# إضافة التسلسل الهرمي لبيانات القطعة الوصفية
if self._is_content(line):
chunks.append({
"content": line,
"hierarchy": current_hierarchy.copy(),
"breadcrumb": " > ".join(current_hierarchy)
})
return self._merge_chunks(chunks)
def _merge_chunks(self, chunks: list[dict]) -> list[dict]:
"""دمج القطع الصغيرة بنفس التسلسل الهرمي."""
merged = []
current = None
for chunk in chunks:
if current and chunk["breadcrumb"] == current["breadcrumb"]:
current["content"] += "\n" + chunk["content"]
else:
if current:
merged.append(current)
current = chunk.copy()
if current:
merged.append(current)
return merged
الاسترجاع متعدد المتجهات
تخزين تمثيلات متعددة لكل قطعة:
class MultiVectorChunker:
"""إنشاء تضمينات متعددة لكل قطعة."""
def __init__(self, embed_model, llm):
self.embed_model = embed_model
self.llm = llm
def create_multi_vectors(self, chunk: str) -> dict:
# التضمين الأصلي
original_emb = self.embed_model.encode(chunk)
# تضمين الملخص (للاستعلامات المجردة)
summary = self._summarize(chunk)
summary_emb = self.embed_model.encode(summary)
# تضمين الأسئلة (ما الأسئلة التي يجيب عليها هذا؟)
questions = self._generate_questions(chunk)
question_embs = [self.embed_model.encode(q) for q in questions]
return {
"chunk": chunk,
"vectors": {
"original": original_emb,
"summary": summary_emb,
"questions": question_embs
}
}
def _generate_questions(self, chunk: str) -> list[str]:
prompt = f"""ولّد 3 أسئلة يجيب عليها هذا النص:
النص: {chunk}
الأسئلة:"""
response = self.llm.invoke(prompt)
return response.content.strip().split('\n')
الفوائد:
- الأصلي: يطابق محتوى مشابه
- الملخص: يطابق استعلامات مجردة
- الأسئلة: يطابق أسئلة المستخدم مباشرة
المقارنة
| الاستراتيجية | جودة السياق | دقة الاسترجاع | التعقيد |
|---|---|---|---|
| قياسي | منخفضة | متوسطة | منخفض |
| هرمي | عالية | عالية | متوسط |
| سياقي | عالية | عالية | عالي |
| متعدد المتجهات | الأعلى | الأعلى | الأعلى |
نصيحة التنفيذ: ابدأ بالتقطيع الهرمي (الأب-الابن) لمكاسب جودة فورية. أضف التقطيع السياقي عندما يكون لديك فشل استرجاع محدد يفتقر للسياق.
التالي، لنصمم معلمات القطع المثلى لحالة استخدامك المحددة. :::