Lewati ke konten utama

RAG Multimodal: Panduan Praktis untuk Belajar dari Dokumen

Pelajari cara membangun pipeline RAG Multimodal untuk sistem tanya jawab visual pada instruksi perakitan IKEA menggunakan OpenAI API dan basis data vektor ChromaDB.
· 11 mnt Baca

Jelajahi bersama AI

Bayangkan Anda sedang merakit furnitur IKEA. Anda menatap langkah 14 pada instruksi, tetapi tidak bisa memahami sekrup mana yang masuk ke mana. Diagramnya menunjukkan sesuatu, tetapi belum nyambung. 

Jika Anda bertanya: "Untuk apa bagian logam kecil di langkah 14?", sebuah sistem RAG (retrieval-augmented generation) berbasis teks standar tidak bisa membantu. Jawabannya ada di diagram itu sendiri, bukan pada teks mana pun di halaman. Instruksi IKEA terkenal tanpa kata.

Di sinilah RAG multimodal berperan.

Dalam tutorial ini, saya akan memandu Anda membangun sistem RAG multimodal yang dapat menjawab pertanyaan tentang dokumen yang berisi gambar, diagram, dan konten campuran. Kita akan menggunakan instruksi perakitan IKEA sebagai studi kasus karena ini adalah uji tekan yang bagus: sebagian besar visual, minim teks, dan jenis hal yang memang sering dibutuhkan orang.

Kita akan menggunakan model OpenAI di sepanjang proses: GPT-4o untuk memahami gambar dan menghasilkan jawaban, serta text-embedding-3-small untuk membuat vektor yang dapat dicari. Untuk mempelajari cara membangun pipeline RAG dengan kerangka lain, saya rekomendasikan kursus RAG dengan LangChain kami.

Pada akhir sesi, Anda akan memiliki sistem yang berfungsi yang dapat dijalankan secara lokal dan diadaptasi untuk dokumen Anda sendiri.

Apa itu RAG Multimodal?

RAG multimodal memperluas pola RAG standar untuk menangani lebih dari sekadar teks. Gagasannya tetap sama, tetapi "konteks" kini mencakup gambar, tabel, diagram, dan konten non-teks lainnya.

Mengapa ini penting? Karena dokumen nyata bukan hanya teks.

Pikirkan jenis dokumen yang benar-benar Anda gunakan: 

  • Manual teknis dengan diagram pengkabelan 
  • Makalah riset dengan plot eksperimen 
  • Laporan keuangan dengan bagan pendapatan
  • Spesifikasi produk dengan gambar komponen 

Sistem yang hanya memproses teks bekerja dengan informasi yang tidak lengkap. Ini seperti mencoba memahami resep hanya dengan membaca daftar bahan dan mengabaikan foto yang menunjukkan seperti apa "aduk lipat perlahan" sebenarnya.

RAG tradisional memperlakukan PDF sebagai aliran karakter. Ia mengekstrak teks, memecahnya menjadi potongan, melakukan embedding potongan-potongan itu, lalu selesai. Setiap gambar dilewati begitu saja, atau mungkin diubah menjadi token placeholder [IMAGE]. Tabel menjadi baris teks yang berantakan dan kehilangan strukturnya.

RAG multimodal mengambil pendekatan berbeda: Gambar dideskripsikan atau di-embed secara langsung, dan tabel diurai ke format terstruktur. Sistem retrieval dapat mencocokkan kueri seperti "diagram topologi jaringan" dengan deskripsi teks arsitektur jaringan sekaligus diagram visualnya. Model generasi dapat memeriksa gambar dan menalar apa yang digambarkannya.

Aspek

RAG Teks Saja

RAG Multimodal

Jenis input

Teks polos, mungkin markdown

Teks, gambar, tabel, diagram, dokumen campuran

Apa yang di-embed

Potongan teks

Potongan teks plus deskripsi gambar atau embedding gambar

Model generasi

LLM apa pun

Model vision-language (GPT-4o, Claude, Gemini)

Terbaik untuk

Dokumen yang kaya teks

Manual, laporan, slide, apa pun yang punya konten visual

Jika "multimodal" masih terasa seperti istilah payung yang kabur, blog ini tentang AI Multimodal adalah penjelasan ringkas dengan contoh konkret.

