در عرصه تولید تقویتشده با بازیابی (RAG) و مدیریت دانش سازمانی، PDF همچنان پرکاربردترین قالب سند است. با این حال، برخورد با PDF به عنوان یک ظرف ساده متنی، یکی از رایجترین و پرهزینهترین اشتباهاتی است که توسعهدهندگان مرتکب میشوند. PDF یک سند نیست؛ بلکه یک دستورالعمل رندرینگ است. برای ساخت پایگاههای دانش مستحکم، باید به لایههای زیرین نگاه کنیم و درک کنیم که چگونه باید ساختار معنایی را استخراج کنیم، نه صرفاً کاراکترهای خام.
چرا استخراج متن ساده شکست میخورد
هنگامی که متن را از یک PDF با ابزارهای پایه استخراج میکنید، اغلب با مشکلات «ترتیب خواندن» مواجه میشوید. ستونهایی که قرار است در کنار هم باشند، ممکن است به صورت خطی به هم چسبیده باشند. جداول اغلب به رشتههای غیرقابل خواندن تبدیل میشوند. پاورقیها در وسط جملات ظاهر میشوند و سرصفحهها تکرار شده یا کاملاً از بین میروند. برای پنجره زمینه LLM، این نویزها منجر به perplexity بالا و دقت بازیابی ضعیف میشود.
تجزیه مؤثر نیازمند تمایز قائل شدن بین ارائه (ظاهر سند) و محتوا (معنای آن) است. این فرآیند شامل تحلیل ساختار داخلی PDF، مانند درخت Tagged PDF، یا به کارگیری تکنیکهای بینایی ماشین برای اسناد اسکنشده است.
رویکردهای مدرن: تحلیل چیدمان در مقابل OCR
امروزه دو استراتژی اصلی برای تجزیه PDF وجود دارد:
- استخراج مبتنی بر ساختار: با PDFهای بومی دیجیتال به خوبی کار میکند. ابزارهایی مانند PyMuPDF یا pdfminer.six اشیاء متنی زیرین و مختصات فضایی آنها را استخراج میکنند.
- تجزیه مبتنی بر بینایی: برای اسناد اسکنشده یا چیدمانهای پیچیده ضروری است. مدلهایی مانند Donut یا LayoutLMv3 صفحه PDF را به عنوان یک تصویر در نظر گرفته و مناطق مختلفی مانند سرصفحهها، جداول و متن بدنه را شناسایی میکنند.
برای یک پایپلاین RAG مدرن، اغلب یک رویکرد ترکیبی بهترین گزینه است. از استخراج مبتنی بر ساختار برای متن بومی استفاده کنید و در صورتی که ساختار داخلی موجود نباشد یا آسیب دیده باشد، به مدلهای مبتنی بر بینایی روی بیاورید.
پیادهسازی عملی با پایتون
بیایید یک مثال عملی با استفاده از PyMuPDF (fitz) را بررسی کنیم که برای استخراج محتوای ساختاریافته بسیار کارآمد است. ما متنی را استخراج میکنیم که سلسله مراتب بصری آن حفظ شده باشد، که این امر به استراتژیهای تکهتکه کردن (chunking) بعدی کمک میکند.
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، یا یک مدل ترکیبی، هدف یکسان است: تبدیل دادههای بصری ساختاریافتهنشده به اطلاعات ساختاریافته و معنایی که LLMها بتوانند واقعاً درک کنند.