Embedding & Vector Search — AI Web

Embedding & Vector Search Embedding mengubah teks menjadi vektor (array angka) yang menangkap makna. Teks yang mirip maknanya → vektor yang berdekatan. Ini…

Embedding adalah teks yang diubah jadi daftar angka, namanya vector, yang mewakili maknanya. Teks yang maknanya mirip dapat angka yang mirip juga. Bayangin rak Toko Buku yang disusun menurut tema: novel tentang sekolah di satu pojok, novel sejarah di pojok lain. Pelanggan yang cari "novel tentang sekolah" tinggal jalan ke pojok yang pas, lalu ambil buku yang paling dekat. Embedding memberi tiap teks satu titik di "peta" seperti itu, cuma petanya punya 1.536 arah, bukan dua. Vector search adalah mencari titik yang paling dekat dengan titik pertanyaan.

🧪 Tebakan cepat: vector dari OpenAI sudah dibuat panjangnya tepat 1. Untuk mengukur kemiripan dua vector seperti itu, kamu masih perlu membagi dengan panjangnya, atau cukup mengalikan angkanya lalu menjumlahkan? Tebak dulu, jawabannya ada di bagian "Normalisasi: cosine jadi dot product".

Embedding: teks jadi daftar angka

Asisten Toko Buku mau bisa menjawab "ada novel tentang sekolah di Belitung?". Kata "sekolah" gak ada di sinopsis Laskar Pelangi (yang ada "bersekolah"), jadi WHERE sinopsis LIKE '%sekolah di Belitung%' gak ketemu. Embedding mencari berdasarkan makna, bukan huruf yang sama persis.

Model embedding juga dipanggil lewat HTTP. Ini kode server Toko Buku dengan SDK OpenAI:

import OpenAI from "openai";
const client = new OpenAI();

const res = await client.embeddings.create({
  model: "text-embedding-3-small",
  input: books.map((b) => b.sinopsis), // 4 sinopsis dalam 1 request
});
const vektor = res.data.map((d) => d.embedding);
console.log(vektor.length, vektor[0].length); // 4 1536

Embedding mainan yang bisa dihitung tangan

Angka dari model gak bisa kita hitung di kertas. Jadi di pelajaran ini dan di Lab, kita pakai embedding mainan yang dihitung lokal: hitung kata (bag-of-words). Tiap kata adalah satu dimensi, nilainya berapa kali kata itu muncul. Kata umum di, dan, yang, dari, ke, pada, dengan, tentang dibuang dulu, karena ada di hampir semua kalimat. Ini bukan model embedding: dia cuma menangkap kata yang sama persis. Tapi rumus kemiripannya sama persis dengan yang dipakai untuk vector asli.

Teks Kata yang dihitung Panjang vector
"novel tentang sekolah di Belitung" novel, sekolah, belitung (masing-masing 1) √3 = 1,732
Sinopsis Laskar Pelangi 15 kata, masing-masing 1, termasuk belitung √15 = 3,873
Sinopsis Bumi Manusia 20 kata √20 = 4,472
Sinopsis Cantik Itu Luka 18 kata √18 = 4,243
Sinopsis Ronggeng Dukuh Paruk 14 kata √14 = 3,742

Panjang vector dihitung dengan Pythagoras: akar dari jumlah kuadrat semua angkanya. Tiga angka 1 berarti √(1² + 1² + 1²) = √3.

Cosine similarity dengan tangan

Cosine similarity mengukur seberapa searah dua vector:

cosine(a, b) = (a · b) / (|a| × |b|)

a · b adalah dot product: kalikan angka di dimensi yang sama, lalu jumlahkan. |a| adalah panjang vector. Hasilnya 1 kalau arahnya sama persis, 0 kalau gak punya kesamaan sama sekali, dan bisa sampai -1 kalau berlawanan (hitung kata gak pernah negatif, tapi vector dari model bisa).

Untuk pertanyaan dan Laskar Pelangi, cuma kata "belitung" yang ada di keduanya, jadi dot product-nya 1 × 1 = 1:

Buku Kata yang sama Dot product Cosine
Laskar Pelangi belitung 1 1 / (1,732 × 3,873) = 1 / 6,708 = 0,149
Bumi Manusia (gak ada) 0 0
Cantik Itu Luka (gak ada) 0 0
Ronggeng Dukuh Paruk (gak ada) 0 0

