Lewati ke konten utama

Llama 4 dengan RAG: Panduan Disertai Proyek Demo

Pelajari cara membangun pipeline retrieval-augmented generation (RAG) menggunakan Llama 4 untuk membuat aplikasi web sederhana.
Diperbarui 28 Sep 2026  · 12 mnt Baca

Jelajahi bersama AI

ChatGPTClaudePerplexity

Llama 4 Scout dipasarkan memiliki jendela konteks masif hingga 10 juta token, tetapi pelatihannya dibatasi pada ukuran masukan maksimum 256k token. Ini berarti kinerja dapat menurun dengan masukan yang lebih besar. Untuk mencegah hal ini, kita dapat menggunakan Llama 4 dengan pipeline retrieval-augmented generation (RAG).

Dalam tutorial ini, saya akan menjelaskan langkah demi langkah cara membangun pipeline RAG menggunakan ekosistem LangChain dan membuat aplikasi web yang memungkinkan pengguna mengunggah dokumen dan mengajukan pertanyaan tentangnya.

Kami terus memberi kabar terbaru kepada pembaca tentang hal-hal terkini di AI melalui The Median, buletin gratis setiap Jumat yang merangkum cerita-cerita utama pekan ini. Berlangganan dan tetap tajam hanya dalam beberapa menit setiap minggu:

Mengapa Menggunakan Llama 4 dengan RAG?

Llama 4 hadir dengan jendela konteks 10 juta token, yang berarti kita dapat memberinya 100 buku dan menerima jawaban kontekstual tentang buku-buku tersebut. Namun, terlepas dari fitur yang mengesankan ini, ada alasan kuat untuk memasangkan Llama 4 dengan RAG. Berikut alasannya:

1. Keterbatasan kinerja dengan masukan besar

Walaupun Llama 4 mendukung jendela konteks yang sangat besar, pelatihannya dibatasi pada ukuran masukan maksimum 256k token. Kinerja model dapat menurun ketika masukan mendekati atau melampaui ambang ini. RAG mengurangi masalah ini dengan mengambil hanya informasi yang paling relevan, sehingga masukan tetap ringkas dan terfokus.

2. Efisiensi dan fokus

RAG memungkinkan pengambilan informasi yang sangat relevan dari basis data vektor, sehingga Llama 4 memproses masukan yang lebih kecil dan lebih terarah. Pendekatan ini meningkatkan efisiensi sekaligus akurasi.

3. Penghematan biaya

Memproses masukan besar dengan jendela konteks penuh Llama 4 dapat sangat mahal secara komputasi. Dengan RAG, hanya data yang paling relevan yang diambil dan diproses, sehingga beban komputasi dan biaya terkaitnya berkurang signifikan.

Membangun Pipeline RAG Llama 4

Dalam tutorial ini, saya akan menjelaskan langkah demi langkah cara membangun pipeline retrieval-augmented generation sederhana yang memungkinkan Anda memuat berkas .docx dan mengajukan pertanyaan tentang isinya.

1. Menyiapkan lingkungan

Pertama, kita perlu membuat kunci API GroqCloud dan menyimpannya sebagai variabel lingkungan. Selanjutnya, kita akan memasang semua paket Python yang diperlukan untuk memuat, memecah, memvektorisasi, dan mengambil konteks, serta menghasilkan respons.

%%capture
%pip install langchain
%pip install langchain-community 
%pip install langchainhub 
%pip install langchain-chroma 
%pip install langchain-groq
%pip install langchain-huggingface
%pip install unstructured[docx]

2. Menginisialisasi LLM dan embedding

Kita akan menyiapkan objek model menggunakan API Groq dengan memberikan nama model dan kunci API. Demikian pula, kita akan mengunduh model embedding dari Hugging Face dan memuatnya sebagai model embedding kita.

from langchain_groq import ChatGroq
from langchain_huggingface import HuggingFaceEmbeddings

llm = ChatGroq(model="meta-llama/llama-4-scout-17b-16e-instruct", api_key=groq_api_key)

embed_model = HuggingFaceEmbeddings(model_name="mixedbread-ai/mxbai-embed-large-v1")

3. Memuat dan memecah data

Kita akan memuat berkas .docx dari folder root dan memecahnya menjadi potongan berukuran 1000 karakter.

from langchain_community.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# Initialize the text splitter
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=100,
    separators=["\n\n", "\n"]
)

# Load the .docx files
loader = DirectoryLoader("./", glob="*.docx", use_multithreading=True)
documents = loader.load()

