Skip to content

3.3 Semantic Recall

Tujuan bab

Setelah bab ini kamu bisa:

  • Menjelaskan cara kerja semantic recall dan bedanya dari message history
  • Mengaktifkannya dengan vector store dan embedder
  • Menjelaskan peran ModelRouterEmbeddingModel dan memilih model embedding
  • Menjelaskan kenapa semantic recall menambah biaya dan latensi, serta kapan itu sepadan
  • Membedakan listMessages() dan recall()

Prasyarat

Analogi yang dipakai dokumentasi

Dokumentasi Mastra membuka topik ini dengan analogi yang bagus: kalau kamu bertanya pada temanmu apa yang ia lakukan akhir pekan lalu, ia akan mencari di ingatannya peristiwa yang terkait dengan "akhir pekan lalu", lalu menceritakannya.

Temanmu tidak memutar ulang seluruh ingatannya sejak lahir. Ia mencari berdasarkan makna, mengambil bagian yang relevan, dan mengabaikan sisanya.

Itu yang dilakukan semantic recall.

Kenapa dua mekanisme sebelumnya tidak cukup

Sampai sini agent punya dua bentuk ingatan, dan keduanya punya batas:

Batasnya
Message historyHanya N pesan terakhir. Yang lebih lama hilang dari jendela.
Working memoryHanya fakta yang muat di satu blok ringkas, dan hanya yang agent anggap layak dicatat.

Sekarang bayangkan pengguna berkata: "Waktu itu kita pernah membahas cara mengatur ulang integrasi Slack — bisa diulang lagi?"

Percakapan itu terjadi tiga minggu lalu, empat ratus pesan yang lalu. Terlalu lama untuk message history. Terlalu spesifik untuk masuk working memory yang isinya profil pengguna.

Semantic recall dibuat persis untuk celah ini.

Cara kerjanya

Semantic recall adalah pencarian berbasis RAG di dalam riwayat percakapanmu sendiri. Mekanismenya:

Dua hal yang layak diperhatikan dari alur itu.

Pencarian berdasarkan makna, bukan kata kunci. Pengguna tidak perlu memakai kata yang persis sama dengan percakapan lamanya. "mengatur ulang integrasi Slack" bisa menemukan percakapan yang menyebut "reset koneksi Slack".

Penulisan terjadi setelah jawaban. Setelah model merespons, seluruh pesan baru — dari pengguna, dari assistant, termasuk pemanggilan tool dan hasilnya — dimasukkan ke vector DB supaya bisa ditemukan di masa depan.

Mengaktifkannya

Semantic recall mati secara default. Menyalakannya butuh tiga hal: semanticRecall: true, sebuah vector store, dan sebuah embedder.

ts
// src/mastra/agents/support-agent.ts
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
import { LibSQLStore, LibSQLVector } from '@mastra/libsql'
import { ModelRouterEmbeddingModel } from '@mastra/core/llm'

export const supportAgent = new Agent({
  id: 'support-agent',
  name: 'SupportAgent',
  instructions: 'Kamu agent support yang membantu.',
  model: 'openai/gpt-5.6-sol',
  memory: new Memory({
    storage: new LibSQLStore({
      id: 'agent-storage',
      url: 'file:./local.db',
    }),
    vector: new LibSQLVector({
      id: 'agent-vector',
      url: 'file:./local.db',
    }),
    embedder: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
    options: {
      semanticRecall: true,
    },
  }),
})

Perhatikan bahwa LibSQLStore dan LibSQLVector bisa menunjuk berkas yang sama. Untuk pengembangan lokal ini praktis: satu berkas, tidak ada layanan tambahan.

Tiga komponen, tiga peran

KomponenPerannya
storageMenyimpan pesan apa adanya, untuk message history
vectorMenyimpan representasi vektor pesan, untuk pencarian makna
embedderMengubah teks menjadi vektor

Ketiganya harus ada. Tanpa vector, tidak ada tempat mencari; tanpa embedder, tidak ada yang bisa mengubah teks jadi bentuk yang bisa dicari.

Tentang embedder

ModelRouterEmbeddingModel memakai format yang sama seperti model router yang kamu kenal dari 0.3 — string provider/model, tanpa impor paket penyedia:

