Skip to content

7.2 Goals, Schedules & Signals

Tujuan bab

Setelah bab ini kamu bisa:

  • Menetapkan tujuan yang bertahan untuk sebuah thread dengan setObjective()
  • Membedakan goal dan isTaskComplete, serta memilih di antaranya
  • Menjadwalkan agent berjalan pada cron dengan mastra.schedules
  • Mengirim pesan dan sinyal ke thread yang sedang berjalan atau menganggur
  • Menjelaskan peran signal providers

Prasyarat

Tiga cara agent bekerja tanpa diminta

Ketiga kemampuan di bab ini menjawab pertanyaan yang berbeda:

KemampuanPertanyaannyaPemicunya
GoalsKapan agent boleh berhenti?Penilaian model terhadap tujuan
SchedulesKapan agent mulai bekerja?Waktu, lewat cron
SignalsApa yang membuat agent tahu ada hal baru?Peristiwa dari luar

Goals — tujuan yang bertahan

Sebuah goal adalah tujuan yang durable dan terikat pada thread: instruksi tetap yang terus dikejar agent melintasi iterasi loop, sampai sebuah model penilai memutuskan tujuan itu tercapai — atau sampai anggaran run habis.

ts
// src/mastra/agents/worker.ts
import { Agent } from '@mastra/core/agent'

export const worker = new Agent({
  id: 'worker',
  name: 'worker',
  instructions: 'Kamu menyelesaikan tugas perangkat lunak dari awal sampai akhir.',
  model: 'openai/gpt-5.6-sol',
  memory,
  goal: {
    judge: 'openai/gpt-5-mini',
    maxRuns: 50,
  },
})

await worker.setObjective('Tambahkan dan uji endpoint /health', {
  threadId,
  resourceId,
})

Dua properti pada konfigurasi goal:

  • judge — model yang menilai apakah tujuan sudah tercapai. Dokumentasi menyebutnya wajib: tanpa judge, goal tidak melakukan apa-apa.
  • maxRuns — anggaran run. Ini rem darurat; tanpanya, agent yang tidak pernah dinilai selesai bisa berputar tanpa batas.

Tujuannya dipersistensi di thread state, sehingga bertahan melewati reload dan dievaluasi di dalam loop — bahkan ketika pesan baru datang di tengah giliran yang sedang berjalan.

goal atau isTaskComplete?

Keduanya memakai mesin yang sama: LLM-as-judge yang menilai keluaran agent tiap iterasi dan menentukan apakah loop lanjut. Perbedaannya di tempat tinggal kriteria itu:

goalisTaskComplete
Disimpan diThread state (durable)Opsi per pemanggilan stream()
Bertahan melewati restartYaTidak
Diatur lewatMethod Agent seperti setObjective()Opsi pada tiap panggilan
Cocok untukTujuan tetap yang dikejar lintas banyak pesanPemeriksaan sekali jalan

Aturannya sederhana: kalau kriteria keberhasilannya harus disebut ulang di setiap panggilan, kamu sebenarnya butuh goal.

Goals memerlukan storage dan memory

Dokumentasi menyebut keduanya sebagai prasyarat: backend storage yang terkonfigurasi, dan thread yang didukung memory. Masuk akal — tujuan yang "bertahan" harus punya tempat bertahan.

Schedules — pekerjaan yang berulang

Sebuah schedule menjalankan agent pada irama cron. Setiap kali menyala, Mastra mengirim prompt — bisa sebagai signal ke sebuah thread, atau sebagai run agent.generate() tanpa thread.

ts
// src/mastra/index.ts
import { Mastra } from '@mastra/core'
import { Agent } from '@mastra/core/agent'
import { LibSQLStore } from '@mastra/libsql'

const pinger = new Agent({
  id: 'pinger',
  name: 'Pinger',
  instructions: 'Laporkan status sistem saat ini dalam satu kalimat.',
  model: 'openai/gpt-5.6-sol',
})

export const mastra = new Mastra({
  agents: { pinger },
  storage: new LibSQLStore({ id: 'mastra-storage', url: 'file:./mastra.db' }),
})

await mastra.schedules.create({
  agentId: 'pinger',
  cron: '0 * * * *',
  prompt: 'Beri saya pembaruan status.',
})

