Tampilan
4.4 Agent & Tool di Workflow
Tujuan bab
Setelah bab ini kamu bisa:
- Memanggil agent dari dalam
execute()sebuah langkah - Menyusun agent langsung sebagai langkah dengan
createStep(agent) - Memilih antara kedua pendekatan itu
- Memakai
structuredOutputsupaya keluaran agent bisa disambung ke langkah berikutnya dengan aman - Menjelaskan schema default yang dipakai agent saat menjadi langkah
Prasyarat
Mengembalikan penilaian ke dalam alur yang pasti
Workflow memberimu determinisme, dan itu yang kamu inginkan untuk sebagian besar langkah. Tapi sebagian pekerjaan memang butuh penilaian: mengklasifikasi keluhan, meringkas dokumen, menyusun kalimat.
Di sinilah kedua dunia bertemu. Alurnya tetap kamu yang tentukan; satu-dua langkah di dalamnya diserahkan ke model.
┌─────────┐ ┌─────────────────┐ ┌─────────┐ ┌─────────┐
│validasi │──▶│ klasifikasi │──▶│ routing │──▶│ simpan │
│ (kode) │ │ (agent) │ │ (kode) │ │ (kode) │
└─────────┘ └─────────────────┘ └─────────┘ └─────────┘
▲
hanya di sini model
diberi kebebasanMastra menyediakan dua cara melakukannya.
Cara pertama: memanggil agent dari dalam execute()
ts
// src/mastra/workflows/ringkas-workflow.ts
import { createStep } from '@mastra/core/workflows'
import { z } from 'zod'
const step1 = createStep({
id: 'step-1',
inputSchema: z.object({ message: z.string() }),
outputSchema: z.object({ list: z.string() }),
execute: async ({ inputData, mastra }) => {
const { message } = inputData
const testAgent = mastra.getAgent('testAgent')
const response = await testAgent.generate(
`Ubah pesan ini menjadi poin-poin: ${message}`,
{
memory: {
thread: 'user-123',
resource: 'test-123',
},
},
)
return { list: response.text }
},
})Perhatikan parameter mastra pada execute. Runtime menyediakannya, dan dari situ kamu bisa mengambil agent mana pun yang terdaftar — persis seperti yang kamu lakukan di skrip biasa.
Cara ini dipakai saat kamu perlu mengendalikan bagaimana agent dipanggil. Contoh di atas menunjukkan satu alasannya: meneruskan opsi memory supaya percakapan tercatat pada thread tertentu. Alasan lain: menyusun prompt dari beberapa sumber, atau mengolah respons sebelum meneruskannya.
Cara kedua: agent sebagai langkah
Kalau kamu tidak perlu mengubah cara agent dipanggil, ada bentuk yang jauh lebih ringkas:
ts
// src/mastra/workflows/ringkas-workflow.ts
import { createWorkflow, createStep } from '@mastra/core/workflows'
import { testAgent } from '../agents/test-agent.ts'
const step1 = createStep(testAgent)
export const testWorkflow = createWorkflow({ /* ... */ })
.map(async ({ inputData }) => {
const { message } = inputData
return {
prompt: `Ubah pesan ini menjadi poin-poin: ${message}`,
}
})
.then(step1)
.then(step2)
.commit()createStep(testAgent) mengubah agent menjadi langkah. Tapi ada satu hal yang harus dipahami sebelum ini bekerja.
Schema default agent sebagai langkah
Saat tidak ada opsi structuredOutput, agent yang dipakai sebagai langkah memakai schema bawaan:
ts
{
inputSchema: {
prompt: string
},
outputSchema: {
text: string
}
}Artinya langkah itu mengharapkan properti prompt dan mengembalikan properti text.
Inilah kenapa contoh di atas memakai .map() sebelum .then(step1). Keluaran langkah sebelumnya bernama message, sementara agent menuntut prompt. .map() yang menjembatani — sekaligus menjadi tempat kamu menyusun kalimat prompt-nya.
inputData: { message: "..." }
│
.map() ← menyusun prompt
▼
{ prompt: "Ubah pesan ini menjadi poin-poin: ..." }
│
createStep(testAgent)
▼
{ text: "- poin satu\n- poin dua" }Agent dengan keluaran terstruktur
Keluaran bertipe { text: string } sulit disambung ke langkah berikutnya dengan aman. Langkah setelahnya harus mengurai teks itu — dan kita sudah membahas di 1.4 kenapa itu ide buruk.
Solusinya: berikan structuredOutput saat membuat langkah.
ts
// src/mastra/workflows/artikel-workflow.ts
import { createStep } from '@mastra/core/workflows'
import { z } from 'zod'
import { testAgent } from '../agents/test-agent.ts'
const articleSchema = z.object({
title: z.string(),
summary: z.string(),
tags: z.array(z.string()),
})
const agentStep = createStep(testAgent, {
structuredOutput: { schema: articleSchema },
})Sekarang outputSchema langkah itu mengikuti schema yang kamu berikan, sehingga langkah berikutnya menerima data bertipe — dan sambungannya diverifikasi compiler seperti langkah biasa.
Ini pola yang layak dijadikan default. Agent di tengah workflow hampir selalu lebih berguna kalau keluarannya terstruktur, karena langkah setelahnya adalah kode, bukan manusia.
Memilih di antara keduanya
Panggil dari execute() | createStep(agent) | |
|---|---|---|
| Kendali atas pemanggilan | Penuh | Terbatas pada opsi createStep |
| Jumlah kode | Lebih banyak | Ringkas |
Opsi memory | Bisa | Tidak langsung |
| Keluaran terstruktur | Lewat generate() | Lewat opsi structuredOutput |
| Cocok untuk | Kasus khusus | Sebagian besar kasus |
Saran praktis: mulai dengan createStep(agent), dan pindah ke pemanggilan manual hanya saat kamu benar-benar butuh sesuatu yang tidak disediakannya.
Tool di dalam workflow
Tool juga bisa dipanggil dari langkah workflow. Perlu diingat perbedaan mendasarnya dari pemakaian tool oleh agent:
- Agent memanggil tool karena ia memutuskan tool itu relevan
- Workflow memanggil tool karena kamu menuliskannya di alur
Dalam konteks workflow, tool pada dasarnya adalah fungsi bertipe yang bisa dipakai ulang. Kalau logika itu hanya dipakai di satu workflow dan tidak pernah oleh agent, seringkali lebih sederhana menulisnya langsung sebagai step — tanpa perlu description yang memang tidak akan dibaca siapa pun.
Arah sebaliknya: workflow dipanggil agent
Ingat dari 2.1 bahwa hubungan ini bisa dibalik. Sebuah workflow yang didaftarkan pada agent lewat konfigurasi workflows otomatis menjadi tool bernama workflow-<key>:
ts
export const researchAgent = new Agent({
id: 'research-agent',
name: 'Research Agent',
instructions: 'Kamu asisten riset.',
model: 'openai/gpt-5.6-sol',
workflows: { researchWorkflow },
})Pola ini sangat berguna dalam praktik: agent menangani percakapan dan memutuskan kapan sebuah proses perlu dijalankan; workflow memastikan proses itu berjalan dengan urutan yang benar setiap kali.
Kamu mendapat keluwesan agent di lapisan percakapan, dan determinisme workflow di lapisan eksekusi.
Kesalahan umum
Gejala: Error tipe pada .then(agentStep) yang menyebut properti prompt tidak ditemukan. Penyebab: Agent yang dipakai sebagai langkah memakai schema bawaan yang mengharapkan { prompt: string }, sementara langkah sebelumnya menghasilkan nama properti yang berbeda. Perbaikan: Sisipkan .map() sebelum langkah agent untuk membentuk objek berisi prompt. Ini juga tempat yang tepat untuk menyusun kalimat prompt-nya.
Gejala: Langkah setelah agent menerima teks yang bentuknya berubah-ubah, sehingga logika pengurainya kadang berhasil kadang gagal. Penyebab: Agent memakai schema keluaran bawaan { text: string }, dan langkah berikutnya mencoba mengurai prosa bebas. Perbaikan: Berikan structuredOutput pada createStep(agent, { ... }) sehingga keluaran langkah itu bertipe dan tervalidasi. Jangan mengurai teks bebas di dalam workflow.
Coba sendiri
Tantangan: Bangun keluhan-workflow yang memproses keluhan pelanggan dari teks bebas menjadi tiket yang terstruktur dan terarah.
Alur yang diminta:
bersihkan— langkah kode: rapikan teks masukanklasifikasi— langkah agent denganstructuredOutput: hasilkan kategori, urgensi 1–5, dan ringkasan satu kalimatrouting— langkah kode: tentukan tim tujuan berdasarkan kategori dan urgensibalasan— langkah agent: susun draf balasan untuk pelanggansimpan— langkah kode: gabungkan semuanya menjadi objek tiket
Ketentuan:
- Langkah 2 memakai
createStep(agent, { structuredOutput }), didahului.map() - Langkah 4 memakai pemanggilan manual dari dalam
execute(), karena ia perlu menyusun prompt dari dua sumber: keluhan asli dan hasil klasifikasi - Langkah 3 tidak boleh memanggil model sama sekali — ini aturan bisnis murni
- Simpan hasil klasifikasi ke workflow state, supaya langkah 5 bisa membacanya tanpa dioper melalui langkah 4
- Jalankan tiga keluhan berbeda dan periksa hasilnya
Checklist penerimaan:
- [ ] Langkah 2 menghasilkan objek bertipe, bukan teks — dan langkah 3 membacanya tanpa mengurai string
- [ ] Langkah 3 murni kode; tidak ada panggilan model di dalamnya
- [ ] Langkah 4 berhasil menyusun prompt dari dua sumber
- [ ] Langkah 5 membaca hasil klasifikasi dari state, bukan dari
inputData - [ ] Kamu bisa menjelaskan kenapa langkah 2 dan 4 memakai cara berbeda
- [ ] Di Studio, kelima langkah terlihat terpisah dengan durasinya masing-masing
Petunjuk: Ketentuan 3 adalah yang paling penting secara desain, dan paling mudah dilanggar. Ada godaan untuk menyerahkan routing ke model juga — toh ia sudah dipanggil di langkah sebelumnya. Tahan godaan itu. Aturan seperti "urgensi 5 selalu ke tim eskalasi" adalah kebijakan perusahaan, dan kebijakan tidak seharusnya bervariasi antar pemanggilan.
Ikhtisar
- Dua cara memakai agent dalam workflow: memanggil dari dalam
execute()lewat parametermastra, ataucreateStep(agent). - Panggil dari
execute()saat perlu mengendalikan pemanggilan — opsimemory, prompt dari banyak sumber, atau pengolahan respons. - Agent sebagai langkah memakai schema bawaan
{ prompt }masuk,{ text }keluar — karena itu hampir selalu didahului.map(). - Berikan
structuredOutputsupaya keluaran agent bertipe dan bisa disambung dengan aman. Jangan mengurai teks bebas di dalam workflow. - Tool bisa dipanggil dari langkah, tapi kalau logikanya hanya dipakai di satu workflow, menulisnya langsung sebagai step lebih sederhana.
- Arah sebaliknya juga berlaku: workflow yang didaftarkan pada agent menjadi tool
workflow-<key>— agent untuk keluwesan, workflow untuk determinisme.
Lanjut ke mana
Satu kemampuan workflow yang paling khas belum dibahas: berhenti di tengah jalan, menunggu manusia selama berhari-hari, lalu melanjutkan dari titik yang sama — 4.5 Suspend & Resume.