L
Artificial Intelligence & LLM • Literally Web ID

Membangun Sistem RAG Enterprise Berbasis Vector Database dan LLM Lokal

⏱️ 15 menit baca • 📅 13 Oct 2026 • 👁️ 1 views
Membangun Sistem RAG Enterprise Berbasis Vector Database dan LLM Lokal

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap korporat modern, adopsi Large Language Models (LLM) sering kali membentur tembok besar: keterbatasan konteks model, risiko halusinasi data, dan yang paling krusial adalah privasi data internal perusahaan. Mengirimkan dokumen finansial, kekayaan intelektual, atau data pelanggan ke API pihak ketiga (seperti OpenAI atau Anthropic) adalah pelanggaran kepatuhan (compliance) yang tidak dapat ditoleransi di sektor perbankan, kesehatan, dan pemerintahan. Oleh karena itu, arsitektur Retrieval-Augmented Generation (RAG) berbasis infrastruktur lokal menjadi solusi mutlak.

Namun, membangun RAG enterprise bukanlah sekadar merangkai skrip Python dari tutorial dasar. Di lingkungan produksi berskala tinggi, tantangan utamanya berkisar pada latensi tinggi, fragmentasi dokumen yang buruk saat proses chunking, pemilihan vector database yang tidak sesuai dengan karakteristik workload (apakah berbasis read-heavy atau write-heavy), serta manajemen memori pada LLM lokal yang berjalan di atas perangkat keras mandiri (on-premise GPU/CPU clusters).

Sebelum vs Sesudah Penerapan

  • Sebelum RAG Enterprise: Karyawan menghabiskan waktu berjam-jam mencari dokumen SOP di shared folder perusahaan yang tidak terindeks. Ketika menggunakan LLM publik, jawaban sering kali melenceng (halusinasi), tidak menyertakan referensi sumber, dan rentan terhadap kebocoran data sensitif (data leakage).
  • Sesudah RAG Enterprise: Sistem mengintegrasikan vector database teroptimasi dengan model LLM lokal via Ollama. Dokumen internal diproses melalui pipeline chunking semantik, disimpan aman di infrastruktur internal (dijembatani oleh Nginx, dipantau via Redis caching), dan menghasilkan jawaban yang akurat, berdasar pada dokumen aktual, serta memiliki latensi respon yang dapat diprediksi.

2. Konsep Inti & Deep Dive Arsitektur

Sistem RAG Enterprise memisahkan penyimpanan pengetahuan statis dari memori dinamis model melalui mekanisme pencarian kemiripan vektor (vector similarity search). Ketika pengguna mengirimkan kueri melalui antarmuka web, permintaan tersebut melewati serangkaian lapisan infrastruktur sebelum mencapai mesin inferensi.

[ Client Browser ] 
       │ (HTTPS / WSS)
       ▼
[ Nginx Reverse Proxy / WAF ]
       │
       ▼
[ FastAPI Backend Worker (Python) ]
       ├───────────────┬───────────────┐
       ▼               ▼               ▼
[ Redis Cache ]  [ Embedding API ]  [ Vector DB (Qdrant/Milvus) ]
       │               │               │
       └───────────────┼───────────────┘
                       ▼
             [ Ollama Local LLM ]

Alur Eksekusi Data (Data Flow)

  1. Inbound Request: Pengguna mengirim kueri melalui antarmuka web. Nginx bertindak sebagai reverse proxy yang melakukan terminasi SSL/TLS dan meneruskannya ke backend FastAPI.
  2. Caching Check: Backend memeriksa Redis untuk melihat apakah kueri serupa telah dieksekusi sebelumnya untuk menghemat siklus komputasi.
  3. Vector Retrieval: Kueri diubah menjadi vektor numerik melalui model embedding lokal, kemudian dikirim ke vector database (misal: Qdrant) untuk mengambil potongan dokumen (chunks) yang memiliki jarak kosinus terdekat (cosine similarity).
  4. Context Injection & Generation: Konteks dokumen yang relevan digabungkan dengan kueri asli ke dalam prompt template, lalu dikirim ke model LLM lokal melalui Ollama.
  5. Response Delivery: Hasil inferensi dikembalikan secara streaming menggunakan Server-Sent Events (SSE) atau WebSocket ke peramban pengguna.

3. Perbandingan Komparatif / Analisis Pendekatan

Pemilihan vector database adalah keputusan arsitektur paling vital yang akan menentukan skalabilitas sistem Anda di masa depan. Berikut adalah analisis mendalam antara tiga solusi utama: Milvus, Qdrant, dan pgvector.

