התקנה ושימוש ב-MarkItDown Image: AI generated

די בפקודה pip install markitdown כדי להפוך קובצי PDF, מסמכי Word, גיליונות Excel ומצגות PowerPoint ל-markdown. אבל אם מזינים לו קובץ PDF שאינו אלא תמונות סרוקות — חוזה סרוק, למשל — מתקבל מסמך ריק. המאמר הזה נע בשני צירים: למה וכיצד להשתמש ב-MarkItDown, וכיצד מוסיפים OCR בדיוק בנקודה שבה האמון הזה קורס — קובצי PDF שמכילים רק תמונות.


למה כדאי להשתמש ב-MarkItDown — תנאי ה-agent-operable

MarkItDown היא ספריית CLI/Python בקוד פתוח של מיקרוסופט הממירה קבצי PDF, Word, Excel, PowerPoint, HTML, תמונה ואודיו ל-markdown.

נתחיל במושג agent-operable (מצב שסוכן יכול לעבוד איתו). כדי שסוכן יוכל לטפל במסמך באופן אוטונומי, המסמך הזה חייב להיות במצב שמכונה יכולה לנתח מבנית — לא רק במצב שאדם מוצא קריא. זה לא תנאי החל רק על קוד. כלל עסקי הקבור בתוך PDF או בגיליון Excel עשוי בעצם שלא להתקיים מבחינת סוכן, שכן אדם חייב לפתוח אותו כדי לדעת מה כתוב בו. זהו הראשון משלושה תנאים — קריא, ניתן לאימות, מתמיד — המפורטים ב-Building Agent-Operable Systems: “קריא ללא רעש”.

אז למה דווקא MarkItDown? אפשר לבנות בעצמך את אותה המרה על ידי הרכבת מנתחים (parsers) של צד שלישי (PyPDF2, python-docx). ההבדל הוא מי בנה את זה. docx, xlsx ו-pptx הן כולן OOXML — מפרט שמיקרוסופט עצמה הבעלים שלו. כאשר החברה שבבעלותה המפרט בונה את הממיר לפורמט שלה עצמה, מקרי קצה (תאים ממוזגים, טבלאות מקוננות, הערות שוליים) נוטים מבחינה מבנית לשרוד טוב יותר מאשר עם מנתח של צד שלישי. אם ההמרה עצמה אינה אמינה, כל מה שנבנה מעליה לכיוון מצב agent-operable מאבד משמעות. זו הסיבה להשתמש ב-MarkItDown — עבור הפורמטים של מיקרוסופט עצמה, האמון הזה מובנה כברירת מחדל.

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


התקנה

התקנה בסיסית

pip install markitdown

ההתקנה הבסיסית לבדה מטפלת רק בפורמטים קלים כמו טקסט רגיל ו-HTML. כדי להמיר מסמכי PDF ו-Office, יש להתקין גם את התוספים (extras) הספציפיים לכל פורמט.

תוספים ספציפיים לכל פורמט

pip install 'markitdown[pdf]'    # PDF (pdfminer.six + pdfplumber)
pip install 'markitdown[docx]'   # Word
pip install 'markitdown[pptx]'   # PowerPoint
pip install 'markitdown[xlsx]'   # Excel
pip install 'markitdown[all]'    # everything above, plus audio transcription, YouTube captions, Azure Document Intelligence, etc.

אם המאמר הזה זקוק רק ל-PDF, markitdown[pdf] מספיק.


שימוש בסיסי

CLI

markitdown example.pdf                  # print markdown to stdout
markitdown example.pdf -o example.md    # save to a file
cat example.pdf | markitdown            # read from stdin (pipe)

ממשק Python

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("example.pdf")
print(result.markdown)

שתי הדרכים בוחרות אוטומטית את הממיר לפי סיומת הקובץ או סוג ה-MIME. כאשר אין מידע על הסיומת — בקריאה מ-stdin, למשל — אפשר לתת רמז באמצעות -x (--extension) או -m (--mime-type).