ts
new ModelRouterEmbeddingModel('openai/text-embedding-3-small')

Model embedding berbeda dari model chat. Ia tidak menghasilkan teks; ia menghasilkan deretan angka yang mewakili makna. Daftar model embedding yang tersedia ada di https://mastra.ai/models/embeddings.

Berganti model embedding tidak sesederhana berganti model chat

Vektor yang dihasilkan satu model embedding tidak sebanding dengan vektor dari model lain. Kalau kamu mengganti embedder setelah data terkumpul, pencarian akan memberi hasil yang kacau sampai seluruh data lama diproses ulang.

Pilih di awal, dan perlakukan perubahannya sebagai migrasi, bukan sebagai penggantian satu baris.

Ada juga opsi embedding lokal lewat @mastra/fastembed, yang tidak memerlukan panggilan API eksternal. Ini berguna saat data tidak boleh keluar dari infrastrukturmu, dan dibahas lagi di 5.2.

Ongkos yang perlu kamu sadari

Semantic recall bukan fitur gratis. Setiap giliran percakapan sekarang melibatkan:

  1. Satu panggilan embedding untuk pesan baru, agar bisa dicari
  2. Satu pencarian vektor di database
  3. Tambahan konteks berupa pesan lama yang ditemukan, yang ikut dikirim ke model
  4. Panggilan embedding lagi untuk menyimpan pesan-pesan baru

Panggilan embedding jauh lebih murah daripada panggilan model chat, jadi tambahan biayanya biasanya kecil. Yang lebih terasa adalah latensi — ada langkah jaringan tambahan sebelum model bisa mulai menjawab.

Pertimbangan praktisnya:

SituasiSemantic recall
Percakapan pendek, sekali pakaiTidak perlu
Asisten yang dipakai berulang selama berminggu-mingguSangat berguna
Support yang pengguna sering merujuk kasus lamaSangat berguna
Aplikasi yang latensinya sangat kritisTimbang dulu; mungkin cukup working memory

listMessages() dan recall()

Dua method untuk mengambil pesan tersimpan, dengan tujuan berbeda:

MethodCara mencariUntuk apa
listMessages()Berdasarkan thread ID, dengan paginasiMenampilkan riwayat percakapan di UI
recall()Berdasarkan makna, lewat vectorSearchStringMenemukan pesan yang relevan, bukan yang terbaru

Pakai listMessages() saat kamu membangun tampilan daftar percakapan — pengguna ingin melihat urutan kronologis. Pakai recall() saat kamu butuh "pesan mana yang berkaitan dengan topik ini", tanpa peduli kapan diucapkan.

Bagaimana ketiga lapisan bekerja bersama

Setelah tiga bab, agent-mu bisa punya tiga lapisan ingatan sekaligus:

┌──────────────────────────────────────────────────────┐
│ Konteks yang dikirim ke model                        │
├──────────────────────────────────────────────────────┤
│ instructions        ← identitas & aturan (Bagian 1)  │
│ working memory      ← profil pengguna (3.2)          │
│ pesan hasil recall  ← relevan dari masa lalu (3.3)   │
│ lastMessages        ← percakapan terkini (3.1)       │
│ pesan baru          ← yang barusan diketik pengguna  │
└──────────────────────────────────────────────────────┘

Perhatikan bahwa semuanya menumpuk di satu tempat: jendela konteks model. Ini alasan kenapa tiap lapisan harus dijaga tetap ringkas — dan kenapa ada lapisan keempat yang dirancang khusus untuk percakapan yang sudah terlalu panjang, yaitu isi bab berikutnya.

Kalau gabungan semuanya melewati batas konteks model, memory processors bisa menyaring, memangkas, atau memprioritaskan isinya. Itu topik lanjutan yang tidak dibahas terpisah di materi ini, tapi layak diingat keberadaannya.

Kesalahan umum

Gejala: Semantic recall aktif tapi tidak pernah menemukan apa pun dari percakapan lama. Penyebab: Umumnya salah satu dari dua. Data lama tidak pernah masuk ke vector store karena semantic recall baru dinyalakan setelah percakapan itu terjadi — penulisan ke vector DB hanya berlaku untuk pesan yang datang setelah fitur aktif. Atau vector dan storage menunjuk basis data berbeda yang tidak konsisten. Perbaikan: Uji dengan percakapan baru yang seluruhnya terjadi setelah fitur aktif. Kalau tetap kosong, periksa apakah vector benar-benar terkonfigurasi dan embedder tidak melempar error saat dipanggil.