Schedule di atas berjalan tiap jam. Karena tidak menyebut thread, tiap penyalaan adalah run agent.generate() yang terisolasi.

Hal-hal yang perlu diketahui:

  • Schedule dipersistensi, sehingga bertahan melewati restart dan redeploy
  • Mastra menyalakan penjadwal saat schedule pertama dibuat
  • mastra.schedules adalah permukaan CRUD kanonik untuk mengelolanya saat runtime
  • Permukaan yang sama juga mengelola schedule untuk workflow — berikan workflowId alih-alih agentId

Tidak semua storage adapter mendukung schedules

Dokumentasi mencatat bahwa schedules memerlukan storage adapter yang mengimplementasikan domain schedules. Ingat dari 0.2 bahwa storage Mastra terbagi menjadi domain, dan dukungan tiap adapter berbeda-beda. Periksa dulu sebelum merancang di atasnya.

Dua bentuk penyalaan

Tanpa thread:                    Dengan thread:
  cron menyala                     cron menyala
     ↓                                ↓
  agent.generate()                 signal masuk ke thread
     ↓                                ↓
  hasil berdiri sendiri            agent melanjutkan percakapan
                                    dengan konteks yang sudah ada

Bentuk pertama untuk pekerjaan yang berdiri sendiri — ringkasan harian, pemeriksaan berkala. Bentuk kedua untuk "dorongan terjadwal" ke dalam percakapan yang sedang berlangsung, misalnya agent yang mengingatkan sesuatu di tengah sesi kerja panjang.

Signals — memberi tahu agent ada hal baru

Signals membalik cara kamu berinteraksi dengan agent. Alih-alih memulai tiap interaksi dengan agent.stream(), kamu berlangganan ke sebuah thread lalu mengirim pesan atau sinyal ke sana.

ts
// run.mjs
import { Agent } from '@mastra/core/agent'

const agent = new Agent({
  id: 'support-agent',
  name: 'Support Agent',
  instructions: 'Bantu pengguna membandingkan pilihan.',
  model: 'openai/gpt-5.6-sol',
})

const thread = {
  resourceId: 'user_123',
  threadId: 'thread_456',
}

const subscription = await agent.subscribeToThread(thread)

await agent.sendMessage('Bandingkan itu dengan pilihan sebelumnya.', thread)

for await (const chunk of subscription.stream) {
  console.log(chunk)
}

Yang dilakukan Mastra saat pesan tiba: membangunkan agent yang menganggur, atau mengantarkan masukan ke loop yang sedang berjalan — atau mengantrikannya untuk giliran berikutnya.

Empat cara mengirim, empat maksud berbeda

MethodUntukContoh
sendMessage()Masukan yang ditulis pengguna"Bandingkan dengan yang tadi"
queueMessage()Masukan pengguna, ditahan untuk giliran berikutnyaPesan susulan saat agent sibuk
sendSignal()Konteks sistem tingkat rendahNotifikasi background task, pengingat kebijakan
sendStateSignal()Jalur state yang durablePerubahan keadaan yang harus bertahan
sendNotificationSignal()Peristiwa yang harus menjadi catatan inbox durablePeringatan yang perlu dilacak

Garis pemisah utamanya: sendMessage() untuk yang ditulis manusia, sendSignal() untuk konteks yang dihasilkan sistem. Memakai sendMessage() untuk notifikasi mesin membuat riwayat percakapan tercemar hal-hal yang tidak pernah dikatakan siapa pun.

Signal providers — jembatan ke dunia luar

Kemampuan terakhir menutup lingkarannya. Signal provider memantau sistem eksternal — GitHub, Slack, CI — lewat polling atau webhook, lalu meneruskan peristiwa yang cocok ke thread agent.

GitHub PR di-merge

  Signal provider (webhook)

  Signal masuk ke thread

  Agent bangun dan menindaklanjuti

Ini yang membuat agent SRE atau agent go-to-market yang disebut di pengantar bagian ini mungkin dibangun: agent tidak menunggu ditanya, ia bereaksi terhadap perubahan.

Ketiganya bersama

Pembagiannya: schedule dan signal menentukan kapan agent mulai; goal menentukan kapan ia boleh berhenti.

Kesalahan umum

