Tutorial: Membuat Chatbot AI untuk Brand Sendiri (dengan RAG)

Setiap brand kini ingin asisten AI yang paham produk dan kebijakannya — bukan chatbot generik. Rahasianya adalah RAG (Retrieval-Augmented Generation): AI menjawab berdasarkan dokumenmu sendiri, bukan tebakan. Tutorial ini membangunnya langkah demi langkah dengan AI SDK.
Untuk siapa tutorial ini?
- Tingkat: Pemula–Menengah (paham dasar React/Next.js akan membantu)
- Perkiraan waktu: 60–90 menit
- Hasil akhir: chatbot di halaman web yang menjawab berdasarkan dokumen yang kamu beri
Istilah penting dulu:
- Embedding = mengubah teks menjadi deretan angka (vektor) agar komputer bisa mengukur "kemiripan makna".
- Chunk = potongan kecil dokumen.
- RAG = teknik mencari potongan dokumen yang relevan lalu menyuapkannya ke AI sebagai bahan jawaban.
- Halusinasi = saat AI mengarang jawaban yang terdengar yakin padahal salah.
Prasyarat
-
Node.js 18+ (cek
node -vdi Terminal). -
Proyek Next.js. Belum punya?
npx create-next-app@latest chatbot-brandlalucd chatbot-brand. -
Database dengan dukungan vektor — gunakan Neon atau Supabase (keduanya mendukung ekstensi
pgvectoruntuk menyimpan embedding). -
Paket yang diperlukan, instal lewat Terminal:
npm install ai @ai-sdk/react -
Kunci API AI di file
.env.local(lihat tutorial AI Workflow untuk caranya).
Cara kerja RAG (gambaran besar)
Baca ini sekali agar paham alurnya sebelum koding:
- Dokumenmu dipecah jadi potongan kecil (chunks).
- Tiap chunk diubah jadi embedding dan disimpan ke database.
- Saat user bertanya, pertanyaan juga diubah jadi embedding lalu dicari chunk paling mirip.
- Chunk relevan diselipkan ke prompt AI sebagai konteks, lalu AI menjawab.
Langkah 1: Siapkan embedding dokumen
Buat file lib/siapkan-dokumen.ts. Kode ini mengubah potongan teks menjadi embedding lalu menyimpannya.
// lib/siapkan-dokumen.ts
import { embedMany } from 'ai'
export async function siapkanDokumen(chunks: string[]) {
// ubah banyak teks sekaligus menjadi embedding
const { embeddings } = await embedMany({
model: 'openai/text-embedding-3-small',
values: chunks, // contoh: ["Jam buka kami 09.00-17.00", "Garansi 30 hari", ...]
})
// simpan pasangan { teks, embedding } ke database vektor milikmu
await simpanKeDatabase(chunks, embeddings)
}Tips memecah dokumen: potong tiap 300–500 kata, atau per paragraf. Chunk terlalu besar membuat pencarian kurang tepat; terlalu kecil membuat konteks hilang.
Langkah 2: Cari konteks relevan
Buat file lib/cari-konteks.ts:
// lib/cari-konteks.ts
import { embed } from 'ai'
export async function cariKonteks(pertanyaan: string) {
// ubah pertanyaan user menjadi embedding
const { embedding } = await embed({
model: 'openai/text-embedding-3-small',
value: pertanyaan,
})
// ambil 4 chunk paling mirip (cosine similarity) dari database
return cariChunkTerdekat(embedding, 4) // mengembalikan array string
}Istilah: Cosine similarity adalah cara mengukur seberapa "searah" dua vektor — makin searah, makin mirip maknanya.
Langkah 3: Buat route API chat
Buat file app/api/chat/route.ts. Gunakan streamText agar jawaban muncul mengalir seperti ChatGPT.
// app/api/chat/route.ts
import { streamText, convertToModelMessages } from 'ai'
import { cariKonteks } from '@/lib/cari-konteks'
export async function POST(req: Request) {
const { messages } = await req.json()
// ambil teks pertanyaan terakhir dari user
const pertanyaan = messages[messages.length - 1].content
const konteks = await cariKonteks(pertanyaan)
const result = streamText({
model: 'openai/gpt-5-mini',
system: `Kamu asisten resmi brand. Jawab HANYA berdasarkan konteks berikut.
Jika informasinya tidak ada di konteks, katakan kamu tidak tahu.
Konteks:
${konteks.join('\n')}`,
messages: convertToModelMessages(messages),
})
return result.toUIMessageStreamResponse()
}Instruksi "jawab HANYA berdasarkan konteks" sangat penting untuk mencegah AI berhalusinasi.
Langkah 4: Buat UI chat dengan useChat
Buat file components/chat.tsx:
// components/chat.tsx
'use client'
import { useChat } from '@ai-sdk/react'
import { useState } from 'react'
export function Chat() {
const { messages, sendMessage, status } = useChat()
const [input, setInput] = useState('')
return (
<div className="mx-auto flex max-w-lg flex-col gap-3 p-4">
{messages.map((m) => (
<div key={m.id} className="rounded-md bg-card p-3">
<strong>{m.role === 'user' ? 'Kamu' : 'Asisten'}:</strong>{' '}
{m.parts.map((p, i) => (p.type === 'text' ? <span key={i}>{p.text}</span> : null))}
</div>
))}
<form
onSubmit={(e) => {
e.preventDefault()
sendMessage({ text: input })
setInput('')
}}
className="flex gap-2"
>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Tanya sesuatu..."
className="flex-1 rounded-md border bg-background px-3 py-2"
/>
<button
disabled={status !== 'ready'}
className="rounded-md bg-primary px-4 py-2 text-primary-foreground"
>
Kirim
</button>
</form>
</div>
)
}Tampilkan di app/page.tsx:
import { Chat } from "@/components/chat"
export default function Home() {
return <Chat />
}Yang harus kamu lihat: jalankan npm run dev, buka http://localhost:3000, ketik pertanyaan tentang isi dokumenmu. Jawaban muncul mengalir dan hanya berdasarkan dokumen yang kamu beri.
Langkah 5: Beri batasan dan kepribadian
Atur nada bicara dan batasan di system prompt (Langkah 3): bahasa, panjang jawaban, dan kapan harus mengarahkan ke manusia. Inilah yang membuat chatbot terasa "milik brand", bukan generik.
Troubleshooting
- Jawaban selalu "saya tidak tahu" → kemungkinan pencarian konteks kosong. Pastikan dokumen sudah di-embed (Langkah 1) dan database terisi.
- Error koneksi database → cek variabel lingkungan database di
.env.local. - Jawaban mengarang di luar dokumen → perkuat instruksi "jawab HANYA berdasarkan konteks" di
system. - UI tidak mengirim pesan → pastikan file route ada di
app/api/chat/route.ts(nama folder harus persischat).
Tips produksi
- Cantumkan sumber (chunk mana yang dipakai) agar jawaban bisa diverifikasi.
- Batasi rate dan simpan riwayat untuk evaluasi.
- Perbarui embedding tiap kali dokumen berubah.
Dengan pola ini kamu bisa membuat asisten untuk FAQ, dokumentasi produk, atau onboarding. Untuk menjalankannya di servermu sendiri, lihat Panduan Deploy.
Suka dengan artikel ini?
Beri dukunganmu dengan menekan tombol suka — bantu pembaca lain menemukan konten terbaik.

