2026-06-12 · 7 min
Supabase pgvector RAG 1k Documents: Cost + Latency Real
Klien firma hukum mid-size Jakarta (8 lawyer, ~1000 kontrak digital sejak 2023) request internal search bertenaga AI: “find me kontrak yang ada klausa indemnity untuk perusahaan listing IDX.” Saya build RAG sederhana di Supabase pgvector. Hasil terukur setelah 6 minggu.
Setup
Stack:
- Supabase Postgres 17 + pgvector 0.7
- Embedding: OpenAI
text-embedding-3-small(1536 dim) - LLM: Claude Sonnet 4.7 untuk synthesis
- App: Next.js 15 dengan Server Actions
- Auth: Supabase Auth (related: Bun + Supabase Auth migration)
Data: 1,047 dokumen PDF kontrak, average 18 halaman, total ~15,8MB text setelah extract.
Indexing pipeline
// Pipeline pseudocode
for each pdf:
text = extract(pdf)
chunks = splitter.split(text, {chunkSize: 800, overlap: 100})
for each chunk:
embedding = openai.embeddings(chunk)
db.insert(document_chunks, {pdf_id, chunk_text, embedding, metadata})
Total chunk: 18,420 (1,047 dokumen × rata-rata 17 chunk).
Embedding cost: 18,420 chunk × ~600 token avg = ~11M token × $0.020/M = $0.22 (~Rp 3,5k). One-time.
Index pgvector: HNSW dengan m=16, ef_construction=64. Build time 4 menit 22 detik.
CREATE INDEX ON document_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
Query latency
Test 200 sample query (real query dari lawyer klien).
Vector retrieval (top-10 chunk):
- P50: 78ms
- P95: 280ms
- P99: 510ms
Source latency: pgvector HNSW + Supabase Singapore region. 200ms baseline dari Jakarta → Singapore tetap ada.
End-to-end query (retrieval + LLM synthesis):
- P50: 2,8 detik
- P95: 5,4 detik
- P99: 8,2 detik
LLM dominate latency. Claude Sonnet streaming, tapi total time-to-final-token tetap 2-5 detik.
Cost (bulanan)
- Supabase Pro: $25/mo (sudah include 8GB storage + DB + auth)
- OpenAI embeddings (query): ~5,000 query/bulan × 50 token avg × $0.020/M = $0.005 (negligible)
- Claude API (synthesis): ~5,000 query × 2,500 token input + 800 output × $3/M + $15/M = $52,5
Wait, mahal. Saya optimize:
- Tambah cache layer untuk query yang serupa (Redis di Hetzner): ~30% cache hit
- Switch sebagian query ke Claude Haiku untuk pertanyaan sederhana: 60% query
- Final Claude API cost: ~$23/mo
Total: $25 (Supabase) + $0,01 (embedding) + $23 (Claude) = $48/mo
Klien acceptable. Per query cost: $48 / 5000 = ~$0,0096 atau Rp 154/query. Bandingkan dengan junior associate lawyer cari kontrak manual: ~15 menit × Rp 75k/jam = Rp 18,750/query.
Akurasi: chunk size matters
Initial attempt: chunk size 1500 character. Retrieval kadang miss konteks penting.
Test 50 query dengan ground truth (manual annotated):
- Chunk 1500: precision@5 = 62%, recall@10 = 71%
- Chunk 800 + overlap 100: precision@5 = 78%, recall@10 = 86%
- Chunk 400 + overlap 50: precision@5 = 73%, recall@10 = 91% (tapi context per chunk kurang)
Saya pilih 800 chunk + 100 overlap sebagai best balance. Re-index satu kali = $0,22 cost.
Pitfall yang bikin malu
Klien lawyer test query: “Cari kontrak yang ada klausa force majeure terkait pandemic post-2020.”
System return: top result adalah kontrak yang punya kata “force majeure” + “pandemic” + “2020” — tapi konteks-nya negatif (klausa explicit EXCLUDE pandemic dari force majeure). Lawyer perlu kebalikan: kontrak yang INCLUDE pandemic ke force majeure.
Root cause: cosine similarity tidak understand negation. “Termasuk pandemic” dan “tidak termasuk pandemic” dekat di embedding space karena kata-katanya overlap.
Fix:
- Tambah re-ranking step dengan Claude: pass top-20 retrieval, minta Claude filter berdasarkan semantic intent yang true ke query
- Cost naik per query (extra LLM call): +~$0,005/query, atau ~$25/mo extra
- Precision@5 naik ke 91%
Final cost: ~$73/mo. Klien setuju.
Yang juga break
-
Embedding rate limit: OpenAI free tier limit 3 req/min embedding. Saya pakai paid tier sejak awal, no issue. Worth-noting kalau hackathon.
-
pgvector index rebuild lambat: kalau data growth besar (kasus saya: klien tambah 200 kontrak baru), index rebuild ~1 menit dengan SQL
REINDEX INDEX CONCURRENTLY. Tidak ada downtime. -
PDF extract noise: text dari PDF kadang ada header/footer / page number yang noise. Saya tulis cleanup regex; tapi 5% PDF tetap noisy. Saya tag those for human review.
-
Bahasa Indonesia + Inggris mixed: kontrak banyak hybrid (term Inggris di kalimat Indonesia). Embedding model OpenAI multilingual handle this. Test: jangan pakai monolingual Indonesian model untuk legal hybrid.
Memory
Supabase Postgres usage:
- pgvector table size: 1,2GB (chunk text + embedding)
- Index size: 380MB
- Total DB: 1,7GB. Masih jauh dari Pro tier 8GB limit.
Kalau growth ke 10x (10k dokumen): estimate 17GB. Butuh upgrade Supabase Team ($599/mo) atau migrate ke self-hosted Postgres + pgvector di Hetzner.
Verdict
Untuk RAG 100-10k dokumen di Indonesian SMB context: Supabase pgvector + OpenAI embeddings + Claude synthesis sangat cost-effective.
Cost-per-query Rp 200-an, vs lawyer manual research Rp 18k+: 90x cheaper. Klien sangat happy, sekarang lawyer pakai daily, expand ke dokumen non-kontrak (legal opinion, court ruling).
Bukan magic — chunk size, re-ranking, dan negation handling butuh iterate. Setup awal 8 jam, fine-tune sampai akurasi acceptable 12 jam tambahan.
Untuk volume > 1jt vector: pertimbangkan dedicated vector DB. Untuk SMB Indonesia: pgvector lebih dari cukup.
Ditulis oleh Reza Pradipta