L
Artificial Intelligence & LLM • Literally Web ID

Membangun Sistem RAG (Retrieval-Augmented Generation) Skala Produksi dengan LLM Lokal dan Vector Database

⏱️ 15 menit baca • 📅 15 Oct 2026 • 👁️ 1 views
Membangun Sistem RAG (Retrieval-Augmented Generation) Skala Produksi dengan LLM Lokal dan Vector Database

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap kecerdasan buatan enterprise modern, model bahasa besar (LLM) komersial seperti OpenAI GPT-4 atau Anthropic Claude menawarkan kapabilitas penalaran yang luar biasa. Namun, penerapan model-model ini di lingkungan produksi sering kali terbentur oleh tiga tembok besar: biaya operasional yang tinggi, latensi jaringan eksternal, dan privasi data. Banyak organisasi enterprise memegang data sensitif—baik itu kekayaan intelektual, catatan finansial, atau data medis pribadi—yang secara hukum dan etika tidak boleh dikirim ke API pihak ketiga.

Solusi untuk dilema ini adalah membangun infrastruktur Retrieval-Augmented Generation (RAG) lokal yang sepenuhnya independen. Dengan menggabungkan LLM open-source (seperti Llama 3 atau Mistral) yang berjalan di atas perangkat keras mandiri (GPU lokal atau instance cloud terisolasi), database vektor berperforma tinggi, serta sistem backend web modern, perusahaan dapat menciptakan sistem tanya-jawab dokumen yang sangat akurat, aman, dan berdaulat atas datanya sendiri.

[!WARNING] Keterbatasan utama LLM generik di lingkungan enterprise adalah fenomena halusinasi—di mana model menghasilkan informasi yang salah tetapi terdengar sangat meyakinkan. Tanpa RAG yang terstruktur dengan baik, LLM akan gagal menjawab pertanyaan spesifik domain karena ketidaktahuan terhadap data internal perusahaan.

Sebelum penerapan RAG skala produksi, organisasi umumnya mengalami berbagai masalah klasik: dokumen internal menumpuk tanpa struktur pencarian yang cerdas, karyawan menghabiskan waktu berjam-jam untuk mencari informasi, dan integrasi pencarian berbasis teks tradisional (seperti LIKE %keyword% di MySQL) gagal menangkap makna semantik dari sebuah pertanyaan. Setelah penerapan arsitektur RAG skala produksi dengan database vektor dan LLM lokal, perusahaan mendapatkan sistem penemuan pengetahuan instan dengan latensi sub-detik, akurasi tinggi berbasis sumber (grounded generation), dan kepatuhan penuh terhadap regulasi privasi data seperti GDPR dan HIPAA.

2. Konsep Inti & Deep Dive Arsitektur

Arsitektur RAG bekerja dengan menjembatani kesenjangan pengetahuan antara LLM pra-latih dan data privat perusahaan. Proses ini dibagi menjadi dua fase utama: Fase Ingesti Data (Offline Pipeline) dan Fase Inferensi & Query (Online Pipeline).

Alur Semantik Data (ASCII Flow Chart)

+-------------------------------------------------------------------------+
|                        FASE INGESTI DATA (OFFLINE)                      |+
+-------------------------------------------------------------------------+
| Dokumen Sumber (PDF, Markdown, DB) ---> Text Extractor & Cleaner        |
|                                              |                          |
|                                              v                          |
|                                   Chunking Strategis                    |
|                                              |                          |
|                                              v                          |
|                                   Embedding Model (Local)               |
|                                              |                          |
|                                              v                          |
|                                   Vector Database (Qdrant/Milvus)       |
+-------------------------------------------------------------------------+

+-------------------------------------------------------------------------+
|                        FASE INFERENSI & QUERY (ONLINE)                  |
+-------------------------------------------------------------------------+
| User Query (Frontend Web) ---> API Backend (Go/Node/PHP)                 |
|                                      |                                  |
|                                      v                                  |
|                            Embedding Query (Local)                      |
|                                      |                                  |
|                                      v                                  |
|                            Vector Similarity Search                     |
|                                      |                                  |
|                                      v                                  |
|                            Context Assembly (Prompt Engineering)        |
|                                      |                                  |
|                                      v                                  |
|                            LLM Lokal (Ollama / vLLM)                    |
|                                      |                                  |
|                                      v                                  |
|                            Response to Client (WebSocket/JSON)          |
+-------------------------------------------------------------------------+