Gejala: goal dikonfigurasi tapi agent berperilaku seperti biasa — tidak ada pengejaran tujuan sama sekali. Penyebab: judge tidak diberikan. Dokumentasi menyatakan model penilai wajib ada agar goal berfungsi. Perbaikan: Tambahkan judge pada konfigurasi goal, dan pastikan storage serta memory terpasang.

Gejala: Riwayat percakapan penuh pesan yang tidak pernah ditulis pengguna — notifikasi sistem, pembaruan status, pengingat internal. Penyebab: Memakai sendMessage() untuk konteks yang dihasilkan sistem, padahal itu tugas sendSignal(). Perbaikan: Pisahkan keduanya. sendMessage() hanya untuk yang benar-benar ditulis manusia; segala konteks mesin lewat sendSignal() atau variannya.

Coba sendiri

Tantangan: Bangun monitor-agent yang memantau sesuatu secara berkala, mengejar tujuan yang bertahan, dan bisa dibangunkan dari luar.

Ketentuan:

  1. Buat agent dengan memory dan storage terpasang
  2. Konfigurasikan goal dengan judge berupa model ringan dan maxRuns yang sengaja kecil, misalnya 5
  3. Tetapkan objective lewat setObjective() — sesuatu yang jelas kriterianya, misalnya "kumpulkan tiga sumber terpercaya tentang topik X dan ringkas masing-masing dalam dua kalimat"
  4. Buat schedule cron yang menyala tiap menit dan mengirim prompt ke thread yang sama dengan objective itu
  5. Berlangganan thread tersebut, dan amati apa yang terjadi selama beberapa penyalaan
  6. Dari skrip terpisah, kirim sendSignal() berisi konteks sistem — misalnya "sumber dari domain contoh.com sedang tidak dapat diakses"
  7. Amati apakah agent memperhitungkan sinyal itu
  8. Naikkan maxRuns dan bandingkan perilakunya

Checklist penerimaan:

  • [ ] Agent berhenti setelah tujuan dinilai tercapai — bukan setelah satu giliran
  • [ ] Dengan maxRuns: 5, agent berhenti karena anggaran habis kalau tujuannya belum tercapai — dan kamu bisa membedakan kedua sebab berhenti itu
  • [ ] Schedule menyala sesuai cron dan masuk ke thread yang benar
  • [ ] Sinyal dari langkah 6 sampai ke agent dan terlihat pengaruhnya
  • [ ] Sinyal itu tidak muncul sebagai pesan pengguna di riwayat
  • [ ] Kamu bisa menjelaskan kenapa kasus ini memakai goal dan bukan isTaskComplete

Petunjuk: Ketentuan 2 dengan maxRuns kecil disengaja. Anggaran run adalah pengaman yang paling mudah diabaikan sampai kamu pernah melihat agent berputar dua puluh kali karena judge tidak pernah puas. Lihat sendiri batas itu bekerja selagi murah — dan perhatikan bahwa "berhenti karena selesai" dan "berhenti karena kehabisan anggaran" adalah dua hasil yang sangat berbeda.

Ikhtisar

  • Goal adalah tujuan durable yang terikat thread, dinilai oleh model judge tiap iterasi sampai tercapai atau maxRuns habis. judge wajib; storage dan memory adalah prasyarat.
  • Bedanya dari isTaskComplete: goal bertahan di thread state, isTaskComplete diberikan per pemanggilan.
  • Schedule menjalankan agent pada cron, dan dikelola lewat mastra.schedules. Ia dipersistensi, dan penjadwal menyala saat schedule pertama dibuat.
  • Schedule bisa berupa run terisolasi (tanpa thread) atau signal ke thread yang ada. Ia juga bisa menjadwalkan workflow lewat workflowId.
  • Tidak semua storage adapter mendukung domain schedules — periksa dulu.
  • Signals membalik alur: berlangganan thread, lalu kirim masukan. sendMessage() untuk manusia, sendSignal() untuk sistem — jangan tertukar, atau riwayat percakapanmu tercemar.
  • Signal providers memantau sistem eksternal lewat polling atau webhook dan meneruskan peristiwa ke thread.

Lanjut ke mana

Sejauh ini satu agent yang bekerja lebih lama. Bab berikutnya membagi pekerjaan ke beberapa agent yang lebih spesialis: 7.3 Subagents & Skills.

Materi belajar mandiri. Bukan dokumentasi resmi Mastra.