Skip to content

5.2 Chunking & Embedding

Tujuan bab

Setelah bab ini kamu bisa:

  • Membuat MDocument dari teks, HTML, Markdown, atau JSON
  • Memilih strategi chunking yang sesuai dengan jenis dokumenmu
  • Menjelaskan pengaruh maxSize dan overlap terhadap mutu retrieval
  • Menghasilkan embedding untuk banyak potongan sekaligus
  • Menghindari kesalahan parameter yang tidak terlihat di dokumentasi lama

Prasyarat

  • 5.1 Konsep RAG
  • Paket @mastra/rag terpasang: npm install @mastra/rag@latest

Membuat dokumen

MDocument adalah pembungkus yang menyeragamkan berbagai format masukan:

ts
// src/mastra/rag/ingest.ts
import { MDocument } from '@mastra/rag'

const doc = MDocument.fromText('Isi teks biasa...')
const docFromHTML = MDocument.fromHTML('<html>Isi HTML...</html>')
const docFromMarkdown = MDocument.fromMarkdown('# Isi Markdown...')
const docFromJSON = MDocument.fromJSON(`{ "key": "value" }`)

Pilih yang sesuai formatnya. Ini bukan sekadar kerapian — pemilihan yang tepat memungkinkan strategi chunking yang memahami struktur, dan itu berpengaruh nyata pada hasil.

Memotong dokumen

ts
const chunks = await doc.chunk({
  strategy: 'recursive',
  maxSize: 512,
  overlap: 50,
  separators: ['\n'],
  extract: {
    title: true,
    summary: true,
    keywords: true,
  },
})

Parameternya maxSize, bukan size

Sebagian dokumentasi yang beredar — termasuk satu halaman resmi — menulis size: 512. Itu ditolak pada versi sekarang.

Type-check terhadap @mastra/rag yang terpasang mengembalikan pesan yang tegas: "'size' does not exist in type … BaseChunkOptions …". Pakai maxSize.

Ini contoh kenapa menyalin potongan kode dari hasil pencarian berisiko, bahkan ketika sumbernya terlihat resmi.

Sembilan strategi

StrategiCocok untuk
recursivePilihan default. Memotong cerdas berdasarkan struktur konten
characterPemotongan sederhana berbasis karakter
tokenPemotongan yang sadar batas token
markdownDokumen Markdown
semantic-markdownMarkdown, dipotong berdasarkan keluarga heading yang berkaitan
htmlDokumen HTML, sadar strukturnya
jsonData JSON, sadar strukturnya
latexDokumen LaTeX
sentenceTeks yang struktur kalimatnya penting dijaga

Catatan penting dari dokumentasi: tiap strategi menerima parameter yang berbeda, disesuaikan dengan cara kerjanya. Ini terlihat langsung di tipenya — separators misalnya hanya bermakna untuk recursive.

Dua contoh strategi lain:

ts
// Menjaga struktur kalimat tetap utuh
const chunks = await doc.chunk({
  strategy: 'sentence',
  maxSize: 450,
  minSize: 50,
  overlap: 0,
  sentenceEnders: ['.'],
})
ts
// Markdown, dipotong berdasarkan hubungan antar bagian
const chunks = await doc.chunk({
  strategy: 'semantic-markdown',
  joinThreshold: 500,
  modelName: 'gpt-5',
})

Ekstraksi metadata

extract meminta LLM menganalisis tiap potongan dan menambahkan metadata. Field yang tersedia: title, summary, questions, keywords, dan schema untuk ekstraksi terstruktur memakai schema Zod.

Hasilnya masuk ke chunk.metadata:

js
// chunks[0].metadata
{
  documentTitle: 'AI Systems Overview',
  sectionSummary: 'Ikhtisar konsep dan penerapan kecerdasan buatan',
  excerptKeywords: 'KEYWORDS: AI, machine learning, algorithms',
}

Ini memanggil LLM, sekali per potongan

Ekstraksi metadata melibatkan panggilan model, sehingga memerlukan API key dan menambah biaya ingest secara linear terhadap jumlah potongan. Aktifkan hanya field yang benar-benar kamu pakai saat retrieval.

Perhatikan juga nama field-nya. Sebagian contoh yang beredar menulis extract: { metadata: true } — itu ditolak compiler pada versi sekarang dengan pesan "'metadata' does not exist in type 'ExtractParams'".