# Split the documents into chunks
chunks = text_splitter.split_documents(documents)

# Print the number of chunks
print(len(chunks))
29

4. Membangun dan mengisi vector store

Inisialisasi Chroma vector store dan berikan potongan teks ke basis data vektor. Teks akan diubah menjadi embedding sebelum disimpan. Setelah itu, kita akan menjalankan pencarian kemiripan untuk menguji apakah vector store kita terisi dengan benar.

from langchain_chroma import Chroma

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embed_model,
    persist_directory="./Vectordb",
)

query = "What is this tutorial about?"
docs = vectorstore.similarity_search(query)
print(docs[0].page_content)
Learn how to Fine-tune Stable Diffusion XL with DreamBooth and LoRA on your personal images. 

Let's try another prompt:

Prompt:

5. Membuat pipeline RAG

Selanjutnya, kita akan mengubah vector store menjadi retriever dan membuat templat prompt untuk pipeline RAG.

# Create retriever
retriever = vectorstore.as_retriever()

# Import PromptTemplate
from langchain_core.prompts import PromptTemplate

# Define a clearer, more professional prompt template
template = """You are an expert assistant tasked with answering questions based on the provided documents.
Use only the given context to generate your answer.
If the answer cannot be found in the context, clearly state that you do not know.
Be detailed and precise in your response, but avoid mentioning or referencing the context itself.

Context:
{context}

Question:
{question}

Answer:"""

# Create the PromptTemplate
rag_prompt = PromptTemplate.from_template(template)

Terakhir, kita akan membuat rantai RAG yang menyediakan konteks dan pertanyaan dalam prompt RAG, meneruskannya melalui model Llama 4, lalu menghasilkan respons yang bersih.

from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | rag_prompt
    | llm
    | StrOutputParser()
)

Sekarang kita akan menanyakan dokumen tersebut kepada rantai RAG.

from IPython.display import display, Markdown

response = rag_chain.invoke("What this tutorial about?")
Markdown(response)
This tutorial is about setting up and using the Janus project, specifically Janus Pro, a multimodal model that can understand images and generate images from text prompts, and building a local solution to use the model privately on a laptop GPU. It covers learning about the Janus Series, setting up the Janus project, building a Docker container to run the model locally, and testing its capabilities with various image and text prompts.

Seperti yang Anda lihat, responsnya sangat akurat dan sadar konteks. Jika Anda mengalami masalah saat menjalankan kode di atas, silakan merujuk ke workbook DataLab ini: Pipeline RAG Llama 4.

Membangun Aplikasi Obrolan Docx Llama 4

Pada bagian ini, kita akan mengubah kode pipeline RAG yang telah dibahas menjadi aplikasi obrolan fungsional menggunakan paket Gradio. Untuk memulai, Anda perlu memasang Gradio menggunakan perintah pip berikut:

$ pip install gradio

Aplikasi ini dirancang sederhana dan mudah digunakan, memungkinkan pengguna mengunggah satu berkas .docx, beberapa berkas .docx, atau arsip .zip yang berisi berkas .docx. Pengguna kemudian dapat mengajukan pertanyaan tentang isi dari semua berkas yang diunggah, memberikan cara cepat dan efisien untuk mengambil informasi.

Fitur aplikasi:

  • Pemuat dokumen: Aplikasi mengekstrak dan memuat dokumen langsung dari berkas yang diunggah.
  • Pemecahan teks: Dokumen dipecah menjadi potongan yang mudah diolah untuk pemrosesan dan pengambilan yang efisien.
  • Integrasi vector store: Alih-alih ChromaDB, aplikasi menggunakan vector store dalam memori untuk mengelola embedding dokumen.
  • Pipeline RAG: Menggabungkan pengambilan dokumen dengan model bahasa untuk memberikan jawaban akurat berdasarkan konten yang diunggah.
  • Antarmuka pengguna: Menampilkan antarmuka chat tempat pengguna dapat mengajukan pertanyaan dan menerima respons terperinci berdasarkan dokumen yang disediakan.
  • Penanganan galat: Memberikan umpan balik untuk tipe berkas yang tidak didukung dan masalah saat unggah berkas, meningkatkan pengalaman pengguna.
  • Fungsi reset: Pengguna dapat mereset status aplikasi untuk memulai ulang dengan unggahan dokumen baru.

Mari kita satukan semuanya:

main.py:

# ========== Standard Library ==========
import os
import tempfile
import zipfile
from typing import List, Optional, Tuple, Union
import collections