Parameter Milvus Qdrant pgvector
Arsitektur Distributed, Cloud-native Standalone / Distributed (Rust) Ekstensi PostgreSQL
Bahasa Utama C++ / Go Rust C / SQL
Skalabilitas Data Sangat Tinggi (> 100 Juta Vektor) Tinggi (Puluhan Juta Vektor) Menengah (< 10 Juta Vektor)
Konsumsi RAM Tinggi (Dioptimalkan untuk sharding) Efisien (Ditulis dalam Rust) Bergantung pada alokasi RAM Postgres
Kompleksitas Ops Tinggi (Membutuhkan etcd, MinIO, dll) Rendah hingga Menengah Sangat Rendah (Jika sudah pakai Postgres)
Fitur Filter Metadata Sangat Kaya & Kompleks Sangat Cepat (Payload Filtering) Bergantung pada indeks SQL standar
Rekomendasi Kasus Korporat besar dengan multi-terabyte data Aplikasi enterprise standar & real-time search Startup / Sistem monolitik yang sudah pakai PostgreSQL

[!IMPORTANT] Jika infrastruktur perusahaan Anda sudah mapan menggunakan PostgreSQL, mulailah dengan pgvector untuk menghindari kompleksitas operasional tambahan. Namun, jika volume dokumen mencapai jutaan halaman dengan atribut metadata yang sangat dinamis, beralihlah ke Qdrant atau Milvus.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum melangkah ke implementasi teknis, pastikan spesifikasi perangkat keras dan perangkat lunak Anda memenuhi standar enterprise:

  • OS: Ubuntu Server 22.04 LTS (Clean Minimal Install)
  • Hardware: Minimal 16 vCPU, 64 GB RAM, dan GPU NVIDIA (misal: RTX 4090 atau A10G dengan VRAM minimal 24GB untuk menjalankan model LLM 13B/70B terkompresi).
  • Software Dependencies:
    • Docker & Docker Compose v2.20+
    • Python 3.10+
    • Nginx sebagai Reverse Proxy
    • Redis Server untuk caching dan rate limiting

Konfigurasi kernel sistem operasi untuk menangani koneksi konkuren tinggi (/etc/security/limits.conf):

* soft nofile 65535
* hard nofile 65535
* soft nproc 4096
* hard nproc 4096

5. Panduan Implementasi Langkah demi Langkah

Langkah 1: Konfigurasi Docker Compose untuk Infrastruktur Dasar

Buat file docker-compose.yml untuk menjalankan Qdrant, Redis, dan Ollama dalam satu jaringan privat.

version: '3.8'

services:
  qdrant:
    image: qdrant/qdrant:latest
    container_name: enterprise_qdrant
    restart: always
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage
    environment:
      - QDRANT__SERVICE__HTTP_PORT=6333

  redis:
    image: redis:7.2-alpine
    container_name: enterprise_redis
    restart: always
    ports:
      - "6379:6379"
    command: redis-server --save 60 1 --loglevel warning
    volumes:
      - redis_data:/data

  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]

volumes:
  qdrant_data:
  redis_data:
  ollama_data:

Langkah 2: Skrip Python Pipeline Ingesti Dokumen (Chunking & Embedding)

Buat skrip ingest.py untuk membaca dokumen teks, memecahnya menggunakan strategi semantik, dan menyimpannya ke Qdrant.

import os
from typing import List
from qdrant_client import QdrantClient
from qdrant_client.http import models
from sentence_transformers import SentenceTransformer

# Inisialisasi konfigurasi
QDRANT_HOST = "localhost"
QDRANT_PORT = 6333
COLLECTION_NAME = "enterprise_knowledge_base"

# Muat model embedding lokal
embedder = SentenceTransformer('all-MiniLM-L6-v2', device='cuda')

# Inisialisasi client Qdrant
client = QdrantClient(host=QDRANT_HOST, port=QDRANT_PORT)

def create_collection_if_not_exists():
    collections = client.get_collections().collections
    exists = any(c.name == COLLECTION_NAME for c in collections)
    if not exists:
        client.create_collection(
            collection_name=COLLECTION_NAME,
            vectors_config=models.VectorParams(
                size=384,  # Ukuran dimensi untuk all-MiniLM-L6-v2
                distance=models.Distance.COSINE
            )
        )
        print(f"Koleksi {COLLECTION_NAME} berhasil dibuat.")

