استراتيجيات تقسيم النص: ثابت أم حسب الجمل أم تكراري
طبّقوا وقارنوا أدوات تقسيم النص إلى أحجام ثابتة، وعند حدود الجمل، وبطريقة تكرارية، وتعرّفوا إلى تأثير حجم الأجزاء والتداخل في جودة الاسترجاع.
استراتيجيات تقسيم النص: ثابت أم حسب الجمل أم تكراري درس مجاني في AI Engineering Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في AI Engineering Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
أهمية جودة التقسيم
التقسيم هو عملية تقسيم المستندات المحمّلة إلى أجزاء أصغر تتسع لها نافذة سياق LLM، ويمكن فهرسة كل جزء منها واسترجاعه بشكل مستقل. وتؤثر طريقة تقسيمك في جودة الاسترجاع أكثر من أي عامل آخر تقريبًا. فإذا قُسّمت الإجابة بين جزأين، فلن يكون أيٌّ منهما كافيًا وحده للإجابة عن السؤال. أما المقطع الذي يخلط بين موضوعين غير مرتبطين، فسيُسترجع عند طرح أسئلة عن كليهما، لكنه لن يكون مفيدًا لأيٍّ منهما.
التقسيم إلى أحجام ثابتة
يقسّم التقسيم إلى أحجام ثابتة النص إلى مقاطع يتكون كل منها بالضبط من N محرفًا أو N رمزًا، بصرف النظر عن حدود الجمل أو الفقرات. وهذه أبسط استراتيجية وأسرعها تنفيذًا. أما عيبها الرئيسي فهو أنها غالبًا ما تقطع الجمل إلى نصفين، فتُنشئ مقاطع تبدأ أو تنتهي في منتصف الفكرة. ويُعد ذلك مقبولًا للنصوص الكثيفة والمنسقة بانتظام، مثل تفريغات تصدير قواعد البيانات، لكنه يؤدي إلى جودة استرجاع ضعيفة في النثر السردي أو الوثائق التقنية.
def fixed_size_chunks(text, chunk_size=500, overlap=50):
'''Split text into fixed-size character chunks with overlap'''
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start += chunk_size - overlap # overlap keeps context at boundaries
return chunks
example = 'This is a long document. ' * 100
chunks = fixed_size_chunks(example, chunk_size=200, overlap=20)
print(f'Produced {len(chunks)} chunks, first: {chunks[0][:80]}...')إضافة التداخل إلى المقاطع الثابتة
يتمثل التحسين الرئيسي للتقسيم إلى أحجام ثابتة في التداخل: إذ يشترك كل مقطع في N محرفًا مع المقطع السابق. ويضمن ذلك ظهور المعلومات القريبة من حد المقطع في كلا المقطعين المتجاورين. ومع تداخل يتراوح بين 50 و100 رمز، ستُلتقط الجملة التي تعبر حدًا ما كاملةً في أحد المقطعين على الأقل. ويعني التداخل الأكبر تغطية أفضل، لكنه يؤدي إلى زيادة حجم الفهرس ووجود محتوى مكرر في نتائج الاسترجاع.
# Overlap example with a sentence at the boundary
text = 'A B C D E F G H I J'
chunk_size = 6 # characters
overlap = 2
i = 0
while i < len(text):
print(repr(text[i:i+chunk_size]))
i += chunk_size - overlap
# Output shows overlapping content:
# 'A B C D'
# 'C D E F'
# 'E F G H'
# Each adjacent pair shares 2 charsالتقسيم عند حدود الجمل
يستخدم التقسيم عند حدود الجمل مكتبات NLP مثل nltk أو spacy لاكتشاف نهايات الجمل قبل التقسيم. وهذا يضمن عدم قطع أي جملة إلى نصفين. تجمع الجمل إلى أن تؤدي إضافة الجملة التالية إلى تجاوز الحجم المستهدف، ثم تبدأ مقطعًا جديدًا. والنتيجة مقاطع تتضمن دائمًا أفكارًا مكتملة، ما ينتج جودة تضمين أفضل بكثير من التقسيم عند عدد ثابت من المحارف.
import nltk
nltk.download('punkt', quiet=True)
def sentence_chunks(text, max_tokens=300):
sentences = nltk.sent_tokenize(text)
chunks = []
current = []
current_len = 0
for sent in sentences:
sent_tokens = len(sent.split())
if current_len + sent_tokens > max_tokens and current:
chunks.append(' '.join(current))
current = []
current_len = 0
current.append(sent)
current_len += sent_tokens
if current:
chunks.append(' '.join(current))
return chunksالتقسيم التكراري للنص حسب المحارف
يُعد التقسيم التكراري حسب المحارف الاستراتيجية الأكثر استخدامًا في أنظمة RAG الإنتاجية. إذ يجرّب تسلسلًا هرميًا من الفواصل بالترتيب: فواصل الفقرات أولًا (\n\n)، ثم فواصل الأسطر (\n)، ثم النقاط التي تنهي الجمل، ثم المسافات، وأخيرًا المحارف الفردية. ويقسّم النص عند أكبر حد ذي معنى يحافظ على بقاء المقطع دون الحجم المستهدف، ما ينتج مقاطع تحترم بنية المستند قدر الإمكان.
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # target size in characters
chunk_overlap=200, # overlap between consecutive chunks
separators=['\n\n', '\n', '. ', '! ', '? ', ' ', ''],
length_function=len
)
with open('document.txt') as f:
text = f.read()
chunks = splitter.split_text(text)
print(f'Split into {len(chunks)} chunks')
for i, chunk in enumerate(chunks[:3]):
print(f'Chunk {i}: {len(chunk)} chars — {chunk[:60]}...')التقسيم المراعي للرموز
تُعد أعداد المحارف تقديرًا غير دقيق لأعداد الرموز. فقد يتكون مقطع من 1000 محرف من 200 رمز أو 400 رمز، بحسب طول الكلمات واللغة. وللتحكم الدقيق، قسّم النص حسب عدد الرموز باستخدام tiktoken. وهذا مهم لملاءمة المقاطع مع نافذة سياق النموذج ولتقدير التكلفة بدقة. وتغلّف TokenTextSplitter في LangChain مكتبة tiktoken لتنفيذ التقسيم المراعي للرموز.
from langchain_text_splitters import TokenTextSplitter
import tiktoken
# Split by actual token count, not characters
splitter = TokenTextSplitter(
encoding_name='cl100k_base', # GPT-4 tokenizer
chunk_size=256, # target tokens per chunk
chunk_overlap=32 # overlap in tokens
)
chunks = splitter.split_text(text)
# Verify token count
enc = tiktoken.get_encoding('cl100k_base')
for chunk in chunks[:3]:
tokens = len(enc.encode(chunk))
print(f'Chunk: {tokens} tokens')اختيار حجم المقطع المناسب
يُعد حجم المقطع معلمة فائقة مهمة. فالمقاطع الصغيرة (100–200 رمز) دقيقة، إذ تتضمن معلومات مركّزة للغاية تُضمَّن جيدًا. لكنها تفقد السياق المحيط، ولذلك قد لا يمتلك LLM معلومات كافية للإجابة. أما المقاطع الكبيرة (500–1000 رمز) فتوفّر سياقًا أكبر، لكن تضميناتها تمثّل متوسط موضوع أوسع، ما يصعّب استرجاعها عند الاستعلامات المحددة. وتستخدم معظم أنظمة الإنتاج 256–512 رمزًا، مع إجراء اختبارات تجريبية على بياناتها.
إضافة السياق إلى المقاطع
من التحسينات الفعّالة رؤوس المقاطع السياقية: أضف عنوان المستند وعنوان القسم إلى بداية نص كل مقطع قبل تضمينه. وبذلك يلتقط التضمين ليس محتوى المقطع فحسب، بل أيضًا موضعه في المستند. فعلى سبيل المثال، سيُسترجع مقطع يقرأ المزايا وسياسة الإجازات: يستحق الموظفون 15 يومًا سنويًا... بدقة أكبر بكثير عند طرح أسئلة عن سياسة الإجازات، مقارنة بالنص نفسه من دون الرأس.
def create_contextual_chunks(doc, splitter):
title = doc['metadata'].get('title', '')
section = doc['metadata'].get('section', '')
text = doc['text']
raw_chunks = splitter.split_text(text)
contextual_chunks = []
for chunk in raw_chunks:
# Prepend document context to each chunk
context_header = f'{title}\n{section}\n\n' if title else ''
contextual_chunks.append({
'text': context_header + chunk,
'metadata': doc['metadata']
})
return contextual_chunksمقارنة الاستراتيجيات على بياناتك
لا توجد استراتيجية تقسيم واحدة هي الأفضل دائمًا. أنشئ تقييمًا سريعًا: خذ 20 سؤالًا تعرف إجاباتها، ونفّذ الاسترجاع باستخدام كل استراتيجية تقسيم، ثم قِس عدد المرات التي يظهر فيها المقطع الصحيح ضمن النتائج الخمس الأولى (معدل الإصابة@5). يستغرق إعداد ذلك ساعة واحدة، ويوفّر عليك أسابيع من التخمين. وغالبًا ستجد أن تنسيق مستنداتك المحدد (النثر القانوني الكثيف مقابل المستندات التقنية المنظّمة) يفضّل استراتيجية معينة بوضوح على غيرها.
def evaluate_chunking_strategy(questions_and_answers, retriever):
hits = 0
for qa in questions_and_answers:
results = retriever.retrieve(qa['question'], top_k=5)
result_texts = [r['text'] for r in results]
# Check if answer text appears in any retrieved chunk
if any(qa['answer'] in text for text in result_texts):
hits += 1
hit_rate = hits / len(questions_and_answers)
print(f'Hit rate@5: {hit_rate:.1%} ({hits}/{len(questions_and_answers)})')
return hit_rateالتعامل مع أنواع المستندات الخاصة
تحتاج بعض أنواع المستندات إلى تقسيم مخصص. يجب تقسيم ملفات التعليمات البرمجية عند حدود الدوال أو الفئات، لا حسب عدد المحارف. ويجب تقسيم ملفات Markdown عند حدود العناوين، بحيث يتوافق كل مقطع مع قسم واحد. أما الجداول فينبغي إبقاؤها كاملةً في مقطع واحد (فالتقسيم داخل الجدول يجعل المحتوى غير قابل للفهم). خطّط لاستراتيجية التقسيم بناءً على أنواع المستندات في مجموعة مستنداتك، بدلًا من تطبيق مقسّم واحد يناسب الجميع.
from langchain_text_splitters import MarkdownHeaderTextSplitter
# Split Markdown by header hierarchy
md_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
('#', 'h1'),
('##', 'h2'),
('###', 'h3')
]
)
with open('documentation.md') as f:
md_text = f.read()
# Each chunk gets metadata from its heading hierarchy
chunks = md_splitter.split_text(md_text)
for chunk in chunks[:3]:
print('Section:', chunk.metadata)
print('Text:', chunk.page_content[:80])
print()تتبّع تسلسل المقطع
يحتاج كل مقطع إلى معرّف ثابت وفريد مشتق من المستند المصدر وموضع المقطع. استخدم هذا المعرّف لتحديث مقاطع محددة عند تغيّر المستندات، من دون إعادة فهرسة مجموعة المستندات بأكملها. وتعمل دالة تجزئة حتمية لمسار المصدر مضافًا إليه فهرس المقطع بشكل جيد. وسجّل أيضًا موضع المقطع (المقطع 3 من أصل 12 في المستند X) في البيانات الوصفية، إذ يساعد ذلك على إعادة تجميع الأقسام الكاملة عند استرجاع عدة مقاطع متجاورة معًا.
import hashlib
def assign_chunk_ids(chunks, source_path):
for i, chunk in enumerate(chunks):
key = f'{source_path}::chunk_{i}'
chunk_id = hashlib.sha256(key.encode()).hexdigest()[:16]
chunk['id'] = chunk_id
chunk['metadata']['chunk_index'] = i
chunk['metadata']['total_chunks'] = len(chunks)
return chunks
# IDs are stable across re-runs if source and position match
chunks = sentence_chunks(text)
chunks = [{'text': c, 'metadata': {}} for c in chunks]
assign_chunk_ids(chunks, 'docs/policy_v3.pdf')تحقق سريع
اختبر مدى فهمك لمفاهيم هندسة الذكاء الاصطناعي التي تناولها هذا الدرس.
مراجعة الدرس
تعلّمت في هذا الدرس أن التقسيم إلى أحجام ثابتة بسيط لكنه يقطع الجمل إلى نصفين، وأن التقسيم عند حدود الجمل يحافظ على الأفكار المكتملة، وأن التقسيم التكراري حسب المحارف يحترم بنية المستند وهو الخيار الأكثر شيوعًا في بيئات الإنتاج، إضافة إلى تقنيات متقدمة تشمل التقسيم المراعي للرموز، والرؤوس السياقية، والمقسّمات المخصصة لـ Markdown والتعليمات البرمجية، ومعرّفات المقاطع الثابتة للتحديثات التدريجية. سنتناول بعد ذلك تضمين هذه المقاطع وتخزينها في قاعدة بيانات متجهية.
تعلم Python مع معلم ذكاء اصطناعي — مجانًا
اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.
- الدورات
- 30
- الدروس
- 120
الأسئلة الشائعة
هل درس «استراتيجيات تقسيم النص: ثابت أم حسب الجمل أم تكراري» مجاني؟
نعم — نص درس «استراتيجيات تقسيم النص: ثابت أم حسب الجمل أم تكراري» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة AI Engineering Academy، انتقل إلى CoddyKit PRO. تتضمن دورة AI Engineering Academy 4 دروس في المجموع.
ماذا ستتعلم في «استراتيجيات تقسيم النص: ثابت أم حسب الجمل أم تكراري»؟
طبّقوا وقارنوا أدوات تقسيم النص إلى أحجام ثابتة، وعند حدود الجمل، وبطريقة تكرارية، وتعرّفوا إلى تأثير حجم الأجزاء والتداخل في جودة الاسترجاع. تتمرن على AI Engineering Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ AI Engineering Academy؟
لا تُشترط خبرة سابقة. AI Engineering Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «استراتيجيات تقسيم النص: ثابت أم حسب الجمل أم تكراري»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس AI Engineering Academy هذا؟
نعم. كل درس في AI Engineering Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تحميل المستندات واستخراج النصوص
- استراتيجيات تقسيم النص: ثابت أم حسب الجمل أم تكراري
- الفهرسة: تحويل الأجزاء إلى Embeddings وتخزينها
- الاستعلام والاسترجاع والتوليد