# ========== Third-Party Libraries ==========
import gradio as gr
from groq import Groq
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import DirectoryLoader, UnstructuredFileLoader
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_groq import ChatGroq
from langchain_huggingface import HuggingFaceEmbeddings

# ========== Configs ==========
TITLE = """<h1 align="center">🗨️🦙 Llama 4 Docx Chatter</h1>"""
AVATAR_IMAGES = (
    None,
    "./logo.png",
)

# Acceptable file extensions
TEXT_EXTENSIONS = [".docx", ".zip"]

# ========== Models & Clients ==========
GROQ_API_KEY = os.getenv("GROQ_API_KEY")
client = Groq(api_key=GROQ_API_KEY)
llm = ChatGroq(model="meta-llama/llama-4-scout-17b-16e-instruct", api_key=GROQ_API_KEY)
embed_model = HuggingFaceEmbeddings(model_name="mixedbread-ai/mxbai-embed-large-v1")

# ========== Core Components ==========
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=100,
    separators=["\n\n", "\n"],
)

rag_template = """You are an expert assistant tasked with answering questions based on the provided documents.
Use only the given context to generate your answer.
If the answer cannot be found in the context, clearly state that you do not know.
Be detailed and precise in your response, but avoid mentioning or referencing the context itself.
Context:
{context}
Question:
{question}
Answer:"""
rag_prompt = PromptTemplate.from_template(rag_template)


# ========== App State ==========
class AppState:
    vectorstore: Optional[InMemoryVectorStore] = None
    rag_chain = None


state = AppState()

# ========== Utility Functions ==========


def load_documents_from_files(files: List[str]) -> List:
    """Load documents from uploaded files directly without moving."""
    all_documents = []

    # Temporary directory if ZIP needs extraction
    with tempfile.TemporaryDirectory() as temp_dir:
        for file_path in files:
            ext = os.path.splitext(file_path)[1].lower()

            if ext == ".zip":
                # Extract ZIP inside temp_dir
                with zipfile.ZipFile(file_path, "r") as zip_ref:
                    zip_ref.extractall(temp_dir)

                # Load all docx from extracted zip
                loader = DirectoryLoader(
                    path=temp_dir,
                    glob="**/*.docx",
                    use_multithreading=True,
                )
                docs = loader.load()
                all_documents.extend(docs)

            elif ext == ".docx":
                # Load single docx directly
                loader = UnstructuredFileLoader(file_path)
                docs = loader.load()
                all_documents.extend(docs)

    return all_documents


def get_last_user_message(chatbot: List[Union[gr.ChatMessage, dict]]) -> Optional[str]:
    """Get last user prompt."""
    for message in reversed(chatbot):
        content = (
            message.get("content") if isinstance(message, dict) else message.content
        )
        if (
            message.get("role") if isinstance(message, dict) else message.role
        ) == "user":
            return content
    return None


# ========== Main Logic ==========




def upload_files(
    files: Optional[List[str]], chatbot: List[Union[gr.ChatMessage, dict]]
):
    """Handle file upload - .docx or .zip containing docx."""
    if not files:
        return chatbot

    file_summaries = []  # <-- Collect formatted file/folder info
    documents = []

    with tempfile.TemporaryDirectory() as temp_dir:
        for file_path in files:
            filename = os.path.basename(file_path)
            ext = os.path.splitext(file_path)[1].lower()

            if ext == ".zip":
                file_summaries.append(f"📦 **{filename}** (ZIP file) contains:")
                try:
                    with zipfile.ZipFile(file_path, "r") as zip_ref:
                        zip_ref.extractall(temp_dir)
                        zip_contents = zip_ref.namelist()

                        # Group files by folder
                        folder_map = collections.defaultdict(list)
                        for item in zip_contents:
                            if item.endswith("/"):
                                continue  # skip folder entries themselves
                            folder = os.path.dirname(item)
                            file_name = os.path.basename(item)
                            folder_map[folder].append(file_name)

                        # Format nicely
                        for folder, files_in_folder in folder_map.items():
                            if folder:
                                file_summaries.append(f"📂 {folder}/")
                            else:
                                file_summaries.append(f"📄 (root)")
                            for f in files_in_folder:
                                file_summaries.append(f"   - {f}")

                    # Load docx files extracted from ZIP
                    loader = DirectoryLoader(
                        path=temp_dir,
                        glob="**/*.docx",
                        use_multithreading=True,
                    )
                    docs = loader.load()
                    documents.extend(docs)

                except zipfile.BadZipFile:
                    chatbot.append(
                        gr.ChatMessage(
                            role="assistant",
                            content=f"❌ Failed to open ZIP file: {filename}",
                        )
                    )

            elif ext == ".docx":
                file_summaries.append(f"📄 **{filename}**")
                loader = UnstructuredFileLoader(file_path)
                docs = loader.load()
                documents.extend(docs)

            else:
                file_summaries.append(f"❌ Unsupported file type: {filename}")

    if not documents:
        chatbot.append(
            gr.ChatMessage(
                role="assistant", content="No valid .docx files found in upload."
            )
        )
        return chatbot

    # Split documents
    chunks = text_splitter.split_documents(documents)
    if not chunks:
        chatbot.append(
            gr.ChatMessage(
                role="assistant", content="Failed to split documents into chunks."
            )
        )
        return chatbot

    # Create Vectorstore
    state.vectorstore = InMemoryVectorStore.from_documents(
        documents=chunks,
        embedding=embed_model,
    )
    retriever = state.vectorstore.as_retriever()

    # Build RAG Chain
    state.rag_chain = (
        {"context": retriever, "question": RunnablePassthrough()}
        | rag_prompt
        | llm
        | StrOutputParser()
    )

    # Final display
    chatbot.append(
        gr.ChatMessage(
            role="assistant",
            content="**Uploaded Files:**\n"
            + "\n".join(file_summaries)
            + "\n\n✅ Ready to chat!",
        )
    )
    return chatbot


