Docling: Panduan Langkah demi Langkah Membangun Aplikasi Kecerdasan Dokumen
Gunakan Docling untuk mengonversi PDF berantakan menjadi data terstruktur yang dapat dicari. Integrasikan LangGraph untuk penalaran berbasis pengambilan dan Streamlit untuk front end interaktif. Bangun, uji, dan deploy Aplikasi Kecerdasan Dokumen lokal yang terasa seperti ChatGPT pribadi Anda untuk dokumen.
Alat ekstraksi PDF tradisional seperti pypdf atau PDFMiner memberi Anda teks mentah namun kehilangan struktur dokumen. Tabel menjadi teks acak, header bercampur dengan isi, dan gambar menghilang. Untuk sistem RAG, data berantakan ini berarti pengambilan yang buruk dan jawaban yang tidak andal. Docling adalah toolkit open-source dari IBM Research yang menggunakan model visi komputer untuk memahami tata letak dokumen, menjaga tabel, gambar, heading, dan strukturnya. Docling memproses dokumen hingga 30 kali lebih cepat dibanding metode berbasis OCR tradisional dan berjalan secara lokal di mesin Anda.
Dalam tutorial ini, kita akan menggunakan Docling untuk membangun Document Intelligence Assistant — aplikasi web Streamlit yang memungkinkan Anda mengunggah dokumen, memvisualisasikan strukturnya, dan mengajukan pertanyaan menggunakan chatbot bertenaga RAG. Anda akan mempelajari cara memproses dokumen multi-format dengan Docling, mengekstrak dan menampilkan tabel serta gambar, membangun vector store dengan ChromaDB, dan membuat agen percakapan dengan LangGraph. Di akhir, Anda akan memiliki aplikasi yang berfungsi untuk mengubah dokumen kompleks menjadi data terstruktur dan memungkinkan tanya jawab cerdas.
Pratinjau aplikasi:
Visualisasi struktur dokumen:
Prasyarat
Sebelum memulai tutorial ini, Anda sebaiknya sudah memiliki:
Keterampilan teknis: Nyaman dengan kelas Python, dekorator, type hints, dan context manager. Kita akan menggunakan operasi async dan pola factory di seluruh bagian. Pemahaman tentang cara model bahasa besar bekerja dengan prompt, token, dan embedding diperlukan. Keakraban dengan sistem retrieval-augmented generation dan basis data vektor membantu namun tidak wajib — kami akan menjelaskan konsep inti sambil membangun.
Lingkungan pengembangan: Python 3.10 atau lebih baru dengan pip untuk manajemen paket. Editor kode seperti VS Code direkomendasikan. Anda memerlukan kunci API OpenAI dariplatform.openai.com — biaya pemrosesan sekitar $0,10–0,20 per dokumen.
Komitmen waktu: Sediakan 60–90 menit untuk menyelesaikan tutorial, termasuk membaca penjelasan, menulis kode, dan menguji aplikasi. Tutorial ini mengasumsikan keterampilan Python tingkat menengah.
Memahami Docling: Fitur dan Kapabilitas
Kebanyakan alat pemrosesan dokumen memperlakukan PDF seperti file gambar atau aliran teks. Mereka menjalankan OCR pada setiap halaman atau mengekstrak teks biasa tanpa memahami apa yang dibaca. Docling mengambil pendekatan berbeda. Ini adalahtoolkit open-source dari IBM Research yang menggunakan model visi komputer untuk memahami struktur dokumen seperti halnya manusia.
Analisis tata letak: Model yang dilatih di DocLayNet mengidentifikasi elemen berbeda seperti header, teks isi, tabel, dan gambar dengan menganalisis tata letak halaman
Struktur tabel: TableFormer menangani tabel dan mengonversinya menjadi data terstruktur
Model-model ini memahami bahwa dokumen bukan hanya gumpalan teks. Dokumen memiliki hierarki, relasi, dan makna.
Pemahaman struktur ini penting untuk sistem RAG. Saat Anda membangun aplikasi retrieval-augmented generation, kualitas pemrosesan dokumen berdampak langsung pada akurasi pengambilan. Jika ekstraksi PDF Anda mengubah tabel finansial menjadi teks acak, pencarian vektor Anda akan mengambil sampah. Docling mempertahankan struktur, sehingga saat Anda melakukan chunking dan embedding dokumen, Anda bekerja dengan data yang bersih dan terorganisasi.
Docling mendukung berbagai format dokumen secara default:
Dokumen PDF
Dokumen Word (DOCX)
Presentasi PowerPoint (PPTX)
Spreadsheet Excel (XLSX)
File HTML
Gambar
Anda juga dapat mengaktifkan OCR untuk dokumen hasil pindaian menggunakan mesin seperti EasyOCR, Tesseract, atau RapidOCR. Toolkit ini mengekspor ke beberapa format termasuk Markdown (bagus untuk LLM), JSON (untuk pipeline data terstruktur), dan DocTags (format yang menangkap elemen kompleks seperti persamaan matematika dan blok kode).
Selain fleksibilitas format, Docling menawarkan keunggulan performa. Pemrosesan dokumen berbasis OCR tradisional lambat karena memperlakukan setiap halaman sebagai gambar yang perlu dikenali karakternya. Docling melewati langkah ini untuk dokumen digital, menawarkan pemrosesan yang jauh lebih cepat. Docling berjalan secara lokal di perangkat keras umum, jadi Anda tidak membayar biaya API atau mengirim dokumen sensitif ke layanan pihak ketiga. Kecepatan pemrosesan bervariasi berdasarkan kompleksitas dokumen, jumlah halaman, dan spesifikasi perangkat keras, dengan performa tipikal mulai dari kurang dari satu detik hingga beberapa detik per halaman pada perangkat modern.
IBM terus meningkatkan kapabilitas Docling. Mereka merilisGranite-Docling, model visi-bahasa dengan 258 juta parameter yang unggul pada tata letak kompleks dan menawarkan dukungan multibahasa eksperimental (dengan bahasa Inggris sebagai bahasa utama dan dukungan awal untuk Arab, Tionghoa, dan Jepang). Toolkit ini sekarang juga mendukung ekstraksi gambar dengan resolusi yang dapat dikonfigurasi, yang akan kita gunakan dalam aplikasi untuk menampilkan gambar aktual dari PDF berdampingan dengan konten teksnya.
Untuk kasus penggunaan kita, Docling masuk akal karena kita memerlukan data terstruktur, bukan hanya teks mentah. Jika Anda hanya butuh ekstraksi teks dasar, alat yang lebih sederhana seperti pypdf mungkin cukup. Namun karena kita membangun aplikasi AI yang memproses dokumen untuk analisis dan percakapan, pemrosesan sadar struktur milik Docling adalah pilihan yang lebih baik. Docling sangat berharga saat bekerja dengan dokumen teknis, makalah riset, atau laporan bisnis di mana tabel dan tata letak penting.
Menyiapkan Lingkungan Pengembangan
Sebelum kita mulai membangun pemroses dokumen, Anda perlu menyiapkan struktur proyek dan menginstal paket yang diperlukan. Bagian ini membahas penyiapan awal: membuat direktori, menginstal dependensi, dan mengonfigurasi kunci API Anda.
Membuat struktur proyek
Mulailah dengan membuat direktori baru untuk proyek Anda:
File __init__.py memberi tahu Python bahwa src/ adalah sebuah paket, sehingga memungkinkan impor seperti from src.document_processor import DocumentProcessor.
Struktur proyek Anda:
docling-demo/
├── src/
│ └── __init__.pyDidukung oleh
Instalasi pertama memakan waktu beberapa menit karena Docling mengunduh model AI pra-latih (~500MB). Model-model ini menangani analisis tata letak dan pengenalan struktur tabel. Model disimpan di cache lokal, sehingga menjalankan ulang berikutnya lebih cepat.
Mengonfigurasi variabel lingkungan
Buat file .env untuk menyimpan kunci API OpenAI Anda:
OPENAI_API_KEY=your-openai-api-key-hereDidukung oleh
Dapatkan kunci API Anda dari platform.openai.com. Anda akan membutuhkannya untuk embeddings dan agen chat.
Buat templat .env.example:
OPENAI_API_KEY=your-openai-api-key-hereDidukung oleh
Tambahkan .gitignore untuk menghindari melakukan commit data sensitif:
Buat app.py di root proyek Anda. Kita akan membangun file ini secara bertahap sepanjang tutorial. Untuk sekarang, tambahkan impor dan konfigurasi dasar:
import streamlit as st
from dotenv import load_dotenv
# Load environment variables
load_dotenv()# Page configuration
st.set_page_config(
page_title="Document Intelligence Assistant",
page_icon="📄",
layout="wide")
st.title("Document Intelligence Assistant")
st.write("Application setup complete. We'll build the functionality next.")Didukung oleh
Dengan lingkungan siap, kita bisa membangun pemroses dokumen yang menggunakan Docling untuk mengekstrak struktur dari file yang diunggah.
> Catatan: Bagian di bawah akan menguraikan skrip aplikasi dalam potongan. Jadi, untuk melihat gambaran utuh dan mengikuti dengan mudah, kami menyarankan membuka repositori GitHub untuk proyek ini di tab terpisah.
Pemroses dokumen adalah tempat Docling bekerja. Komponen ini mengambil file yang diunggah dan mengubahnya menjadi data terstruktur yang dapat kita gunakan untuk RAG dan visualisasi. Kita memerlukan dua keluaran dari proses ini: teks markdown bersih untuk vector store dan objek dokumen Docling asli yang mempertahankan semua informasi struktural seperti tabel dan gambar.
Mari kita bangun ini dengan membuat file baru bernama document_processor.py di direktori src/ Anda. Kita akan membuat kelas DocumentProcessor yang mengonfigurasi pipeline pemrosesan Docling dan menangani unggahan file.
Mengonfigurasi opsi pipeline
Docling memungkinkan Anda mengontrol cara memproses dokumen melalui opsi pipeline. Untuk PDF, Anda dapat menyalakan OCR untuk dokumen hasil pindaian, mengaktifkan pengenalan struktur tabel, dan mengekstrak gambar:
from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions
from langchain_core.documents import Document
classDocumentProcessor:def__init__(self):# Configure pipeline options for PDF processing
pipeline_options = PdfPipelineOptions()
pipeline_options.do_ocr =True
pipeline_options.do_table_structure =True
pipeline_options.generate_picture_images =True
pipeline_options.images_scale =2.0Didukung oleh
Kita membuat objek PdfPipelineOptions untuk mengonfigurasi cara Docling memproses file PDF. Setiap opsi mengontrol kemampuan pemrosesan tertentu: do_ocr mengaktifkan optical character recognition untuk dokumen hasil pindaian, do_table_structure mengaktifkan deteksi dan parsing tabel, generate_picture_images memberi tahu Docling untuk mengekstrak gambar tersemat sebagai objek PIL, dan images_scale menetapkan pengali resolusi untuk gambar yang diekstraksi.
# Initialize converter with PDF options
self.converter = DocumentConverter(
format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options)})Didukung oleh
DocumentConverter adalah mesin pemrosesan utama Docling. Kita menginisialisasinya dengan opsi pipeline PDF kita yang dibungkus dalam objek PdfFormatOption, yang mengaitkan pengaturan ini secara khusus dengan file input PDF.
Mari lihat apa yang dilakukan setiap opsi pipeline. Flag do_ocr mengaktifkan optical character recognition. Saat memproses PDF digital dengan lapisan teks tersemat, Docling secara otomatis melewati OCR untuk menghemat waktu. Untuk dokumen hasil pindaian atau gambar yang berisi teks, pengaturan ini memberi tahu Docling untuk menjalankan model visi guna mengekstrak teks.
Opsi do_table_structure mengaktifkan pengenalan struktur tabel. Tanpa ini, tabel diekstrak sebagai teks biasa dengan format yang hilang. Dengan ini aktif, Docling menggunakan model AI TableFormer untuk mengidentifikasi baris, kolom, header, dan relasi sel. Representasi terstruktur ini memungkinkan Anda mengekspor tabel sebagai pandas DataFrame nanti, menjaga format tabular.
Mengatur generate_picture_images ke True mengaktifkan ekstraksi gambar. Secara default, Docling hanya mencatat lokasi gambar tanpa mengekstrak gambar sebenarnya. Mengaktifkan ini memberi Anda objek gambar PIL yang bisa ditampilkan di UI atau diproses dengan model visi. Parameter images_scale mengontrol resolusi ekstraksi—nilai 2.0 menggandakan resolusi untuk kualitas lebih baik saat menampilkan gambar atau menjalankan analisis tambahan.
Memproses file yang diunggah
Dengan pipeline terkonfigurasi, kita dapat menambahkan metode yang memproses file yang diunggah melalui Streamlit. Metode ini menangani objek file Streamlit, menyimpannya sementara, menjalankan konversi Docling, dan mengembalikan markdown untuk RAG serta dokumen Docling untuk visualisasi:
import os
import tempfile
from typing import List, Any
defprocess_uploaded_files(self, uploaded_files)->tuple[List[Document], List[Any]]:
documents =[]
docling_docs =[]
temp_dir = tempfile.mkdtemp()try:for uploaded_file in uploaded_files:# Save uploaded file to temporary location
temp_file_path = os.path.join(temp_dir, uploaded_file.name)withopen(temp_file_path,"wb")as f:
f.write(uploaded_file.getbuffer())Didukung oleh
Streamlit menyediakan file yang diunggah sebagai objek file dalam memori, tetapi Docling memerlukan file aktual di disk untuk diproses. Kita membuat direktori sementara dan menulis setiap file yang diunggah ke sana, mempertahankan nama file aslinya.
# Process the document with Docling
result = self.converter.convert(temp_file_path)# Export to markdown
markdown_content = result.document.export_to_markdown()Didukung oleh
Panggilan converter.convert() menjalankan pipeline analisis dokumen penuh milik Docling. Docling mengidentifikasi tata letak dokumen, menerapkan OCR jika perlu, mendeteksi tabel dan gambar, dan membangun representasi terstruktur. Harapkan 20–30 detik untuk pemrosesan tanpa ekstraksi gambar, atau 40–60 detik dengan gambar diaktifkan.
Setelah konversi selesai, kita ekspor ke format markdown yang menghasilkan teks bersih dan ramah LLM dengan format yang terjaga — header tetap header, daftar tetap terstruktur, dan tabel dikonversi menjadi tabel markdown.
# Create LangChain document
doc = Document(
page_content=markdown_content,
metadata={"filename": uploaded_file.name,"file_type": uploaded_file.type,"source": uploaded_file.name,})
documents.append(doc)# Store the Docling document for structure visualization
docling_docs.append({"filename": uploaded_file.name,"doc": result.document
})Didukung oleh
Kita membuat dua representasi untuk setiap dokumen yang diproses. Objek Document LangChain berisi teks markdown sebagai page_content dengan metadata terkait—ini masuk ke vector store untuk RAG. Objek dokumen Docling asli disimpan terpisah bersama nama filenya, mempertahankan semua informasi struktural (tabel, gambar, hierarki) untuk visualisasi nanti.
finally:import shutil
shutil.rmtree(temp_dir)return documents, docling_docs
Didukung oleh
Blok finally memastikan file sementara dibersihkan terlepas dari apakah pemrosesan berhasil atau gagal. Kita mengembalikan tuple yang berisi kedua representasi dokumen: dokumen LangChain untuk sistem RAG dan dokumen Docling untuk visualisasi struktur.
Pemroses dokumen sekarang siap. Selanjutnya, kita akan membangun antarmuka Streamlit yang memungkinkan pengguna mengunggah file dan melihat status pemrosesan.
Sekarang kita dapat memproses dokumen dengan Docling, kita perlu cara untuk memvisualisasikan apa yang diekstrak. Objek dokumen Docling mentah berisi informasi struktural yang kaya (heading, tabel, gambar, dan metadata) tetapi bentuk aslinya tidak ramah pengguna. Kita akan membangun lapisan visualisasi yang mengubah data ini menjadi antarmuka interaktif dengan empat tampilan: dasbor ringkasan, kerangka hierarkis, tabel interaktif, dan gambar yang diekstrak.
Buat file baru bernama structure_visualizer.py di direktori src/ Anda. Komponen ini akan mengurai struktur dokumen Docling dan mengorganisasikannya untuk ditampilkan.
Membangun kelas visualizer
Mulailah dengan membuat kelas yang membungkus dokumen Docling dan menyediakan metode untuk mengekstrak berbagai elemen struktural:
from typing import List, Dict, Any
import pandas as pd
from docling_core.types.doc import DoclingDocument
classDocumentStructureVisualizer:def__init__(self, docling_document: DoclingDocument):
self.doc = docling_document
Didukung oleh
Inisialisator menerima objek DoclingDocument (objek yang sama yang dikembalikan oleh DocumentConverter.convert()). Objek ini berisi semua yang diekstrak Docling dari dokumen. Atribut texts berisi semua elemen teks dengan label dan posisinya, tables menyimpan data tabel dengan informasi struktur, pictures menyimpan metadata gambar dan data gambar aktual, dan pages menyediakan informasi tingkat halaman.
Mengekstrak hierarki dokumen
Dokumen memiliki struktur yang melampaui paragraf. Heading menciptakan hierarki yang membantu pembaca menavigasi konten. Berikut cara mengekstrak hierarki ini:
defget_document_hierarchy(self)-> List[Dict[str, Any]]:
hierarchy =[]ifnothasattr(self.doc,"texts")ornot self.doc.texts:return hierarchy
for item in self.doc.texts:
label =getattr(item,"label",None)if label and"header"in label.lower():
text =getattr(item,"text","")
prov =getattr(item,"prov",[])
page_no = prov[0].page_no if prov elseNone
hierarchy.append({"type": label,"text": text,"page": page_no,"level": self._infer_heading_level(label)})return hierarchyDidukung oleh
Kita melakukan iterasi melalui semua item teks dalam dokumen, memfilter yang memiliki label mengandung header. Setiap item teks memiliki atribut label yang ditetapkan Docling selama analisis tata letak. Label umum termasuk section_header, page_header, title, dan text biasa. Atribut prov (kependekan dari provenance) berisi informasi posisi, termasuk halaman tempat elemen muncul. Kita mengekstrak teks heading, nomor halamannya, dan menyimpulkan tingkat hierarkinya dari tipe label.
Saat menampilkan kerangka, tingkat heading menentukan indentasi. Metode pembantu memetakan tipe label ke level numerik:
def_infer_heading_level(self, label:str)->int:if"title"in label.lower():return1elif"section"in label.lower():return2elif"subsection"in label.lower():return3else:return4Didukung oleh
Ini membuat hierarki di mana judul dokumen adalah level 1, header seksi level 2, subseksi level 3, dan header lainnya default ke level 4.
Mengonversi tabel ke DataFrame
Dengan hierarki dokumen diekstraksi, mari tangani tabel. Berbeda dengan ekstraksi teks sederhana yang mengubah tabel menjadi string acak, Docling mempertahankan strukturnya sebagai salah satu fitur paling berharganya:
defget_tables_info(self)-> List[Dict[str, Any]]:
tables_info =[]ifnothasattr(self.doc,"tables")ornot self.doc.tables:return tables_info
for i, table inenumerate(self.doc.tables,1):try:
df = table.export_to_dataframe(doc=self.doc)
prov =getattr(table,"prov",[])
page_no = prov[0].page_no if prov elseNone
caption_text =getattr(table,"caption_text",None)
caption = caption_text if caption_text andnotcallable(caption_text)elseNone
tables_info.append({"table_number": i,"page": page_no,"caption": caption,"dataframe": df,"shape": df.shape,"is_empty": df.empty
})except Exception as e:print(f"Warning: Could not process table {i}: {e}")continuereturn tables_infoDidukung oleh
Metode table.export_to_dataframe(doc=self.doc) mengonversi representasi tabel Docling menjadi pandas DataFrame. Kita mengekstrak keterangan (caption) dan nomor halaman jika tersedia.
Mengekstrak gambar dengan PIL
Selain tabel, Docling dapat mengekstrak data gambar aktual dari dokumen. Ini adalah fitur yang lebih baru yang melampaui sekadar melacak posisi gambar — Docling mengambil byte gambar sebenarnya sehingga Anda dapat menampilkannya. (Kita mengaktifkan ini di opsi pipeline pemroses dokumen.)
defget_pictures_info(self)-> List[Dict[str, Any]]:
pictures_info =[]ifnothasattr(self.doc,"pictures")ornot self.doc.pictures:return pictures_info
for i, pic inenumerate(self.doc.pictures,1):
prov =getattr(pic,"prov",[])if prov:
page_no = prov[0].page_no
bbox = prov[0].bbox
caption_text =getattr(pic,"caption_text",None)
caption = caption_text if caption_text andnotcallable(caption_text)elseNone
pil_image =Nonetry:ifhasattr(pic,"image")and pic.image isnotNone:ifhasattr(pic.image,"pil_image"):
pil_image = pic.image.pil_image
except Exception as e:print(f"Warning: Could not extract image {i}: {e}")
pictures_info.append({"picture_number": i,"page": page_no,"caption": caption,"pil_image": pil_image,"bounding_box":{"left": bbox.l,"top": bbox.t,"right": bbox.r,"bottom": bbox.b
}if bbox elseNone})return pictures_infoDidukung oleh
Setiap gambar memiliki informasi provenance, termasuk nomor halaman dan koordinat bounding box. Bounding box mendefinisikan posisi gambar pada halaman menggunakan koordinat kiri, atas, kanan, dan bawah.
Saat ekstraksi gambar diaktifkan, objek gambar memiliki atribut image yang berisi data gambar aktual. Kita mengakses gambar PIL melalui picture.image.pil_image, yang mengembalikan objek PIL Image yang dapat ditampilkan langsung oleh Streamlit dengan st.image(). Blok try-except menangani kasus di mana ekstraksi gambar gagal atau tidak diaktifkan, dengan mulus kembali hanya menampilkan metadata.
Membuat ringkasan dokumen
Visualizer membutuhkan satu metode lagi: ringkasan tingkat tinggi yang memberi pengguna gambaran struktur dokumen:
Kita menghitung jumlah halaman, elemen teks, tabel, dan gambar dalam dokumen. Kamus text_types merinci elemen teks berdasarkan labelnya, menunjukkan berapa banyak judul, header, paragraf, dan elemen lain yang diidentifikasi Docling. Ini memberi pengguna gambaran cepat tentang struktur dan kompleksitas dokumen.
Menambahkan visualizer ke Streamlit
Dengan visualizer selesai, mari integrasikan ke Streamlit dengan empat tab: Summary, Hierarchy, Tables, dan Images.
defrender_structure_viz():
st.title("📊 Document Structure")ifnot st.session_state.docling_docs:
st.info("👈 Please upload and process your documents first!")return
doc_names =[doc["filename"]for doc in st.session_state.docling_docs]
selected_doc_name = st.selectbox("Select document to analyze:", doc_names)
selected_doc_data =next((doc for doc in st.session_state.docling_docs if doc["filename"]== selected_doc_name),None)ifnot selected_doc_data:return
visualizer = DocumentStructureVisualizer(selected_doc_data["doc"])
tab1, tab2, tab3, tab4 = st.tabs(["📑 Summary","🏗️ Hierarchy","📊 Tables","🖼️ Images"])Didukung oleh
Kita membuat dropdown untuk memungkinkan pengguna memilih dokumen mana yang akan dianalisis (berguna saat banyak file diunggah). Setelah mendapatkan dokumen Docling yang dipilih dari session state, kita membuat instance visualizer dan membuat empat tab untuk tampilan berbeda.
with tab1:
st.subheader("Document Summary")
summary = visualizer.get_document_summary()
col1, col2, col3, col4 = st.columns(4)with col1:
st.metric("Pages", summary["num_pages"])with col2:
st.metric("Tables", summary["num_tables"])with col3:
st.metric("Images", summary["num_pictures"])with col4:
st.metric("Text Items", summary["num_texts"])
st.subheader("Content Types")
text_types_df = pd.DataFrame([{"Type": k,"Count": v}for k, v insorted(summary["text_types"].items(), key=lambda x:-x[1])])
st.dataframe(text_types_df, use_container_width=True)with tab2:
st.subheader("Document Hierarchy")
hierarchy = visualizer.get_document_hierarchy()if hierarchy:for item in hierarchy:
indent =" "*(item["level"]-1)
st.markdown(f"{indent}**{item['text']}** _(Page {item['page']})_")else:
st.info("No hierarchical structure detected")with tab3:
st.subheader("Tables")
tables_info = visualizer.get_tables_info()if tables_info:for table_data in tables_info:
st.markdown(f"### Table {table_data['table_number']} (Page {table_data['page']})")if table_data["caption"]:
st.caption(table_data["caption"])ifnot table_data["is_empty"]:
st.dataframe(table_data["dataframe"], use_container_width=True)else:
st.info("Table is empty")
st.divider()else:
st.info("No tables found in this document")with tab4:
st.subheader("Images")
pictures_info = visualizer.get_pictures_info()if pictures_info:for pic_data in pictures_info:
st.markdown(f"**Image {pic_data['picture_number']}** (Page {pic_data['page']})")if pic_data["caption"]:
st.caption(pic_data["caption"])if pic_data["pil_image"]isnotNone:
st.image(pic_data["pil_image"], use_container_width=True)else:
st.info("Image data not available")if pic_data["bounding_box"]:
bbox = pic_data["bounding_box"]with st.expander("📐 Position Details"):
st.text(f"Position: ({bbox['left']:.1f}, {bbox['top']:.1f}) - "f"({bbox['right']:.1f}, {bbox['bottom']:.1f})")
st.divider()else:
st.info("No images found in this document")Didukung oleh
Visualizer struktur telah selesai. Pengguna dapat mengunggah dokumen dan langsung melihat anatominya — berapa banyak halaman, tabel, dan gambar yang dikandungnya, struktur hierarkis dengan heading dan seksi, tabel interaktif yang bisa dijelajahi, serta gambar aktual yang diekstrak dari dokumen. Transparansi ini membantu pengguna memahami apa yang diekstrak Docling dan membangun kepercayaan pada sistem.
Dengan pemrosesan dokumen dan visualisasi yang berfungsi, kita dapat membangun sistem RAG yang memungkinkan tanya jawab atas dokumen yang telah diproses ini.
Dengan pemrosesan dokumen dan visualisasi selesai, kita dapat membangun kapabilitas Q&A. RAG (Retrieval Augmented Generation) memungkinkan pengguna mengajukan pertanyaan tentang dokumen mereka. Sistem mengonversi dokumen menjadi embedding, menyimpannya di basis data vektor, dan mengambil potongan relevan untuk menjawab pertanyaan.
Bagian ini membahas dua komponen: pengelola vector store yang melakukan chunking dan embedding dokumen, serta alat pencarian yang mengambil informasi relevan.
Menyiapkan vector store
Vector store adalah tempat embedding dokumen berada. Saat pengguna mengajukan pertanyaan, kita mencari potongan relevan di store ini dan memberikannya ke LLM sebagai konteks.
Buat vectorstore.py di direktori src/ Anda:
from typing import List
from langchain_core.documents import Document
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import ChromaDidukung oleh
Impor ini menghadirkan komponen LangChain untuk penanganan dokumen, pemecahan teks, pembuatan embedding, dan penyimpanan vektor ChromaDB. Berikut cara kerjanya bersama:
Kita menginisialisasi dua komponen kunci. Model text-embedding-3-small dari OpenAI mengonversi teks menjadi vektor. Model ini lebih kecil dan lebih cepat daripada text-embedding-3-large, yang penting saat melakukan embedding ratusan potongan. RecursiveCharacterTextSplitter membagi dokumen menjadi potongan 1000 karakter dengan overlap 100 karakter, memastikan informasi penting di batas potongan tidak terpotong di tengah kalimat.
Mengapa 1000 karakter?
1000 karakter menyeimbangkan presisi dan konteks — potongan lebih kecil memberi pengambilan yang presisi namun kehilangan konteks, potongan lebih besar menjaga konteks namun menurunkan relevansi.
Tambahkan metode chunking:
defchunk_documents(self, documents: List[Document])-> List[Document]:"""Split documents into smaller chunks for better retrieval."""
chunks = self.text_splitter.split_documents(documents)return chunksDidukung oleh
Pemecah menangani metadata secara otomatis, mempertahankan informasi dari dokumen asli. Setiap potongan mengetahui dari file mana asalnya, yang menjadi penting saat agen mengutip sumber dalam jawabannya.
Sekarang tambahkan metode yang membuat vector store:
defcreate_vectorstore(self, chunks: List[Document])-> Chroma:"""Create a Chroma vector store from document chunks."""
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=self.embeddings,
collection_name="documents")return vectorstoreDidukung oleh
ChromaDB menangani pekerjaan berat. ChromaDB melakukan embedding setiap potongan menggunakan model embedding kita dan menyimpan vektor di memori. Saat Anda menjalankan pencarian kemiripan, ChromaDB menghitung cosine similarity antara vektor kueri dan semua vektor dokumen, mengembalikan kecocokan terdekat.
Membuat alat pencarian
Dengan vector store siap menangani embedding, kita perlu memberi agen cara untuk menanyakannya. Tools adalah fungsi yang bisa dipanggil LLM saat membutuhkan informasi yang tidak dimilikinya.
Buat tools.py di direktori src/ Anda:
from typing import Annotated
from langchain_core.tools import tool
defcreate_search_tool(vectorstore):"""Create a search tool that has access to the vector store."""Didukung oleh
Fungsi factory ini menggunakan pola closure: menerima vector store dan mengembalikan tool yang dapat menelusurinya. Tool mempertahankan akses ke vector store tanpa perlu variabel global.
@tooldefsearch_documents(query: Annotated[str,"The search query or question about the documents"])->str:"""Search the uploaded documents for relevant information."""Didukung oleh
Dekorator @tool mengubah fungsi ini menjadi tool LangChain yang dapat dipanggil agen. Type hint Annotated mendeskripsikan parameter, membantu LLM memahami apa yang harus dilewatkan saat memanggil tool.
try:
results = vectorstore.similarity_search(query, k=8)ifnot results:return"No relevant information found in the documents for this query."Didukung oleh
Kita mengambil 8 potongan serupa (k=8) dari vector store. Ini memberi LLM cukup konteks tanpa membebani dengan informasi berulang. Jumlah yang tepat bergantung pada tipe dokumen—dokumen teknis dengan informasi padat mungkin lebih baik dengan potongan lebih sedikit (k=4–6), sementara dokumen naratif mungkin mendapatkan manfaat dari lebih banyak (k=10–12).
context_parts =[]for i, doc inenumerate(results,1):
source = doc.metadata.get("filename", doc.metadata.get("source","Unknown source"))
content = doc.page_content.strip()
context_parts.append(f"[Source {i}: {source}]\n"f"Content: {content}\n")return"\n---\n".join(context_parts)Didukung oleh
Kita memformat setiap potongan dengan nama file sumbernya, lalu menggabungkannya dengan pemisah. LLM menerima konteks terstruktur ini dan menggunakannya untuk menghasilkan jawaban sambil mengutip sumber.
except Exception as e:returnf"Error searching documents: {str(e)}"return search_documentsDidukung oleh
Penanganan galat memastikan agen menerima pesan yang jelas jika pencarian gagal, alih-alih crash. Fungsi mengembalikan tool yang terkonfigurasi siap digunakan agen.
Vector store dan alat pencarian sekarang siap. Selanjutnya, kita akan membangun agen LangGraph yang mengorkestrasi pengambilan dan generasi untuk menjawab pertanyaan pengguna.
Alat pencarian dapat menanyakan vector store, tetapi membutuhkan koordinator yang cerdas. Agen LangGraph memutuskan kapan harus mencari, menafsirkan hasil, dan menghasilkan jawaban bahasa alami. Kita juga akan mengimplementasikan streaming untuk menampilkan progres secara real-time.
Membangun agen LangGraph
Agen menerima pertanyaan, memutuskan kapan mencari dokumen, dan menghasilkan jawaban berdasarkan konteks yang diambil.
Buat agent.py di direktori src/ Anda:
from typing import List
from langchain_core.tools import BaseTool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaverDidukung oleh
Kita mengimpor komponen untuk penanganan tool, model chat OpenAI, implementasi agen ReAct LangGraph, dan memori percakapan.
SYSTEM_PROMPT ="""You are a helpful document intelligence assistant. You have access to documents that have been uploaded and processed.
GUIDELINES:
- Use the search_documents tool to find relevant information
- Keep it simple: one well-crafted search is usually sufficient
- Only search again if the first results are clearly incomplete
- Provide clear, accurate answers based on the document contents
- Always cite your sources with filenames
- If information isn't found, say so clearly
- Be concise but thorough
When answering:
1. Search the documents with a focused query
2. Synthesize a clear answer from the results
3. Include source citations (filenames)
4. Only search again if absolutely necessary
"""defcreate_documentation_agent(tools: List[BaseTool], model_name:str="gpt-4o-mini"):"""Create a document intelligence assistant agent using LangGraph."""
llm = ChatOpenAI(model=model_name, temperature=0)
memory = MemorySaver()Didukung oleh
Kita menggunakan gpt-4o-mini alih-alih gpt-5 karena lebih cepat dan lebih murah namun tetap mampu menangani Q&A dokumen dengan baik. Temperature disetel ke 0 untuk respons yang konsisten dan faktual. MemorySaver memberi agen memori percakapan, sehingga mengingat pertukaran sebelumnya dalam satu sesi.
agent = create_react_agent(
llm,
tools=tools,
prompt=SYSTEM_PROMPT,
checkpointer=memory
)return agentDidukung oleh
create_react_agent milik LangGraph mengimplementasikan pola ReAct (Reasoning + Acting). Agen bernalar tentang apa yang perlu dilakukan, mengambil tindakan menggunakan tools, mengamati hasil, dan mengulang sampai memperoleh jawaban. Pola ini bekerja baik untuk RAG karena agen dapat memutuskan kapan harus menelusur dan bagaimana menggunakan konteks yang diambil.
Mengimplementasikan respons streaming
Agen sekarang dapat menelusur dan menghasilkan jawaban, namun pengguna tidak seharusnya menunggu 10 detik menatap layar kosong saat agen bekerja. Streaming menampilkan progres secara real-time — pertama indikator “thinking” muncul, lalu “searching”, kemudian jawaban muncul token demi token.
Perbarui fungsi render_chat() di app.py untuk melengkapi penanganan respons yang sebelumnya kita tandai TODO:
if prompt:
st.session_state.messages.append({"role":"user","content": prompt})with st.chat_message("user"):
st.markdown(prompt)Didukung oleh
Kita menambahkan pesan pengguna ke riwayat percakapan dan menampilkannya di antarmuka chat.
with st.chat_message("assistant"):
status_placeholder = st.empty()
message_placeholder = st.empty()try:
config ={"configurable":{"thread_id":"document_chat"}}Didukung oleh
Kita membuat placeholder untuk indikator status dan pesan respons. Konfigurasi mencakup thread ID yang digunakan LangGraph untuk mempertahankan memori percakapan lintas giliran.
defgenerate_response():"""Generator that yields tokens from LangGraph stream."""
status_placeholder.markdown("🤔 **Thinking...**")
first_content_token =True
tool_call_detected =False
final_answer_started =FalseDidukung oleh
Fungsi generator ini memproses output stream LangGraph. Kita melacak status dengan flag untuk menampilkan pesan status yang sesuai saat agen menjalankan alur kerjanya.
for msg, metadata in st.session_state.agent.stream({"messages":[HumanMessage(content=prompt)]},
config=config,
stream_mode="messages",):
langgraph_node = metadata.get("langgraph_node","")Didukung oleh
Parameter stream_mode="messages" memberi kita token LLM nyata saat dihasilkan, bukan hanya output akhir. LangGraph memancarkan event sepanjang eksekusi agen—saat mulai berpikir, saat memanggil tools, dan saat menghasilkan teks.
Ketika node agen mulai menghasilkan jawaban akhir, kita memperbarui status dan mulai mengalirkan token konten. Setiap token langsung ditampilkan, menciptakan efek mengetik yang mulus.
status_placeholder.empty()with message_placeholder.container():
full_response = st.write_stream(generate_response())Didukung oleh
Setelah streaming selesai, kita menghapus indikator status. st.write_stream() milik Streamlit menangani tampilan token demi token secara otomatis, mengakumulasi token dan memperbarui UI dengan mulus. Hasilnya adalah pengalaman chat yang terasa responsif dan memberi keyakinan kepada pengguna bahwa sistem sedang bekerja.
st.session_state.messages.append({"role":"assistant","content": full_response})except Exception as e:
st.error(f"Error generating response: {str(e)}")Didukung oleh
Kita menyimpan respons lengkap ke riwayat pesan dan menangani kesalahan yang terjadi selama streaming.
Sistem RAG selesai. Pengguna kini dapat mengunggah dokumen, memprosesnya dengan Docling, mengeksplorasi strukturnya, dan bercakap secara alami tentang kontennya. Agen menelusur secara cerdas, mengutip sumber, dan mengalirkan jawaban untuk pengalaman yang mulus.
Pengujian dan Validasi
Document Intelligence Assistant Anda selesai. Sebelum melakukan deployment atau berbagi, Anda harus menguji aplikasi untuk memverifikasi semuanya bekerja dengan benar.
Streamlit akan membuka jendela peramban di http://localhost:8501. Anda akan melihat sidebar dengan kontrol unggah dan dua tab di area utama.
Menguji pipeline pemrosesan dokumen
Unggah dokumen PDF contoh dengan tabel dan gambar untuk menguji kemampuan pemrosesan penuh:
Klik “Upload Documents” di sidebar
Pilih file PDF (idealnya yang memiliki tabel, gambar, dan heading yang jelas)
Klik “Process & Index”
Perhatikan indikator pemrosesan:
“Processing documents with Docling…” (20–60 detik tergantung ukuran dokumen)
“Chunking documents…”
“Creating vector store…”
“Creating agent…”
Jika selesai, Anda akan melihat “✅ Ready to chat!” di bagian status.
Menguji visualisasi struktur dokumen
Beralih ke tab “Document Structure” untuk memverifikasi Docling mengekstrak semuanya dengan benar:
Tab Summary: Periksa jumlahnya sesuai dengan dokumen Anda (halaman, tabel, gambar, elemen teks)
Tab Hierarchy: Verifikasi heading muncul dalam urutan yang benar dengan indentasi yang sesuai
Tab Tables: Pastikan tabel tampil sebagai DataFrame interaktif, bukan teks acak
Tab Images: Verifikasi gambar dirender dengan benar (jika ekstraksi gambar diaktifkan)
Jika tabel tampil kosong atau gambar tidak muncul, periksa bahwa Anda mengaktifkan do_table_structure=True dan generate_picture_images=True di opsi pipeline.
Menguji sistem Q&A
Kembali ke tab “Chat” dan uji agen dengan pertanyaan contoh berikut:
Pertanyaan awal yang baik:
“Dokumen ini tentang apa?”
“Ringkaslah topik utama yang dibahas”
“Tabel apa yang ada di dokumen ini?”
“Daftar gambar atau figur beserta keterangannya”
Pertanyaan spesifik dokumen (sesuaikan berdasarkan dokumen Anda):
“Data apa yang ditampilkan pada Tabel 1?”
“Jelaskan metodologi yang dijelaskan di seksi 3”
“Apa temuan utamanya?”
“Siapa para penulisnya?”
Pertanyaan tindak lanjut untuk menguji memori percakapan:
Setelah menanyakan tentang tabel: “Apa yang data itu sampaikan kepada kita?”
Setelah mendapat jawaban: “Bisakah Anda menjelaskannya dengan lebih sederhana?”
Apa yang perlu diperhatikan
Cari jawaban langsung dari dokumen Anda dengan kutipan sumber, aliran token yang mulus, dan indikator status (“Thinking…”, “Searching documents…”, “Generating answer…”). Waspadai respons generik yang bukan dari dokumen Anda, kutipan sumber yang hilang, respons lambat tanpa indikator status, atau galat tentang kunci API yang hilang.
Verifikasi file .env ada dengan OPENAI_API_KEY=your-key-here
Mulai ulang Streamlit
Pemrosesan memakan waktu lebih dari 60 detik
Normal untuk menjalankan pertama (Docling mengunduh ~500MB model) atau dokumen besar
Agen memberi jawaban generik
Verifikasi dokumen telah diproses dan vector store dibuat
Coba pertanyaan yang lebih spesifik
Tabel tampil sebagai DataFrame kosong
Pastikan do_table_structure=True di PdfPipelineOptions
Coba PDF lain dengan tabel native
Aplikasi kini tervalidasi dan siap digunakan. Anda dapat mengujinya dengan dokumen Anda sendiri atau melakukan deployment agar dapat digunakan orang lain.
Penutup
Sekarang Anda memiliki Document Intelligence Assistant yang berfungsi yang memproses PDF, dokumen Word, presentasi PowerPoint, dan file HTML sambil mempertahankan strukturnya. Docling mengekstrak teks, tabel, gambar, dan hierarki dokumen yang dapat dieksplorasi pengguna melalui tab interaktif. Sistem RAG, dipadukan dengan ChromaDB dan LangGraph, memungkinkan Q&A percakapan dengan kutipan sumber yang dialirkan secara real-time. Ini menunjukkan bagaimana pemrosesan dokumen yang sadar struktur meningkatkan kualitas pengambilan dibandingkan ekstraksi teks dasar. Kode sumber lengkap tersedia direpositori GitHub.
Fondasi ini membuka banyak arah pengembangan:
Tambahkan pemrosesan batch dan penyimpanan vektor persisten
Saya adalah kreator konten data science dengan pengalaman lebih dari 2 tahun dan salah satu dengan jumlah pengikut terbesar di Medium. Saya suka menulis artikel mendalam tentang AI dan ML dengan sedikit gaya sarkastik—karena harus ada sesuatu untuk membuatnya tidak terlalu membosankan. Saya telah menghasilkan lebih dari 130 artikel dan satu kursus di DataCamp, dengan satu lagi sedang dalam proses. Konten saya telah dilihat oleh lebih dari 5 juta pasang mata, dan 20 ribu di antaranya menjadi pengikut di Medium dan LinkedIn.
Kuasai pertanyaan wawancara basis data, dari konsep SQL dasar hingga skenario desain sistem tingkat lanjut. Panduan mendalam ini mencakup semua yang Anda perlukan untuk sukses di wawancara DBMS dan meraih peran berikutnya.
Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Dari pengujian kode dan penerapan kontrol versi hingga pengambilan data web dan pengembangan paket, ambil langkah selanjutnya dalam perjalanan Anda sebagai pengembang Python!
Alat ekstraksi PDF tradisional seperti pypdf atau PDFMiner memberi Anda teks mentah namun kehilangan struktur dokumen. Tabel menjadi teks acak, header bercampur dengan isi, dan gambar menghilang. Untuk sistem RAG, data berantakan ini berarti pengambilan yang buruk dan jawaban yang tidak andal. Docling adalah toolkit open-source dari IBM Research yang menggunakan model visi komputer untuk memahami tata letak dokumen, menjaga tabel, gambar, heading, dan strukturnya. Docling memproses dokumen hingga 30 kali lebih cepat dibanding metode berbasis OCR tradisional dan berjalan secara lokal di mesin Anda.
Dalam tutorial ini, kita akan menggunakan Docling untuk membangun Document Intelligence Assistant — aplikasi web Streamlit yang memungkinkan Anda mengunggah dokumen, memvisualisasikan strukturnya, dan mengajukan pertanyaan menggunakan chatbot bertenaga RAG. Anda akan mempelajari cara memproses dokumen multi-format dengan Docling, mengekstrak dan menampilkan tabel serta gambar, membangun vector store dengan ChromaDB, dan membuat agen percakapan dengan LangGraph. Di akhir, Anda akan memiliki aplikasi yang berfungsi untuk mengubah dokumen kompleks menjadi data terstruktur dan memungkinkan tanya jawab cerdas.
Pratinjau aplikasi:
Visualisasi struktur dokumen:
Prasyarat
Sebelum memulai tutorial ini, Anda sebaiknya sudah memiliki:
Keterampilan teknis: Nyaman dengan kelas Python, dekorator, type hints, dan context manager. Kita akan menggunakan operasi async dan pola factory di seluruh bagian. Pemahaman tentang cara model bahasa besar bekerja dengan prompt, token, dan embedding diperlukan. Keakraban dengan sistem retrieval-augmented generation dan basis data vektor membantu namun tidak wajib — kami akan menjelaskan konsep inti sambil membangun.
Lingkungan pengembangan: Python 3.10 atau lebih baru dengan pip untuk manajemen paket. Editor kode seperti VS Code direkomendasikan. Anda memerlukan kunci API OpenAI dari platform.openai.com — biaya pemrosesan sekitar $0,10–0,20 per dokumen.
Komitmen waktu: Sediakan 60–90 menit untuk menyelesaikan tutorial, termasuk membaca penjelasan, menulis kode, dan menguji aplikasi. Tutorial ini mengasumsikan keterampilan Python tingkat menengah.
Memahami Docling: Fitur dan Kapabilitas
Kebanyakan alat pemrosesan dokumen memperlakukan PDF seperti file gambar atau aliran teks. Mereka menjalankan OCR pada setiap halaman atau mengekstrak teks biasa tanpa memahami apa yang dibaca. Docling mengambil pendekatan berbeda. Ini adalah toolkit open-source dari IBM Research yang menggunakan model visi komputer untuk memahami struktur dokumen seperti halnya manusia.
Saat Anda memasukkan dokumen ke Docling, dua model AI menganalisisnya:
Model-model ini memahami bahwa dokumen bukan hanya gumpalan teks. Dokumen memiliki hierarki, relasi, dan makna.
Pemahaman struktur ini penting untuk sistem RAG. Saat Anda membangun aplikasi retrieval-augmented generation, kualitas pemrosesan dokumen berdampak langsung pada akurasi pengambilan. Jika ekstraksi PDF Anda mengubah tabel finansial menjadi teks acak, pencarian vektor Anda akan mengambil sampah. Docling mempertahankan struktur, sehingga saat Anda melakukan chunking dan embedding dokumen, Anda bekerja dengan data yang bersih dan terorganisasi.
Docling mendukung berbagai format dokumen secara default:
Anda juga dapat mengaktifkan OCR untuk dokumen hasil pindaian menggunakan mesin seperti EasyOCR, Tesseract, atau RapidOCR. Toolkit ini mengekspor ke beberapa format termasuk Markdown (bagus untuk LLM), JSON (untuk pipeline data terstruktur), dan DocTags (format yang menangkap elemen kompleks seperti persamaan matematika dan blok kode).
Selain fleksibilitas format, Docling menawarkan keunggulan performa. Pemrosesan dokumen berbasis OCR tradisional lambat karena memperlakukan setiap halaman sebagai gambar yang perlu dikenali karakternya. Docling melewati langkah ini untuk dokumen digital, menawarkan pemrosesan yang jauh lebih cepat. Docling berjalan secara lokal di perangkat keras umum, jadi Anda tidak membayar biaya API atau mengirim dokumen sensitif ke layanan pihak ketiga. Kecepatan pemrosesan bervariasi berdasarkan kompleksitas dokumen, jumlah halaman, dan spesifikasi perangkat keras, dengan performa tipikal mulai dari kurang dari satu detik hingga beberapa detik per halaman pada perangkat modern.
IBM terus meningkatkan kapabilitas Docling. Mereka merilis Granite-Docling, model visi-bahasa dengan 258 juta parameter yang unggul pada tata letak kompleks dan menawarkan dukungan multibahasa eksperimental (dengan bahasa Inggris sebagai bahasa utama dan dukungan awal untuk Arab, Tionghoa, dan Jepang). Toolkit ini sekarang juga mendukung ekstraksi gambar dengan resolusi yang dapat dikonfigurasi, yang akan kita gunakan dalam aplikasi untuk menampilkan gambar aktual dari PDF berdampingan dengan konten teksnya.
Untuk kasus penggunaan kita, Docling masuk akal karena kita memerlukan data terstruktur, bukan hanya teks mentah. Jika Anda hanya butuh ekstraksi teks dasar, alat yang lebih sederhana seperti pypdf mungkin cukup. Namun karena kita membangun aplikasi AI yang memproses dokumen untuk analisis dan percakapan, pemrosesan sadar struktur milik Docling adalah pilihan yang lebih baik. Docling sangat berharga saat bekerja dengan dokumen teknis, makalah riset, atau laporan bisnis di mana tabel dan tata letak penting.
Menyiapkan Lingkungan Pengembangan
Sebelum kita mulai membangun pemroses dokumen, Anda perlu menyiapkan struktur proyek dan menginstal paket yang diperlukan. Bagian ini membahas penyiapan awal: membuat direktori, menginstal dependensi, dan mengonfigurasi kunci API Anda.
Membuat struktur proyek
Mulailah dengan membuat direktori baru untuk proyek Anda:
Di dalam direktori ini, buat folder
src/untuk modul Python Anda:File
__init__.pymemberi tahu Python bahwasrc/adalah sebuah paket, sehingga memungkinkan impor sepertifrom src.document_processor import DocumentProcessor.Struktur proyek Anda:
Menginstal dependensi
Buat file
requirements.txtdi root proyek Anda:Kendala
numpy<2ada karena dependensi Docling (TensorFlow dan Transformers) memerlukan NumPy 1.x.Instal paket-paket tersebut:
Instalasi pertama memakan waktu beberapa menit karena Docling mengunduh model AI pra-latih (~500MB). Model-model ini menangani analisis tata letak dan pengenalan struktur tabel. Model disimpan di cache lokal, sehingga menjalankan ulang berikutnya lebih cepat.
Mengonfigurasi variabel lingkungan
Buat file
.envuntuk menyimpan kunci API OpenAI Anda:Dapatkan kunci API Anda dari platform.openai.com. Anda akan membutuhkannya untuk embeddings dan agen chat.
Buat templat
.env.example:Tambahkan
.gitignoreuntuk menghindari melakukan commit data sensitif:Membuat file aplikasi utama
Buat
app.pydi root proyek Anda. Kita akan membangun file ini secara bertahap sepanjang tutorial. Untuk sekarang, tambahkan impor dan konfigurasi dasar:Uji penyiapan Anda:
Streamlit membuka jendela peramban di
http://localhost:8501yang menampilkan halaman dasar Anda.Struktur proyek akhir Anda (sebelum kita menambahkan skrip baru di bagian berikut):
Dengan lingkungan siap, kita bisa membangun pemroses dokumen yang menggunakan Docling untuk mengekstrak struktur dari file yang diunggah.
> Catatan: Bagian di bawah akan menguraikan skrip aplikasi dalam potongan. Jadi, untuk melihat gambaran utuh dan mengikuti dengan mudah, kami menyarankan membuka repositori GitHub untuk proyek ini di tab terpisah.
Membangun Pemroses Dokumen Inti
📄 Skrip lengkap: src/document_processor.py
Pemroses dokumen adalah tempat Docling bekerja. Komponen ini mengambil file yang diunggah dan mengubahnya menjadi data terstruktur yang dapat kita gunakan untuk RAG dan visualisasi. Kita memerlukan dua keluaran dari proses ini: teks markdown bersih untuk vector store dan objek dokumen Docling asli yang mempertahankan semua informasi struktural seperti tabel dan gambar.
Mari kita bangun ini dengan membuat file baru bernama
document_processor.pydi direktorisrc/Anda. Kita akan membuat kelasDocumentProcessoryang mengonfigurasi pipeline pemrosesan Docling dan menangani unggahan file.Mengonfigurasi opsi pipeline
Docling memungkinkan Anda mengontrol cara memproses dokumen melalui opsi pipeline. Untuk PDF, Anda dapat menyalakan OCR untuk dokumen hasil pindaian, mengaktifkan pengenalan struktur tabel, dan mengekstrak gambar:
Kita membuat objek
PdfPipelineOptionsuntuk mengonfigurasi cara Docling memproses file PDF. Setiap opsi mengontrol kemampuan pemrosesan tertentu:do_ocrmengaktifkan optical character recognition untuk dokumen hasil pindaian,do_table_structuremengaktifkan deteksi dan parsing tabel,generate_picture_imagesmemberi tahu Docling untuk mengekstrak gambar tersemat sebagai objek PIL, danimages_scalemenetapkan pengali resolusi untuk gambar yang diekstraksi.DocumentConverteradalah mesin pemrosesan utama Docling. Kita menginisialisasinya dengan opsi pipeline PDF kita yang dibungkus dalam objekPdfFormatOption, yang mengaitkan pengaturan ini secara khusus dengan file input PDF.Mari lihat apa yang dilakukan setiap opsi pipeline. Flag
do_ocrmengaktifkan optical character recognition. Saat memproses PDF digital dengan lapisan teks tersemat, Docling secara otomatis melewati OCR untuk menghemat waktu. Untuk dokumen hasil pindaian atau gambar yang berisi teks, pengaturan ini memberi tahu Docling untuk menjalankan model visi guna mengekstrak teks.Opsi
do_table_structuremengaktifkan pengenalan struktur tabel. Tanpa ini, tabel diekstrak sebagai teks biasa dengan format yang hilang. Dengan ini aktif, Docling menggunakan model AI TableFormer untuk mengidentifikasi baris, kolom, header, dan relasi sel. Representasi terstruktur ini memungkinkan Anda mengekspor tabel sebagai pandas DataFrame nanti, menjaga format tabular.Mengatur
generate_picture_imageskeTruemengaktifkan ekstraksi gambar. Secara default, Docling hanya mencatat lokasi gambar tanpa mengekstrak gambar sebenarnya. Mengaktifkan ini memberi Anda objek gambar PIL yang bisa ditampilkan di UI atau diproses dengan model visi. Parameterimages_scalemengontrol resolusi ekstraksi—nilai 2.0 menggandakan resolusi untuk kualitas lebih baik saat menampilkan gambar atau menjalankan analisis tambahan.Memproses file yang diunggah
Dengan pipeline terkonfigurasi, kita dapat menambahkan metode yang memproses file yang diunggah melalui Streamlit. Metode ini menangani objek file Streamlit, menyimpannya sementara, menjalankan konversi Docling, dan mengembalikan markdown untuk RAG serta dokumen Docling untuk visualisasi:
Streamlit menyediakan file yang diunggah sebagai objek file dalam memori, tetapi Docling memerlukan file aktual di disk untuk diproses. Kita membuat direktori sementara dan menulis setiap file yang diunggah ke sana, mempertahankan nama file aslinya.
Panggilan
converter.convert()menjalankan pipeline analisis dokumen penuh milik Docling. Docling mengidentifikasi tata letak dokumen, menerapkan OCR jika perlu, mendeteksi tabel dan gambar, dan membangun representasi terstruktur. Harapkan 20–30 detik untuk pemrosesan tanpa ekstraksi gambar, atau 40–60 detik dengan gambar diaktifkan.Setelah konversi selesai, kita ekspor ke format markdown yang menghasilkan teks bersih dan ramah LLM dengan format yang terjaga — header tetap header, daftar tetap terstruktur, dan tabel dikonversi menjadi tabel markdown.
Kita membuat dua representasi untuk setiap dokumen yang diproses. Objek
DocumentLangChain berisi teks markdown sebagaipage_contentdengan metadata terkait—ini masuk ke vector store untuk RAG. Objek dokumen Docling asli disimpan terpisah bersama nama filenya, mempertahankan semua informasi struktural (tabel, gambar, hierarki) untuk visualisasi nanti.Blok
finallymemastikan file sementara dibersihkan terlepas dari apakah pemrosesan berhasil atau gagal. Kita mengembalikan tuple yang berisi kedua representasi dokumen: dokumen LangChain untuk sistem RAG dan dokumen Docling untuk visualisasi struktur.Pemroses dokumen sekarang siap. Selanjutnya, kita akan membangun antarmuka Streamlit yang memungkinkan pengguna mengunggah file dan melihat status pemrosesan.
Mengimplementasikan Visualisasi Struktur Dokumen
📄 Skrip lengkap: src/structure_visualizer.py
Sekarang kita dapat memproses dokumen dengan Docling, kita perlu cara untuk memvisualisasikan apa yang diekstrak. Objek dokumen Docling mentah berisi informasi struktural yang kaya (heading, tabel, gambar, dan metadata) tetapi bentuk aslinya tidak ramah pengguna. Kita akan membangun lapisan visualisasi yang mengubah data ini menjadi antarmuka interaktif dengan empat tampilan: dasbor ringkasan, kerangka hierarkis, tabel interaktif, dan gambar yang diekstrak.
Buat file baru bernama
structure_visualizer.pydi direktorisrc/Anda. Komponen ini akan mengurai struktur dokumen Docling dan mengorganisasikannya untuk ditampilkan.Membangun kelas visualizer
Mulailah dengan membuat kelas yang membungkus dokumen Docling dan menyediakan metode untuk mengekstrak berbagai elemen struktural:
Inisialisator menerima objek
DoclingDocument(objek yang sama yang dikembalikan olehDocumentConverter.convert()). Objek ini berisi semua yang diekstrak Docling dari dokumen. Atributtextsberisi semua elemen teks dengan label dan posisinya,tablesmenyimpan data tabel dengan informasi struktur,picturesmenyimpan metadata gambar dan data gambar aktual, danpagesmenyediakan informasi tingkat halaman.Mengekstrak hierarki dokumen
Dokumen memiliki struktur yang melampaui paragraf. Heading menciptakan hierarki yang membantu pembaca menavigasi konten. Berikut cara mengekstrak hierarki ini:
Kita melakukan iterasi melalui semua item teks dalam dokumen, memfilter yang memiliki label mengandung
header. Setiap item teks memiliki atributlabelyang ditetapkan Docling selama analisis tata letak. Label umum termasuksection_header,page_header,title, dantextbiasa. Atributprov(kependekan dari provenance) berisi informasi posisi, termasuk halaman tempat elemen muncul. Kita mengekstrak teks heading, nomor halamannya, dan menyimpulkan tingkat hierarkinya dari tipe label.Saat menampilkan kerangka, tingkat heading menentukan indentasi. Metode pembantu memetakan tipe label ke level numerik:
Ini membuat hierarki di mana judul dokumen adalah level 1, header seksi level 2, subseksi level 3, dan header lainnya default ke level 4.
Mengonversi tabel ke DataFrame
Dengan hierarki dokumen diekstraksi, mari tangani tabel. Berbeda dengan ekstraksi teks sederhana yang mengubah tabel menjadi string acak, Docling mempertahankan strukturnya sebagai salah satu fitur paling berharganya:
Metode
table.export_to_dataframe(doc=self.doc)mengonversi representasi tabel Docling menjadi pandas DataFrame. Kita mengekstrak keterangan (caption) dan nomor halaman jika tersedia.Mengekstrak gambar dengan PIL
Selain tabel, Docling dapat mengekstrak data gambar aktual dari dokumen. Ini adalah fitur yang lebih baru yang melampaui sekadar melacak posisi gambar — Docling mengambil byte gambar sebenarnya sehingga Anda dapat menampilkannya. (Kita mengaktifkan ini di opsi pipeline pemroses dokumen.)
Setiap gambar memiliki informasi provenance, termasuk nomor halaman dan koordinat bounding box. Bounding box mendefinisikan posisi gambar pada halaman menggunakan koordinat kiri, atas, kanan, dan bawah.
Saat ekstraksi gambar diaktifkan, objek gambar memiliki atribut
imageyang berisi data gambar aktual. Kita mengakses gambar PIL melaluipicture.image.pil_image, yang mengembalikan objek PIL Image yang dapat ditampilkan langsung oleh Streamlit denganst.image(). Blok try-except menangani kasus di mana ekstraksi gambar gagal atau tidak diaktifkan, dengan mulus kembali hanya menampilkan metadata.Membuat ringkasan dokumen
Visualizer membutuhkan satu metode lagi: ringkasan tingkat tinggi yang memberi pengguna gambaran struktur dokumen:
Kita menghitung jumlah halaman, elemen teks, tabel, dan gambar dalam dokumen. Kamus
text_typesmerinci elemen teks berdasarkan labelnya, menunjukkan berapa banyak judul, header, paragraf, dan elemen lain yang diidentifikasi Docling. Ini memberi pengguna gambaran cepat tentang struktur dan kompleksitas dokumen.Menambahkan visualizer ke Streamlit
Dengan visualizer selesai, mari integrasikan ke Streamlit dengan empat tab: Summary, Hierarchy, Tables, dan Images.
Kita membuat dropdown untuk memungkinkan pengguna memilih dokumen mana yang akan dianalisis (berguna saat banyak file diunggah). Setelah mendapatkan dokumen Docling yang dipilih dari session state, kita membuat instance visualizer dan membuat empat tab untuk tampilan berbeda.
Visualizer struktur telah selesai. Pengguna dapat mengunggah dokumen dan langsung melihat anatominya — berapa banyak halaman, tabel, dan gambar yang dikandungnya, struktur hierarkis dengan heading dan seksi, tabel interaktif yang bisa dijelajahi, serta gambar aktual yang diekstrak dari dokumen. Transparansi ini membantu pengguna memahami apa yang diekstrak Docling dan membangun kepercayaan pada sistem.
Dengan pemrosesan dokumen dan visualisasi yang berfungsi, kita dapat membangun sistem RAG yang memungkinkan tanya jawab atas dokumen yang telah diproses ini.
Membangun Vector Store RAG dan Alat Pencarian
📄 Skrip lengkap: src/vectorstore.py | src/tools.py
Dengan pemrosesan dokumen dan visualisasi selesai, kita dapat membangun kapabilitas Q&A. RAG (Retrieval Augmented Generation) memungkinkan pengguna mengajukan pertanyaan tentang dokumen mereka. Sistem mengonversi dokumen menjadi embedding, menyimpannya di basis data vektor, dan mengambil potongan relevan untuk menjawab pertanyaan.
Bagian ini membahas dua komponen: pengelola vector store yang melakukan chunking dan embedding dokumen, serta alat pencarian yang mengambil informasi relevan.
Menyiapkan vector store
Vector store adalah tempat embedding dokumen berada. Saat pengguna mengajukan pertanyaan, kita mencari potongan relevan di store ini dan memberikannya ke LLM sebagai konteks.
Buat
vectorstore.pydi direktorisrc/Anda:Impor ini menghadirkan komponen LangChain untuk penanganan dokumen, pemecahan teks, pembuatan embedding, dan penyimpanan vektor ChromaDB. Berikut cara kerjanya bersama:
Kita menginisialisasi dua komponen kunci. Model
text-embedding-3-smalldari OpenAI mengonversi teks menjadi vektor. Model ini lebih kecil dan lebih cepat daripadatext-embedding-3-large, yang penting saat melakukan embedding ratusan potongan.RecursiveCharacterTextSplittermembagi dokumen menjadi potongan 1000 karakter dengan overlap 100 karakter, memastikan informasi penting di batas potongan tidak terpotong di tengah kalimat.Mengapa 1000 karakter?
1000 karakter menyeimbangkan presisi dan konteks — potongan lebih kecil memberi pengambilan yang presisi namun kehilangan konteks, potongan lebih besar menjaga konteks namun menurunkan relevansi.
Tambahkan metode chunking:
Pemecah menangani metadata secara otomatis, mempertahankan informasi dari dokumen asli. Setiap potongan mengetahui dari file mana asalnya, yang menjadi penting saat agen mengutip sumber dalam jawabannya.
Sekarang tambahkan metode yang membuat vector store:
ChromaDB menangani pekerjaan berat. ChromaDB melakukan embedding setiap potongan menggunakan model embedding kita dan menyimpan vektor di memori. Saat Anda menjalankan pencarian kemiripan, ChromaDB menghitung cosine similarity antara vektor kueri dan semua vektor dokumen, mengembalikan kecocokan terdekat.
Membuat alat pencarian
Dengan vector store siap menangani embedding, kita perlu memberi agen cara untuk menanyakannya. Tools adalah fungsi yang bisa dipanggil LLM saat membutuhkan informasi yang tidak dimilikinya.
Buat
tools.pydi direktorisrc/Anda:Fungsi factory ini menggunakan pola closure: menerima vector store dan mengembalikan tool yang dapat menelusurinya. Tool mempertahankan akses ke vector store tanpa perlu variabel global.
Dekorator
@toolmengubah fungsi ini menjadi tool LangChain yang dapat dipanggil agen. Type hintAnnotatedmendeskripsikan parameter, membantu LLM memahami apa yang harus dilewatkan saat memanggil tool.Kita mengambil 8 potongan serupa (k=8) dari vector store. Ini memberi LLM cukup konteks tanpa membebani dengan informasi berulang. Jumlah yang tepat bergantung pada tipe dokumen—dokumen teknis dengan informasi padat mungkin lebih baik dengan potongan lebih sedikit (k=4–6), sementara dokumen naratif mungkin mendapatkan manfaat dari lebih banyak (k=10–12).
Kita memformat setiap potongan dengan nama file sumbernya, lalu menggabungkannya dengan pemisah. LLM menerima konteks terstruktur ini dan menggunakannya untuk menghasilkan jawaban sambil mengutip sumber.
Penanganan galat memastikan agen menerima pesan yang jelas jika pencarian gagal, alih-alih crash. Fungsi mengembalikan tool yang terkonfigurasi siap digunakan agen.
Vector store dan alat pencarian sekarang siap. Selanjutnya, kita akan membangun agen LangGraph yang mengorkestrasi pengambilan dan generasi untuk menjawab pertanyaan pengguna.
Membuat Agen LangGraph dengan Streaming
📄 Skrip lengkap: src/agent.py
Alat pencarian dapat menanyakan vector store, tetapi membutuhkan koordinator yang cerdas. Agen LangGraph memutuskan kapan harus mencari, menafsirkan hasil, dan menghasilkan jawaban bahasa alami. Kita juga akan mengimplementasikan streaming untuk menampilkan progres secara real-time.
Membangun agen LangGraph
Agen menerima pertanyaan, memutuskan kapan mencari dokumen, dan menghasilkan jawaban berdasarkan konteks yang diambil.
Buat
agent.pydi direktorisrc/Anda:Kita mengimpor komponen untuk penanganan tool, model chat OpenAI, implementasi agen ReAct LangGraph, dan memori percakapan.
Kita menggunakan
gpt-4o-minialih-alihgpt-5karena lebih cepat dan lebih murah namun tetap mampu menangani Q&A dokumen dengan baik. Temperature disetel ke 0 untuk respons yang konsisten dan faktual.MemorySavermemberi agen memori percakapan, sehingga mengingat pertukaran sebelumnya dalam satu sesi.create_react_agentmilik LangGraph mengimplementasikan pola ReAct (Reasoning + Acting). Agen bernalar tentang apa yang perlu dilakukan, mengambil tindakan menggunakan tools, mengamati hasil, dan mengulang sampai memperoleh jawaban. Pola ini bekerja baik untuk RAG karena agen dapat memutuskan kapan harus menelusur dan bagaimana menggunakan konteks yang diambil.Mengimplementasikan respons streaming
Agen sekarang dapat menelusur dan menghasilkan jawaban, namun pengguna tidak seharusnya menunggu 10 detik menatap layar kosong saat agen bekerja. Streaming menampilkan progres secara real-time — pertama indikator “thinking” muncul, lalu “searching”, kemudian jawaban muncul token demi token.
Perbarui fungsi
render_chat()diapp.pyuntuk melengkapi penanganan respons yang sebelumnya kita tandai TODO:Kita menambahkan pesan pengguna ke riwayat percakapan dan menampilkannya di antarmuka chat.
Kita membuat placeholder untuk indikator status dan pesan respons. Konfigurasi mencakup thread ID yang digunakan LangGraph untuk mempertahankan memori percakapan lintas giliran.
Fungsi generator ini memproses output stream LangGraph. Kita melacak status dengan flag untuk menampilkan pesan status yang sesuai saat agen menjalankan alur kerjanya.
Parameter
stream_mode="messages"memberi kita token LLM nyata saat dihasilkan, bukan hanya output akhir. LangGraph memancarkan event sepanjang eksekusi agen—saat mulai berpikir, saat memanggil tools, dan saat menghasilkan teks.Ketika node agen mulai menghasilkan jawaban akhir, kita memperbarui status dan mulai mengalirkan token konten. Setiap token langsung ditampilkan, menciptakan efek mengetik yang mulus.
Setelah streaming selesai, kita menghapus indikator status.
st.write_stream()milik Streamlit menangani tampilan token demi token secara otomatis, mengakumulasi token dan memperbarui UI dengan mulus. Hasilnya adalah pengalaman chat yang terasa responsif dan memberi keyakinan kepada pengguna bahwa sistem sedang bekerja.Kita menyimpan respons lengkap ke riwayat pesan dan menangani kesalahan yang terjadi selama streaming.
Sistem RAG selesai. Pengguna kini dapat mengunggah dokumen, memprosesnya dengan Docling, mengeksplorasi strukturnya, dan bercakap secara alami tentang kontennya. Agen menelusur secara cerdas, mengutip sumber, dan mengalirkan jawaban untuk pengalaman yang mulus.
Pengujian dan Validasi
Document Intelligence Assistant Anda selesai. Sebelum melakukan deployment atau berbagi, Anda harus menguji aplikasi untuk memverifikasi semuanya bekerja dengan benar.
Menjalankan aplikasi
Mulai aplikasi dari root proyek Anda:
Streamlit akan membuka jendela peramban di
http://localhost:8501. Anda akan melihat sidebar dengan kontrol unggah dan dua tab di area utama.Menguji pipeline pemrosesan dokumen
Unggah dokumen PDF contoh dengan tabel dan gambar untuk menguji kemampuan pemrosesan penuh:
Perhatikan indikator pemrosesan:
Jika selesai, Anda akan melihat “✅ Ready to chat!” di bagian status.
Menguji visualisasi struktur dokumen
Beralih ke tab “Document Structure” untuk memverifikasi Docling mengekstrak semuanya dengan benar:
Jika tabel tampil kosong atau gambar tidak muncul, periksa bahwa Anda mengaktifkan
do_table_structure=Truedangenerate_picture_images=Truedi opsi pipeline.Menguji sistem Q&A
Kembali ke tab “Chat” dan uji agen dengan pertanyaan contoh berikut:
Pertanyaan awal yang baik:
Pertanyaan spesifik dokumen (sesuaikan berdasarkan dokumen Anda):
Pertanyaan tindak lanjut untuk menguji memori percakapan:
Apa yang perlu diperhatikan
Cari jawaban langsung dari dokumen Anda dengan kutipan sumber, aliran token yang mulus, dan indikator status (“Thinking…”, “Searching documents…”, “Generating answer…”). Waspadai respons generik yang bukan dari dokumen Anda, kutipan sumber yang hilang, respons lambat tanpa indikator status, atau galat tentang kunci API yang hilang.
Masalah umum dan perbaikannya
Galat “No module named ‘docling’”
pip install docling langchain langchain-openai langchain-chroma langgraph streamlit streamlit-extras pandas python-dotenvGalat “OpenAI API key not found”
Verifikasi file
.envada denganOPENAI_API_KEY=your-key-hereMulai ulang Streamlit
Pemrosesan memakan waktu lebih dari 60 detik
Agen memberi jawaban generik
Tabel tampil sebagai DataFrame kosong
Pastikan
do_table_structure=TruediPdfPipelineOptionsCoba PDF lain dengan tabel native
Aplikasi kini tervalidasi dan siap digunakan. Anda dapat mengujinya dengan dokumen Anda sendiri atau melakukan deployment agar dapat digunakan orang lain.
Penutup
Sekarang Anda memiliki Document Intelligence Assistant yang berfungsi yang memproses PDF, dokumen Word, presentasi PowerPoint, dan file HTML sambil mempertahankan strukturnya. Docling mengekstrak teks, tabel, gambar, dan hierarki dokumen yang dapat dieksplorasi pengguna melalui tab interaktif. Sistem RAG, dipadukan dengan ChromaDB dan LangGraph, memungkinkan Q&A percakapan dengan kutipan sumber yang dialirkan secara real-time. Ini menunjukkan bagaimana pemrosesan dokumen yang sadar struktur meningkatkan kualitas pengambilan dibandingkan ekstraksi teks dasar. Kode sumber lengkap tersedia di repositori GitHub.
Fondasi ini membuka banyak arah pengembangan:
Untuk eksplorasi lebih dalam tentang sistem RAG dan aplikasi LLM, AI Engineering track kami membahas konsep dan pola yang Anda gunakan di sini.