def chunk_text(text: str, chunk_size: int = 500, overlap: int = 50) -> List[str]:
    """Memecah teks menjadi potongan dengan ukuran dan overlap tertentu."""
    words = text.split()
    chunks = []
    for i in range(0, len(words), chunk_size - overlap):
        chunk = " ".join(words[i:i + chunk_size])
        chunks.append(chunk)
    return chunks

def ingest_document(doc_id: str, filepath: str):
    create_collection_if_not_exists()
    
    if not os.path.exists(filepath):
        raise FileNotFoundError(f"File {filepath} tidak ditemukan.")
        
    with open(filepath, 'r', encoding='utf-8') as f:
        content = f.read()
        
    chunks = chunk_text(content)
    vectors = embedder.encode(chunks, show_progress_bar=True)
    
    points = []
    for idx, (chunk, vector) in enumerate(zip(chunks, vectors)):
        point_id = f"{doc_id}_{idx}"
        points.append(
            models.PointStruct(
                id=idx,
                vector=vector.tolist(),
                payload={
                    "doc_id": doc_id,
                    "chunk_index": idx,
                    "text": chunk
                }
            )
        )
        
    client.upsert(
        collection_name=COLLECTION_NAME,
        points=points
    )
    print(f"Berhasil mengunggah {len(points)} chunk dari dokumen {doc_id} ke Qdrant.")

if __name__ == "__main__":
    # Contoh eksekusi ingesti
    # ingest_document("sop_hr_2024", "/path/to/document.txt")
    pass

Langkah 3: Backend FastAPI untuk RAG Pipeline

Buat file main.py yang melayani kueri pengguna, melakukan pencarian vektor, dan berinteraksi dengan Ollama.

import requests
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from qdrant_client import QdrantClient
from sentence_transformers import SentenceTransformer

app = FastAPI(title="Enterprise RAG API", version="1.0.0")

# Inisialisasi komponen
embedder = SentenceTransformer('all-MiniLM-L6-v2', device='cuda')
client = QdrantClient(host="localhost", port=6333)
COLLECTION_NAME = "enterprise_knowledge_base"
OLLAMA_URL = "http://localhost:11434/api/generate"

class QueryRequest(BaseModel):
    prompt: str
    top_k: int = 3

@app.post("/api/v1/query")
def query_rag(request: QueryRequest):
    try:
        # 1. Buat embedding dari kueri pengguna
        query_vector = embedder.encode(request.prompt).tolist()
        
        # 2. Cari dokumen relevan di Qdrant
        search_results = client.search(
            collection_name=COLLECTION_NAME,
            query_vector=query_vector,
            limit=request.top_k
        )
        
        context_texts = [hit.payload["text"] for hit in search_results]
        context = "\n\n".join(context_texts)
        
        # 3. Susun Prompt untuk LLM Lokal
        augmented_prompt = (
            f"Gunakan konteks di bawah ini untuk menjawab pertanyaan secara akurat.\n"
            f"Jika jawaban tidak ditemukan dalam konteks, katakan 'Saya tidak tahu'.\n\n"
            f"Konteks:\n{context}\n\n"
            f"Pertanyaan: {request.prompt}\nJawaban:"
        )
        
        # 4. Kirim permintaan ke Ollama (Llama 3)
        payload = {
            "model": "llama3",
            "prompt": augmented_prompt,
            "stream": False
        }
        
        response = requests.post(OLLAMA_URL, json=payload)
        if response.status_code != 200:
            raise HTTPException(status_code=500, detail="Gagal berkomunikasi dengan Ollama")
            
        result = response.json()
        return {
            "query": request.prompt,
            "response": result.get("response", ""),
            "sources": context_texts
        }
        
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

Langkah 4: Konfigurasi Nginx Reverse Proxy

Konfigurasikan Nginx /etc/nginx/sites-available/rag-enterprise untuk mengamankan akses backend FastAPI dengan enkripsi SSL/TLS.

server {
    listen 80;
    server_name rag.internal.enterprise.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name rag.internal.enterprise.com;

    ssl_certificate /etc/letsencrypt/live/rag.internal.enterprise.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/rag.internal.enterprise.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    client_max_body_size 50M;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Konfigurasi timeout untuk inferensi LLM yang memakan waktu
        proxy_read_timeout 300s;
        proxy_connect_timeout 300s;
        proxy_send_timeout 300s;
    }
}