Dalam fase ingesti, dokumen mentah dipecah menjadi bagian-bagian kecil (chunks) agar muat dalam batas konteks token model embedding. Setiap chunk dikonversi menjadi representasi vektor numerik berdimensi tinggi (misalnya 768 atau 1538 dimensi) menggunakan model embedding lokal seperti bge-large-en atau text-embedding-3-small yang berjalan di server internal. Vektor-vektor ini beserta metadata aslinya disimpan ke dalam database vektor yang diindeks secara khusus menggunakan algoritma HNSW (Hierarchical Navigable Small World) atau IVF (Inverted File Index).

Ketika pengguna mengajukan pertanyaan melalui antarmuka web, backend menerima request, mengubah pertanyaan tersebut menjadi vektor menggunakan model embedding yang sama, lalu melakukan vector similarity search (biasanya menggunakan Cosine Distance atau Inner Product) terhadap database vektor untuk mengambil top-K dokumen yang paling relevan secara semantik. Dokumen-dokumen ini kemudian disuntikkan ke dalam prompt LLM sebagai konteks tambahan, memungkinkan model untuk menghasilkan jawaban yang akurat, faktual, dan merujuk langsung pada data perusahaan.

3. Perbandingan Komparatif / Analisis Pendekatan

Memilih komponen yang tepat adalah kunci kesuksesan arsitektur RAG skala produksi. Berikut adalah analisis mendalam perbandingan antara tiga database vektor utama dan dua pendekatan arsitektur backend.

Tabel Komparasi Vector Database (Milvus vs Qdrant vs pgvector)

Parameter Evaluasi Milvus Qdrant pgvector Rekomendasi Enterprise
Bahasa Utama C++ / Go Rust C (Ekstensi PostgreSQL) Qdrant untuk skala menengah-besar; pgvector untuk kesederhanaan operasional.
Konsumsi RAM Tinggi (Optimasi cluster besar) Sangat Efisien (Memory-mapped storage) Bergantung pada RAM server PostgreSQL Qdrant unggul dalam efisiensi sumber daya server tunggal.
Skalabilitas Skala masif (Distributed by design) Baik (Skala horizontal dengan Raft consensus) Terbatas pada kapasitas satu instance DB (kecuali menggunakan Citus) Milvus untuk skala multi-terabyte dataset.
Fitur Filter Metadata Sangat Kaya (Boolean expression kompleks) Sangat Kaya (Payload filtering secepat kilat) Mengandalkan SQL standard (WHERE clause) Qdrant memberikan kemudahan dan kecepatan filtering payload.
Kompleksitas Ops Tinggi (Membutuhkan etcd, MinIO, Pulsar) Rendah (Binary tunggal dengan Docker) Sangat Rendah (Jika sudah menggunakan Postgres) Qdrant atau pgvector untuk meminimalkan overhead DevOps.

[!TIP] Jika perusahaan Anda sudah menggunakan infrastruktur PostgreSQL yang matang dan ukuran dataset vektor Anda berada di bawah 10 juta baris, menggunakan ekstensi pgvector adalah pilihan paling bijak untuk menghindari penambahan kompleksitas operasional cluster database baru.

4. Prasyarat Sistem & Persiapan Lingkungan

Untuk membangun sistem RAG lokal dengan performa tinggi, kita memerlukan spesifikasi perangkat keras dan perangkat lunak standar enterprise. Panduan ini mengasumsikan penggunaan sistem operasi Ubuntu Server 22.04 LTS atau Debian 12.

Spesifikasi Minimum Hardware

  • CPU: 8 Cores (Intel Xeon Gold atau AMD EPYC)
  • RAM: 32 GB ECC RAM
  • GPU: NVIDIA GPU dengan minimal VRAM 16GB (contoh: NVIDIA RTX 4090, A10G, atau L4) untuk menjalankan LLM lokal secara efisien menggunakan vLLM atau Ollama.
  • Storage: 500 GB NVMe SSD dengan performa IOPS tinggi.

Persiapan Environment & Dependensi Dasar

Jalankan perintah berikut di terminal untuk memperbarui sistem dan menginstal dependensi dasar seperti Docker, Docker Compose, dan utilitas pendukung:

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git build-essential software-properties-common ufw fail2ban

