Skip to content

5.3 Vector Store & Retrieval

Tujuan bab

Setelah bab ini kamu bisa:

  • Menyimpan embedding ke vector store dengan upsert()
  • Mencari potongan yang relevan dengan query() dan mengatur topK
  • Menyambungkan RAG ke agent lewat createVectorQueryTool()
  • Memakai reranking untuk memperbaiki urutan hasil
  • Menulis instruksi agent yang mencegah jawabannya melenceng dari sumber

Prasyarat

Menyimpan

Setelah punya embedding, simpan ke vector store:

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

const pgVector = new PgVector({
  id: 'pg-vector',
  connectionString: process.env.POSTGRES_CONNECTION_STRING,
})

await pgVector.upsert({
  indexName: 'embeddings',
  vectors: embeddings,
})

indexName adalah nama koleksi tempat vektor disimpan. Satu vector store bisa memuat beberapa index — berguna kalau kamu punya beberapa basis pengetahuan yang harus dipisahkan.

upsert berarti "insert atau update". Menjalankan ingest dua kali tidak menggandakan data selama pengenalnya sama.

Memilih vector store

StorePaketKapan dipilih
libSQL@mastra/libsqlPengembangan lokal, dataset kecil
PostgreSQL + pgvector@mastra/pgProduksi, terutama bila sudah memakai Postgres
MongoDB@mastra/mongodbProduksi, bila sudah memakai MongoDB
Upstash@mastra/upstashServerless
OracleDB@mastra/oracledbLingkungan enterprise

Mastra juga mendukung Pinecone dan Qdrant. Saran praktis: mulai dari libSQL saat belajar — sama seperti untuk storage, ia tidak butuh layanan terpisah. Pindah ke pgvector saat masuk produksi, terutama kalau aplikasimu sudah memakai Postgres, karena itu berarti satu sistem lebih sedikit untuk dikelola.

Mencari

ts
const results = await pgVector.query({
  indexName: 'embeddings',
  queryVector,
  topK: 3,
})

console.log('Potongan yang mirip:', results)

queryVector adalah embedding dari pertanyaan pengguna — dibuat dengan model embedding yang sama seperti saat ingest.

topK menentukan berapa potongan yang diambil. Ini tuas paling langsung yang kamu punya:

topKAkibatnya
Terlalu kecil (1–2)Potongan yang benar bisa terlewat
Wajar (3–5)Titik awal yang baik untuk kebanyakan kasus
Terlalu besar (15+)Konteks membengkak, dan yang relevan tenggelam di antara yang tidak

Kesalahan yang umum adalah menaikkan topK saat jawaban kurang tepat. Kadang itu memang solusinya, tapi sering justru memperburuk — kembali ke masalah ketepatan yang dibahas di 5.1.

Menyambungkan ke agent

Sampai sini kamu bisa mencari secara manual. Yang belum: membuat agent bisa mencari sendiri saat ia merasa perlu.

Untuk itu ada createVectorQueryTool():

ts
// src/mastra/tools/knowledge-tool.ts
import { createVectorQueryTool } from '@mastra/rag'
import { ModelRouterEmbeddingModel } from '@mastra/core/llm'

export const queryTool = createVectorQueryTool({
  vectorStoreName: 'pinecone',
  indexName: 'docs',
  model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
})

Hasilnya adalah tool biasa — yang berarti semua yang kamu pelajari di Bagian 2 berlaku. Ia disambungkan ke agent lewat properti tools, dan model yang memutuskan kapan memanggilnya.

Perhatikan model di sini adalah model embedding, bukan model chat. Ia dipakai untuk mengubah pertanyaan menjadi vektor sebelum pencarian. Harus sama dengan yang dipakai saat ingest.

Deskripsi bawaannya

Menurut dokumentasi, tool ini punya nilai bawaan:

  • id: 'VectorQuery {vectorStoreName} {indexName} Tool'
  • description: "Access the knowledge base to find information needed to answer user questions"

Keduanya bisa kamu ganti, dan sebaiknya diganti. Ingat dari 2.2: deskripsi tool adalah dasar model memutuskan kapan memanggilnya. Deskripsi generik seperti bawaan itu bekerja buruk begitu agent-mu punya lebih dari satu sumber pengetahuan.

Bandingkan:

ts
// Bawaan — terlalu umum
description: 'Access the knowledge base to find information needed to answer user questions'

// Spesifik — jauh lebih baik
description:
  'Cari di dokumentasi kebijakan HR perusahaan: cuti, tunjangan, jam kerja, ' +
  'dan prosedur pengunduran diri. Bukan untuk pertanyaan teknis produk.'

Catatan dari dokumentasi: model, id, dan description hanya bisa diatur saat pembuatan, tidak bisa ditimpa saat runtime.

Opsi lain yang berguna

OpsiDefaultKegunaan
enableFilterfalseMenyaring hasil berdasarkan metadata
includeSourcestrueMenyertakan objek retrieval lengkap
includeVectorsfalseMenyertakan vektor di hasil
rerankerMenyusun ulang hasil, lihat bawah
databaseConfigPenyetelan khusus per jenis basis data