def user_message(
    text_prompt: str, chatbot: List[Union[gr.ChatMessage, dict]]
) -> Tuple[str, List[Union[gr.ChatMessage, dict]]]:
    """Add user's text input to conversation."""
    if text_prompt.strip():
        chatbot.append(gr.ChatMessage(role="user", content=text_prompt))
    return "", chatbot


def process_query(
    chatbot: List[Union[gr.ChatMessage, dict]],
) -> List[Union[gr.ChatMessage, dict]]:
    """Process user's query through RAG pipeline."""
    prompt = get_last_user_message(chatbot)
    if not prompt:
        chatbot.append(
            gr.ChatMessage(role="assistant", content="Please type a question first.")
        )
        return chatbot

    if state.rag_chain is None:
        chatbot.append(
            gr.ChatMessage(role="assistant", content="Please upload documents first.")
        )
        return chatbot

    chatbot.append(gr.ChatMessage(role="assistant", content="Thinking..."))

    try:
        response = state.rag_chain.invoke(prompt)
        chatbot[-1].content = response
    except Exception as e:
        chatbot[-1].content = f"Error: {str(e)}"

    return chatbot


def reset_app(
    chatbot: List[Union[gr.ChatMessage, dict]],
) -> List[Union[gr.ChatMessage, dict]]:
    """Reset application state."""
    state.vectorstore = None
    state.rag_chain = None
    return [
        gr.ChatMessage(
            role="assistant", content="App reset! Upload new documents to start."
        )
    ]


# ========== UI Layout ==========

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.HTML(TITLE)
    chatbot = gr.Chatbot(
        label="Llama 4 RAG",
        type="messages",
        bubble_full_width=False,
        avatar_images=AVATAR_IMAGES,
        scale=2,
        height=350,
    )

    with gr.Row(equal_height=True):
        text_prompt = gr.Textbox(
            placeholder="Ask a question...", show_label=False, autofocus=True, scale=28
        )
        send_button = gr.Button(
            value="Send",
            variant="primary",
            scale=1,
            min_width=80,
        )
        upload_button = gr.UploadButton(
            label="Upload",
            file_count="multiple",
            file_types=TEXT_EXTENSIONS,
            scale=1,
            min_width=80,
        )
        reset_button = gr.Button(
            value="Reset",
            variant="stop",
            scale=1,
            min_width=80,
        )

    send_button.click(
        fn=user_message,
        inputs=[text_prompt, chatbot],
        outputs=[text_prompt, chatbot],
        queue=False,
    ).then(fn=process_query, inputs=[chatbot], outputs=[chatbot])

    text_prompt.submit(
        fn=user_message,
        inputs=[text_prompt, chatbot],
        outputs=[text_prompt, chatbot],
        queue=False,
    ).then(fn=process_query, inputs=[chatbot], outputs=[chatbot])

    upload_button.upload(
        fn=upload_files, inputs=[upload_button, chatbot], outputs=[chatbot], queue=False
    )
    reset_button.click(fn=reset_app, inputs=[chatbot], outputs=[chatbot], queue=False)

demo.queue().launch()