מה שהמרת PDF עושה בפועל — היא קוראת רק את שכבת הטקסט

ממיר ה-PDF של MarkItDown (packages/markitdown/src/markitdown/converters/_pdf_converter.py) משתמש תחילה ב-pdfplumber כדי לזהות מבנה טבלה/טופס בכל עמוד, וחוזר ל-page.extract_text() כאשר עמוד אינו טבלה. אם pdfplumber נכשל, הוא חוזר ל-pdfminer.six.

שתי הספריות הן כלים הקוראים שכבת טקסט שכבר קיימת בתוך ה-PDF. אם עמוד מורכב רק מתמונה סרוקה ללא שכבת טקסט, extract_text() מחזירה מחרוזת ריקה, והפלט של MarkItDown מתברר כריק למעשה. אין כלל נתיב קוד בחבילה הבסיסית שמעבד תמונה או מזהה תווים בתוכה.

במילים אחרות, אלו שתי בעיות שונות:

  • PDF עם שכבת טקסט (מיוצא מ-Word, רוב החוזים האלקטרוניים) → ההתקנה הבסיסית עובדת היטב. אותו אמון שנושא את הפורמטים של מיקרוסופט עצמה עובר גם לכאן.
  • PDF ללא שכבת טקסט (מסמך שעבר דרך סורק, תמונה המוטבעת כתמונה) → ההתקנה הבסיסית מחזירה תוצאה ריקה. כאן נשבר תנאי ה"קריא" של agent-operable.

הגדרת OCR — היכן שמתקנים את האמון השבור

מכאן מתחיל הציר השני של המאמר. האמון ש-MarkItDown מבטיחה עבור הפורמטים של מיקרוסופט עצמה חייב להיבנות מחדש בנפרד עבור קובצי PDF שמכילים רק תמונות.

1. התקנת התוסף

תמיכת ה-OCR נמצאת בחבילה נפרדת, markitdown-ocr. היא נמצאת באותו מאגר (microsoft/markitdown), אך אינה כלולה ב-pip install markitdown.

pip install markitdown-ocr
pip install openai   # an OpenAI-compatible client — this plugin has no OCR model of its own

לאחר ההתקנה, יש לוודא שהתוסף מזוהה.

markitdown --list-plugins
#   * ocr             (package: markitdown_ocr)

2. מה שאינו ניתן למשא ומתן — לקוח LLM Vision

markitdown-ocr אינה כוללת מנוע OCR מסורתי כמו Tesseract או PaddleOCR. כל מה שהיא בעצם עושה הוא לקודד את התמונה ל-base64 ולבקש מ-API של chat.completions תואם OpenAI (למשל gpt-4o) “לחלץ את הטקסט מהתמונה הזאת” (LLMVisionOCRService ב-_ocr_service.py). לכן, מפתח API המסוגל לקרוא למודל ראייה הוא דרישה שאינה ניתנת למשא ומתן כדי ש-OCR יעבוד בכלל.

from markitdown import MarkItDown
from openai import OpenAI

md = MarkItDown(
    enable_plugins=True,
    llm_client=OpenAI(),      # reads OPENAI_API_KEY from the environment
    llm_model="gpt-4o",
)

result = md.convert("scanned_contract.pdf")
print(result.markdown)

כל לקוח תואם OpenAI — כולל Azure OpenAI — אפשר להעביר באותה הדרך.

from openai import AzureOpenAI

md = MarkItDown(
    enable_plugins=True,
    llm_client=AzureOpenAI(
        api_key="...",
        azure_endpoint="https://<resource>.openai.azure.com/",
        api_version="2024-02-01",
    ),
    llm_model="gpt-4o",
)

3. זה לא עובד מה-CLI — מאומת מול הקוד בפועל

קובץ ה-README של markitdown-ocr מציג את הפקודה הבאה כדוגמת שימוש:

markitdown document.pdf --use-plugins --llm-client openai --llm-model gpt-4o

אך בבדיקה ישירה של הגדרות ארגומנטי ה-CLI של הליבה של MarkItDown (packages/markitdown/src/markitdown/__main__.py), מתברר כי דגלים כאלה אינם קיימים — אין --llm-client ואין --llm-model. ה-CLI תומך ב--o (קובץ פלט), -x (רמז סיומת), -m (רמז MIME), -c (קידוד תווים), -d/--use-docintel, --use-cu, -p/--use-plugins, --list-plugins ו---keep-data-uris, וזה הכול — הבנאי של MarkItDown מקבל רק enable_plugins=args.use_plugins. במילים אחרות, דוגמת ה-CLI ב-README היא שגיאת תיעוד; היא בפועל אינה פועלת.

OCR עובד רק דרך ממשק Python — יש להעביר llm_client/llm_model ישירות. אין דרך למסור מפתח API ל-CLI.

4. איך זה עובד בפועל — OCR לתמונות משובצות וגיבוי OCR לעמוד מלא

PdfConverterWithOCR (_pdf_converter_with_ocr.py) פועל בשני שלבים.

  1. תמונות משובצות בעדיפות ראשונה: כאשר עמוד מערבב טקסט עם תמונות (נניח, חתימה סרוקה שהוטבעה באמצע גוף הטקסט), רק התמונה נחתכת, נשלחת ל-LLM Vision, והתוצאה משולבת בחזרה עם הטקסט שמסביב לפי מיקומה המקורי על ציר Y, תוך שמירה על סדר הקריאה.
  2. גיבוי OCR לעמוד מלא: אם חילוץ הטקסט חוזר ריק לגמרי (PDF סרוק לחלוטין), העמוד כולו מעובד כתמונת PNG ברזולוציית 300dpi ונשלח ל-LLM Vision עמוד אחר עמוד (_ocr_full_pages).

הטקסט המחולץ תמיד עטוף בפורמט הבא:

*[Image OCR]
<extracted text>
[End OCR]*

5. שיפור הדיוק באמצעות פרומפט מותאם אישית

הפרומפט המובנה כברירת מחדל הוא “חלץ את כל הטקסט מהתמונה הזאת, תוך שמירה על הפריסה והסדר המקוריים”. במסמכים עתירי טבלאות, טקסט אנכי, או חותמות וחתימות, החלפתו באמצעות llm_prompt יכולה לשפר את הדיוק.

md = MarkItDown(
    enable_plugins=True,
    llm_client=OpenAI(),
    llm_model="gpt-4o",
    llm_prompt="Extract all text from this image, but preserve table structure as markdown tables.",
)

6. עיבוד קבצים מרובים בבת אחת

בהרצת תיקייה שלמה של קובצי PDF סרוקים בבת אחת, כל קובץ מפעיל קריאת API משלו — יש לעטוף כל המרה בנפרד כדי שכשל בודד לא יפיל את כל האצווה.

from pathlib import Path
from markitdown import MarkItDown
from openai import OpenAI

md = MarkItDown(enable_plugins=True, llm_client=OpenAI(), llm_model="gpt-4o")

for pdf_path in Path("scans/").glob("*.pdf"):
    try:
        result = md.convert(str(pdf_path))
        pdf_path.with_suffix(".md").write_text(result.markdown, encoding="utf-8")
    except Exception as e:
        print(f"failed: {pdf_path.name}{e}")

7. מה קורה אם מתקינים את התוסף בלי llm_client?

התוסף נטען, אך ה-OCR כבה בשקט. register_converters() ב-_plugin.py בונה LLMVisionOCRService רק כאשר גם llm_client וגם llm_model קיימים; אחרת, הוא רושם את הממירים עם ocr_service=None. במצב הזה, המערכת חוזרת אוטומטית ובשקט לחילוץ שכבת הטקסט בלבד — בלי שגיאה, בלי אזהרה. רוב הבעיות מהסוג “התקנתי את התוסף אבל ה-OCR לא עובד” נובעות בדיוק מהנקודה הזאת.