Bagaimana RAG Multimodal Bekerja?

Pipeline-nya memiliki lebih banyak komponen bergerak dibanding RAG teks saja, tetapi strukturnya serupa. Data diproses dan di-embed, masuk ke penyimpanan vektor, lalu diambil kembali saat seseorang mengajukan pertanyaan.

Multimodal RAG Pipeline

Alurnya seperti ini:

Pemasukan dan parsing dokumen

Dokumen mentah (PDF, gambar, slide) dipecah menjadi bagian-bagiannya. Anda mengekstrak teks, mengidentifikasi gambar, mengurai tabel, dan memahami bagaimana semuanya berelasi secara spasial. Sebuah diagram pada halaman 5 mungkin memiliki keterangan di bawahnya dan referensi di teks halaman 4. Anda perlu menangkap keterkaitan tersebut.

Prapemrosesan konten

Jenis konten yang berbeda perlu perlakuan berbeda. Untuk gambar, ada dua opsi utama: menghasilkan deskripsi teks menggunakan model visi (seperti GPT-4o), atau melakukan embedding gambar secara langsung menggunakan encoder multimodal (seperti CLIP). Pendekatan deskripsi lebih mudah dan bekerja dengan embedding teks standar. Tabel dikonversi ke format terstruktur atau diringkas dalam bahasa alami.

Setelah itu, semuanya diubah menjadi vektor. Jika Anda menggunakan pendekatan deskripsi, Anda cukup melakukan embedding semua teks (teks asli plus deskripsi gambar yang dihasilkan) dengan model embedding teks standar. Kuncinya adalah kueri tentang konten visual kini dapat mencocokkan deskripsi tersebut.

Pengindeksan dan penyimpanan 

Vektor disimpan dalam basis data beserta metadata, termasuk dokumen sumber, nomor halaman, jenis konten, dan untuk gambar, path ke file asli. Anda akan memerlukan path gambar itu nanti saat ingin menampilkannya ke model generasi.

Retrieval

Ketika menerima kueri, lakukan embedding menggunakan model yang sama, cari tetangga terdekat, dan kembalikan kecocokan teratas. Potongan yang diambil bisa merujuk ke teks, gambar, atau keduanya.

Generasi

Kirim kueri beserta konteks yang diambil ke model vision-language. Jika potongan yang diambil merujuk ke gambar, muat gambar-gambar tersebut dan sertakan dalam prompt. Model melihat konteks teks sekaligus gambar aslinya, lalu menghasilkan jawaban.

Langkah generasi adalah tempat GPT-4o benar-benar unggul. Ia dapat melihat diagram IKEA yang menunjukkan penempatan sekrup dan menjelaskan apa yang terjadi dengan bahasa yang lugas. Ia dapat membaca grafik dan merangkum tren. Ia dapat melihat detail pada gambar teknis yang mungkin terlewatkan oleh deskripsi teks.

Mengimplementasikan RAG Multimodal

Mari kita bangun sistemnya. Kita akan membuat sistem yang dapat menjawab pertanyaan tentang instruksi perakitan IKEA, langkah demi langkah dari penyiapan hingga antarmuka kueri yang berfungsi.

Langkah 0: Memilih kerangka dan alat

Untuk tutorial ini, kita buat sederhana:

Komponen

Pilihan

Alasan

Embeddings

text-embedding-3-small

Murah, kualitas bagus, API yang sama

Penyimpanan vektor

ChromaDB

Jalan lokal, tanpa setup, persisten

Model visi

GPT-4o

Terbaik di kelasnya untuk pemahaman gambar

Generasi

GPT-4o

Model yang sama, menangani kedua tugas

Anda bisa menukar alternatif. Pinecone atau Weaviate untuk penyimpanan vektor jika butuh skala. Claude atau Gemini adalah alternatif bagus untuk generasi. Arsitektur umumnya tetap sama dalam kasus tersebut.

Mengapa tidak menggunakan CLIP atau ColPali untuk embeddings? Bisa saja. Itu memungkinkan Anda meng-embed gambar secara langsung tanpa harus membuat deskripsi teks terlebih dahulu. Tetapi menambah kompleksitas dan sering kali membutuhkan GPU. Pendekatan deskripsi bekerja baik untuk konten bergaya dokumen, di mana Anda ingin menangkap detail semantik, bukan sekadar kemiripan visual.

