AizuDemy

Tutorial Build Local Multimodal RAG: Ollama, Qwen2-VL & LanceDB Python

Tutorial Build Local Multimodal RAG: Ollama, Qwen2-VL & LanceDB Python
🎧
Dengarkan Artikel Ini
Suara AI Otomatis • 7 mnt baca baca
⚡ TL;DR

Poin Kunci Artikel Ini:

  • Ekstraktor teks standar seperti PyPDF, PDFMiner, atau Unstructured merusak tata letak tersebut.
  • Gunakan lingkungan virtual Python 3.10+ untuk menjaga isolasi paket.Pastikan runtime Ollama telah terpasang pada sistem operasi lokal Anda.
  • Saat pengguna mengajukan pertanyaan, LanceDB mencari konteks teks paling relevan.
📋 Daftar Isi Materi Tutup ▴

Limitasi Text-Only RAG dan Masalah Data Visual Enterprise

Retrieval-Augmented Generation (RAG) konvensional berbasis teks murni mengalami kegagalan struktural saat mengolah dokumen bisnis modern. File PDF teknis, laporan keuangan, diagram arsitektur jaringan, flowchart proses, serta dokumen pemindaian (scanned documents) mengandung informasi spasial yang saling terikat. Ekstraktor teks standar seperti PyPDF, PDFMiner, atau Unstructured merusak tata letak tersebut. Elemen kunci seperti header tabel, legenda grafik, dan hubungan hierarkis antar teks hilang selama proses pemotongan teks (chunking).

Mengirimkan dokumen visual sensitif—seperti desain sirkuit paten, rekam medis pasien, dokumen hukum internal, atau cetak biru infrastruktur—ke penyedia Vision-Language Model (VLM) berbasis cloud seperti OpenAI GPT-4o atau Anthropic Claude 3.5 Sonnet menimbulkan risiko serius terhadap privasi data, kepatuhan regulasi (seperti GDPR dan UU PDP), serta beban biaya API yang tinggi pada skala enterprise.

Solusi arsitektural terbaik adalah membangun pipeline Local Multimodal RAG. Dengan menjalankan pemrosesan embedding visual, indeks vektor, dan inferensi model secara lokal, seluruh data sensitif tetap berada di dalam perimeter jaringan privat tanpa mengorbankan kemampuan analisis visual AI.

Arsitektur Sistem Local Multimodal RAG

Sistem ini dirancang menggunakan tiga komponen utama yang terintegrasi secara modular:

  • Ollama: Runtime server lokal berkinerja tinggi berbasis llama.cpp. Mengelola alokasi memori GPU (VRAM), eksekusi kuantisasi, dan eksposur REST API lokal untuk mengoperasikan model Vision-Language secara efisien.
  • Qwen2-VL (Vision-Language): Model open-source unggulan dari Alibaba Cloud. Memiliki arsitektur NaViT (Native Resolution Vision Transformer) dan M-RoPE (Multimodal Rotary Position Embedding) yang mampu mengolah gambar dengan rasio aspek dan resolusi dinamis tanpa distorsi pemotongan (cropping).
  • LanceDB: Vector database embedded berbasis format kolom Apache Arrow. Menyediakan latensi pencarian terendah, penggunaan RAM sangat efisien, dan mendukung ekosistem Python secara native tanpa membutuhkan server database eksternal terpisah.

Langkah 1: Konfigurasi Environment dan Pengunduhan Model

Siapkan peranti lunak dan dependensi Python yang dibutuhkan. Gunakan lingkungan virtual Python 3.10+ untuk menjaga isolasi paket.

pip install lancedb ollama pillow pyarrow pypdfium2 sentence-transformers pandas

Pastikan runtime Ollama telah terpasang pada sistem operasi lokal Anda. Buka terminal dan unduh bobot model Qwen2-VL varian 7 miliar parameter:

ollama pull qwen2-vl:7b

Untuk perangkat dengan keterbatasan VRAM GPU (di bawah 10GB), unduh varian model 2 miliar parameter atau versi kuantisasi 4-bit:

ollama pull qwen2-vl:2b

Langkah 2: Ekstraksi Halaman PDF dan Generasi Konteks Visual

