𝗦𝘁𝗼𝗽 𝗣𝗮𝗿𝘀𝗶𝗻𝗴 𝗣𝗗𝗙𝘀 𝗮𝘁 𝗥𝗲𝗻𝗱𝗲𝗿 𝗧𝗶𝗺𝗲

Most developers build PDF extraction tools the wrong way.

They try to guess document structure from the visual output. They render a page to a canvas and look at pixel positions. They use computer vision to find columns or tables.

This approach is backwards.

A PDF already contains the structure you need in the operator stream.

A table is not just a set of pixels. It is a set of path operators like moveTo, lineTo, and rectangle. Zone boundaries are encoded in the CTM stack. You do not need to reconstruct what is already there.

Stop using visual heuristics. Use the source data.

I previously tried using De Casteljau subdivision for bounding boxes. I rejected it during testing.

De Casteljau is a subdivision algorithm. You split curves until the segments are small enough. This works for rendering, but it is bad for bounding boxes.

You have to choose a tolerance. If the tolerance is too loose, the box is wrong. If it is too tight, you waste resources on recursion. There is a better way. An analytical solution using the quadratic formula is exact. It does not recurse. It does not allocate segments.

The same logic applies to zone detection.

Many tools calculate zone boundaries by finding the midpoint between two text groups. This is a visual guess. It is not structural.

If you use midpoints, sub-pixel rounding will place regions in the wrong zones.

The fix is simple. Use the top edge of the bounding box. A region belongs to a zone based on where it starts. Use the actual Y-coordinate of the top edge.

Building a real PDF extractor is harder. You must:

This is more work than pixel-based guessing. But it produces deterministic results.

A pixel-based tool gives different results at 100% zoom than it does at 150% zoom. It is pattern-matching visual artifacts, not extracting structure.

If you do not parse the operator stream, you are building a demo. It might work on your test files, but it will fail on real user uploads.

The path through the operator stream is difficult. You must understand the fill and stroke state machines and the PDF specification. But you only have to learn it once. Then it works for every PDF.

הפסק לנתח קובצי PDF בזמן הרינדור: ארכיטקטורה טובה יותר לחילוץ מובנה

ניתוח (parsing) של קובצי PDF הוא משימה קשה. לעשות זאת בזמן הרינדור (render-time) הוא קשה עוד יותר.

ברוב יישומי ה-RAG (Retrieval-Augmented Generation) המודרניים, המטרה היא לקחת מסמכים לא מובנים, להפוך אותם לנתונים שניתן לחפש בהם, ולספק תשובות מדויקות למשתמשים. הדרך המקובלת לעשות זאת היא:

  1. קבלת קובץ PDF.
  2. חילוץ טקסט גולמי.
  3. חלוקה למקטעים (chunking).
  4. יצירת embeddings ושמירה בבסיס נתונים וקטורי.

אבל יש בעיה.

הבעיה: אובדן מבנה ואיטיות

כאשר אנחנו מנתחים PDF רק בזמן שהמשתמש מבקש מידע (render-time), אנחנו נתקלים בשתי בעיות עיקריות:

1. אובדן הקשר (Context) ומבנה

קובצי PDF הם לא סתם טקסט; הם מסמכים עם מבנה היררכי. יש להם כותרות, טבלאות, רשימות וערות שוליים. כשמחלצים רק טקסט גולמי, המבנה הזה נעלם. אם יש טבלה עם נתונים קריטיים, ה-LLM עשוי לקבל שורה של טקסט חסרת משמעות במקום את הקשר בין העמודות.

2. זמן תגובה (Latency)

תהליך ה-parsing של PDF הוא כבד ותובעני מבחינה חישובית. אם ננסה לבצע זאת בזמן שהמשתמש מחכה לתשובה, זמן התגובה של האפליקציה יזנק, מה שיהפוך את חוויית המשתמש לגרועה.

הפתרון: ארכיטקטורת "חילוץ מובנה" בזמן הזנה

במקום לנתח את ה-PDF בזמן השאילתה, עלינו לבצע את הניתוח בזמן שהמסמך נכנס למערכת (Ingestion-time).

הנה הארכיטקטורה המוצעת:

  1. שלב ההזנה (Ingestion Phase):

    • קבלת ה-PDF.
    • שימוש בכלי parsing מתקדמים (כמו Unstructured, LlamaParse או מודלים מבוססי Vision) כדי להפוך את ה-PDF לפורמט מובנה כמו Markdown או JSON.
    • שמירת המבנה המלא: כותרות, טבלאות (כ-Markdown tables) וקשרים היררכיים.
  2. שלב האינדוקס (Indexing Phase):

    • חלוקה למקטעים (chunking) המבוססת על המבנה (למשל, לפי כותרות ולא לפי מספר תווים קבוע).
    • יצירת embeddings עבור כל מקטע, תוך שמירה על המטא-דאטה המבני.
    • אחסון בבסיס נתונים וקטורי.
  3. שלב השאילתה (Query Phase):

    • המשתמש שואל שאלה.
    • המערכת מבצעת חיפוש וקטורי ומחזירה מקטעים שהם כבר "עשירים" במבנה.
    • ה-LLM מקבל טקסט בפורמט Markdown ברור, מה שמאפשר לו להבין טבלאות ומבנה מסמך בקלות.

למה זה טוב יותר?

מאפיין ניתוח בזמן רינדור ניתוח בזמן הזנה
מהירות (Latency) איטית מאוד מהירה מאוד
דיוק המבנה נמוך (טקסט גולמי) גבוה (Markdown/JSON)
הבנת טבלאות קשה/לא אפשרית מצוינת
יכולת הרחבה (Scalability) קשה קלה

סיכום

אם אתם בונים יישומי RAG ברמה מקצועית, אל תסתפקו בחילוץ טקסט פשוט. המפתח לדיוק של ה-LLM טמון באיכות הנתונים שהוא מקבל. על ידי מעבר מניתוח בזמן רינדור לניתוח מובנה בזמן הזנה, אתם מבטיחים שהמודל שלכם יבין לא רק מה כתוב במסמך, אלא גם איך המידע מאורגן.