enableFilter otomatis menyala kalau sebuah filter diberikan lewat request context. databaseConfig memberi akses ke penyetelan spesifik — misalnya minScore untuk pgvector, atau namespace untuk Pinecone.

Reranking

Pencarian vektor mengurutkan hasil berdasarkan kedekatan vektor. Itu bagus, tapi tidak sempurna — potongan yang paling dekat secara vektor tidak selalu yang paling menjawab pertanyaan.

Reranking menambahkan langkah kedua: sebuah model menilai ulang hasil pencarian dan mengurutkannya kembali.

ts
// src/mastra/tools/knowledge-tool.ts
import { createVectorQueryTool } from '@mastra/rag'
import {
  ModelRouterEmbeddingModel,
  ModelRouterLanguageModel,
} from '@mastra/core/llm'

export const queryTool = createVectorQueryTool({
  vectorStoreName: 'pgVector',
  indexName: 'docs',
  model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
  reranker: {
    model: new ModelRouterLanguageModel('openai/gpt-5-mini'),
    options: {
      topK: 3,
    },
  },
})

reranker.model bukan string

Ini titik di mana reranker berbeda dari model pada Agent. Di agent kamu menulis model: 'openai/gpt-5-mini' sebagai string biasa; di sini string ditolak compiler, yang menuntut MastraLanguageModel.

Bungkus dengan ModelRouterLanguageModel — kelas ini yang mengubah string provider/model menjadi objek model. Sebagian contoh dokumentasi menulisnya sebagai string polos, dan itu tidak akan kompilasi.

Perhatikan juga bahwa ada dua kelas berbeda di sini: ModelRouterEmbeddingModel untuk mengubah teks jadi vektor, dan ModelRouterLanguageModel untuk model bahasa yang menilai ulang. Keduanya bukan hal yang sama dan tidak bisa saling menggantikan.

Dokumentasi menyebut bahwa penilaiannya menggabungkan beberapa komponen dengan bobot bawaan: semantic 0.4, vector 0.4, position 0.2. Bobot itu bisa disesuaikan lewat reranker.options.weights:

ts
reranker: {
  model: new ModelRouterLanguageModel('openai/gpt-5.6-sol'),
  options: {
    weights: {
      semantic: 0.5,
      vector: 0.3,
      position: 0.2,
    },
    topK: 5,
  },
}

Pola yang lazim: ambil lebih banyak kandidat dari pencarian vektor, lalu persempit dengan reranker. Kamu mendapat jangkauan pencarian yang lebar tanpa membanjiri konteks model.

Tanpa reranking:        Dengan reranking:
query → topK 3          query → topK 10 → rerank → 3 terbaik
        ↓                                            ↓
   3 terdekat                              3 paling relevan
   secara vektor

Ongkosnya satu panggilan model tambahan per pencarian. Untuk aplikasi yang ketepatannya penting, biasanya sepadan.

Instruksi agent berbasis RAG

Ini bagian yang tidak melibatkan kode RAG sama sekali, tapi menentukan apakah seluruh usahamu berbuah.

Ingat kegagalan ketiga dari 5.1: potongan yang benar ditemukan, tapi model tetap menjawab dari pengetahuan umumnya. Yang mencegahnya adalah instruksi.

ts
// src/mastra/agents/hr-agent.ts
import { Agent } from '@mastra/core/agent'
import { queryTool } from '../tools/knowledge-tool.ts'

export const hrAgent = new Agent({
  id: 'hr-agent',
  name: 'HR Agent',
  instructions: `
    Kamu asisten kebijakan HR perusahaan.

    Pakai queryTool untuk mencari di dokumentasi kebijakan sebelum menjawab
    pertanyaan apa pun tentang cuti, tunjangan, jam kerja, atau prosedur
    kepegawaian.

    Jawab HANYA berdasarkan isi dokumen yang kamu temukan. Kalau dokumen tidak
    memuat jawabannya, katakan terus terang bahwa informasi itu tidak ada di
    kebijakan yang kamu akses, dan sarankan pengguna menghubungi tim HR.
    Jangan mengisi kekosongan dengan pengetahuan umum tentang praktik HR di
    tempat lain.

    Sebutkan bagian dokumen yang menjadi dasar jawabanmu.
  `,
  model: 'openai/gpt-5.6-sol',
  tools: { queryTool },
})

Empat elemen yang bekerja di sini:

  1. Perintah memakai tool sebelum menjawab — bukan sekadar menyebut tool itu ada
  2. Batasan sumber yang tegas — "HANYA berdasarkan isi dokumen"
  3. Jalan keluar yang jelas saat tidak ketemu — model diberi tahu apa yang harus dikatakan, bukan hanya apa yang dilarang
  4. Permintaan menyebut sumber — membuat jawaban bisa diperiksa

Elemen ketiga adalah yang paling sering hilang, dan konsisten dengan prinsip dari 1.2: sebutkan apa yang harus dilakukan, bukan hanya apa yang dilarang.

Alur lengkap

Kesalahan umum