Langkah ini mengonversi halaman dokumen PDF menjadi gambar bitmap beresolusi tinggi, lalu memanfaatkan Qwen2-VL untuk menganalisis dan mendeskripsikan secara eksplisit seluruh konten visual, teks, serta elemen struktur tabel.

import io
import ollama
import pypdfium2 as pdfium
from PIL import Image

def convert_pdf_page_to_image(pdf_path: str, page_index: int = 0, scale: float = 2.0) -> Image.Image:
    """Mengonversi halaman PDF menjadi objek PIL Image dengan resolusi yang disesuaikan."""
    pdf_file = pdfium.PdfDocument(pdf_path)
    page = pdf_file[page_index]
    bitmap = page.render(scale=scale)
    return bitmap.to_pil()

def generate_visual_description(image: Image.Image) -> str:
    """Mengekstrak teks, struktur tabel, dan makna visual dari gambar menggunakan Qwen2-VL lokal."""
    buffer = io.BytesIO()
    image.save(buffer, format="PNG")
    image_bytes = buffer.getvalue()

    prompt_instruction = (
        "Transkripsikan seluruh teks yang ada pada gambar ini secara presisi. "
        "Jika terdapat tabel, rekonstruksi menjadi format Markdown table. "
        "Jelaskan grafik, diagram, atau hubungan elemen visual secara detail dan terstruktur."
    )

    response = ollama.chat(
        model="qwen2-vl:7b",
        messages=[{
            "role": "user",
            "content": prompt_instruction,
            "images": [image_bytes]
        }]
    )
    return response["message"]["content"]

Langkah 3: Pembuatan Skema Data dan Indeksasi Vektor di LanceDB

Rancang skema tabel LanceDB menggunakan Pydantic. Teks hasil analisis deskriptif visual diekstrak menjadi vektor (embedding) menggunakan model embedding lokal untuk pencarian semantik berkecepatan tinggi.

import lancedb
from lancedb.pydantic import LanceModel, Vector
from lancedb.embeddings import get_registry

# Inisialisasi koneksi database di direktori lokal
db = lancedb.connect("./lancedb_multimodal_data")

# Gunakan model embedding teks lokal via BAAI/bge-small-en-v1.5
embedding_func = get_registry().get("sentence-transformers").create(name="BAAI/bge-small-en-v1.5")

class MultimodalDocumentSchema(LanceModel):
    doc_id: str
    page_number: int
    image_path: str
    visual_context: str = embedding_func.SourceField()
    vector: Vector(embedding_func.ndims()) = embedding_func.VectorField()

# Buat atau timpa tabel di LanceDB
table = db.create_table("document_store", schema=MultimodalDocumentSchema, mode="overwrite")

def add_page_to_index(doc_id: str, page_num: int, image_path: str, visual_context: str):
    """Memasukkan rekam data halaman dokumen ke dalam indeks LanceDB."""
    table.add([{
        "doc_id": doc_id,
        "page_number": page_num,
        "image_path": image_path,
        "visual_context": visual_context
    }])

Langkah 4: End-to-End Multimodal Query dan Retrieval Pipeline

Proses pencarian (retrieval) bekerja secara hibrida. Saat pengguna mengajukan pertanyaan, LanceDB mencari konteks teks paling relevan. Gambar halaman dokumen asli terkait dikirimkan kembali ke Qwen2-VL bersama konteks teks untuk memberikan jawaban akhir dengan tingkat akurasi tinggi.

def execute_multimodal_rag(query_text: str, top_k: int = 1) -> str:
    """Menjalankan pipeline pencarian vektor dan sintesis jawaban multimodal."""
    # 1. Pencarian vektor kemiripan semantik di LanceDB
    search_results = table.search(query_text).limit(top_k).to_pandas()
    
    if search_results.empty:
        return "Konteks dokumen tidak ditemukan di dalam basis data lokal."

    best_match = search_results.iloc[0]
    retrieved_context = best_match["visual_context"]
    image_path = best_match["image_path"]

    # 2. Baca file gambar hasil pencarian
    with open(image_path, "rb") as img_file:
        image_data = img_file.read()

    # 3. Formulasi prompt multimodal untuk sintesis akhir
    rag_prompt = (
        f"Gunakan konteks terlampir berikut untuk menjawab pertanyaan pengguna secara akurat.\n\n"
        f"Konteks Terintegrasi: {retrieved_context}\n\n"
        f"Pertanyaan Pengguna: {query_text}"
    )

    response = ollama.chat(
        model="qwen2-vl:7b",
        messages=[{
            "role": "user",
            "content": rag_prompt,
            "images": [image_data]
        }]
    )
    return response["message"]["content"]