Gejala: Setelah mengganti model embedding, hasil pencarian jadi acak dan tidak relevan. Penyebab: Vektor lama dihasilkan model berbeda dan tidak sebanding dengan vektor baru. Pencarian membandingkan dua sistem koordinat yang berlainan. Perbaikan: Proses ulang seluruh data dengan model embedding yang baru. Perlakukan penggantian embedder sebagai migrasi data, dan hindari melakukannya setelah produksi berjalan kecuali benar-benar perlu.

Coba sendiri

Tantangan: Bangun konsultan-agent yang membantu pengguna mengelola proyek, dan buktikan bahwa semantic recall benar-benar menemukan percakapan lama berdasarkan makna — bukan kata kunci.

Ketentuan:

  1. Aktifkan ketiga lapisan: message history dengan lastMessages: 5 (sengaja kecil), working memory, dan semantic recall dengan LibSQLVector
  2. Di thread proyek-1, lakukan percakapan berisi minimal 12 giliran yang membahas beberapa topik berbeda — misalnya penjadwalan, anggaran, dan masalah dengan vendor
  3. Pastikan salah satu topik dibahas di giliran-giliran awal, sehingga ia jelas keluar dari jendela lastMessages: 5
  4. Di akhir, tanyakan sesuatu tentang topik awal itu tanpa memakai kata yang sama dengan yang dipakai sebelumnya
  5. Ulangi percobaan dengan semantic recall dimatikan, dan bandingkan
  6. Amati di Studio apakah ada pesan lama yang disisipkan ke konteks

Checklist penerimaan:

  • [ ] Dengan semantic recall aktif, agent bisa menjawab pertanyaan langkah 4 dengan merujuk isi percakapan awal
  • [ ] Dengan semantic recall mati, agent tidak bisa menjawabnya — ini pembanding yang membuktikan fiturnya benar-benar bekerja
  • [ ] Pertanyaan di langkah 4 memakai kata yang berbeda dari percakapan asli, dan tetap berhasil
  • [ ] Kamu bisa menunjukkan di Studio bahwa konteks berisi lebih dari lima pesan terakhir
  • [ ] Kamu bisa menjelaskan pembagian tugas: apa yang dipegang working memory dan apa yang dipegang semantic recall dalam percobaan ini

Petunjuk: Langkah 5 adalah bagian terpenting dan paling sering dilewati. Tanpa pembanding, kamu tidak pernah benar-benar tahu apakah jawaban yang benar datang dari semantic recall atau kebetulan tertangkap working memory. Jalankan keduanya.

Ikhtisar

  • Semantic recall adalah pencarian berbasis makna di dalam riwayat percakapanmu sendiri — mengisi celah antara message history yang terbatas dan working memory yang ringkas.
  • Mati secara default. Menyalakannya butuh semanticRecall: true, sebuah vector store, dan sebuah embedder.
  • storage menyimpan pesan, vector menyimpan representasi maknanya, embedder yang mengubah teks jadi vektor. Ketiganya wajib.
  • ModelRouterEmbeddingModel memakai format provider/model yang sama seperti model chat.
  • Mengganti model embedding adalah migrasi, bukan penggantian satu baris — vektor dari model berbeda tidak sebanding.
  • Setelah model menjawab, semua pesan baru (pengguna, assistant, tool) dimasukkan ke vector DB untuk pencarian di masa depan.
  • listMessages() mengambil berdasarkan thread dan urutan; recall() mengambil berdasarkan makna.

Lanjut ke mana

Tiga lapisan ingatan sudah aktif, dan semuanya menumpuk di jendela konteks yang sama. Bab terakhir Bagian 3 membahas apa yang terjadi saat percakapan tumbuh sedemikian panjang sehingga pendekatan itu tidak lagi memadai — 3.4 Observational Memory.

Materi belajar mandiri. Bukan dokumentasi resmi Mastra.