8. הוא אפילו מנסה לשחזר קובצי PDF פגומים

כאשר pdfplumber ו-pdfminer אינם מצליחים לפתוח קובץ PDF כלל — EOF קטוע, למשל — markitdown-ocr חוזר לעבד את העמודים ישירות באמצעות PyMuPDF (fitz) ומנסה שוב. אם גם הנתיב הזה נכשל, הוא משאיר *[Error: Could not process scanned PDF]* בתוצאה.

9. עלות ומהירות

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


אימות תוצאת ה-OCR (Verifier) — יגיע בנפרד

כל מה שלמעלה עוסק באופן ה"הפעלה" של OCR. הפעלתו אינה זהה ליכולת לבטוח בתוצאה — במעצב הנוכחי, שבו הכול עובר בשקט בין אם llm_client הוגדר ובין אם לא, ובין אם העמוד ריק ובין אם לא, אין נוהל דטרמיניסטי לשפוט האם פלט ה-OCR אכן אמין.

הנוהל הזה אינו מכוסה כאן. כיצד ליישם את עקרון ה-Symbolic Feedback Loop — ה-LLM מייצר ← כלי דטרמיניסטי שופט ← משוב ← חזרה — על פלט ה-OCR הוא נושא למאמר/כלי נפרד.


רשימת בדיקה מעשית

  • אם עוסקים רק בקובצי PDF רגילים בעלי שכבת טקסט, ההתקנה הבסיסית markitdown[pdf] מספיקה. אין צורך בתוסף OCR.
  • אם יש לטפל בקובצי PDF סרוקים או מצולמים, markitdown-ocr בתוספת מפתח API של OpenAI (או תואם) הם הכרחיים. אין אינטגרציה עם מנוע OCR מסורתי, מקומי וחינמי כמו Tesseract.
  • אם תוצאת ה-OCR חוזרת ריקה, הדבר הראשון שיש לבדוק הוא האם llm_client/llm_model הועברו בפועל. הכשל שקט, כך שכלום לא מופיע ביומנים.
  • אי אפשר להפעיל OCR רק מה-CLI. נדרש סקריפט Python הקורא ל-MarkItDown(enable_plugins=True, llm_client=..., llm_model=...).
  • עדיין אין דרך מכנית לשפוט עד כמה התוצאה אמינה — כרגע, אדם חייב לבדוק דוגמית באופן ידני.

טבלת סיכום

מצבמה נדרשהערות
PDF עם שכבת טקסטpip install markitdown[pdf]pdfplumber/pdfminer מחלצים את הטקסט
חלק מהתמונות המשובצות דורשות OCRmarkitdown-ocr + לקוח LLM Visionחותך רק את התמונות, משלב עם הטקסט
סרוק לחלוטין (העמוד כולו תמונה)markitdown-ocr + לקוח LLM Visionמעבד את העמוד כולו ב-300dpi, ואז מבצע OCR
PDF פגוםכמו לעיל (גיבוי אוטומטי)מנסה שוב באמצעות עיבוד עם PyMuPDF
llm_client לא הוגדרOCR מדולג בשקט, ללא שגיאה
מסירת --llm-client ב-CLIבלתי אפשרי (הדגל הזה אינו קיים בפועל)רק דרך ממשק Python
שיפוט אמינות תוצאת ה-OCR(מתוכנן)ייכלל בכלי אימות נפרד

מאמרים קשורים


מקורות

יומן שינויים

  • 2026-07-09: גרסה ראשונית — נוספה מסגרת ה-agent-operable, הגדרת ה-OCR הורחבה כמחצית השנייה, נוסף סעיף מציין מיקום עבור כלי האימות