Langkah 1: Menyiapkan lingkungan

Mari mulai dengan dependensi dan variabel lingkungan untuk menyiapkan lingkungan proyek.

Menginstal dependensi

Pertama, Anda memerlukan poppler yang terpasang untuk menangani pemrosesan PDF.

  • Ubuntu/Debian: sudo apt-get install poppler-utils

  • macOS (menggunakan Homebrew): brew install poppler

  • Windows: Unduh dari halaman rilis dan tambahkan ke PATH

Selanjutnya, instal paket Python. Selain ChromaDB, kita membutuhkan pdf2image untuk mengonversi PDF ke gambar, pillow untuk pemrosesan gambar, dan requests untuk mengunduh file PDF.

pip install openai chromadb pdf2image pillow requests

Menyiapkan kunci API dan file

Untuk menggunakan model, setel kunci API OpenAI Anda sebagai variabel lingkungan.

Unix/Linux/macOS:

export OPENAI_API_KEY="your-api-key-here"

Windows: 

setx OPENAI_API_KEY="your-api-key-here"

Sekarang, mari menyiapkan struktur proyek dan mengambil data contoh. Skripnya sudah menyertakan URL unduhan, tetapi jika Anda ingin melihat PDF-nya terlebih dahulu, berikut tautan langsung ke meja MALM dan rangka tempat tidur MALM. 

Buat file bernama step1_setup.py:

"""
Step 1: Setup and Download Sample Data
"""
import os
import requests

# Create directories
for d in ["data", "data/images", "data/cache"]:
    os.makedirs(d, exist_ok=True)
print("✓ Created directories")

# Download IKEA assembly instructions
SAMPLE_PDFS = {
    "malm_desk.pdf": "https://www.ikea.com/us/en/assembly_instructions/malm-desk-white__AA-516949-7-2.pdf",
    "malm_bed.pdf": "https://www.ikea.com/us/en/assembly_instructions/malm-bed-frame-low__AA-75286-15_pub.pdf",
}

for filename, url in SAMPLE_PDFS.items():
    filepath = f"data/{filename}"
    if not os.path.exists(filepath):
        print(f"Downloading {filename}...")
        response = requests.get(url, timeout=30)
        with open(filepath, "wb") as f:
            f.write(response.content)
        print(f"  ✓ Saved ({len(response.content) / 1024:.1f} KB)")
    else:
        print(f"✓ {filename} already exists")

Untuk menyelesaikan penyiapan awal, jalankan skripnya:

python step1_setup.py

Anda akan melihat PDF IKEA diunduh ke folder data/ Anda.

Langkah 2: Prapemrosesan dokumen

Instruksi IKEA itu rumit. Sebagian besar berupa diagram dengan teks minimal. Kita perlu mengonversi halaman PDF menjadi gambar, lalu menggunakan GPT-4o untuk mendeskripsikan apa yang ditampilkan setiap halaman.

Buat step2_preprocess.py:

"""
Step 2: Preprocess Documents
Convert PDFs to images and generate descriptions with GPT-4o
"""
import os
import json
import base64
from pdf2image import convert_from_path
from openai import OpenAI

client = OpenAI()

DATA_DIR = "data"
IMAGES_DIR = "data/images"
CACHE_FILE = "data/cache/descriptions.json"


def convert_pdf_to_images(pdf_path):
    """Convert a PDF to page images."""
    pdf_name = os.path.splitext(os.path.basename(pdf_path))[0]
    images = convert_from_path(pdf_path, dpi=150)
    
    image_paths = []
    for i, image in enumerate(images):
        image_path = f"{IMAGES_DIR}/{pdf_name}_page_{i+1:03d}.png"
        image.save(image_path, "PNG")
        image_paths.append(image_path)
    
    return image_paths


