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
inputboleh satu teks atau array. Menurut API reference OpenAI (dicek Oktober 2026): paling banyak 2.048 teks per request, tiap teks paling banyak 8.192 token.res.data[i].embeddingadalah array angka desimal. Panjangnya 1.536 untuktext-embedding-3-smalldan 3.072 untuktext-embedding-3-large. Parameterdimensions(misalnyadimensions: 512) memendekkannya; panduan "Vector embeddings" OpenAI menulis vector yang dipendekkan begitu tetap mewakili maknanya, dengan sedikit akurasi yang hilang.- Satu angka di dalamnya gak berarti apa-apa kalau dibaca sendiri. Yang berguna cuma perbandingannya dengan vector lain.
- Harganya $0,02 per 1 juta token (halaman "Pricing" OpenAI, Oktober 2026). Keempat sinopsis totalnya 194 token, jadi biayanya sekitar $0,000004.
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)
}
- Satu loop menghitung tiga hal sekaligus: dot product dan jumlah kuadrat kedua vector.
cariMiripmembandingkan pertanyaan dengan semua buku, mengurutkan dari skor terbesar, lalu mengambilkteratas.
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:
- Index time, sekali per buku: embed sinopsis, simpan vector-nya bersama id buku. Kalau sinopsisnya berubah atau buku baru datang, embed ulang buku itu.
- 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
- Model beda untuk index dan pertanyaan. Vector
text-embedding-3-small(1.536 angka) gak bisa dibandingkan dengan vectortext-embedding-3-large(3.072 angka), atau dengan model lain. Pakai model dandimensionsyang sama di kedua waktu. - Mengira hitung kata itu embedding. Di Lab, "kisah murid di sebuah pulau" memilih Cantik Itu Luka (0,118) cuma karena kata "kisah". Hitung kata gak tahu bahwa "murid", "guru", dan "sekolah" berkaitan; model embedding dilatih dari teks sangat banyak justru untuk menangkap itu.
- Lupa embed ulang. Sinopsis diubah tapi vector lama masih tersimpan, jadi hasil pencarian memakai isi lama.
- Memendekkan vector tanpa normalisasi ulang. Setelah
slice, panjangnya bukan 1 lagi, jadi dot product gak sama dengan cosine.
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.