Strategi Optimasi VRAM, Resolusi Dinamis, dan Handling Exception

Menjalankan model Vision-Language pada perangkat keras lokal menuntut konsumsi VRAM GPU yang lumayan besar. Resolusi gambar berlebih dapat memicu error CUDA Out-Of-Memory (OOM). Terapkan strategi pengkondisian berikut:

1. Scaling dan Downsizing Resolusi Gambar

Batasi dimensi maksimal piksel sebelum mengumpankan gambar ke dalam model VLM:

def resize_image_aspect_ratio(image: Image.Image, max_dimension: int = 1024) -> Image.Image:
    """Mencegah CUDA OOM dengan membatasi resolusi maksimum gambar."""
    width, height = image.size
    if max(width, height) > max_dimension:
        scale_factor = max_dimension / float(max(width, height))
        new_dimensions = (int(width * scale_factor), int(height * scale_factor))
        return image.resize(new_dimensions, Image.Resampling.LANCZOS)
    return image

2. Matriks Alokasi VRAM berdasarkan Hardware

  • GPU 8GB VRAM (NVIDIA RTX 3060 / RTX 4060): Gunakan model qwen2-vl:2b atau varian kuantisasi qwen2-vl:7b-instruct-q4_K_M dengan batasan resolusi gambar 768px.
  • GPU 12GB - 16GB VRAM (NVIDIA RTX 3060 12GB / RTX 4070 / T4): Gunakan model qwen2-vl:7b versi FP16 / Q8_0 dengan batas resolusi gambar 1024px-1280px.
  • GPU 24GB VRAM (NVIDIA RTX 3090 / RTX 4090): Dapat mengolah multiple image batch secara paralel menggunakan resolusi gambar asli hingga 2K.

3. Defensive Exception Handling untuk Fallback System

def safe_visual_context_generation(image: Image.Image) -> str:
    """Handling penanganan kegagalan VRAM dengan resolusi adaptif secara otomatis."""
    try:
        return generate_visual_description(image)
    except Exception as error_log:
        if "out of memory" in str(error_log).lower():
            # Fallback otomatis ke resolusi lebih rendah jika VRAM penuh
            downscaled_image = resize_image_aspect_ratio(image, max_dimension=512)
            return generate_visual_description(downscaled_image)
        raise error_log

Panduan Desain Arsitektur Produksi dan Monitoring

Saat menaikkan skala implementasi dari lingkungan pengujian (PoC) ke skala produksi enterprise, lakukan verifikasi dan konfigurasi pada beberapa area penting:

  • Sistem Penyimpanan Terdistribusi: Pastikan direktori penyimpanan LanceDB dikonfigurasi pada media penyimpanan NVMe SSD berkecepatan tinggi untuk menjamin nilai IOPS read/write saat proses pencarian vektor berskala jutaan baris.
  • Pipeline OCR Hybrid Fallback: Tambahkan mesin OCR lokal tambahan (seperti PaddleOCR atau Tesseract) sebagai mekanisme pembanding jika dokumen memiliki gangguan visual (noisy scanning) atau tingkat kontras warna yang sangat rendah.
  • Backend Service Integration: Bungkus logika Python di atas ke dalam kerangka kerja FastAPI. Gunakan arsitektur asynchronous queue (seperti Celery atau Redis Queue) untuk menangani tugas pemrosesan PDF dan vektorisasi gambar yang berat tanpa memblokir thread HTTP utama.
  • Visual Frontend UI: Hubungkan REST API lokal ini dengan antarmuka pengguna populer seperti Open WebUI, Streamlit, atau Chainlit untuk menghadirkan pengalaman interaksi obrolan dokumen multimodal yang intuitif bagi pengguna akhir.

Membangun Local Multimodal RAG berbasis Ollama, Qwen2-VL, dan LanceDB memberikan kedaulatan data secara menyeluruh, menghilangkan biaya ketergantungan API cloud, dan menghasilkan analisis dokumen berpresisi tinggi yang tak mampu dicapai oleh RAG berbasis teks murni.

📖 Artikel Terkait