def describe_page(image_path):
    """Use GPT-4o to describe an instruction page."""
    with open(image_path, "rb") as f:
        base64_image = base64.b64encode(f.read()).decode("utf-8")
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": """Describe this IKEA assembly instruction page in detail.
Include: step numbers, parts shown, tools needed, actions demonstrated, 
quantities (like "2x"), warnings, and part numbers if visible.
This description will help people find this page when they have questions."""
            },
            {
                "role": "user",
                "content": [
                    {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{base64_image}"}},
                    {"type": "text", "text": "Describe this instruction page."}
                ]
            }
        ],
        max_tokens=500
    )
    return response.choices[0].message.content


def main():
    # Load cache if exists
    cache = {}
    if os.path.exists(CACHE_FILE):
        with open(CACHE_FILE, "r") as f:
            cache = json.load(f)
    
    all_pages = []
    
    # Process each PDF
    for pdf_file in os.listdir(DATA_DIR):
        if not pdf_file.endswith(".pdf"):
            continue
        
        print(f"\nProcessing {pdf_file}...")
        pdf_path = f"{DATA_DIR}/{pdf_file}"
        
        # Convert to images
        print("  Converting to images...")
        image_paths = convert_pdf_to_images(pdf_path)
        print(f"  ✓ {len(image_paths)} pages")
        
        # Generate descriptions
        print("  Generating descriptions with GPT-4o...")
        for i, image_path in enumerate(image_paths):
            if image_path in cache:
                description = cache[image_path]
                status = "cached"
            else:
                description = describe_page(image_path)
                cache[image_path] = description
                # Save cache after each page
                with open(CACHE_FILE, "w") as f:
                    json.dump(cache, f, indent=2)
                status = "generated"
            
            all_pages.append({
                "source_pdf": pdf_file,
                "page_number": i + 1,
                "image_path": image_path,
                "description": description
            })
            print(f"    Page {i+1}/{len(image_paths)} [{status}]")
    
    # Save all page data
    with open("data/cache/all_pages.json", "w") as f:
        json.dump(all_pages, f, indent=2)
    
    print(f"\n✓ Processed {len(all_pages)} pages total")


if __name__ == "__main__":
    main()

Berikut yang dilakukan fungsi-fungsinya:

  • convert_pdf_to_images(pdf_path): merender setiap halaman PDF menjadi gambar PNG pada 150 DPI menggunakan pdf2image. Ini mengekstrak diagram yang tidak memiliki teks yang dapat dipilih, menyimpannya dengan penamaan konsisten seperti malm_desk_page_001.png.

  • describe_page(image_path): mengenkode gambar sebagai base64 dan mengirimkannya ke GPT-4o dengan system prompt yang disesuaikan. Model mengembalikan deskripsi teks terperinci yang menangkap langkah, bagian, dan kuantitas untuk pencarian semantik.

  • main(): mengorkestrasi semuanya: memuat deskripsi yang di-cache untuk menghindari pemrosesan ulang, mengonversi PDF menjadi gambar, menghasilkan deskripsi baru sesuai kebutuhan, menyimpan cache bertahap, dan menghasilkan all_pages.json beserta metadata untuk pengindeksan.

Jalankan skripnya seperti ini:

python step2_preprocess.py

Langkah ini memakan waktu beberapa menit. GPT-4o menganalisis setiap halaman dan menghasilkan deskripsi terperinci. Deskripsinya di-cache, jadi jika Anda menjalankannya lagi, halaman yang sudah diproses akan dilewati.

Apa yang terjadi di sini? Kita mengubah konten visual menjadi teks yang bisa dicari. Saat seseorang bertanya, "Bagaimana cara memasang kaki?", retriever akan mencari deskripsi ini dan menemukan halaman yang menyebutkan pemasangan kaki, meskipun halaman asli sama sekali tidak memiliki teks.

Setelah Anda membangun pipeline dasar, peningkatan berikutnya berasal dari trik retrieval, seperti pencarian hibrida, reranking, atau penulisan ulang kueri. Untuk mengenalnya, baca blog ini tentang Teknik RAG Lanjutan.

Langkah 3: Menyimpan dan mengindeks data multimodal

Sekarang kita melakukan embedding deskripsi dan menyimpannya di ChromaDB.

Buat step3_index.py:

"""
Step 3: Create Embeddings and Index
"""
import os
import json
from openai import OpenAI
import chromadb

client = OpenAI()

PAGES_FILE = "data/cache/all_pages.json"
CHROMA_DIR = "data/chroma_db"
COLLECTION_NAME = "ikea_instructions"


def get_embedding(text):
    """Get embedding from OpenAI."""
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding


def main():
    # Load page descriptions
    with open(PAGES_FILE, "r") as f:
        pages = json.load(f)
    print(f"Loaded {len(pages)} pages")
    
    # Initialize ChromaDB with persistent storage
    chroma_client = chromadb.PersistentClient(path=CHROMA_DIR)
    
    # Delete existing collection if present
    try:
        chroma_client.delete_collection(name=COLLECTION_NAME)
    except:
        pass
    
    collection = chroma_client.create_collection(
        name=COLLECTION_NAME,
        metadata={"hnsw:space": "cosine"}
    )
    print(f"Created collection: {COLLECTION_NAME}")
    
    # Index each page
    print("\nCreating embeddings...")
    for i, page in enumerate(pages):
        # Combine source info with description
        text_to_embed = f"Source: {page['source_pdf']}, Page {page['page_number']}\n\n{page['description']}"
        
        embedding = get_embedding(text_to_embed)
        
        collection.add(
            ids=[f"page_{i}"],
            embeddings=[embedding],
            documents=[page["description"]],
            metadatas=[{
                "source_pdf": page["source_pdf"],
                "page_number": page["page_number"],
                "image_path": page["image_path"]
            }]
        )
        
        if (i + 1) % 5 == 0 or i == len(pages) - 1:
            print(f"  Indexed {i + 1}/{len(pages)} pages")
    
    print(f"\n✓ Index created with {collection.count()} documents")
    
    # Quick retrieval test
    print("\nTesting retrieval...")
    test_query = "How do I attach the legs?"
    query_embedding = get_embedding(test_query)
    results = collection.query(query_embeddings=[query_embedding], n_results=2)
    
    print(f"Query: '{test_query}'")
    for meta in results["metadatas"][0]:
        print(f"  → {meta['source_pdf']}, Page {meta['page_number']}")


if __name__ == "__main__":
    main()

Di sini, fungsi get_embedding(text) mengonversi teks dari deskripsi dan info sumber menjadi vektor padat menggunakan model text-embedding-3-small dari OpenAI. Dengan 1536 dimensi, vektor-vektor tersebut mampu menangkap makna semantik untuk pencarian kemiripan.

main() memuat halaman yang telah diproses dari all_pages.json dari langkah sebelumnya, menginisialisasi ChromaDB dengan penyimpanan persisten, menghapus/membuat ulang koleksi, menghasilkan embedding untuk setiap halaman, menyimpannya dengan metadata kaya (sumber, nomor halaman, path gambar), dan menjalankan uji retrieval cepat untuk memverifikasi pengindeksan bekerja.

Jalankan:

python step3_index.py

Metadata penting di sini. Kita menyimpan image_path agar dapat mengambil gambar asli nanti dan menunjukkannya ke GPT-4o saat generasi. Deskripsi adalah yang dicari, tetapi gambar adalah yang ditampilkan.

Langkah 4: Menyusun pipeline RAG

Berikut pipeline RAG multimodal lengkap. Sebuah kueri di-embed, halaman relevan diambil, gambar dimuat, dan GPT-4o menghasilkan jawaban berdasarkan deskripsi teks dan visual.

Buat step4_query.py:

"""
Step 4: Query the Multimodal RAG System
"""
import os
import sys
import base64
from openai import OpenAI
import chromadb

client = OpenAI()

CHROMA_DIR = "data/chroma_db"
COLLECTION_NAME = "ikea_instructions"
TOP_K = 3

# Initialize ChromaDB
chroma_client = chromadb.PersistentClient(path=CHROMA_DIR)


def get_embedding(text):
    """Get embedding from OpenAI."""
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding


def retrieve(query, top_k=TOP_K):
    """Find relevant instruction pages."""
    collection = chroma_client.get_collection(name=COLLECTION_NAME)
    query_embedding = get_embedding(query)
    
    results = collection.query(
        query_embeddings=[query_embedding],
        n_results=top_k,
        include=["documents", "metadatas"]
    )
    return results


def generate_answer(query, retrieved_results):
    """Generate answer using GPT-4o with retrieved images."""
    if not retrieved_results["ids"][0]:
        return "No relevant pages found.", []
    
    # Build image content for GPT-4o
    image_content = []
    sources = []
    
    for doc, metadata in zip(retrieved_results["documents"][0], retrieved_results["metadatas"][0]):
        image_path = metadata["image_path"]
        
        if not os.path.exists(image_path):
            continue
        
        with open(image_path, "rb") as f:
            base64_image = base64.b64encode(f.read()).decode("utf-8")
        
        image_content.append({
            "type": "image_url",
            "image_url": {"url": f"data:image/png;base64,{base64_image}"}
        })
        image_content.append({
            "type": "text",
            "text": f"[Page {metadata['page_number']} from {metadata['source_pdf']}]"
        })
        
        sources.append(f"{metadata['source_pdf']}, Page {metadata['page_number']}")
    
    # Generate with GPT-4o
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": """You answer questions about IKEA assembly based on instruction pages.
Be specific, reference page numbers when helpful, and say if something isn't clear from the images."""
            },
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": f"Question: {query}\n\nRelevant instruction pages:"},
                    *image_content,
                    {"type": "text", "text": "Answer based on these pages."}
                ]
            }
        ],
        max_tokens=600
    )
    
    return response.choices[0].message.content, sources


