Tampilan
3.2 Working Memory
Tujuan bab
Setelah bab ini kamu bisa:
- Menjelaskan perbedaan working memory dan message history, serta kapan masing-masing dipakai
- Mengaktifkan working memory dan merancang
templateyang berguna - Memilih antara lingkup
resourcedanthread, serta memahami akibat memilih salah - Menjelaskan kenapa working memory berupa teks Markdown, bukan struktur data
- Menghindari kebocoran data antar pengguna akibat lingkup yang salah
Prasyarat
Masalah yang tidak diselesaikan message history
Message history menyimpan sepuluh, dua puluh, atau lima puluh pesan terakhir. Sekarang bayangkan percakapan ini:
Pesan 1 : "Halo, saya Sari. Saya alergi kacang."
Pesan 2-40: (percakapan panjang soal hal lain)
Pesan 41 : "Rekomendasikan restoran untuk makan malam."Dengan lastMessages: 20, pesan pertama sudah lama keluar dari jendela. Agent tidak lagi tahu soal alergi itu, dan bisa merekomendasikan restoran Thailand dengan senang hati.
Menaikkan lastMessages menjadi 50 memang menyelesaikan kasus ini — tapi dengan cara yang salah. Kamu membayar 50 pesan di setiap panggilan, hanya untuk mempertahankan satu fakta penting yang muncul sekali.
Working memory menyelesaikannya dengan memisahkan fakta dari percakapan. Alih-alih berharap fakta penting kebetulan masih ada di jendela pesan terakhir, agent memelihara catatan terpisah yang selalu ikut serta.
┌─────────────────────────────────────┐
│ Message history │ ← bergulir, yang lama hilang
│ 20 pesan terakhir │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Working memory │ ← bertahan, selalu ikut
│ # Profil Pengguna │
│ - Nama: Sari │
│ - Alergi: kacang │
│ - Preferensi: masakan Jepang │
└─────────────────────────────────────┘Mengaktifkannya
Bentuk paling minimal:
ts
// src/mastra/agents/asisten-agent.ts
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
export const asistenAgent = new Agent({
id: 'personal-assistant',
name: 'PersonalAssistant',
instructions: 'Kamu asisten pribadi yang membantu.',
model: 'openai/gpt-5.6-sol',
memory: new Memory({
options: {
workingMemory: {
enabled: true,
},
},
}),
})Dengan itu, agent mulai memelihara catatannya sendiri. Tapi tanpa arahan, ia akan memutuskan sendiri apa yang layak dicatat — dan hasilnya tidak konsisten. Untuk itu ada template.
Template: memberi bentuk pada ingatan
Working memory adalah blok teks Markdown yang bisa diperbarui agent dari waktu ke waktu. template memberi kerangka awalnya:
ts
// src/mastra/agents/asisten-agent.ts
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
export const asistenAgent = new Agent({
id: 'personal-assistant',
name: 'PersonalAssistant',
instructions: 'Kamu asisten pribadi yang membantu.',
model: 'openai/gpt-5.6-sol',
memory: new Memory({
options: {
workingMemory: {
enabled: true,
scope: 'resource',
template: `# Profil Pengguna
- **Nama**:
- **Lokasi**:
- **Minat**:
- **Preferensi**:
- **Tujuan jangka panjang**:
`,
},
},
}),
})Template bukan sekadar tampilan. Ia memberitahu agent apa yang layak diperhatikan. Field kosong berfungsi seperti formulir: agent cenderung mengisinya saat informasi itu muncul dalam percakapan, dan cenderung tidak mencatat hal di luar kerangka.
Kenapa Markdown, bukan JSON?
Pertanyaan yang wajar, mengingat Mastra memakai Zod di mana-mana. Alasannya: working memory ditulis dan dibaca oleh model, bukan oleh kodemu.
Model bekerja dengan teks. Format Markdown longgar dan memaafkan — kalau seorang pengguna menyebutkan sesuatu yang tidak ada field-nya, agent bisa menambahkan baris baru tanpa merusak apa pun. Struktur kaku justru akan membuat agent membuang informasi yang tidak muat.
Konsekuensinya untuk desainmu: rancang template untuk dibaca model, bukan untuk diurai program. Kalau kamu butuh data terstruktur yang dijamin bentuknya, tempatnya bukan di sini — pakai database biasa, dan berikan aksesnya lewat tool.
Merancang template yang berguna
| Prinsip | Contoh buruk | Contoh baik |
|---|---|---|
| Spesifik pada domain | - Catatan: | - Alergi & pantangan makanan: |
| Field yang benar-benar dipakai | 15 field "untuk jaga-jaga" | 5 field yang memang mempengaruhi jawaban |
| Nama yang tidak ambigu | - Level: | - Tingkat keahlian memasak (pemula/mahir): |
| Cukup ringkas | Tiga halaman | Satu layar |
Prinsip terakhir penting karena alasan yang sama seperti instructions: working memory ikut dikirim di setiap panggilan model.
Lingkup: resource atau thread
Ini keputusan terpenting di bab ini, dan yang paling berisiko kalau salah.
| Lingkup | Ingatan bertahan | Cocok untuk |
|---|---|---|
resource (default) | Lintas semua thread milik pengguna yang sama | Asisten pribadi, bot layanan pelanggan, aplikasi edukasi |
thread | Hanya di dalam satu thread | Sesi yang harus terisolasi, konteks per kasus |
scope: 'resource' scope: 'thread'
user-8842 user-8842
├── thread A ─┐ ├── thread A ── [ingatan A]
├── thread B ─┼─ [ingatan bersama] ├── thread B ── [ingatan B]
└── thread C ─┘ └── thread C ── [ingatan C]Dokumentasi memberi peringatan yang layak digarisbawahi: berpindah antar lingkup berarti agent tidak akan melihat ingatan dari lingkup yang lain. Ingatan berlingkup thread benar-benar terpisah dari ingatan berlingkup resource. Kalau kamu mengubah scope pada aplikasi yang sudah berjalan, ingatan yang sudah terkumpul tidak ikut berpindah.
Kapan memilih thread
Default resource cocok untuk sebagian besar kasus, tapi ada situasi di mana justru berbahaya:
- Kios atau perangkat bersama — satu
resourcedipakai banyak orang bergantian, sehingga preferensi orang sebelumnya terbawa - Konteks per kasus — agent support di mana tiap tiket harus dinilai sendiri tanpa terpengaruh tiket lain
- Data sensitif yang tidak boleh menyeberang sesi — misalnya konsultasi yang harus dianggap selesai saat sesi berakhir
Lingkup resource memperbesar akibat kesalahan resource
Di 3.1 kita sudah menyebut bahaya memakai nilai resource yang sama untuk semua pengguna. Dengan working memory berlingkup resource, akibatnya jauh lebih parah: profil, preferensi, dan fakta pribadi satu pengguna akan langsung terbaca oleh pengguna lain.
Pastikan resource berasal dari sesi terautentikasi sebelum mengaktifkan working memory di aplikasi yang dipakai lebih dari satu orang.
Melihatnya bekerja
ts
// run.mjs
import { mastra } from './src/mastra/index.ts'
const agent = mastra.getAgentById('personal-assistant')
await agent.generate('Halo, saya Sari. Saya alergi kacang dan suka masakan Jepang.', {
memory: { resource: 'user-8842', thread: 'sesi-1' },
})
// Thread berbeda, resource sama
const hasil = await agent.generate('Rekomendasikan menu makan malam untuk saya.', {
memory: { resource: 'user-8842', thread: 'sesi-2' },
})
console.log(hasil.text)Dengan scope: 'resource', panggilan kedua tetap memperhitungkan alergi dan preferensi — meskipun berada di thread yang berbeda. Inilah perbedaan yang tidak bisa dicapai message history, berapa pun besar lastMessages-nya.
Di Studio, isi working memory bisa dilihat di panel samping saat percakapan berlangsung. Ini cara tercepat memeriksa apakah agent benar-benar mencatat apa yang kamu harapkan.
Working memory bersama observational memory
Satu hal yang berguna diketahui sekarang dan akan masuk akal penuh di 3.4: kalau observational memory aktif, opsi observationalMemory.observation.manageWorkingMemory memungkinkan sistem itu yang memperbarui working memory untuk agent.
Artinya kedua mekanisme tidak bersaing — yang satu bisa memelihara yang lain.
Kesalahan umum
Gejala: Working memory aktif tapi isinya tetap kosong, atau terisi hal-hal yang tidak relevan. Penyebab: Tidak ada template, sehingga agent tidak punya acuan tentang apa yang layak dicatat. Atau template-nya memakai nama field yang ambigu, sehingga agent tidak yakin informasi mana yang masuk ke mana. Perbaikan: Tulis template dengan field yang spesifik pada domainmu dan namanya tidak bisa ditafsirkan dua cara. Kalau sebuah field tetap kosong padahal informasinya jelas disebutkan pengguna, perjelas nama field-nya.
Gejala: Pengguna melaporkan bahwa agent menyebut preferensi yang bukan miliknya. Penyebab: Nilai resource tidak unik per pengguna — misalnya diisi nilai tetap, atau diambil dari input klien yang bisa diubah. Dengan lingkup resource (default), semua yang berbagi nilai itu berbagi ingatan. Perbaikan: Pastikan resource berasal dari ID pengguna pada sesi terautentikasi di sisi server. Ini masalah keamanan, bukan sekadar bug fungsional.
Coba sendiri
Tantangan: Bangun tutor-agent, asisten belajar bahasa Inggris yang menyesuaikan diri dengan tiap murid dari waktu ke waktu.
Ketentuan:
- Aktifkan working memory dengan
scope: 'resource'dantemplateyang memuat minimal: nama, tingkat kemampuan, topik yang sudah dikuasai, jenis kesalahan yang sering diulang, dan tujuan belajar - Setiap field punya nama yang tidak ambigu — bayangkan model harus mengisinya tanpa bertanya
- Jalankan percakapan empat giliran di thread
sesi-1, di mana murid menyebutkan tujuannya dan membuat setidaknya dua kesalahan tata bahasa yang serupa - Buka thread baru
sesi-2dengan resource yang sama, lalu tanyakan "menurutmu apa yang harus saya latih hari ini?" - Buat versi kedua agent yang identik tapi
scope: 'thread', ulangi langkah 3 dan 4, lalu bandingkan - Periksa isi working memory di Studio setelah langkah 3
Checklist penerimaan:
- [ ] Setelah langkah 3, working memory di Studio berisi nama murid, tujuan, dan pola kesalahannya — bukan sekadar ringkasan percakapan
- [ ] Pada versi
scope: 'resource', jawaban di langkah 4 merujuk pada kesalahan yang dibuat disesi-1 - [ ] Pada versi
scope: 'thread', jawaban di langkah 4 tidak merujuk apa pun darisesi-1 - [ ] Kamu bisa menjelaskan kapan
scope: 'thread'justru pilihan yang benar untuk aplikasi semacam ini - [ ] Tidak ada field template yang tetap kosong padahal informasinya jelas muncul dalam percakapan
Petunjuk: Untuk poin terakhir, field "jenis kesalahan yang sering diulang" biasanya yang paling sering gagal terisi. Model perlu tahu bahwa ia diharapkan menyimpulkan pola, bukan menunggu murid menyebutkannya. Nama field yang lebih eksplisit membantu — misalnya "Pola kesalahan yang kamu amati (isi sendiri berdasarkan jawaban murid)".
Ikhtisar
- Working memory menyimpan fakta yang bertahan, terpisah dari percakapan yang bergulir. Ia menyelesaikan masalah yang tidak bisa diselesaikan dengan menaikkan
lastMessages. - Isinya adalah blok teks Markdown yang diperbarui agent, bukan struktur data — karena pembaca dan penulisnya adalah model.
templatememberi kerangka dan menentukan apa yang layak dicatat. Tanpa itu, hasilnya tidak konsisten.- Dua lingkup:
resource(default, lintas semua thread pengguna) danthread(terisolasi per percakapan). Keduanya tidak saling melihat, dan berpindah lingkup tidak memindahkan ingatan yang sudah ada. - Lingkup
resourcememperbesar akibat kesalahan pada nilairesource— profil pribadi bisa terbaca pengguna lain. Ambil dari sesi terautentikasi. - Working memory ikut dikirim di setiap panggilan model, jadi jaga tetap ringkas.
Lanjut ke mana
Sekarang agent mengingat percakapan terkini dan fakta penting tentang penggunanya. Yang belum: kemampuan menemukan kembali sesuatu yang dibicarakan jauh di masa lalu, saat topiknya muncul lagi — 3.3 Semantic Recall.