تثبيت واستخدام MarkItDown Image: AI generated

يكفي تنفيذ pip install markitdown لتحويل ملفات PDF ومستندات Word وجداول Excel وعروض PowerPoint إلى markdown. لكن إذا أعطيته ملف PDF لا يحتوي إلا على صور ممسوحة ضوئيًا — عقدًا ممسوحًا ضوئيًا مثلًا — فستحصل على مستند فارغ. يسير هذا المقال على مسارين: لماذا وكيف تستخدم MarkItDown، وكيف تضيف OCR في النقطة الدقيقة التي تنهار فيها تلك الثقة — ملفات PDF التي تحتوي على صور فقط.


لماذا يجب أن تستخدم MarkItDown — شرط agent-operable

MarkItDown هي مكتبة مفتوحة المصدر من مايكروسوفت لسطر الأوامر/Python تحوّل ملفات PDF وWord وExcel وPowerPoint وHTML والصور والصوت إلى markdown.

لنبدأ بمفهوم agent-operable (حالة يمكن للوكيل العمل بها). لكي يتعامل الوكيل مع مستند بشكل مستقل، يجب أن يكون ذلك المستند في حالة يمكن لآلة تحليلها هيكليًا — لا في حالة يجدها الإنسان قابلة للقراءة فقط. هذا ليس شرطًا يقتصر على الكود. قاعدة عمل مدفونة داخل ملف PDF أو جدول Excel قد تكون كأنها غير موجودة بالنسبة لوكيل، لأن الإنسان يجب أن يفتحها ليعرف ما تقوله. هذا هو الأول من ثلاثة شروط — قابل للقراءة، قابل للتحقق، دائم — الموضحة في بناء أنظمة agent-operable: “قابل للقراءة بلا ضوضاء”.

فلماذا MarkItDown تحديدًا؟ يمكنك بناء نفس التحويل بنفسك بتجميع محلّلات من جهات خارجية (PyPDF2، python-docx). الفرق هو من الذي بناه. docx وxlsx وpptx كلها OOXML — مواصفة تملكها مايكروسوفت نفسها. عندما تبني الشركة المالكة للمواصفة المحوّل الخاص بصيغتها، تكون الحالات الحدية (الخلايا المدمجة، الجداول المتداخلة، الحواشي) أكثر موثوقية هيكليًا مقارنة بمحلّل من جهة خارجية. إذا لم يكن التحويل نفسه موثوقًا، فإن كل ما يُبنى فوقه نحو حالة agent-operable يصبح بلا معنى. هذا هو سبب استخدام MarkItDown — بالنسبة لصيغ مايكروسوفت الخاصة، تأتي هذه الثقة مدمجة افتراضيًا.

المشكلة أن هذه الثقة لا تنتقل إلى ملفات PDF، وخاصة تلك التي تحتوي على صور فقط. يركز النصف الثاني من هذا المقال على سد تلك الفجوة باستخدام OCR.


التثبيت

التثبيت الأساسي

pip install markitdown

التثبيت الأساسي وحده يتعامل فقط مع الصيغ الخفيفة مثل النص العادي وHTML. لتحويل مستندات PDF وOffice، تحتاج إلى الإضافات الخاصة بكل صيغة.

الإضافات الخاصة بكل صيغة

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] كافية.


الاستخدام الأساسي

سطر الأوامر

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

لا تحمل markitdown-ocr محرك OCR تقليديًا مثل Tesseract أو PaddleOCR. كل ما تفعله فعليًا هو ترميز الصورة بـ base64 وطلب من واجهة 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. لا يعمل من سطر الأوامر — تم التحقق من الكود الفعلي

يقدّم ملف README الخاص بـ markitdown-ocr هذا الأمر كمثال للاستخدام:

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

لكن عند فحص تعريفات وسيطات سطر الأوامر لنواة MarkItDown مباشرة (packages/markitdown/src/markitdown/__main__.py)، يتضح أن هذه الأعلام غير موجودة إطلاقًا — لا يوجد --llm-client ولا --llm-model. سطر الأوامر يدعم -o (ملف الإخراج)، و-x (تلميح الامتداد)، و-m (تلميح MIME)، و-c (ترميز الأحرف)، و-d/--use-docintel، و--use-cu، و-p/--use-plugins، و--list-plugins، و--keep-data-uris، وهذا كل شيء — منشئ MarkItDown لا يستقبل سوى enable_plugins=args.use_plugins. بعبارة أخرى، مثال سطر الأوامر في README خطأ توثيقي؛ لا يعمل فعليًا.

يعمل OCR فقط عبر واجهة برمجة Python — يجب تمرير llm_client/llm_model مباشرة. لا توجد طريقة لتزويد سطر الأوامر بمفتاح API.

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 على الإطلاق — نهاية ملف مبتورة مثلًا — يعود markitdown-ocr إلى عرض الصفحات مباشرة باستخدام PyMuPDF (fitz) ويعيد المحاولة. إذا فشل هذا المسار أيضًا، يترك *[Error: Could not process scanned PDF]* في النتيجة.

9. التكلفة والسرعة

بما أنه يرسل صفحات كاملة إلى نموذج رؤية، تتراكم التكلفة والتأخير بما يتناسب مع عدد صفحات المستند الممسوح ضوئيًا. العرض بدقة 300dpi ليس رخيصًا من حيث الرموز لكل صورة، لذا من الأسلم أخذ عينة من بضع صفحات أولًا لتقدير الجودة والتكلفة قبل تشغيل مستند كامل من مئات الصفحات.


التحقق من نتيجة OCR — قادم بشكل منفصل

كل ما سبق يتعلق بكيفية “تشغيل” 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 من سطر الأوامر وحده. يلزم سكريبت 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 في سطر الأوامرغير ممكن (هذا العلم غير موجود فعليًا)واجهة Python فقط
الحكم على موثوقية نتيجة OCR(مخطَّط له)ستغطيه أداة تحقق منفصلة

مقالات ذات صلة


المراجع

سجل التغييرات

  • 2026-07-09: الإصدار الأول — إضافة إطار agent-operable، توسيع إعداد OCR ليكون محور النصف الثاني، إضافة قسم نائب لأداة التحقق