Tampilan
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 mengaturtopK - 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
| Store | Paket | Kapan dipilih |
|---|---|---|
| libSQL | @mastra/libsql | Pengembangan lokal, dataset kecil |
| PostgreSQL + pgvector | @mastra/pg | Produksi, terutama bila sudah memakai Postgres |
| MongoDB | @mastra/mongodb | Produksi, bila sudah memakai MongoDB |
| Upstash | @mastra/upstash | Serverless |
| OracleDB | @mastra/oracledb | Lingkungan 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:
topK | Akibatnya |
|---|---|
| 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
| Opsi | Default | Kegunaan |
|---|---|---|
enableFilter | false | Menyaring hasil berdasarkan metadata |
includeSources | true | Menyertakan objek retrieval lengkap |
includeVectors | false | Menyertakan vektor di hasil |
reranker | — | Menyusun ulang hasil, lihat bawah |
databaseConfig | — | Penyetelan 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 vektorOngkosnya 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:
- Perintah memakai tool sebelum menjawab — bukan sekadar menyebut tool itu ada
- Batasan sumber yang tegas — "HANYA berdasarkan isi dokumen"
- Jalan keluar yang jelas saat tidak ketemu — model diberi tahu apa yang harus dikatakan, bukan hanya apa yang dilarang
- 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:
- Pakai
LibSQLVectorsebagai vector store — hindari menyiapkan Postgres untuk latihan ini - Tulis skrip ingest terpisah dari skrip agent. Ingest dijalankan sekali; agent dijalankan berkali-kali
- Buat
createVectorQueryTool()denganiddandescriptionyang kamu tulis sendiri, bukan bawaan - Tulis
instructionsagent yang memuat keempat elemen dari bab ini - Siapkan sepuluh pertanyaan uji beserta jawaban yang kamu tahu benar, termasuk dua pertanyaan yang jawabannya sengaja tidak ada di dokumen
- Jalankan kesepuluhnya, catat: berapa yang benar, berapa yang salah, dan berapa yang dijawab "tidak tahu"
- Ulangi dengan
topKyang berbeda — coba 2, 5, dan 10 — dan bandingkan - Tambahkan
rerankerpada 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
topKdiubah - [ ] Jawaban agent menyebutkan bagian dokumen yang menjadi rujukannya
- [ ] Setelah reranker ditambahkan, kamu bisa menyebutkan apakah hasilnya membaik, memburuk, atau sama — dengan angka
- [ ]
descriptiontool-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 denganquery({ indexName, queryVector, topK }). - Model embedding saat query harus sama dengan saat ingest — kalau tidak, hasilnya tidak bermakna.
topK3–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.modeldi sini adalah model embedding.- Ganti
iddandescriptionbawaannya. 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.modelharus objekModelRouterLanguageModel, bukan string — berbeda darimodelpadaAgent. Jangan tertukar denganModelRouterEmbeddingModel.- 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.