Quick start: Dựng Vision RAG chạy sau 5 phút
Dùng Tesseract hay PyMuPDF để cào báo cáo tài chính là một cực hình. Cột số nhảy lung tung. Bảng merge ô vỡ nát. Dòng chú thích lẫn vào thân bài.
Vision RAG xử lý việc này theo hướng hoàn toàn khác. Thay vì trích xuất text thô, hệ thống render thẳng trang PDF thành ảnh. Mô hình sau đó nhúng (embed) đồng thời cả tín hiệu chữ lẫn bố cục thị giác vào không gian vector. Nhờ vậy, biểu đồ cột hay bảng đa tầng đều được giữ nguyên ngữ nghĩa không gian.
Dưới đây là cách triển khai nhanh bằng byaldi — thư viện bọc ngoài mô hình ColPali giúp đơn giản hóa toàn bộ khâu indexing và search.
Bước 1: Chuẩn bị môi trường
Bạn cần Python 3.10+ cùng card đồ họa có tối thiểu 8GB VRAM (như RTX 3060 hoặc T4 trên Colab):
# Cài đặt byaldi và các dependency xử lý ảnh
pip install byaldi pdf2image torch torchvision --upgrade
# Bắt buộc cài poppler để render PDF sang image (Ubuntu/Debian)
sudo apt-get update && sudo apt-get install -y poppler-utils
Bước 2: Index tài liệu và tìm kiếm
Tạo file quick_vision_rag.py rồi chạy thử với một file báo cáo tài chính bất kỳ:
from byaldi import RAGMultiModalModel
# Tải checkpoint ColPali v1.2 (dựa trên PaliGemma-3B)
RAG = RAGMultiModalModel.from_pretrained("vidore/colpali-v1.2")
# 1. Đánh chỉ mục trực tiếp file PDF
print("Bắt đầu index tài liệu...")
RAG.index(
input_path="bao_cao_tai_chinh_q3.pdf", # Đường dẫn file hoặc folder chứa nhiều PDF
index_name="financial_reports", # Tên index lưu trữ trên ổ cứng
store_collection_with_index=True, # Lưu kèm bản sao ảnh để tiện preview
overwrite=True
)
# 2. Truy vấn dữ liệu dạng bảng bằng câu hỏi tự nhiên
query = "Doanh thu mảng dịch vụ đám mây tăng trưởng bao nhiêu % so với cùng kỳ?"
results = RAG.search(query, k=3)
# 3. Trích xuất trang liên quan nhất
for idx, res in enumerate(results):
print(f"Top {idx + 1} | Trang: {res.page_num} | Điểm tương đồng: {res.score:.4f}")
Không regex. Không chia chunk text. Hệ thống tự chỉ điểm chính xác trang chứa bảng số liệu bạn cần.
Bên dưới nắp ca-pô: ColPali và Byaldi vận hành thế nào?
Quy trình RAG truyền thống thường đi theo chuỗi:
PDF -> OCR/PyPDF -> Text Chunks -> Bi-Encoder Embedding -> Vector DB
Điểm gãy nằm ở bước đầu tiên. Text thô đánh mất sạch vị trí dòng, cỡ chữ, màu sắc và các đường kẻ phân tách. Với báo cáo tài chính hay hồ sơ kỹ thuật, mất bố cục là mất thông tin.
ColPali: Mang cơ chế Late Interaction lên ảnh tài liệu
ColPali là sự kết hợp giữa kiến trúc ColBERT và Vision-Language Model PaliGemma-3B. Thay vì ép toàn bộ trang giấy vào 1 vector duy nhất (Single-Vector), mô hình băm trang ảnh thành một mạng lưới các patch nhỏ:
- Image tokenization: Mỗi trang tài liệu render ra ảnh (kích thước 448×448) được chia thành 1024 visual tokens. Mỗi token biểu diễn một vùng ảnh cục bộ kèm ngữ cảnh toàn trang.
- Query tokenization: Câu hỏi của người dùng được tách thành các text token (thường từ 10 – 30 tokens).
- Late Interaction (MaxSim): Thay vì tính khoảng cách cosine đơn giản, mô hình cho từng token trong câu hỏi tìm token hình ảnh khớp nhất trên trang. Tổng điểm tương đồng của các cặp tốt nhất này chính là điểm số cuối cùng.
Cách làm này giải quyết bài toán chi tiết nhỏ. Dù con số nằm khuất ở ô dưới cùng bên phải của bảng hay dòng chú thích font 6pt ở chân trang, token câu hỏi vẫn có thể bắt trúng vị trí đó.
Byaldi: Cầu nối biến nghiên cứu thành sản phẩm
Chạy ColPali thuần rất cồng kềnh. Bạn phải tự xử lý việc render PDF, lưu hàng triệu vector đa chiều và viết thuật toán MaxSim tối ưu. Byaldi (do Sean MacAvaney phát triển) giải quyết trọn vẹn lớp kỹ thuật này. Thư viện tích hợp sẵn engine tìm kiếm multi-vector tối ưu bằng C++/CUDA, giúp bạn gọi index và search chỉ với 2 hàm duy nhất.
Nâng cao: Ghép Byaldi với VLM để tạo pipeline Q&A hoàn chỉnh
Tìm đúng trang mới là bước trích xuất. Muốn trả lời trọn vẹn câu hỏi của người dùng, ta lấy trang ảnh tìm được ném vào một mô hình VLM (như GPT-4o-mini hoặc Qwen2-VL-7B) để đọc số liệu:
import base64
from pdf2image import convert_from_path
from byaldi import RAGMultiModalModel
from openai import OpenAI
# 1. Tải lại index đã lưu trên đĩa
RAG = RAGMultiModalModel.from_index(".byaldi/financial_reports")
query = "Biên lợi nhuận gộp quý 3 thay đổi thế nào so với quý 2?"
results = RAG.search(query, k=1)
top_hit = results[0]
# 2. Render trang tìm được sang file ảnh để gửi LLM
pages = convert_from_path("bao_cao_tai_chinh_q3.pdf", first_page=top_hit.page_num, last_page=top_hit.page_num)
image_path = f"page_{top_hit.page_num}.png"
pages[0].save(image_path, "PNG")
def encode_image_base64(path):
with open(path, "rb") as f:
return base64.b64encode(f.read()).decode("utf-8")
# 3. Gửi trang ảnh sang VLM phân tích
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": f"Đọc bảng biểu trong hình và trả lời ngắn gọn: {query}"},
{
"type": "image_url",
"image_url": {"url": f"data:image/png;base64,{encode_image_base64(image_path)}"}
}
]
}
]
)
print("\n--- KẾT QUẢ ---")
print(response.choices[0].message.content)
Kinh nghiệm tối ưu thực chiến khi chạy production
Mô hình chạy mượt trên notebook không đồng nghĩa với việc thả lên server là xong. Dưới đây là 3 điểm mấu chốt bạn cần tính toán trước khi scale.
1. Bài toán RAM và dung lượng lưu trữ Multi-Vector
Mỗi trang PDF sinh ra 1024 vector (chiều dài 128 float16), tốn khoảng 256 KB dữ liệu vector/trang. Con số này tưởng nhỏ nhưng nhân lên rất nhanh:
- Tập 10.000 trang: Chiếm ~2.5 GB RAM chỉ để giữ index.
- Tập 100.000 trang: Ngốn 25 GB RAM. Chi phí phần cứng bắt đầu tăng vọt.
- Giải pháp: Sử dụng định dạng nén Binary Quantization hoặc bfloat16 mà Byaldi hỗ trợ sẵn. Khi quy mô chạm ngưỡng trên 50.000 trang, hãy chuyển sang các vector database hỗ trợ late-interaction gốc như Vespa hoặc Qdrant thay vì lưu file cục bộ.
2. Chuẩn hóa DPI khi render PDF
PaliGemma chuẩn hóa kích thước đầu vào về 448×448 pixel. Tuy nhiên, chất lượng font chữ khi render ban đầu quyết định mô hình có đọc được nội dung hay không.
- DPI < 100: Chữ nhỏ ở phần footnote bị nhòe nét, giảm Recall@k rõ rệt.
- DPI > 200: Không tăng thêm độ chính xác nhưng khiến thời gian convert PDF bằng poppler lâu gấp 3 lần, làm tràn CPU.
- Khuyến nghị: Cố định ở
dpi=150. Đây là mức tối ưu cả về độ nét lẫn tốc độ xử lý I/O.
3. Phối hợp Hybrid: BM25 + Byaldi
ColPali rất mạnh về ngữ nghĩa không gian và bảng biểu. Dẫu vậy, mô hình vision đôi khi bỏ sót các chuỗi ký tự ngẫu nhiên như mã số thuế, số seri hóa đơn hoặc mã hợp đồng.
Pipeline thực tế nên kết hợp song song:
- BM25 / Meilisearch: Bắt chính xác chuỗi ký tự định danh (mã hóa đơn, SKU, tên riêng).
- Byaldi (ColPali): Bắt ngữ nghĩa tổng quan, sơ đồ quy trình và cấu trúc bảng số liệu.
- Reranker: Lấy Top 10 của cả hai nhánh, chạy qua cross-encoder để chọn ra 2-3 trang chuẩn nhất trước khi gọi API VLM. Cách này vừa tăng Recall, vừa tiết kiệm đáng kể tiền token hình ảnh hàng tháng.