def answer_question(query):
    """Full RAG pipeline."""
    print(f"\nQuery: {query}")
    print("-" * 50)
    
    print("Retrieving relevant pages...")
    results = retrieve(query)
    print(f"  Found {len(results['ids'][0])} pages")
    
    print("Generating answer...")
    answer, sources = generate_answer(query, results)
    
    return answer, sources


def main():
    # Check index exists
    try:
        collection = chroma_client.get_collection(name=COLLECTION_NAME)
        print(f"Connected to index ({collection.count()} documents)")
    except:
        print("ERROR: Index not found. Run step2_index.py first.")
        return
    
    # Single query from command line
    if len(sys.argv) > 1:
        query = " ".join(sys.argv[1:])
        answer, sources = answer_question(query)
        print(f"\nAnswer:\n{answer}")
        print(f"\nSources: {', '.join(sources)}")
        return
    
    # Interactive mode
    print("\nAsk questions about IKEA assembly. Type 'quit' to exit.\n")
    
    while True:
        try:
            query = input("Question: ").strip()
        except KeyboardInterrupt:
            break
        
        if not query or query.lower() in ["quit", "exit"]:
            break
        
        answer, sources = answer_question(query)
        print(f"\nAnswer:\n{answer}")
        print(f"\nSources: {', '.join(sources)}\n")