Menguji Aplikasi Obrolan Docx Llama 4

Sekarang, kita akan menjalankan aplikasi web dan menguji fungsinya. Untuk memulai aplikasi, jalankan perintah berikut di terminal:

$ python main.py 

Setelah server web aktif, buka URL berikut atau salin dan tempel secara manual ke peramban Anda untuk mengakses antarmuka web: http://127.0.0.1:7860

Berikut tampilan antarmuka obrolan kami:

antarmuka gradio llama 4 rag

Kita akan mengunggah berkas .zip yang berisi berkas .docx, dan saat melakukannya, Anda akan melihat semua folder dan berkas di dalam berkas zip tersebut.

uji antarmuka gradio llama 4 rag

Selanjutnya, kita dapat mengajukan pertanyaan spesifik tentang dokumen-dokumen tersebut:

uji antarmuka gradio llama 4 rag

Anda juga dapat menekan tombol “Reset” untuk mengunggah berkas baru dan mengobrol tentangnya hanya dalam beberapa detik.

uji antarmuka gradio llama 4 rag

Setelah mengujinya secara lokal, Anda dapat dengan mudah menerapkan aplikasi web di Hugging Face Space secara gratis. Berikut tautan ke aplikasi yang sudah diterapkan: kingabzpro/Llama-4-RAG 

uji antarmuka gradio llama 4 rag

Sumber: kingabzpro/Llama-4-RAG 

Kode sumber, notebook, dan berkas konfigurasi dapat ditemukan di repositori kingabzpro/Llama-4-RAG. 

Kesimpulan

Bahkan dengan hadirnya model ber-jendela konteks panjang, RAG tidak akan menjadi usang. RAG tetap menjadi metode yang efisien dan hemat biaya untuk mengambil informasi dari sumber eksternal, memberikan konteks tambahan bagi model.

Selain itu, sistem RAG secara inheren lebih ekonomis untuk dioperasikan dibandingkan memberi model konteks yang sangat luas secara langsung, yang memerlukan sumber daya komputasi besar. Hal ini menjadikan RAG solusi yang praktis dan skalabel untuk banyak kasus penggunaan.

Untuk mempelajari lebih lanjut tentang Llama 4, lihat sumber berikut:


Abid Ali Awan's photo
Author
Abid Ali Awan
LinkedIn
Twitter

Sebagai data scientist tersertifikasi, saya bersemangat memanfaatkan teknologi mutakhir untuk menciptakan aplikasi machine learning yang inovatif. Dengan latar belakang kuat di pengenalan ucapan, analisis dan pelaporan data, MLOps, conversational AI, dan NLP, saya mengasah keterampilan dalam mengembangkan sistem cerdas yang berdampak nyata. Selain keahlian teknis, saya juga komunikator andal yang mampu menyederhanakan konsep kompleks menjadi bahasa yang jelas dan ringkas. Karena itu, saya menjadi blogger yang dicari di bidang data science, membagikan wawasan dan pengalaman kepada komunitas profesional data yang terus berkembang. Saat ini, saya berfokus pada pembuatan dan penyuntingan konten, bekerja dengan large language model untuk mengembangkan konten yang kuat dan menarik agar membantu bisnis dan individu memaksimalkan data mereka.

Topik
Kecerdasan Buatan
Large Language Models

Pelajari AI lewat kursus-kursus ini!

Kursus

Bekerja dengan Llama 3

2 jam
15.7K
Jelajahi teknik terbaru untuk menjalankan Llama LLM secara lokal dan mengintegrasikannya ke dalam stack Anda.
Lihat DetailRight Arrow
Mulai Kursus
Lihat SelengkapnyaRight Arrow
Terkait

blogs

Tutorial Korelasi di R

Dapatkan pengenalan dasar-dasar korelasi di R: pelajari lebih lanjut tentang koefisien korelasi, matriks korelasi, plotting korelasi, dan sebagainya.
David Woods's photo

David Woods

13 mnt

blogs

Spaghetti Plot dan Jalur Badai

Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Hugo Bowne-Anderson's photo

Hugo Bowne-Anderson

13 mnt

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026

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.
Dario Radečić's photo

Dario Radečić

15 mnt

blogs

12 Alternatif ChatGPT Terbaik yang Bisa Anda Coba pada 2026

Artikel ini menyajikan daftar alternatif ChatGPT yang akan meningkatkan produktivitas Anda.
Javier Canales Luna's photo

Javier Canales Luna

14 mnt

Lihat SelengkapnyaLihat Selengkapnya