# Instal Docker Engine & Docker Compose v2
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Verifikasi instalasi Docker
sudo docker run hello-world

Jika Anda menggunakan GPU NVIDIA, pastikan Anda telah menginstal NVIDIA Container Toolkit agar container Docker dapat mengakses akselerasi GPU secara langsung:

distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
    sed 's#uri https://nvidia.github.io/libnvidia-container/#uri https://nvidia.github.io/libnvidia-container/stable/$distribution/libnvidia-container.list#' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

sudo apt update
sudo apt install -y nvidia-container-toolkit
sudo systemctl restart docker

5. Panduan Implementasi Langkah demi Langkah

Kita akan membangun stack produksi menggunakan Docker Compose yang mencakup Qdrant sebagai Vector Database, Ollama untuk manajemen LLM lokal, dan backend Node.js (TypeScript) sebagai orchestrator RAG.

1. File Konfigurasi Docker Compose (docker-compose.yml)

Buat direktori proyek /opt/enterprise-rag dan buat file docker-compose.yml dengan konfigurasi lengkap berikut:

version: '3.8'

services:
  qdrant:
    image: qdrant/qdrant:v1.7.4
    container_name: enterprise_qdrant
    restart: always
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage
    environment:
      - QDRANT__SERVICE__HTTP_PORT=6333
      - QDRANT__SERVICE__GRPC_PORT=6334
    networks:
      - rag_net

  ollama:
    image: ollama/ollama:latest
    container_name: enterprise_ollama
    restart: always
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    networks:
      - rag_net

  rag-backend:
    build: .
    container_name: enterprise_rag_backend
    restart: always
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
      - QDRANT_HOST=qdrant
      - QDRANT_PORT=6333
      - OLLAMA_HOST=http://ollama:11434
      - EMBEDDING_MODEL=bge-large
      - LLM_MODEL=llama3
    depends_on:
      - qdrant
      - ollama
    networks:
      - rag_net

volumes:
  qdrant_data:
  ollama_data:

networks:
  rag_net:
    driver: bridge

2. Kode Backend Orchestrator RAG (src/index.ts)

Berikut adalah kode implementasi backend menggunakan Node.js dan TypeScript untuk menangani embedding, pencarian vektor di Qdrant, dan komunikasi dengan Ollama.

import express, { Request, Response } from 'express';
import { QdrantClient } from '@qdrant/js-client-rest';
import axios from 'axios';

const app = express();
app.use(express.json());

const PORT = process.env.PORT || 3000;
const QDRANT_HOST = process.env.QDRANT_HOST || 'localhost';
const QDRANT_PORT = parseInt(process.env.QDRANT_PORT || '6333');
const OLLAMA_HOST = process.env.OLLAMA_HOST || 'http://localhost:11434';
const LLM_MODEL = process.env.LLM_MODEL || 'llama3';

const qdrantClient = new QdrantClient({ host: QDRANT_HOST, port: QDRANT_PORT });
const COLLECTION_NAME = 'enterprise_docs';

// Fungsi pembantu untuk menghasilkan embedding teks via Ollama
async function getEmbedding(text: string): Promise<number[]> {
    try {
        const response = await axios.post(`${OLLAMA_HOST}/api/embeddings`, {
            model: 'bge-large',
            prompt: text
        });
        return response.data.embedding;
    } catch (error) {
        console.error('Gagal menghasilkan embedding:', error);
        throw new Error('Embedding generation failed');
    }
}

// Endpoint Ingesti Dokumen
app.post('/api/ingest', async (req: Request, res: Response) => {
    try {
        const { document_id, text, metadata } = req.body;
        if (!text || !document_id) {
            return res.status(400).json({ error: 'Parameter text dan document_id wajib diisi.' });
        }

        const vector = await getEmbedding(text);

        // Pastikan koleksi ada
        const collections = await qdrantClient.getCollections();
        const exists = collections.collections.some(c => c.name === COLLECTION_NAME);
        if (!exists) {
            await qdrantClient.createCollection(COLLECTION_NAME, {
                vectors: { size: vector.length, distance: 'Cosine' }
            });
        }

        // Upsert ke Qdrant
        await qdrantClient.upsert(COLLECTION_NAME, {
            wait: true,
            points: [
                {
                    id: document_id,
                    vector: vector,
                    payload: { text, ...metadata }
                }
            ]
        });

        return res.status(200).json({ success: true, message: 'Dokumen berhasil di-ingest.' });
    } catch (error: any) {
        console.error('Error ingesti:', error.message);
        return res.status(500).json({ error: 'Internal Server Error' });
    }
});