if __name__ == "__main__":
    main()

Penjelasan fungsi kunci:

  • get_embedding(text): menghasilkan vektor embedding untuk kueri menggunakan text-embedding-3-small, memungkinkan pencarian kemiripan semantik di ChromaDB.​

  • retrieve(query, top_k=TOP_K): melakukan embedding kueri, menanyakan koleksi untuk tetangga terdekat top-k, dan mengembalikan dokumen serta metadata (mis. path gambar) melalui include=["documents", "metadatas"].​

  • generate_answer(query, retrieved_results): memuat gambar teratas sebagai base64, menyusun prompt GPT-4o multimodal yang menyelipkan label halaman dan gambar, dan menghasilkan respons terperinci dengan instruksi spesifik IKEA.​

  • answer_question(query): mengorkestrasi retrieval dan generasi, dengan logging progres.

  • main(): memvalidasi indeks ada, mendukung kueri tunggal via CLI atau mode chat interaktif (“quit”, “exit” atau Ctrl+C untuk berhenti).

Langkah 5: Menjalankan pipeline RAG

Anda bisa menjalankannya dan mengetik pertanyaan Anda, atau langsung menjalankannya dengan pertanyaan spesifik, seperti ini:

python step4_query.py "How do I attach the legs to the desk?"
Retrieving relevant pages...
Found 3 pages
Generating answer...

Answer:
To attach the legs to the MALM desk, follow these steps shown on pages 5-6:

