في مجال التوليد المعزز بالاسترجاع (RAG) وإدارة المعرفة المؤسسية، تظل ملفات PDF الأكثر شيوعًا بين تنسيقات المستندات. ومع ذلك، فإن التعامل مع ملف PDF كحاوية نصية بسيطة يُعد من أكثر الأخطاء شيوعًا و تكلفةً التي يرتكبها المطورون. ملف PDF ليس مجرد مستند؛ إنه دليل تعليمات للتصيير (Rendering). لبناء قواعد معرفة متينة، يجب أن ننظر إلى ما هو أعمق، وأن نفهم كيفية استخراج البنية الدلالية بدلاً من مجرد الأحرف الخام.
لماذا يفشل استخراج النص البسيط
عند استخراج النص من ملف PDF باستخدام أدوات أساسية، غالبًا ما تواجه مشاكل في "ترتيب القراءة". قد يتم دمج الأعمدة المقصودة لتكون بجانب بعضها البعض بشكل خطي. وغالبًا ما تُسطّح الجداول إلى سلاسل نصية غير مقروءة. وتظهر الهوامش السفلية في منتصف الجمل، بينما تتكرر رؤوس الأقسام أو تُفقد تمامًا. بالنسبة لنافذة سياق النموذج اللغوي الكبير (LLM)، يؤدي هذا الضجيج إلى ارتفاع في التشتت (Perplexity) وضعف في دقة الاسترجاع.
يتطلب التحليل الفعال التمييز بين العرض (كيف يبدو الأمر) والمحتوى (ماذا يعني). يتضمن ذلك تحليل البنية الداخلية لملف PDF، مثل شجرة Tagged PDF، أو استخدام تقنيات الرؤية الحاسوبية للمستندات الممسوحة ضوئيًا.
النهج الحديثة: تحليل التخطيط مقابل OCR
هناك استراتيجيتان رئيسيتان لتحليل ملفات PDF اليوم:
- الاستخراج القائم على البنية: يعمل بشكل جيد مع ملفات PDF الأصلية رقميًا. تستخرج الأدوات مثل PyMuPDF أو pdfminer.six كائنات النص الأساسية وإحداثياتها المكانية.
- التحليل القائم على الرؤية: ضروري للمستندات الممسوحة ضوئيًا أو التخطيطات المعقدة. تعامل النماذج مثل Donut أو LayoutLMv3 صفحة PDF كصورة، لتحديد المناطق مثل الرؤوس، والجداول، والنص الأساسي.
بالنسبة لخط أنابيب RAG الحديث، غالبًا ما يكون النهج الهجين هو الأفضل. استخدم الاستخراج القائم على البنية للنص الأصلي، وارجع إلى النماذج القائمة على الرؤية عندما تكون البنية الداخلية مفقودة أو تالفة.
التطبيق العملي باستخدام Python
لنلقِ نظرة على مثال عملي باستخدام PyMuPDF (fitz)، والذي يتميز بكفاءة عالية في استخراج المحتوى المنظم. سنقوم باستخراج النص مع الحفاظ على التسلسل الهرمي البصري، مما يساعد استراتيجيات التقطيع اللاحقة.
import fitz # PyMuPDF
def extract_structured_text(pdf_path):
doc = fitz.open(pdf_path)
structured_data = []
for page_num, page in enumerate(doc):
# استخراج كتل النص مع صناديق الحدود
blocks = page.get_text("dict")["blocks"]
for block in blocks:
if "lines" in block:
line_text = ""
# جمع الأسطر من الكتلة
for line in block["lines"]:
for span in line["spans"]:
line_text += span["text"]
# تحديد ما إذا كانت هذه الكتلة رأسًا أو نصًا أساسيًا
# بناءً على حجم الخط (نهج استدلالي)
font_size = blocks[0]["lines"][0]["spans"][0]["size"] if block.get("lines") else 12
is_header = font_size > 14
structured_data.append({
"page": page_num,
"is_header": is_header,
"content": line_text.strip(),
"bbox": block.get("bbox", [0,0,0,0]) # (x0, y0, x1, y1)
})
return structured_data
# مثال على الاستخدام
data = extract_structured_text("corporate_report.pdf")
for item in data[:5]:
print(f"Type: {'Header' if item['is_header'] else 'Body'} - {item['content']}")
التقطيع وإثراء البيانات الوصفية
بمجرد استخراج النص، تكون الخطوة الحرجة التالية هي التقطيع (Chunking). على عكس النص العادي، يستفيد محتوى PDF من التقطيع الدلالي. بدلاً من الاعتماد على عدد أحرف ثابت، فكر في التقطيع حسب الفقرة أو القسم، مع الحفاظ على الرؤوس كبيانات وصفية.
بالنسبة لكل قطعة، قم بحقن بيانات وصفية مثل رقم الصفحة، وعنوان المستند، وما إذا كانت القطعة قد تم تحديدها كعنوان. يسمح ذلك لقاعدة البيانات المتجهة الخاصة بك بتصفية النتائج بشكل أكثر فعالية. على سبيل المثال، يمكن حصر استعلام المستخدم حول "النتائج المالية" في القطعات الموسومة كأقسام "جدول" أو "ملخص"، مما يقلل بشكل كبير من الضجيج.
الخاتمة
لا يزال تحليل ملفات PDF مشكلة غير محلولة، ولكنه تحدي هندسي أساسي لأي شخص يبني تطبيقات مدعومة بالذكاء الاصطناعي. من خلال تجاوز استخراج النص البسيط واعتماد استراتيجيات تحترم بنية المستند، تضع الأساس لقاعدة معرفة عالية الدقة. سواء اخترت تحليل التخطيط، أو OCR، أو نموذجًا هجينًا، يظل الهدف واحدًا: تحويل البيانات المرئية غير المهيكلة إلى معلومات دلالية منظمة يمكن للنماذج اللغوية الكبيرة (LLMs) فهمها حقًا.