Laskar Pelangi di urutan pertama. Kodenya, untuk dua array angka yang panjangnya sama:

function cosine(a, b) {
  let dot = 0, panjangA = 0, panjangB = 0;
  for (let i = 0; i < a.length; i++) {
    dot += a[i] * b[i]; // dot product
    panjangA += a[i] * a[i]; // jumlah kuadrat
    panjangB += b[i] * b[i];
  }
  return dot / (Math.sqrt(panjangA) * Math.sqrt(panjangB));
}

function cariMirip(vektorPertanyaan, buku, k = 2) {
  return buku
    .map((b) => ({ judul: b.judul, skor: cosine(vektorPertanyaan, b.vektor) }))
    .sort((x, y) => y.skor - x.skor) // skor terbesar di depan
    .slice(0, k); // ambil k teratas (top-k)
}

Normalisasi: cosine jadi dot product

Normalisasi artinya membagi tiap angka dengan panjang vector-nya, jadi panjangnya tepat 1. Arahnya gak berubah. Pertanyaan tadi jadi tiga angka 1 / 1,732 = 0,577, dan Laskar Pelangi jadi 15 angka 1 / 3,873 = 0,258. Dot product-nya 0,577 × 0,258 = 0,149, sama dengan cosine di atas, karena kedua panjangnya sekarang 1 dan pembaginya 1 × 1.

Panduan "Vector embeddings" OpenAI (dicek Oktober 2026) menulis embedding OpenAI sudah dinormalisasi ke panjang 1. Jadi cukup dot product, gak perlu membagi dengan panjang lagi. Itu jawaban tebakannya. Panduan yang sama menulis cosine similarity dan jarak Euclidean memberi urutan yang identik untuk vector seperti itu.

Satu catatan dari panduan itu: kalau kamu memendekkan vector sendiri (misalnya slice(0, 256)) dan bukan lewat dimensions, normalisasi ulang dulu sebelum memakai dot product.

Index time dan query time

Pencarian ini punya dua waktu:

  1. Index time, sekali per buku: embed sinopsis, simpan vector-nya bersama id buku. Kalau sinopsisnya berubah atau buku baru datang, embed ulang buku itu.
  2. Query time, tiap pertanyaan: embed pertanyaannya dengan model yang sama, hitung kemiripan dengan semua vector buku, urutkan, ambil top-k.

Untuk 4 buku, vector cukup disimpan di kolom JSON atau di memori. Kalau bukunya ribuan, membandingkan satu per satu jadi lambat, dan di situ vector database dipakai (ada pelajarannya nanti).

💡 Kenapa cosine, bukan jarak biasa

Jarak Euclidean (jarak garis lurus antara dua titik) pada vector yang belum dinormalisasi ikut terpengaruh panjang teks. Di Lab, coba "novel tentang politik 1965" dengan Euclidean tanpa normalisasi: Laskar Pelangi (0 kata sama, jarak 4,243) malah di atas Cantik Itu Luka (1 kata sama, jarak 4,359), karena sinopsis Laskar lebih pendek. Cosine cuma melihat arah, jadi Cantik Itu Luka (0,136) tetap di atas Laskar Pelangi (0). Setelah dinormalisasi, ketiga ukuran memberi urutan yang sama.

Metrik lain punya tugas lain: Levenshtein distance menghitung berapa huruf yang harus diubah (cocok untuk salah ketik, "Laskar Pelangii"), Hamming distance menghitung posisi yang beda di dua deretan sama panjang, dan hash SHA-256 cuma bisa bilang sama persis atau tidak. Untuk makna, yang umum dipakai cosine similarity.

⚠️ Jebakan yang sering ketemu

TL;DR: Embedding mengubah teks jadi vector (daftar angka) yang mewakili maknanya; teks bermakna mirip punya vector yang searah. text-embedding-3-small memberi 1.536 angka (bisa dipendekkan dengan dimensions), harganya $0,02 per 1 juta token per Oktober 2026. Kemiripan diukur dengan cosine similarity: dot product dibagi perkalian panjang kedua vector, 1 searah, 0 gak berkaitan. Contoh hitung kata: "novel tentang sekolah di Belitung" vs Laskar Pelangi = 1 / (1,732 × 3,873) = 0,149. Vector OpenAI sudah panjang 1, jadi cukup dot product. Embed semua buku sekali (index time), embed pertanyaan dengan model yang sama (query time), urutkan, ambil top-k.

Yang akan kamu pelajari