1. First, insert the wooden dowels into the pre-drilled holes on the leg 
   pieces (you'll see "4x" indicating four dowels are needed)

2. Apply the cam locks to the desk top using the Phillips screwdriver

3. Align each leg with the corresponding holes on the underside of the desk

4. Tighten the cam locks by rotating them clockwise until the legs are 
   firmly attached

The instructions show this should be done with the desk top face-down on 
a soft surface to avoid scratching.

Sources: malm_desk.pdf, Page 5, malm_desk.pdf, Page 6, malm_desk.pdf, Page 7

Terlihat bagus! Sistem mengambil halaman berdasarkan deskripsi, memuat gambar aslinya, dan GPT-4o menghasilkan jawaban dengan melihat langsung diagramnya.

Evaluasi RAG Multimodal

Membangun sistem RAG multimodal adalah satu hal. Mengetahui apakah sistem tersebut bekerja dengan baik adalah hal lain. Dan membuatnya berjalan andal di produksi menambah lapisan tantangan lainnya. 

Metrik RAG standar tetap berlaku, tetapi Anda perlu memperluasnya untuk konten visual.

Kualitas retrieval

Precision@k mengukur berapa banyak halaman yang Anda ambil yang benar-benar relevan. Ini akan memberi penalti untuk halaman yang tidak relevan di top-k, tetapi untuk generasi, memiliki gambar kunci lebih penting daripada mencapai top-k yang sempurna.

Recall@k mengukur seberapa sering halaman yang benar muncul dalam hasil top k Anda. Ini lebih relevan untuk kasus penggunaan RAG IKEA kita karena kita ingin sistem menemukan setidaknya satu halaman relevan di hasil top-k (mis., k=3). Dengan begitu, GPT-4o mendapatkan konteks visual untuk menjawab dengan benar, meskipun hasil lainnya berisik. 

Untuk sistem IKEA kita, Anda akan membangun set uji dengan sekitar 50 pertanyaan dan memberi label halaman "benar" untuk masing-masing. Lalu, Anda dapat mengevaluasinya menggunakan fungsi berikut:

def evaluate_retrieval(test_cases, top_k=3):
    """
    test_cases: [{"query": str, "relevant_pages": ["image_path1", ...]}]
    """
    hits = 0
    for test in test_cases:
        results = retrieve(test["query"], top_k=top_k)
        retrieved = [m["image_path"] for m in results["metadatas"][0]]
        if any(p in retrieved for p in test["relevant_pages"]):
            hits += 1
    return hits / len(test_cases)

Kualitas generasi

Sesuatu yang lebih sulit diukur secara otomatis adalah kualitas generasi. 

Anda dapat meminta orang menilai jawaban untuk akurasi dan kegunaan, atau menggunakan LLM sebagai juri untuk mengevaluasi apakah jawaban benar berdasarkan gambar sumber. 

Untuk diagram dengan langkah bernomor, verifikasi bahwa model memberikan nomor langkah yang benar.

Pemeriksaan khusus multimodal

Evaluasi lebih lanjut dapat menjawab pertanyaan berikut:

  • Apakah sistem menemukan jawaban berbasis gambar ketika jawabannya bersifat visual? 

  • Saat model merujuk sesuatu dalam gambar, apakah rujukan tersebut akurat? 

Ini memerlukan tinjauan manual, setidaknya untuk sampel kueri.

Praktik Terbaik untuk RAG Multimodal

Jika Anda ingin membangun pipeline RAG multimodal yang siap produksi, ada beberapa hal yang perlu diingat:

  • Biaya prapemrosesan bertambah: Pemanggilan visi untuk deskripsi gambar adalah bagian yang mahal. Untuk korpus dokumen besar, proses secara batch di jam sibuk rendah dan cache deskripsi secara agresif. Pertimbangkan model yang lebih murah (dalam kasus kita, misalnya GPT-4o-mini) untuk menghasilkan deskripsi jika kualitasnya memadai untuk kasus penggunaan Anda.

  • Latensi kueri penting: GPT-4o dengan gambar memakan waktu 5 hingga 10 detik per kueri. Streaming membantu pengguna melihat hasil parsial lebih cepat, dan caching kueri umum membantu untuk pertanyaan berulang. Mengambil 2 halaman alih-alih 5 dapat menghemat token jika itu sudah cukup sebagai konteks.

  • Pantau hal yang tepat: Lacak latensi retrieval (seharusnya di bawah 100 ms untuk koleksi kecil), latensi generasi (harap 5 hingga 10 detik dengan gambar), biaya API per kueri, dan umpan balik pengguna jika Anda mengumpulkannya.

  • Penanganan galat menyelamatkan Anda: Segala sesuatu bisa gagal. Bangun fallback, seperti pada cuplikan kode berikut.

def answer_question_safe(query):
    """Wrapper with error handling."""
    try:
        return answer_question(query)
    except Exception as e:
        print(f"Error: {e}")
        # Fall back to text-only using descriptions
        results = retrieve(query)
        if results["documents"][0]:
            context = results["documents"][0][0][:500]
            return f"Based on the instructions: {context}...", []
        return "Sorry, couldn't process that question.", []

Saat pipeline Anda perlu mengambil beberapa langkah (cari → verifikasi → cari ulang → jawab), pola agentic RAG membantu Anda menyusunnya dengan rapi.

Kesimpulan

RAG multimodal memperluas retrieval-augmented generation untuk menangani seluruh ragam konten dalam dokumen dunia nyata, termasuk gambar, diagram, tabel, dan bagan. 

Kita berhasil menggunakannya untuk melakukan kueri pada instruksi IKEA, yang sangat visual, di mana pendekatan teks saja tidak akan berhasil. Seluruh pipeline berjalan secara lokal kecuali panggilan API.

Wawasan kuncinya adalah Anda tidak memerlukan embedding multimodal yang rumit agar ini berhasil. Dengan menghasilkan deskripsi teks yang baik dari konten visual, Anda dapat menggunakan retrieval teks standar untuk menemukan gambar relevan, lalu membiarkan model vision-language melakukan pekerjaan berat saat generasi.

Jika Anda ingin mengembangkan apa yang kita bahas di sini, mulailah perjalanan Anda dengan jalur karier AI Engineer hari ini!

FAQ RAG Multimodal

Jenis data apa yang dapat ditangani RAG multimodal?

Gambar, PDF dengan grafik, tabel, diagram, audio (melalui transkrip), dan keyframe video. Intinya, konten apa pun yang ingin Anda rujuk oleh AI saat menjawab pertanyaan.

Apakah saya perlu basis data vektor terpisah untuk tiap modalitas?

Tidak harus. Kebanyakan sistem produksi menggunakan satu penyimpanan vektor dengan ruang embedding terpadu. Anda dapat meletakkan semuanya ke dalam ruang berdimensi sama, sehingga retrieval berfungsi lintas jenis konten.

Bisakah saya menggunakan OpenAI untuk seluruh pipeline?

Ya. GPT-4o menangani pemahaman gambar sekaligus generasi jawaban, sementara text-embedding-3-small menangani sisi vektor. Anda dapat membangun sistem RAG multimodal lengkap tanpa keluar dari ekosistem OpenAI.

Apa tantangan terbesar dalam RAG multimodal?

Penyelarasan. Membuat teks, gambar, dan data terstruktur berada dalam ruang embedding yang sama sehingga retrieval benar-benar berfungsi lintas modalitas. Strategi chunking menempati urutan kedua.

Berapa biaya menjalankannya?

Untuk tutorial IKEA di sini, perkirakan sekitar $2 total. Bagian termahal adalah pemanggilan GPT-4o untuk deskripsi gambar saat prapemrosesan. Setelah diindeks, kueri menjadi murah.

Josep Ferrer

Penulis

Josep Ferrer

Josep adalah Data Scientist freelance yang berfokus pada proyek-proyek Eropa, dengan keahlian dalam penyimpanan data, pemrosesan, analitik lanjutan, dan penyusunan narasi data yang berdampak. 

Sebagai pendidik, ia mengajar Big Data di program Magister di University of Navarra dan berbagi wawasan melalui artikel di platform seperti Medium, KDNuggets, dan DataCamp. Josep juga menulis tentang Data dan Teknologi dalam buletin Databites (databites.tech). 

Ia meraih gelar Sarjana di bidang Fisika Teknik dari Polytechnic University of Catalonia dan gelar Magister di bidang Intelligent Interactive Systems dari Pompeu Fabra University.

Topik

Terkait

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026

blogs

Tutorial Korelasi di R