Memilih ukuran potongan

Ini keputusan yang paling menentukan mutu RAG-mu, dan tidak ada angka ajaib yang benar untuk semua kasus. Yang ada adalah pertimbangan.

Potongan terlalu kecil            Potongan terlalu besar
┌──────────────────┐              ┌──────────────────────────┐
│ "Hal ini tidak   │              │ Bab 3: Kebijakan Retur   │
│  berlaku untuk   │              │ (3 halaman penuh, memuat │
│  pelanggan       │              │  retur, garansi, dan     │
│  korporat."      │              │  pengiriman sekaligus)   │
└──────────────────┘              └──────────────────────────┘
 "Hal ini" = apa?                  Model harus menyaring
 Konteks hilang.                   sendiri, dan sering keliru.

Panduan yang bisa dipakai sebagai titik awal:

Jenis dokumenmaxSize awal yang wajar
FAQ, tanya-jawab pendek256–384
Artikel bantuan, dokumentasi512
Prosa panjang, laporan, buku512–1024
Kode atau data terstrukturIkuti batas unit alaminya

Perlakukan angka ini sebagai titik awal, bukan jawaban. Cara mengujinya ada di bagian Coba sendiri.

Kenapa overlap penting

overlap membuat potongan berbagi sebagian teks dengan tetangganya:

Tanpa overlap:
[......potongan 1......][......potongan 2......]
                        ↑ kalimat yang terbelah di sini
                          kehilangan konteksnya

Dengan overlap 50:
[......potongan 1......]
                  [......potongan 2......]
                  ↑ bagian ini muncul di keduanya

Ini asuransi terhadap pemotongan yang jatuh di tempat yang buruk. Sebuah gagasan yang terbelah tepat di batas potongan tetap punya satu potongan yang memuatnya secara utuh.

Ongkosnya: penyimpanan dan biaya embedding sedikit bertambah, karena sebagian teks diproses dua kali. Untuk sebagian besar kasus, itu pertukaran yang sepadan.

Nilai awal yang wajar: sekitar 10% dari maxSize.

Membuat embedding

Setelah dokumen terpotong, tiap potongan diubah menjadi vektor:

ts
// src/mastra/rag/ingest.ts
import { embedMany } from 'ai'
import { MDocument } from '@mastra/rag'
import { ModelRouterEmbeddingModel } from '@mastra/core/llm'

const doc = MDocument.fromText('Isi dokumenmu di sini...')

const chunks = await doc.chunk({
  strategy: 'recursive',
  maxSize: 512,
  overlap: 50,
})

const { embeddings } = await embedMany({
  values: chunks.map(chunk => chunk.text),
  model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
})

Beberapa hal yang perlu dicatat.

embedMany berasal dari paket ai, bukan dari @mastra/core. Ini salah satu dari sedikit tempat di mana kamu mengimpor dari luar ekosistem Mastra.

Yang di-embed adalah chunk.text, bukan objek chunk-nya. Tiap chunk punya teks dan metadata; yang diubah jadi vektor hanya teksnya.

ModelRouterEmbeddingModel sudah kamu kenal dari 3.3 — format provider/model yang sama.

Memilih model embedding

Dua pertimbangan utama:

Model API (mis. openai/text-embedding-3-small)Model lokal (@mastra/fastembed)
MutuUmumnya lebih tinggiCukup untuk banyak kasus
BiayaPer tokenNol setelah terpasang
Data keluar dari infrastrukturmuYaTidak
KecepatanBergantung jaringanBergantung CPU

Untuk dokumen yang sensitif atau tidak boleh keluar dari lingkunganmu, @mastra/fastembed adalah jawabannya.

Ingat peringatan dari 3.3: mengganti model embedding setelah data terkumpul adalah migrasi, bukan penggantian satu baris. Vektor dari model berbeda tidak sebanding.

Kesalahan umum

Gejala: Error tipe 'size' does not exist in type ... saat memanggil doc.chunk(). Penyebab: Memakai parameter size, yang muncul di sebagian dokumentasi dan contoh lama. Nama yang benar pada versi sekarang adalah maxSize. Perbaikan: Ganti menjadi maxSize. Kalau kamu menemukan parameter lain yang ditolak, periksa apakah ia memang milik strategi yang kamu pakai — tiap strategi menerima parameter yang berbeda.

Gejala: Hasil pencarian sering memuat potongan yang secara topik benar tapi tidak memuat jawabannya, atau memuat kalimat yang merujuk sesuatu yang tidak ada di potongan itu. Penyebab: Potongan terlalu kecil, atau overlap nol sehingga gagasan terbelah di batas potongan. Perbaikan: Naikkan maxSize dan berikan overlap sekitar 10% darinya. Kalau dokumenmu berstruktur — Markdown atau HTML — pertimbangkan strategi yang sadar struktur alih-alih recursive.

Coba sendiri

Tantangan: Bangun skrip ingest untuk satu dokumen nyata, lalu uji secara empiris ukuran potongan mana yang paling baik untuk dokumen itu. Ini latihan mengukur, bukan menebak.

Ketentuan:

  1. Ambil satu dokumen asli sepanjang minimal 10 halaman — dokumentasi, buku panduan, atau kumpulan artikel. Kalau formatnya Markdown, pakai MDocument.fromMarkdown()
  2. Tulis skrip yang memotong dokumen itu dengan tiga konfigurasi berbeda: maxSize 256, 512, dan 1024 — semuanya dengan overlap 10%
  3. Untuk tiap konfigurasi, cetak: jumlah potongan yang dihasilkan, panjang rata-rata, dan isi lengkap tiga potongan pertama
  4. Baca ketiga potongan itu sebagai manusia dan nilai: apakah masing-masing memuat satu gagasan yang utuh dan bisa dipahami tanpa membaca tetangganya?
  5. Ulangi konfigurasi terbaik dengan overlap: 0, dan bandingkan potongan di sekitar batas
  6. Buat embedding untuk konfigurasi terbaik, dan cetak panjang vektor pertama

Checklist penerimaan:

  • [ ] Ketiga konfigurasi berjalan dan menghasilkan jumlah potongan yang jelas berbeda
  • [ ] Kamu bisa menunjuk satu potongan dari konfigurasi 256 yang kehilangan konteks — kalimat yang merujuk sesuatu di luar potongan itu
  • [ ] Kamu bisa menunjuk satu potongan dari konfigurasi 1024 yang memuat lebih dari satu topik
  • [ ] Perbandingan overlap: 0 menunjukkan setidaknya satu gagasan yang terbelah
  • [ ] Kamu memilih satu konfigurasi dan bisa menjelaskan alasannya berdasarkan apa yang kamu lihat, bukan berdasarkan tabel di bab ini
  • [ ] embeddings berhasil dibuat, dan kamu tahu berapa dimensi vektornya

Petunjuk: Langkah 4 adalah bagian yang tidak bisa diotomatiskan dan paling sering dilewati. Membaca potongan sebagai manusia adalah satu-satunya cara menilai apakah potongan itu "utuh". Uji sederhana: kalau kamu menunjukkan satu potongan kepada seseorang tanpa konteks lain, apakah ia bisa memahaminya?

Ikhtisar

  • MDocument menyeragamkan masukan dari teks, HTML, Markdown, dan JSON. Pilih yang sesuai format — ini membuka strategi chunking yang sadar struktur.
  • Parameter ukurannya maxSize, bukan size. Dokumentasi lama keliru di titik ini.
  • Ada sembilan strategi chunking, dan tiap strategi menerima parameter yang berbeda. recursive adalah default yang wajar.
  • Ukuran potongan adalah keputusan desain paling berpengaruh pada mutu RAG. Terlalu kecil kehilangan konteks, terlalu besar mengaburkan yang relevan.
  • overlap melindungi dari gagasan yang terbelah di batas potongan. Titik awal yang wajar: 10% dari maxSize.
  • embedMany diimpor dari paket ai, dan yang di-embed adalah chunk.text.
  • extract menerima title, summary, questions, keywords, dan schemabukan metadata. Ia memanggil LLM sekali per potongan, jadi perhitungkan biayanya untuk dokumen dalam jumlah besar.

Lanjut ke mana

Potongan sudah menjadi vektor. Sekarang bagian terakhir: menyimpannya, mencarinya, dan menyambungkannya ke agent — 5.3 Vector Store & Retrieval.

Materi belajar mandiri. Bukan dokumentasi resmi Mastra.