Gejala: Hasil pencarian tampak acak dan tidak berhubungan dengan pertanyaan, padahal ingest berjalan tanpa error. Penyebab: Model embedding yang dipakai saat query berbeda dari yang dipakai saat ingest. Vektor dari dua model berbeda tidak berada di ruang yang sama, sehingga "kedekatan" di antara keduanya tidak bermakna. Perbaikan: Pastikan model pada createVectorQueryTool() persis sama dengan yang dipakai di embedMany() saat ingest. Kalau perlu berganti model, lakukan ingest ulang seluruh dokumen.

Gejala: Agent punya dua sumber pengetahuan dan sering mencari di yang salah. Penyebab: Kedua tool memakai description bawaan yang berbunyi sama persis — "Access the knowledge base to find information needed to answer user questions". Model tidak punya dasar untuk membedakannya. Perbaikan: Berikan id dan description yang spesifik untuk tiap tool, menyebutkan cakupan masing-masing dan apa yang bukan cakupannya. Keduanya hanya bisa diatur saat pembuatan.

Coba sendiri

Tantangan: Bangun dokumentasi-agent yang menjawab pertanyaan dari dokumentasi nyata, lalu ukur seberapa sering ia benar.

Ketentuan:

  1. Pakai LibSQLVector sebagai vector store — hindari menyiapkan Postgres untuk latihan ini
  2. Tulis skrip ingest terpisah dari skrip agent. Ingest dijalankan sekali; agent dijalankan berkali-kali
  3. Buat createVectorQueryTool() dengan id dan description yang kamu tulis sendiri, bukan bawaan
  4. Tulis instructions agent yang memuat keempat elemen dari bab ini
  5. Siapkan sepuluh pertanyaan uji beserta jawaban yang kamu tahu benar, termasuk dua pertanyaan yang jawabannya sengaja tidak ada di dokumen
  6. Jalankan kesepuluhnya, catat: berapa yang benar, berapa yang salah, dan berapa yang dijawab "tidak tahu"
  7. Ulangi dengan topK yang berbeda — coba 2, 5, dan 10 — dan bandingkan
  8. Tambahkan reranker pada konfigurasi terbaik dan jalankan ulang

Checklist penerimaan:

  • [ ] Kedua pertanyaan tanpa jawaban dijawab dengan jujur "tidak ada di dokumen", bukan dikarang
  • [ ] Kamu punya angka konkret: berapa dari sepuluh yang benar di tiap konfigurasi topK
  • [ ] Kamu bisa menunjukkan minimal satu pertanyaan yang berubah hasilnya saat topK diubah
  • [ ] Jawaban agent menyebutkan bagian dokumen yang menjadi rujukannya
  • [ ] Setelah reranker ditambahkan, kamu bisa menyebutkan apakah hasilnya membaik, memburuk, atau sama — dengan angka
  • [ ] description tool-mu menyebutkan apa yang bukan cakupannya

Petunjuk: Ketentuan 5 adalah yang membuat latihan ini berbeda dari sekadar "mencoba-coba". Tanpa daftar pertanyaan dan jawaban yang disiapkan lebih dulu, kamu akan menilai RAG-mu berdasarkan kesan — dan kesan hampir selalu terlalu optimistis. Dua pertanyaan tanpa jawaban itu khususnya penting: sistem RAG yang tidak pernah bilang "tidak tahu" adalah sistem yang sedang mengarang tanpa ketahuan. Cara berpikir ini adalah pengantar langsung ke Bagian 8.

Ikhtisar

  • Embedding disimpan dengan upsert({ indexName, vectors }) dan dicari dengan query({ indexName, queryVector, topK }).
  • Model embedding saat query harus sama dengan saat ingest — kalau tidak, hasilnya tidak bermakna.
  • topK 3–5 adalah titik awal yang wajar. Menaikkannya tidak selalu memperbaiki jawaban, dan sering memperburuk.
  • createVectorQueryTool() mengubah pencarian menjadi tool biasa, sehingga agent bisa memanggilnya sendiri. model di sini adalah model embedding.
  • Ganti id dan description bawaannya. Deskripsi bawaan terlalu umum dan membuat agent bingung saat ada lebih dari satu sumber pengetahuan. Keduanya hanya bisa diatur saat pembuatan.
  • Reranking menyusun ulang hasil dengan model, memakai bobot bawaan semantic 0.4, vector 0.4, position 0.2. Pola lazimnya: ambil banyak, lalu persempit.
  • reranker.model harus objek ModelRouterLanguageModel, bukan string — berbeda dari model pada Agent. Jangan tertukar dengan ModelRouterEmbeddingModel.
  • Instruksi agent RAG harus memuat: perintah mencari dulu, batasan sumber yang tegas, jalan keluar saat tidak ketemu, dan permintaan menyebut rujukan.

Lanjut ke mana

Bagian 5 selesai. Agent-mu kini punya akses ke pengetahuan yang tidak ada di dalam model. Berikutnya: menyambungkannya ke kemampuan yang hidup di proses atau mesin lain — Bagian 6 — Connections.

Materi belajar mandiri. Bukan dokumentasi resmi Mastra.