// Endpoint Query RAG
app.post('/api/query', async (req: Request, res: Response) => {
    try {
        const { query } = req.body;
        if (!query) {
            return res.status(400).json({ error: 'Query wajib diisi.' });
        }

        const queryVector = await getEmbedding(query);

        // Cari dokumen relevan di Qdrant
        const searchResult = await qdrantClient.search(COLLECTION_NAME, {
            vector: queryVector,
            limit: 3,
            with_payload: true
        });

        const contextTexts = searchResult.map((item: any) => item.payload.text).join('\n\n');

        // Prompt engineering untuk LLM
        const systemPrompt = `Anda adalah asisten AI enterprise yang cerdas dan akurat. Gunakan konteks berikut untuk menjawab pertanyaan pengguna secara faktual. Jika jawaban tidak ditemukan dalam konteks, katakan bahwa Anda tidak memiliki informasi yang cukup.\n\nKonteks:\n${contextTexts}`;

        // Kirim ke Ollama LLM
        const llmResponse = await axios.post(`${OLLAMA_HOST}/api/generate`, {
            model: LLM_MODEL,
            prompt: `Pertanyaan: ${query}\n\nJawaban:`,
            system: systemPrompt,
            stream: false
        });

        return res.status(200).json({
            success: true,
            query,
            answer: llmResponse.data.response,
            sources: searchResult.map((item: any) => item.payload)
        });
    } catch (error: any) {
        console.error('Error query RAG:', error.message);
        return res.status(500).json({ error: 'Internal Server Error' });
    });
});

app.listen(PORT, () => {
    console.log(`Enterprise RAG Backend berjalan pada port ${PORT}`);
});

3. Dockerfile untuk Backend (Dockerfile)

FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . . 
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]

6. Bedah Kode & Penjelasan Detail Baris Kunci

Mari kita bedah beberapa baris kode paling krusial dalam implementasi sistem RAG di atas:

  1. getEmbedding(text: string): Fungsi ini melakukan panggilan HTTP ke instance Ollama lokal untuk mengubah string mentah menjadi vektor floating-point. Pemilihan model embedding (bge-large) sangat penting karena menentukan seberapa baik makna semantik teks dipahami sebelum dicari di database vektor.
  2. qdrantClient.search(...): Baris ini mengeksekusi pencarian kemiripan kosinus (Cosine Distance). Parameter limit: 3 membatasi hanya 3 chunk dokumen paling relevan yang diambil untuk menghemat token konteks LLM dan mengurangi latensi inferensi.
  3. system: systemPrompt: Bagian ini menerapkan teknik Prompt Hardening. Dengan menginstruksikan LLM secara tegas agar hanya menjawab berdasarkan konteks yang disediakan ("Jika jawaban tidak ditemukan dalam konteks, katakan bahwa Anda tidak memiliki informasi yang cukup"), kita secara drastis menekan tingkat halusinasi model.

[!IMPORTANT] Jangan pernah mengizinkan LLM berhalusinasi bebas di lingkungan enterprise. Selalu gunakan sistem prompt pembatas dan kembalikan referensi sumber (source attribution) agar pengguna dapat memverifikasi kebenaran jawaban secara mandiri.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Dalam skenario produksi dengan trafik tinggi (misalnya 500 request per detik dari ribuan karyawan korporat), arsitektur single-node Docker Compose di atas akan mengalami bottleneck pada CPU/GPU Ollama. Untuk mengatasinya, arsitektur harus diubah menjadi High Availability Cluster:

  1. Load Balancer (Nginx / Cloudflare): Meneruskan request API masuk ke beberapa instance backend RAG secara round-robin.
  2. Cluster Qdrant: Dijalankan dalam mode terdistribusi (Distributed Mode) menggunakan konsensus Raft dengan minimal 3 node untuk memastikan high availability dan replikasi data.
  3. Inference Server Terdedikasi (vLLM): Menggantikan Ollama dengan vLLM yang mendukung Continuous Batching dan PagedAttention, meningkatkan throughput inferensi LLM lokal hingga 300% pada hardware GPU enterprise (NVIDIA A100/H100).