6. Bedah Kode & Penjelasan Detail Baris Kunci

  • SentenceTransformer('all-MiniLM-L6-v2', device='cuda'): Memuat model embedding berbobot ringan namun berkinerja tinggi. Parameter device='cuda' memastikan bahwa komputasi vektor dialihkan dari CPU ke GPU, mempercepat proses embedding hingga 10x lipat.
  • chunk_size=500, overlap=50: Strategi pemotongan teks yang krusial. Ukuran 500 kata menjaga agar konteks tetap padat tanpa kehilangan makna global, sementara overlap 50 kata memastikan tidak ada informasi penting yang terpotong di batas antar-chunk.
  • proxy_read_timeout 300s pada Nginx: Inferensi LLM lokal membutuhkan waktu komputasi yang lebih lama dibandingkan HTTP request standar. Tanpa memperpanjang timeout ini, Nginx akan memutus koneksi dengan error 504 Gateway Time-out sebelum model selesai menghasilkan jawaban.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Dalam skenario operasional perbankan tier-1 dengan 5.000 staf korporat yang mengakses sistem secara simultan, pendekatan monolitik akan langsung mengalami bottleneck.

Arsitektur High-Availability (HA)

  • Load Balancing Nginx: Permintaan dibagi secara merata ke beberapa worker FastAPI menggunakan upstream cluster.
  • Qdrant Cluster: Menggunakan arsitektur terdistribusi Qdrant dengan skema replication factor 2 untuk memastikan ketersediaan tinggi (high availability) jika salah satu node fisik mengalami kegagalan perangkat keras.
  • Model Caching dengan Redis: Pertanyaan umum yang sering diajukan pegawai (misal: "Bagaimana prosedur cuti tahunan?") langsung dilayani dari cache Redis dalam waktu < 10 milidetik tanpa membebani GPU Ollama sama sekali.

8. Common Pitfalls, Anti-Patterns & Solusi

[!WARNING] Anti-Pattern 1: Mengabaikan Prompt Injection pada RAG Dokumen internal yang diunggah mungkin berisi teks manipulatif yang dirancang untuk membajak instruksi sistem (system prompt). Solusi: Selalu terapkan sanitasi input dan gunakan delimiter yang jelas antara konteks dokumen dan instruksi sistem di dalam prompt template.

[!WARNING] Anti-Pattern 2: Ukuran Chunk Terlalu Besar Memasukkan satu dokumen penuh (10.000 kata) ke dalam satu vektor akan mengaburkan relevansi semantik, membuat model LLM bingung mencari informasi spesifik. Solusi: Batasi ukuran chunk maksimal 300-500 token dengan pemisahan berbasis paragraf atau struktur heading Markdown.


9. Benchmark, Metrik Kinerja & Tuning Performa

Pengujian performa sistem dilakukan menggunakan tool load testing k6 untuk mengukur latensi end-to-end pada beban 100 concurrent users.

Skrip Uji Beban k6 (load-test.js)

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

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

export default function () {
  const url = 'https://rag.internal.enterprise.com/api/v1/query';
  const payload = JSON.stringify({
    prompt: 'Apa kebijakan keamanan data perusahaan?',
    top_k: 3,
  });

  const params = {
    headers: { 'Content-Type': 'application/json' },
  };

  const res = http.post(url, payload, params);
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 3s': (r) => r.timings.duration < 3000,
  });
  sleep(1);
}

Tuning Parameter Lanjutan

  • Ollama Threads: Atur variabel lingkungan OLLAMA_NUM_PARALLEL=4 untuk mengizinkan pemrosesan antrean paralel di tingkat mesin inferensi.
  • Qdrant HNSW Index Tuning: Optimalkan parameter m dan ef_construct pada indeks HNSW Qdrant untuk menyeimbangkan kecepatan pencarian (recall) dan penggunaan memori RAM.

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum merilis sistem RAG Enterprise ke lingkungan live, pastikan seluruh item dalam checklist berikut telah terpenuhi:

  • Konfigurasi SSL/TLS yang valid dan terpasang sempurna pada Nginx.
  • Pembatasan akses jaringan (Firewall/UFW) hanya mengizinkan port internal ke kontainer Docker.
  • Skrip backup otomatis untuk volume data Qdrant dan metadata PostgreSQL/Redis.
  • Implementasi rate limiting pada API untuk mencegah serangan Denial of Service (DoS).
  • Monitoring sumber daya sistem menggunakan Prometheus dan Grafana (khususnya pemantauan suhu dan VRAM GPU).

Dengan mengikuti panduan komprehensif ini, Anda kini memiliki fondasi arsitektur tingkat enterprise yang tidak hanya aman dari sisi kepatuhan data korporat, tetapi juga tangguh, terukur, dan siap diandalkan dalam menangani beban produksi skala besar.

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