Sáu tháng trước, đội ngũ của chúng tôi đã vấp phải một rào cản nan giải mà hầu như hệ thống RAG (Retrieval-Augmented Generation) nào khi đưa vào production cũng gặp phải: trích xuất các file PDF lộn xộn, có layout phức tạp. Chúng tôi cần nạp dữ liệu từ hơn 45.000 báo cáo tài chính hàng quý cùng các tài liệu đặc tả kỹ thuật.
Mỗi tài liệu đều chứa dày đặc các layout 3 cột, các bảng cân đối kế toán lồng nhau lên đến 12 cột, cùng ký hiệu toán học đan xen trong dòng văn bản. Những công cụ trích xuất văn bản truyền thống đã làm phẳng mọi thứ thành một mớ hỗn độn không thể đọc nổi, xáo trộn các hàng trong bảng và biến các phương trình vi phân thành các chuỗi ký tự vô nghĩa.
Chúng tôi quyết định thay thế toàn bộ pipeline regex mỏng manh trước đây bằng IBM Docling. Kể từ đợt triển khai đó, tỷ lệ lỗi trích xuất đã giảm khoảng 82%, đồng thời xử lý ổn định hàng chục nghìn trang tài liệu phức tạp. Dưới đây là bài phân tích vận hành thực tế so sánh Docling với các công cụ cũ, cùng hướng dẫn chi tiết cách triển khai công cụ này vào ingestion stack của bạn.
So sánh các phương pháp trích xuất tài liệu
Việc trích xuất tài liệu PDF phục vụ cho tìm kiếm vector (vector retrieval) thường quy về ba hướng kiến trúc chính:
1. Trích xuất dựa trên quy tắc và toạ độ không gian (PyPDF, pdfplumber, PyMuPDF)
Các thư viện này trích xuất ký tự thô dựa trên toạ độ bounding box nhúng sẵn trong luồng byte của file PDF. Chúng chạy cực kỳ nhanh—thường dưới 40 mili-giây mỗi trang—và hầu như không tốn RAM.
- Điểm hạn chế: Dễ bị vấp ở các layout đa cột. Các câu văn thường bị nối dính giữa các cột với nhau. Những bảng không có viền kẻ sẽ bị xáo trộn lung tung, còn các ký hiệu toán học bị rã thành những mảnh biểu tượng rời rạc, không thể tìm kiếm được.
2. Pipeline kết hợp Vision-Language hạng nặng (Nougat, OCR + Multimodal LLM)
Phương pháp này rasterize các trang PDF thành ảnh độ phân giải cao, sau đó chuyển trực tiếp cho các mô hình thị giác hoặc các multimodal LLM hàng đầu như GPT-4o hay Claude 3.5 Sonnet để chuyển đổi sang Markdown.
- Điểm hạn chế: Chi phí gọi Cloud API nhanh chóng tăng vọt ngoài tầm kiểm soát. Việc chuyển đổi một báo cáo 100 trang có thể tốn từ 1,50 USD đến 3,00 USD, trong khi độ trễ tăng vọt lên 3 đến 8 giây mỗi trang. Tệ hơn nữa, LLM vẫn thỉnh thoảng gặp hiện tượng hallucinate (bịa đặt) các con số trong những bảng cân đối kế toán dày đặc dữ liệu.
3. Mô hình thị giác nhận diện cấu trúc bố cục (IBM Docling)
Docling là điểm cân bằng hoàn hảo giữa tốc độ và độ chính xác. Công cụ kết hợp các mô hình nhận diện layout chuyên dụng—được huấn luyện trên các tập dữ liệu như DocLayNet—với các bộ dự đoán cấu trúc bảng chuyên sâu (TableFormer). Docling khôi phục đúng thứ tự đọc tự nhiên, dựng lại các ô bị merge (hợp nhất), và chuyển đổi trực tiếp công thức sang LaTeX mà không cần gọi bất kỳ API bên ngoài nào.
Ưu và nhược điểm: 6 tháng vận hành trong Production
Quá trình xử lý hàng triệu trang tài liệu đã giúp chúng tôi nhận ra đâu là thế mạnh vượt trội của Docling và đâu là những điểm cần phải giám sát chặt chẽ.
Ưu điểm
- Thứ tự đọc đáng tin cậy: Docling bám sát luồng nội dung của các tài liệu nhiều cột một cách chuẩn xác, ngăn các đoạn văn không liên quan bị lẫn lộn vào nhau.
- Cấu trúc bảng bền bỉ: Tái tạo chính xác các tiêu đề nhiều hàng và các ô gộp thành định dạng bảng Markdown hoặc HTML gọn gàng, loại bỏ hoàn toàn tình trạng sai lệch số liệu trong ngữ cảnh truy xuất.
- Hỗ trợ công thức LaTeX gốc: Các biểu thức toán học được phân tích cú pháp thẳng thành chuỗi LaTeX, giữ cho các phương trình hoàn toàn nguyên vẹn cho các mô hình embedding hỗ trợ toán học.
- Chạy độc lập (Air-Gapped) và bảo mật: Mọi thứ đều thực thi hoàn toàn trong VPC nội bộ của bạn—không có bất kỳ hồ sơ tài chính hay bệnh án nhạy cảm nào lọt ra ngoài phạm vi an ninh.
- Chia nhỏ văn bản (Chunking) cho downstream: Các bộ chunker tích hợp sẵn liên kết trực tiếp với LangChain và LlamaIndex mà vẫn tôn trọng ranh giới phân mục của tài liệu.
Nhược điểm
- Đòi hỏi tài nguyên tính toán: Khác với mức chiếm dụng tài nguyên gần như bằng 0 của PyPDF, Docling chạy các mô hình deep learning PyTorch ở tầng dưới. Trên CPU 4 nhân, thời gian xử lý rơi vào khoảng 1,5 đến 3,5 giây mỗi trang. Các hàng đợi thông lượng cao chắc chắn sẽ cần đến tăng tốc GPU.
- Dung lượng bộ nhớ: Mỗi worker process thường tiêu tốn từ 2,5GB đến 4GB RAM. Bạn cần tinh chỉnh mức concurrency của container cẩn thận để tránh bị Kubernetes kill do lỗi tràn bộ nhớ (OOM).
- Chế độ dự phòng cho tài liệu scan: Nếu file PDF thiếu lớp text nhúng sẵn và buộc phải chuyển sang dùng EasyOCR hoặc Tesseract, thời gian xử lý có thể tăng vọt lên hơn 6 giây mỗi trang.
Kiến trúc đề xuất và thiết lập môi trường
Tuyệt đối không chạy Docling đồng bộ bên trong web application server của bạn. Thay vào đó, hãy tách riêng quy trình ingestion bằng một hàng đợi background worker như Celery, Temporal hoặc AWS SQS. Hãy cấp phát cho mỗi node worker tối thiểu 4 vCPU và 8GB RAM, hoặc gắn thêm một GPU tiết kiệm chi phí như NVIDIA T4 (16GB VRAM), giúp rút ngắn thời gian chuyển đổi trang xuống chỉ còn 350 mili-giây.
Cài đặt gói thư viện cốt lõi cùng với PyTorch có hỗ trợ CUDA:
# Cài đặt Docling và các tiện ích chunking
pip install docling docling-core
# Cài đặt PyTorch với khả năng tăng tốc CUDA 12.1
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
Hãy cố định phiên bản các package này trong file requirements.txt để tránh việc tự động tải về trọng số mô hình mới ngoài ý muốn khi triển khai trên production.
Hướng dẫn triển khai: Xây dựng Extraction Pipeline
Đoạn script dưới đây minh hoạ pipeline trích xuất hoàn chỉnh của chúng tôi: cấu hình bộ phân tích cú pháp, trích xuất bảng biểu cùng công thức có cấu trúc, và tạo chunk embedding.
Bước 1: Chuyển đổi tài liệu và cấu hình tính năng
Docling cung cấp giao diện chuyển đổi cấp cao thông qua DocumentConverter, cho phép kiểm soát chi tiết các engine OCR và độ sâu phân tích bảng:
from pathlib import Path
from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.pipeline_options import PdfPipelineOptions, TableStructureOptions
def configure_converter() -> DocumentConverter:
# Cấu hình các tùy chọn phân tích cú pháp và OCR
pipeline_options = PdfPipelineOptions()
pipeline_options.do_ocr = True
pipeline_options.do_table_structure = True
pipeline_options.table_structure_options = TableStructureOptions(
do_cell_matching=True
)
# Khởi tạo converter với các tùy chọn tùy chỉnh
converter = DocumentConverter(
format_options={
"pdf": PdfFormatOption(pipeline_options=pipeline_options)
}
)
return converter
converter = configure_converter()
result = converter.convert("sample_financial_report.pdf")
doc = result.document
Bước 2: Kiểm tra bảng biểu, công thức và kết quả Markdown
Sau khi quá trình chuyển đổi hoàn tất, bạn có thể truy xuất tài liệu dưới dạng Markdown đồng nhất, hoặc kiểm tra riêng từng thành phần cấu trúc:
# Xuất toàn bộ tài liệu sang Markdown chuẩn sạch
markdown_content = doc.export_to_markdown()
print("--- Mẫu Markdown tài liệu đã trích xuất ---")
print(markdown_content[:500])
# Kiểm tra từng bảng riêng lẻ được phát hiện
print(f"\nSố bảng được phát hiện: {len(doc.tables)}")
for idx, table in enumerate(doc.tables):
print(f"\n--- Bảng {idx + 1} ---")
# Xuất trực tiếp sang Pandas DataFrame
df = table.export_to_dataframe()
print(df.head())
# Trích xuất các biểu thức toán học
if hasattr(doc, "formulas"):
for idx, formula in enumerate(doc.formulas):
print(f"Công thức {idx + 1}: {formula.text}")
Bước 3: Phân đoạn nhận biết cấu trúc bố cục (Layout-Aware Chunking) cho RAG Embedding
Các bộ tách văn bản theo số ký tự cố định thường xuyên cắt ngang giữa chừng các bảng biểu, làm phá vỡ ngữ cảnh ngữ nghĩa trong quá trình tìm kiếm vector. Bộ công cụ HybridChunker của Docling tôn trọng các ranh giới cấu trúc, giúp giữ trọn vẹn các phần dữ liệu liên quan:
from docling.chunking import HybridChunker
# Khởi tạo chunker với mục tiêu 512 token có nhận thức bố cục
chunker = HybridChunker(max_tokens=512, merge_peers=True)
chunks = list(chunker.chunk(doc))
print(f"Tổng số semantic chunk được tạo ra: {len(chunks)}\n")
for idx, chunk in enumerate(chunks[:3]):
print(f"[Chunk {idx + 1}] Độ dài: {len(chunk.text)} ký tự")
print(chunk.text)
# Giữ lại metadata tiêu đề mục để lọc payload vector
print(f"Metadata: {chunk.meta.headings}\n")
Việc đưa các chunk có cấu trúc này vào mô hình embedding sẽ cung cấp cho công cụ truy xuất một ngữ cảnh mạch lạc và rõ ràng. Các bảng biểu được giữ nguyên vẹn, các câu tuân theo thứ tự đọc tự nhiên, và các biểu thức toán học vẫn giữ được chính xác công thức ban đầu.