8. Common Pitfalls, Anti-Patterns & Solusi

Berikut adalah kesalahan fatal yang sering dilakukan oleh pengembang saat membangun sistem RAG:

  • Anti-Pattern 1: Chunking Dokumen Asal-Asalan. Memecah dokumen berdasarkan jumlah karakter kaku (misalnya tepat 500 karakter) tanpa memperdulikan struktur semantik (seperti paragraf, heading Markdown, atau batas kalimat). Solusi: Gunakan Semantic Chunking yang memecah teks berdasarkan perubahan topik atau batas struktur dokumen alami.
  • Anti-Pattern 2: Mengabaikan Pembersihan Teks (Data Cleaning). Memasukkan dokumen mentah yang berisi karakter khusus, artefak HTML, atau header/footer berulang dari PDF ke dalam vector database. Solusi: Selalu jalankan pipeline Text Normalization untuk menghapus noise sebelum proses embedding.
  • Anti-Pattern 3: Ukuran Top-K yang Berlebihan. Mengambil 20 hingga 50 dokumen sekaligus untuk disuntikkan ke prompt LLM. Solusi: Batasi Top-K antara 3 hingga 5 dokumen saja agar LLM tidak mengalami lost-in-the-middle phenomenon (kehilangan fokus pada informasi penting di tengah konteks yang terlalu panjang).

9. Benchmark, Metrik Kinerja & Tuning Performa

Pengujian performa sistem RAG harus mencakup dua aspek utama: Latensi Infrastruktur dan Akurasi Model (Retrieval & Generation).

Pengujian Latensi Menggunakan k6

Buat skrip pengujian beban load-test.js untuk menguji endpoint /api/query:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '1m', target: 50 },
    { duration: '3m', target: 50 },
    { duration: '1m', target: 0 },
  ],
};

export default function () {
  const url = 'http://localhost:3000/api/query';
  const payload = JSON.stringify({ query: 'Bagaimana prosedur klaim asuransi kesehatan perusahaan?' });
  const params = { headers: { 'Content-Type': 'application/json' } };

  const res = http.post(url, payload, params);
  check(res, {
    'status adalah 200': (r) => r.status === 200,
    'latensi di bawah 2 detik': (r) => r.timings.duration < 2000,
  });
  sleep(1);
}

Jalankan benchmark dengan perintah:

k6 run load-test.js

Metrik Evaluasi Kualitas RAG

Untuk mengukur akurasi RAG secara berkala, gunakan framework evaluasi seperti Ragas atau TruLens dengan metrik:

  • Context Precision: Apakah dokumen yang diambil oleh database vektor benar-benar relevan dengan pertanyaan?
  • Faithfulness: Apakah jawaban yang dihasilkan LLM bersumber sepenuhnya dari konteks yang diberikan tanpa tambahan informasi fiktif?
  • Answer Relevance: Seberapa baik jawaban LLM merespons pertanyaan pengguna secara langsung?

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum meluncurkan sistem RAG enterprise ke lingkungan produksi (Live), pastikan seluruh item dalam checklist berikut telah terpenuhi:

  • Keamanan Transportasi: Semua komunikasi API menggunakan enkripsi SSL/TLS (Let's Encrypt / Certbot di Nginx Reverse Proxy).
  • Hardening Server: Port database vektor (6333) dan Ollama (11434) ditutup dari akses publik menggunakan UFW firewall; hanya dapat diakses melalui Docker internal network.
  • Manajemen Log & Monitoring: Mengkonfigurasi Prometheus dan Grafana untuk memantau penggunaan VRAM GPU, latensi API backend, dan error rate Qdrant.
  • Backup & Recovery: Menjadwalkan cron job harian untuk mencadangkan volume Docker Qdrant (qdrant_data) ke penyimpanan S3/MinIO eksternal.
  • Rate Limiting: Menerapkan pembatasan rate limit di Nginx atau backend Node.js untuk mencegah serangan DDoS atau penyalahgunaan resource LLM.

Dengan mengikuti panduan komprehensif ini, Anda kini memiliki fondasi arsitektur, kode implementasi siap produksi, dan pemahaman mendalam untuk membangun sistem RAG skala enterprise yang aman, berdaulat, dan berkinerja tinggi.

🏷️ Tags: #Nginx #High Concurrency #Performance #Sysadmin #Web Server