Image: AI generated
Cukup dengan
pip install markitdown, PDF, dokumen Word, spreadsheet Excel, dan presentasi PowerPoint berubah menjadi markdown. Tapi berikan PDF yang isinya hanya gambar hasil pindaian — misalnya kontrak yang dipindai — dan hasilnya adalah dokumen kosong. Artikel ini berjalan pada dua jalur: mengapa dan bagaimana menggunakan MarkItDown, dan cara menambahkan OCR tepat di titik di mana kepercayaan itu runtuh — PDF yang hanya berisi gambar.
Mengapa Anda Harus Menggunakan MarkItDown — Kondisi Agent-Operable
MarkItDown adalah pustaka CLI/Python open source dari Microsoft yang mengonversi file PDF, Word, Excel, PowerPoint, HTML, gambar, dan audio menjadi markdown.
Mulai dari konsep agent-operable (keadaan yang bisa dikerjakan oleh agen). Agar sebuah agen dapat menangani dokumen secara otonom, dokumen itu harus berada dalam keadaan yang dapat diurai secara struktural oleh mesin — bukan sekadar keadaan yang dianggap manusia mudah dibaca. Ini bukan kondisi yang hanya berlaku pada kode. Aturan bisnis yang terkubur di dalam PDF atau spreadsheet Excel bisa jadi sama saja dengan tidak ada bagi sebuah agen, karena manusia harus membukanya untuk tahu isinya. Ini adalah kondisi pertama dari tiga kondisi — dapat dibaca, dapat diverifikasi, persisten — yang diuraikan dalam Membangun Sistem Agent-Operable: “dapat dibaca tanpa gangguan (noise)”.
Lalu mengapa harus MarkItDown secara spesifik? Anda bisa membangun konversi yang sama sendiri dengan merangkai parser pihak ketiga (PyPDF2, python-docx). Bedanya ada pada siapa yang membuatnya. docx, xlsx, dan pptx semuanya adalah OOXML — spesifikasi yang dimiliki Microsoft sendiri. Ketika perusahaan pemilik spesifikasi itu membangun konverter untuk formatnya sendiri, kasus-kasus tepi (sel gabungan, tabel bersarang, catatan kaki) secara struktural lebih mungkin bertahan dibandingkan dengan parser pihak ketiga. Jika konversi itu sendiri tidak dapat dipercaya, semua yang dibangun di atasnya menuju keadaan agent-operable jadi tidak berarti. Itulah alasan menggunakan MarkItDown — untuk format milik Microsoft sendiri, kepercayaan itu sudah tersedia secara default.
Masalahnya, kepercayaan ini tidak berlanjut ke PDF, terutama PDF yang hanya berisi gambar. Paruh kedua artikel ini berfokus pada menutup celah tersebut dengan OCR.
Instalasi
Instalasi dasar
pip install markitdown
Instalasi dasar saja hanya menangani format ringan seperti teks biasa dan HTML. Untuk mengonversi dokumen PDF dan Office, diperlukan ekstra khusus format.
Ekstra khusus format
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.
Jika artikel ini hanya memerlukan PDF, markitdown[pdf] sudah cukup.
Penggunaan Dasar
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 API
from markitdown import MarkItDown
md = MarkItDown()
result = md.convert("example.pdf")
print(result.markdown)
Kedua cara ini secara otomatis memilih konverter berdasarkan ekstensi file atau tipe MIME. Ketika tidak ada informasi ekstensi — misalnya saat membaca dari stdin — beri petunjuk dengan -x (--extension) atau -m (--mime-type).
Apa yang Sebenarnya Dilakukan Konversi PDF — Hanya Membaca Lapisan Teks
Konverter PDF milik MarkItDown (packages/markitdown/src/markitdown/converters/_pdf_converter.py) pertama-tama menggunakan pdfplumber untuk mendeteksi struktur tabel/formulir di setiap halaman, dan beralih ke page.extract_text() ketika sebuah halaman bukan tabel. Jika pdfplumber gagal, ia beralih ke pdfminer.six.
Kedua pustaka ini adalah alat yang membaca lapisan teks yang sudah ada di dalam PDF. Jika sebuah halaman hanya terdiri dari gambar hasil pindaian tanpa lapisan teks, extract_text() mengembalikan string kosong, dan keluaran MarkItDown pun pada akhirnya kosong. Tidak ada jalur kode di paket dasar yang me-render gambar atau mengenali karakter di dalamnya.
Dengan kata lain, ini adalah dua masalah yang berbeda:
- PDF dengan lapisan teks (diekspor dari Word, sebagian besar kontrak elektronik) → instalasi dasar bekerja dengan baik. Kepercayaan yang sama yang menopang format milik Microsoft sendiri berlanjut ke sini juga.
- PDF tanpa lapisan teks (dokumen yang melewati pemindai, foto yang disematkan sebagai gambar) → instalasi dasar mengembalikan hasil kosong. Di sinilah kondisi “dapat dibaca” pada agent-operable terputus.
Menyiapkan OCR — Tempat Kepercayaan yang Rusak Ditambal
Di sinilah paruh kedua artikel ini dimulai. Kepercayaan yang diamankan MarkItDown untuk format milik Microsoft sendiri harus dibangun ulang secara terpisah untuk PDF yang hanya berisi gambar.
1. Instal plugin
Dukungan OCR berada dalam paket terpisah, markitdown-ocr. Paket ini ada di repositori yang sama (microsoft/markitdown), tetapi tidak termasuk dalam pip install markitdown.
pip install markitdown-ocr
pip install openai # an OpenAI-compatible client — this plugin has no OCR model of its own
Setelah instalasi, periksa apakah plugin terdeteksi.
markitdown --list-plugins
# * ocr (package: markitdown_ocr)
2. Yang Tidak Bisa Ditawar — Klien LLM Vision
markitdown-ocr tidak menyertakan mesin OCR tradisional seperti Tesseract atau PaddleOCR. Yang sebenarnya dilakukannya hanyalah meng-encode gambar ke base64 dan meminta API chat.completions yang kompatibel dengan OpenAI (misalnya gpt-4o) untuk “mengekstrak teks dari gambar ini” (LLMVisionOCRService di _ocr_service.py). Karena itu, kunci API yang dapat memanggil model visi adalah keharusan mutlak agar OCR bisa berfungsi sama sekali.
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)
Klien kompatibel OpenAI apa pun — termasuk Azure OpenAI — dapat diberikan dengan cara yang sama.
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. Tidak Bekerja dari CLI — Diverifikasi Terhadap Kode Sebenarnya
README markitdown-ocr memberikan perintah berikut sebagai contoh penggunaan:
markitdown document.pdf --use-plugins --llm-client openai --llm-model gpt-4o
Tapi memeriksa langsung definisi argumen CLI inti MarkItDown (packages/markitdown/src/markitdown/__main__.py) menunjukkan bahwa flag semacam itu tidak ada — tidak ada --llm-client atau --llm-model. CLI mendukung -o (file keluaran), -x (petunjuk ekstensi), -m (petunjuk MIME), -c (charset), -d/--use-docintel, --use-cu, -p/--use-plugins, --list-plugins, dan --keep-data-uris, dan itu saja — konstruktor MarkItDown hanya menerima enable_plugins=args.use_plugins. Dengan kata lain, contoh CLI di README adalah bug dokumentasi; itu sebenarnya tidak berjalan.
OCR hanya bekerja lewat Python API — llm_client/llm_model harus diberikan secara langsung. Tidak ada cara untuk memberikan kunci API ke CLI.
4. Cara Kerjanya Sebenarnya — OCR Gambar Tersemat dan Fallback OCR Halaman Penuh
PdfConverterWithOCR (_pdf_converter_with_ocr.py) berjalan dalam dua tahap.
- Gambar tersemat lebih dulu: ketika sebuah halaman mencampur teks dengan gambar (misalnya tanda tangan hasil pindaian yang diletakkan di tengah teks utama), hanya gambar itu yang dipotong, dikirim ke LLM Vision, dan hasilnya disisipkan kembali dengan teks di sekitarnya berdasarkan posisi Y aslinya, menjaga urutan pembacaan.
- Fallback OCR halaman penuh: jika hasil ekstraksi teks kembali kosong sepenuhnya (PDF hasil pindaian penuh), seluruh halaman di-render sebagai PNG 300dpi dan dikirim ke LLM Vision satu halaman pada satu waktu (
_ocr_full_pages).
Teks yang diekstrak selalu dibungkus dalam format ini:
*[Image OCR]
<extracted text>
[End OCR]*
5. Menyempurnakan Akurasi dengan Prompt Kustom
Prompt bawaan adalah “ekstrak semua teks dari gambar ini, pertahankan tata letak dan urutan aslinya”. Untuk dokumen yang banyak mengandung tabel, teks vertikal, atau stempel dan tanda tangan, menimpanya dengan llm_prompt dapat meningkatkan akurasi.
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. Memproses Banyak File Sekaligus
Saat menjalankan seluruh folder PDF hasil pindaian sekaligus, setiap file memicu panggilan API-nya sendiri — setiap konversi perlu dibungkus agar satu kegagalan tidak menjatuhkan seluruh batch.
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. Bagaimana Jika Anda Menginstal Plugin Tanpa llm_client?
Plugin tetap termuat, tetapi OCR mati secara diam-diam. register_converters() di _plugin.py hanya membangun LLMVisionOCRService ketika llm_client dan llm_model sama-sama ada; jika tidak, ia mendaftarkan konverter dengan ocr_service=None. Dalam keadaan itu, sistem secara otomatis dan diam-diam kembali ke ekstraksi lapisan teks saja — tanpa error, tanpa peringatan. Sebagian besar masalah “saya sudah instal plugin tapi OCR tidak jalan” berakar tepat di titik ini.
8. Bahkan Mencoba Memulihkan PDF yang Rusak
Ketika pdfplumber dan pdfminer sama sekali tidak bisa membuka sebuah PDF — misalnya EOF yang terpotong — markitdown-ocr beralih ke me-render halaman langsung dengan PyMuPDF (fitz) dan mencoba lagi. Jika jalur ini juga gagal, ia meninggalkan *[Error: Could not process scanned PDF]* pada hasilnya.
9. Biaya dan Kecepatan
Karena mengirim seluruh halaman ke model visi, biaya dan latensi menumpuk sebanding dengan jumlah halaman dokumen hasil pindaian. Render 300dpi tidak murah dari segi token per gambar, jadi lebih aman untuk mencuplik beberapa halaman terlebih dahulu guna mengukur kualitas dan biaya sebelum menjalankan seluruh dokumen beratus-ratus halaman.
Memverifikasi Hasil OCR — Menyusul Secara Terpisah
Semua yang di atas adalah tentang cara “menyalakan” OCR. Menyalakannya bukan berarti hasilnya dapat dipercaya — dalam desain saat ini, di mana segala sesuatu lolos secara diam-diam baik llm_client diatur atau tidak, dan baik halamannya kosong atau tidak, tidak ada prosedur deterministik untuk menilai apakah keluaran OCR benar-benar dapat dipercaya.
Prosedur itu tidak dibahas di sini. Cara menerapkan prinsip Symbolic Feedback Loop — LLM menghasilkan → alat deterministik menilai → umpan balik → ulangi — pada keluaran OCR adalah artikel/alat yang terpisah.
Daftar Periksa Praktis
- Jika Anda hanya berurusan dengan PDF biasa yang memiliki lapisan teks, instalasi dasar
markitdown[pdf]sudah cukup. Plugin OCR tidak diperlukan. - Jika Anda harus menangani PDF hasil pindaian atau foto,
markitdown-ocrditambah kunci API OpenAI (atau yang kompatibel) wajib ada. Tidak ada integrasi dengan mesin OCR tradisional, lokal, dan gratis seperti Tesseract. - Jika hasil OCR kembali kosong, hal pertama yang perlu diperiksa adalah apakah
llm_client/llm_modelbenar-benar diberikan. Ia gagal secara diam-diam, jadi tidak ada yang muncul di log. - OCR tidak bisa diaktifkan hanya dari CLI. Diperlukan skrip Python yang memanggil
MarkItDown(enable_plugins=True, llm_client=..., llm_model=...). - Belum ada cara mekanis untuk menilai seberapa dapat dipercaya hasilnya — untuk saat ini, manusia harus memeriksa sampel secara manual.
Tabel Ringkasan
| Situasi | Yang Diperlukan | Catatan |
|---|---|---|
| PDF dengan lapisan teks | pip install markitdown[pdf] | pdfplumber/pdfminer mengekstrak teks |
| Beberapa gambar tersemat perlu OCR | markitdown-ocr + klien LLM Vision | memotong gambar saja, disisipkan dengan teks |
| Hasil pindaian penuh (seluruh halaman adalah gambar) | markitdown-ocr + klien LLM Vision | me-render seluruh halaman pada 300dpi, lalu OCR |
| PDF rusak | sama seperti di atas (fallback otomatis) | mencoba lagi dengan me-render lewat PyMuPDF |
llm_client tidak diatur | — | OCR dilewati secara diam-diam, tanpa error |
Memberikan --llm-client di CLI | tidak mungkin (flag tersebut tidak ada) | hanya lewat Python API |
| Menilai keandalan hasil OCR | (direncanakan) | akan dibahas oleh verifier terpisah |
Artikel Terkait
- Membangun Sistem Agent-Operable — tiga kondisi (dapat dibaca, dapat diverifikasi, persisten), dan di mana MarkItDown berperan
- Agent Operable Codebase — prinsip yang sama diterapkan pada kode
Referensi
- microsoft/markitdown — akar repositori
_pdf_converter.py— konverter PDF dasar (hanya ekstraksi lapisan teks)__main__.py— definisi argumen CLI (mengonfirmasi--llm-client/--llm-modeltidak ada)markitdown-ocrREADME_pdf_converter_with_ocr.py_ocr_service.py_plugin.py- markitdown pyproject.toml — daftar ekstra
Changelog
- 2026-07-09: Rilis awal — menambahkan kerangka agent-operable, memperluas penyiapan OCR sebagai paruh kedua, menambahkan bagian placeholder untuk verifier