> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-TW/llms.txt
# 使用 AI SDK UI
[AI SDK UI](https://sdk.vercel.ai) 是一套 React 工具函式與元件庫,用於建構 AI 驅動的介面。本指南將說明如何使用 `@mastra/ai-sdk`,將 Mastra 輸出轉換為 AI SDK 相容格式,讓你能在前端使用其 hook 與元件。
> **備註:** 要從 AI SDK v4 遷移至 v5 嗎?請參閱[遷移指南](https://mastra.zisheng.pro/zh-TW/guides/migrations/ai-sdk-v4-to-v5)。
> **提示:** 想查看更多範例嗎?請前往 Mastra 的 [**UI Dojo**](https://ui-dojo.mastra.ai/) 或參閱 [Next.js 快速入門指南](https://mastra.zisheng.pro/zh-TW/guides/getting-started/next-js)。
## 開始使用
安裝 `@mastra/ai-sdk` 套件,即可搭配使用 Mastra 與 AI SDK UI。`@mastra/ai-sdk` 提供自訂 API 路由及工具函式,能以 AI SDK 相容格式串流 Mastra Agent。其中包括聊天、Workflow 與網路路由 handler,以及用於 UI 整合的工具函式與匯出型別。
`@mastra/ai-sdk` 可與 AI SDK UI 的三個主要 hook 整合:[`useChat()`](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot)、[`useCompletion()`](https://ai-sdk.dev/docs/ai-sdk-ui/completion) 及 [`useObject()`](https://ai-sdk.dev/docs/ai-sdk-ui/object-generation)。
安裝所需套件以開始使用:
**npm**:
```bash
npm install @mastra/ai-sdk@latest @ai-sdk/react ai
```
**pnpm**:
```bash
pnpm add @mastra/ai-sdk@latest @ai-sdk/react ai
```
**Yarn**:
```bash
yarn add @mastra/ai-sdk@latest @ai-sdk/react ai
```
**Bun**:
```bash
bun add @mastra/ai-sdk@latest @ai-sdk/react ai
```
現在可以按照下方的整合指南與操作方式進行!
## 整合指南
一般而言,你會先設定以 AI SDK 相容格式串流 Mastra 內容的 API 路由,再於 `useChat()` 等 AI SDK UI hook 中使用這些路由。請選擇下列其中一種方式:
- [Mastra 伺服器](#mastras-server)
- [不限框架](#framework-agnostic)
設定好 API 路由後,即可在 [`useChat()`](#usechat) hook 中使用。
### Mastra 伺服器
將 Mastra 作為獨立伺服器執行,並將前端(例如使用 Vite + React)連接至其 API 端點。這項作業會使用 Mastra 的[自訂 API 路由](https://mastra.zisheng.pro/zh-TW/docs/server/custom-api-routes)功能。
> **資訊:** Mastra 的 [**UI Dojo**](https://ui-dojo.mastra.ai/) 就是此設定方式的範例。
你可以使用 [`chatRoute()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/chat-route)、[`workflowRoute()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/workflow-route) 及 [`networkRoute()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/network-route) 建立 API 路由,以 AI SDK 相容格式串流 Mastra 內容。實作完成後,即可在 [`useChat()`](#usechat) 中使用這些 API 路由。
**chatRoute()**:
此範例說明如何在 `/chat` 端點設定聊天路由,並使用 ID 為 `weatherAgent` 的 Agent。
```typescript
import { Mastra } from '@mastra/core'
import { chatRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
chatRoute({
path: '/chat',
agent: 'weatherAgent',
}),
],
},
})
```
你也可以使用動態 Agent 路由。詳情請參閱 [`chatRoute()` 參考文件](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/chat-route)。
**workflowRoute()**:
此範例說明如何在 `/workflow` 端點設定 Workflow 路由,並使用 ID 為 `weatherWorkflow` 的 Workflow。
```typescript
import { Mastra } from '@mastra/core'
import { workflowRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
workflowRoute({
path: '/workflow',
workflow: 'weatherWorkflow',
}),
],
},
})
```
你也可以使用動態 Workflow 路由。詳情請參閱 [`workflowRoute()` 參考文件](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/workflow-route)。
> **Workflow 中的 Agent 串流:** 當 Workflow step 將 Agent 串流 pipe 至 Workflow writer(例如 `await response.fullStream.pipeTo(writer)`)時,即使 Agent 在 Workflow step 內執行,其文字區塊與 Tool 呼叫仍會即時轉送至 UI 串流。
>
> 詳情請參閱 [Workflow 串流](https://mastra.zisheng.pro/zh-TW/docs/workflows/overview)。
**networkRoute()**:
此範例說明如何在 `/network` 端點設定網路路由,並使用 ID 為 `weatherAgent` 的 Agent。
```typescript
import { Mastra } from '@mastra/core'
import { networkRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
networkRoute({
path: '/network',
agent: 'weatherAgent',
}),
],
},
})
```
你也可以使用動態網路路由。詳情請參閱 [`networkRoute()` 參考文件](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/network-route)。
### 不限框架
如果不想執行 Mastra 伺服器,而是使用 Next.js 或 Express 等框架,可以在自己的 API 路由 handler 中使用 [`handleChatStream()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/handle-chat-stream)、[`handleWorkflowStream()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/handle-workflow-stream) 及 [`handleNetworkStream()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/handle-network-stream) 函式。
這些函式會傳回 `ReadableStream`,你可以使用 [`createUIMessageStreamResponse()`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream-response) 加以包裝。
> **AI SDK v6 相容性:** 不限框架的 handler 會保留現有的 AI SDK v5/預設行為。如果應用程式使用 AI SDK v6 型別,請傳入 `version: 'v6'`。若要讓 `handleChatStream()` 和 `handleNetworkStream()` 獲得最佳 TypeScript 型別推論,請將 `messages` 以已安裝 `ai` 版本中的 `UIMessage[]` 傳入。
下列範例說明如何搭配 Next.js App Router 使用這些函式。
**handleChatStream()**:
此範例說明如何在 `/chat` 端點設定聊天路由,並使用 ID 為 `weatherAgent` 的 Agent。
```typescript
import { handleChatStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/src/mastra'
export async function POST(req: Request) {
const params = await req.json()
const stream = await handleChatStream({
mastra,
agentId: 'weatherAgent',
params,
})
return createUIMessageStreamResponse({ stream })
}
```
**handleWorkflowStream()**:
此範例說明如何在 `/workflow` 端點設定 Workflow 路由,並使用 ID 為 `weatherWorkflow` 的 Workflow。
```typescript
import { handleWorkflowStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/src/mastra'
export async function POST(req: Request) {
const params = await req.json()
const stream = await handleWorkflowStream({
mastra,
workflowId: 'weatherWorkflow',
params,
})
return createUIMessageStreamResponse({ stream })
}
```
**handleNetworkStream()**:
此範例說明如何在 `/network` 端點設定網路路由,並使用 ID 為 `routingAgent` 的 Agent。
```typescript
import { handleNetworkStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/src/mastra'
export async function POST(req: Request) {
const params = await req.json()
const stream = await handleNetworkStream({
mastra,
agentId: 'routingAgent',
params,
})
return createUIMessageStreamResponse({ stream })
}
```
### `useChat()`
無論是透過 [Mastra 伺服器](#mastras-server)建立 API 路由,或使用[自選框架](#framework-agnostic),現在都可以在 `useChat()` hook 中使用 API 端點。
假設已在 `/chat` 設定使用氣象 Agent 的路由,就可以如下方所示向它提問。請務必設定正確的 `api` URL。
```ts
import { useChat } from '@ai-sdk/react'
import { useState } from 'react'
import { DefaultChatTransport } from 'ai'
export default function Chat() {
const [inputValue, setInputValue] = useState('')
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat',
}),
})
const handleFormSubmit = (e: React.FormEvent) => {
e.preventDefault()
sendMessage({ text: inputValue })
}
return (
{JSON.stringify(messages, null, 2)}
)
}
```
使用 [`prepareSendMessagesRequest`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat#transport.default-chat-transport.prepare-send-messages-request) 自訂傳送至聊天路由的請求,例如將額外組態傳入 Agent。
## 使用 Mastra Memory
當 Agent 已設定 [memory](https://mastra.zisheng.pro/zh-TW/docs/memory/overview) 時,Mastra 會從伺服器上的儲存空間載入對話歷程。使用者端只需傳送新訊息,不要傳送完整對話歷程。
傳送完整歷程不僅重複,還可能造成訊息排序錯誤,因為使用者端時間戳記可能與資料庫中儲存的時間戳記衝突。
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat/weatherAgent',
prepareSendMessagesRequest({ messages }) {
return {
body: {
messages: [messages[messages.length - 1]],
memory: {
thread: 'user-thread-123',
resource: 'user-123',
},
},
}
},
}),
})
```
請根據應用程式本身的狀態設定 `memory.thread` 與 `memory.resource`,例如 URL 參數、驗證 context 或資料庫。
如需瞭解 Mastra memory 如何載入及儲存訊息,請參閱[訊息歷程](https://mastra.zisheng.pro/zh-TW/docs/memory/message-history)。
[`chatRoute()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/chat-route) 與 [`handleChatStream()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/handle-chat-stream) 已支援 memory。請設定使用者端只傳送新訊息,並包含 thread 與 resource 識別碼。
### `useCompletion()`
`useCompletion()` hook 可處理前端與 Mastra Agent 之間的單回合補全,讓你能透過 HTTP 傳送 prompt 並接收串流回應。
前端可以如下所示:
```typescript
import { useCompletion } from '@ai-sdk/react'
export default function Page() {
const { completion, input, handleInputChange, handleSubmit } = useCompletion({
api: '/api/completion',
})
return (
)
}
```
選擇一種後端實作方式:
**Mastra 伺服器**:
```ts
import { Mastra } from '@mastra/core/mastra'
import { registerApiRoute } from '@mastra/core/server'
import { handleChatStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'
export const mastra = new Mastra({
server: {
apiRoutes: [
registerApiRoute('/completion', {
method: 'POST',
handler: async c => {
const { prompt } = await c.req.json()
const mastra = c.get('mastra')
const stream = await handleChatStream({
mastra,
agentId: 'weatherAgent',
params: {
messages: [
{
id: '1',
role: 'user',
parts: [
{
type: 'text',
text: prompt,
},
],
},
],
},
})
return createUIMessageStreamResponse({ stream })
},
}),
],
},
})
```
**Next.js**:
```ts
import { handleChatStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/src/mastra'
// Allow streaming responses up to 30 seconds
export const maxDuration = 30
export async function POST(req: Request) {
const { prompt }: { prompt: string } = await req.json()
const stream = await handleChatStream({
mastra,
agentId: 'weatherAgent',
params: {
messages: [
{
id: '1',
role: 'user',
parts: [
{
type: 'text',
text: prompt,
},
],
},
],
},
})
return createUIMessageStreamResponse({ stream })
}
```
## 自訂 UI
自訂 UI(也稱為生成式 UI)讓你能根據 Mastra 串流的資料,轉譯自訂 React 元件。你可以為 Tool 輸出與 Workflow 進度建立視覺化元件,而不只是顯示原始文字或 JSON;其中也包括 Agent network 執行與自訂事件。
在下列情況使用自訂 UI:
- 將 Tool 輸出轉譯為視覺化元件(例如以氣象卡片取代 JSON)
- 使用狀態指示器顯示 Workflow step 進度
- 透過逐步更新將 Agent network 執行過程視覺化
- 在長時間執行的操作期間顯示進度指示器或狀態更新
### 資料 part 型別
Mastra 會以訊息中的「part」將資料串流至前端。每個 part 都有 `type`,用來決定轉譯方式。`@mastra/ai-sdk` 套件會將 Mastra 串流轉換為 AI SDK 相容的 [UI Message DataPart](https://ai-sdk.dev/docs/reference/ai-sdk-core/ui-message#datauipart)。
| 資料 part 型別 | 來源 | 說明 |
| ---------------------- | ------------------ | --------------------------------------------------------------- |
| `tool-{toolKey}` | AI SDK 內建 | Tool 呼叫及其狀態:`input-available`、`output-available`、`output-error` |
| `data-workflow` | `workflowRoute()` | 包含 step 狀態與最終輸出的 Workflow 執行狀態快照 |
| `data-workflow-step` | `workflowRoute()` | Workflow step 差異,包含已變更 step 的完整 payload |
| `data-network` | `networkRoute()` | 包含依序排列之 step 與輸出的 Agent network 執行狀態 |
| `data-tool-agent` | Tool 中的巢狀 Agent | 目前 step 仍在執行時的精簡巢狀 Agent 快照 |
| `data-tool-agent-step` | Tool 中的巢狀 Agent | 巢狀 step 完成時發出的完整巢狀 Agent step payload |
| `data-tool-workflow` | Tool 中的巢狀 Workflow | 從 Tool 的 `execute()` 中串流的 Workflow 輸出 |
| `data-tool-network` | Tool 中的巢狀 network | 從 Tool 的 `execute()` 中串流的 network 輸出 |
| `data-{custom}` | `writer.custom()` | 用於進度指示器、狀態更新等用途的自訂事件 |
### 轉譯 Tool 輸出
當 Agent 呼叫 Tool 時,AI SDK 會自動建立 `tool-{toolKey}` part。這些 part 包含 Tool 的狀態與輸出,可用來轉譯自訂元件。
Tool part 會依序經歷下列狀態:
- `input-streaming`:正在串流 Tool 輸入(已啟用 Tool 呼叫串流時)
- `input-available`:已使用完整輸入呼叫 Tool,正在等待執行
- `output-available`:Tool 已執行完成並產生輸出
- `output-error`:Tool 執行失敗
下列範例會將氣象 Tool 的輸出轉譯為自訂 `WeatherCard` 元件。
**後端**:
使用 `outputSchema` 定義 Tool,讓前端知道要轉譯的資料結構。
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const weatherTool = createTool({
id: 'get-weather',
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('The location to get the weather for'),
}),
outputSchema: z.object({
temperature: z.number(),
feelsLike: z.number(),
humidity: z.number(),
windSpeed: z.number(),
conditions: z.string(),
location: z.string(),
}),
execute: async inputData => {
const response = await fetch(
`https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_API_KEY}&q=${inputData.location}`,
)
const data = await response.json()
return {
temperature: data.current.temp_c,
feelsLike: data.current.feelslike_c,
humidity: data.current.humidity,
windSpeed: data.current.wind_kph,
conditions: data.current.condition.text,
location: data.location.name,
}
},
})
```
**前端**:
檢查訊息中的 `tool-{toolKey}` part,並根據 Tool 的狀態與輸出轉譯自訂元件。
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import { WeatherCard } from './weather-card'
import { Loader } from './loader'
export function Chat() {
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat/weatherAgent',
}),
})
return (
{messages.map(message => (
{message.parts.map((part, index) => {
// Handle user text messages
if (part.type === 'text' && message.role === 'user') {
return
{part.text}
}
// Handle weather tool output
if (part.type === 'tool-weatherTool') {
switch (part.state) {
case 'input-available':
return
case 'output-available':
return
case 'output-error':
return
Error: {part.errorText}
default:
return null
}
}
return null
})}
))}
)
}
```
> **提示:** Tool part 型別遵循 `tool-{toolKey}` 模式,其中 `toolKey` 是向 Agent 註冊 Tool 時使用的 key。例如,如果將 Tool 註冊為 `tools: { weatherTool }`,part 型別會是 `tool-weatherTool`。
### 轉譯 Workflow 資料
使用 `workflowRoute()` 或 `handleWorkflowStream()` 時,Mastra 會針對 Workflow 狀態快照發出 `data-workflow` part,並針對已變更 step 的完整 payload 發出 `data-workflow-step` part。如此可避免長時間執行的 Workflow 在每個中間快照中重複所有已完成 step 的輸出。
**後端**:
定義包含多個 step 的 Workflow,讓它在執行時發出 `data-workflow` 與 `data-workflow-step` part。
```typescript
import { createStep, createWorkflow } from '@mastra/core/workflows'
import { z } from 'zod'
const fetchWeather = createStep({
id: 'fetch-weather',
inputSchema: z.object({
location: z.string(),
}),
outputSchema: z.object({
temperature: z.number(),
conditions: z.string(),
}),
execute: async ({ inputData }) => {
// Fetch weather data...
return { temperature: 22, conditions: 'Sunny' }
},
})
const planActivities = createStep({
id: 'plan-activities',
inputSchema: z.object({
temperature: z.number(),
conditions: z.string(),
}),
outputSchema: z.object({
activities: z.string(),
}),
execute: async ({ inputData, mastra }) => {
const agent = mastra?.getAgent('activityAgent')
const response = await agent?.generate(
`Suggest activities for ${inputData.conditions} weather at ${inputData.temperature}°C`,
)
return { activities: response?.text || '' }
},
})
export const activitiesWorkflow = createWorkflow({
id: 'activities-workflow',
inputSchema: z.object({
location: z.string(),
}),
outputSchema: z.object({
activities: z.string(),
}),
})
.then(fetchWeather)
.then(planActivities)
activitiesWorkflow.commit()
```
向 Mastra 註冊 Workflow,並透過 `workflowRoute()` 公開,以便將 Workflow 事件串流至前端。
```typescript
import { Mastra } from '@mastra/core'
import { workflowRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
workflows: { activitiesWorkflow },
server: {
apiRoutes: [
workflowRoute({
path: '/workflow/activitiesWorkflow',
workflow: 'activitiesWorkflow',
}),
],
},
})
```
**前端**:
檢查 `data-workflow` part 以轉譯 Workflow 狀態快照。若需要剛變更之 step 的完整 payload,也請讀取 `data-workflow-step` part。
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import type { WorkflowDataPart, WorkflowStepDataPart } from '@mastra/ai-sdk'
type WorkflowData = WorkflowDataPart['data']
type WorkflowStepData = WorkflowStepDataPart['data']
type StepStatus = 'running' | 'success' | 'failed' | 'suspended' | 'waiting'
function StepIndicator({
name,
status,
output,
}: {
name: string
status: StepStatus
output: unknown
}) {
return (
{name}
{status}
{status === 'success' && output &&
{JSON.stringify(output, null, 2)}}
)
}
export function WorkflowChat() {
const { messages, sendMessage, status } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/workflow/activitiesWorkflow',
prepareSendMessagesRequest: ({ messages }) => ({
body: {
inputData: {
location: messages[messages.length - 1]?.parts[0]?.text,
},
},
}),
}),
})
return (
{messages.map(message => (
{message.parts.map((part, index) => {
if (part.type === 'data-workflow') {
const workflowData = part.data as WorkflowData
const steps = Object.values(workflowData.steps)
return (
Workflow: {workflowData.name}
Status: {workflowData.status}
{steps.map(step => (
))}
)
}
if (part.type === 'data-workflow-step') {
const stepData = part.data as WorkflowStepData
return (
)
}
return null
})}
))}
)
}
```
如需 Workflow 串流的詳細資訊,請參閱 [Workflow 串流](https://mastra.zisheng.pro/zh-TW/docs/workflows/overview)。
### 轉譯 network 資料
使用 `networkRoute()` 或 `handleNetworkStream()` 時,Mastra 會發出包含 Agent network 執行狀態的 `data-network` part,其中包括呼叫了哪些 Agent 及其輸出。
**後端**:
向 Mastra 註冊 Agent,並透過 `networkRoute()` 公開路由 Agent,以便將 network 執行事件串流至前端。
```typescript
import { Mastra } from '@mastra/core'
import { networkRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
agents: { routingAgent, researchAgent, weatherAgent },
server: {
apiRoutes: [
networkRoute({
path: '/network',
agent: 'routingAgent',
}),
],
},
})
```
**前端**:
檢查 `data-network` part,並使用 `NetworkDataPart` 型別轉譯每個 Agent 的執行 step,以確保型別安全。
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import type { NetworkDataPart } from '@mastra/ai-sdk'
type NetworkData = NetworkDataPart['data']
function AgentStep({ step }: { step: NetworkData['steps'][number] }) {
return (
{step.name}
{step.status}
{step.input && (
Input:
{JSON.stringify(step.input, null, 2)}
)}
{step.output && (
Output:
{typeof step.output === 'string' ? step.output : JSON.stringify(step.output, null, 2)}
)}
)
}
export function NetworkChat() {
const { messages, sendMessage, status } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/network',
}),
})
return (
{messages.map(message => (
{message.parts.map((part, index) => {
if (part.type === 'data-network') {
const networkData = part.data as NetworkData
return (
Agent Network: {networkData.name}
{networkData.status}
{networkData.steps.map((step, stepIndex) => (
))}
)
}
return null
})}
))}
)
}
```
如需 Agent network 的詳細資訊,請參閱 [Agent Network](https://mastra.zisheng.pro/zh-TW/docs/agents/networks)。
### 自訂事件
在 Tool 的 `execute()` 函式中使用 `writer.custom()` 發出自訂資料 part。這適合用於進度指示器、狀態更新,或 Tool 執行期間的任何自訂 UI 更新。
自訂事件型別必須以 `data-` 開頭,才能被辨識為資料 part。
> **警告:** 你必須對 `writer.custom()` 呼叫使用 `await`,否則可能遇到 `WritableStream is locked` 錯誤。
**後端**:
在 Tool 的 `execute()` 函式中使用 `writer.custom()`,於不同執行階段發出以 `data-` 為前綴的自訂事件。
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const taskTool = createTool({
id: 'process-task',
description: 'Process a task with progress updates',
inputSchema: z.object({
task: z.string().describe('The task to process'),
}),
outputSchema: z.object({
result: z.string(),
status: z.string(),
}),
execute: async (inputData, context) => {
const { task } = inputData
// Emit "in progress" custom event
await context?.writer?.custom({
type: 'data-tool-progress',
data: {
status: 'in-progress',
message: 'Gathering information...',
},
})
// Simulate work
await new Promise(resolve => setTimeout(resolve, 3000))
// Emit "done" custom event
await context?.writer?.custom({
type: 'data-tool-progress',
data: {
status: 'done',
message: `Successfully processed "${task}"`,
},
})
return {
result: `Task "${task}" has been completed successfully!`,
status: 'completed',
}
},
})
```
**前端**:
篩選符合自訂事件型別的訊息 part,並轉譯會隨新事件抵達而更新的進度指示器。
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import { useMemo } from 'react'
type ProgressData = {
status: 'in-progress' | 'done'
message: string
}
function ProgressIndicator({ progress }: { progress: ProgressData }) {
return (
{progress.status === 'in-progress' ? (
) : (
)}
{progress.message}
)
}
export function TaskChat() {
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat/taskAgent',
}),
})
// Extract the latest progress event from messages
const latestProgress = useMemo(() => {
const allProgressParts: ProgressData[] = []
messages.forEach(message => {
message.parts.forEach(part => {
if (part.type === 'data-tool-progress') {
allProgressParts.push(part.data as ProgressData)
}
})
})
return allProgressParts[allProgressParts.length - 1]
}, [messages])
return (
{latestProgress &&
}
{messages.map(message => (
{message.parts.map((part, index) => {
if (part.type === 'text') {
return
{part.text}
}
return null
})}
))}
)
}
```
### Tool 串流
Tool 也能使用 `context.writer.write()` 串流資料,以提供較低階的控制,或直接將 Agent 串流 pipe 至 Tool 的 writer。詳情請參閱 [Tool 串流](https://mastra.zisheng.pro/zh-TW/docs/agents/using-tools)。
### 範例
如需自訂 UI 模式的即時範例,請前往 [Mastra 的 UI Dojo](https://ui-dojo.mastra.ai/)。該儲存庫包含以下實作:
- [生成式 UI](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/generative-user-interfaces.tsx):用於 Tool 輸出的自訂元件
- [Workflow](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/workflow.tsx):Workflow step 視覺化
- [Agent Network](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/network.tsx):network 執行畫面
- [自訂事件](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/generative-user-interfaces-with-custom-events.tsx):搭配自訂事件的進度指示器
## 操作方式
### 串流轉換
若要手動將 Mastra 串流轉換為 AI SDK 相容格式,請使用 [`toAISdkStream()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/to-ai-sdk-stream) 工具函式。如需具體使用模式,請參閱[範例](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/to-ai-sdk-stream)。
`toAISdkStream()` 會保留現有的 AI SDK v5/預設行為。如果應用程式使用 AI SDK v6 型別,請傳入 `version: 'v6'`。
```typescript
import { toAISdkStream } from '@mastra/ai-sdk'
const v5Stream = toAISdkStream(mastraStream, { from: 'agent' })
const v6Stream = toAISdkStream(mastraStream, { from: 'agent', version: 'v6' })
```
### 載入歷史訊息
從 Mastra memory 載入訊息並顯示於聊天 UI 時,請使用 [`toAISdkV5Messages()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/to-ai-sdk-v5-messages) 或 [`toAISdkV4Messages()`](https://mastra.zisheng.pro/zh-TW/reference/ai-sdk/to-ai-sdk-v4-messages),將訊息轉換為適合 `useChat()` 之 `initialMessages` 的 AI SDK 格式。
### 傳遞額外資料
[`sendMessage()`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat#send-message) 可讓你從前端將額外資料傳遞至 Mastra。接著可在伺服器上將此資料作為 [`RequestContext`](https://mastra.zisheng.pro/zh-TW/docs/server/request-context) 使用。
以下是前端程式碼範例:
```typescript
import { useChat } from '@ai-sdk/react'
import { useState } from 'react'
import { DefaultChatTransport } from 'ai'
export function ChatAdditional() {
const [inputValue, setInputValue] = useState('')
const { messages, sendMessage } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat-extra',
}),
})
const handleFormSubmit = (e: React.FormEvent) => {
e.preventDefault()
sendMessage(
{ text: inputValue },
{
body: {
data: {
userId: 'user123',
preferences: {
language: 'en',
temperature: 'celsius',
},
},
},
},
)
}
return (
{JSON.stringify(messages, null, 2)}
)
}
```
使用下列任一範例實作後端。
**Mastra 伺服器**:
如上方所示,將 `chatRoute()` 加入 Mastra 組態。接著加入伺服器層級的 middleware:
```typescript
import { Mastra } from '@mastra/core'
export const mastra = new Mastra({
server: {
middleware: [
async (c, next) => {
const requestContext = c.get('requestContext')
if (c.req.method === 'POST') {
const clonedReq = c.req.raw.clone()
const body = await clonedReq.json()
if (body?.data) {
for (const [key, value] of Object.entries(body.data)) {
requestContext.set(key, value)
}
}
}
await next()
},
],
},
})
```
> **資訊:** 你可以在 Tool 中透過 `requestContext` 參數存取此資料。詳情請參閱 [Request Context 文件](https://mastra.zisheng.pro/zh-TW/docs/server/request-context)。
**Next.js**:
```typescript
import { handleChatStream } from '@mastra/ai-sdk'
import { RequestContext } from '@mastra/core/request-context'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/src/mastra'
export async function POST(req: Request) {
const { messages, data } = await req.json()
const requestContext = new RequestContext()
if (data) {
for (const [key, value] of Object.entries(data)) {
requestContext.set(key, value)
}
}
const stream = await handleChatStream({
mastra,
agentId: 'weatherAgent',
params: {
messages,
requestContext,
},
})
return createUIMessageStreamResponse({ stream })
}
```
### 由使用者核准 Workflow 暫停/繼續
Workflow 可以暫停執行並等待使用者輸入,再繼續進行。這適合用於核准流程、確認,或任何 human-in-the-loop 情境。
此 Workflow 會使用:
- `suspendSchema` / `resumeSchema`:定義暫停 payload 與繼續輸入的資料結構
- `suspend()`:暫停 Workflow 並將暫停 payload 傳送至 UI
- `resumeData`:包含 Workflow 繼續執行時的使用者回應
- `bail()`:提早結束 Workflow(例如使用者拒絕時)
**後端**:
建立會暫停以等待核准的 Workflow step。該 step 會檢查 `resumeData` 以判斷是否正在繼續執行,並於第一次執行時呼叫 `suspend()`。
```typescript
import { createStep, createWorkflow } from '@mastra/core/workflows'
import { z } from 'zod'
const requestApproval = createStep({
id: 'request-approval',
inputSchema: z.object({ requestId: z.string(), summary: z.string() }),
outputSchema: z.object({
approved: z.boolean(),
requestId: z.string(),
approvedBy: z.string().optional(),
}),
resumeSchema: z.object({
approved: z.boolean(),
approverName: z.string().optional(),
}),
suspendSchema: z.object({
message: z.string(),
requestId: z.string(),
}),
execute: async ({ inputData, resumeData, suspend, bail }) => {
// User rejected - bail out
if (resumeData?.approved === false) {
return bail({ message: 'Request rejected' })
}
// User approved - continue
if (resumeData?.approved) {
return {
approved: true,
requestId: inputData.requestId,
approvedBy: resumeData.approverName || 'User',
}
}
// First execution - suspend and wait
return await suspend({
message: `Please approve: ${inputData.summary}`,
requestId: inputData.requestId,
})
},
})
export const approvalWorkflow = createWorkflow({
id: 'approval-workflow',
inputSchema: z.object({ requestId: z.string(), summary: z.string() }),
outputSchema: z.object({
approved: z.boolean(),
requestId: z.string(),
approvedBy: z.string().optional(),
}),
}).then(requestApproval)
approvalWorkflow.commit()
```
註冊 Workflow。暫停/繼續需要儲存空間才能持久保存狀態。
```typescript
import { Mastra } from '@mastra/core'
import { workflowRoute } from '@mastra/ai-sdk'
import { LibSQLStore } from '@mastra/libsql'
export const mastra = new Mastra({
workflows: { approvalWorkflow },
storage: new LibSQLStore({
id: 'mastra-storage',
url: 'file:../mastra.db',
}),
server: {
apiRoutes: [
workflowRoute({ path: '/workflow/approvalWorkflow', workflow: 'approvalWorkflow' }),
],
},
})
```
**前端**:
偵測 Workflow 何時暫停,並傳送包含 `runId`、`step` 及 `resumeData` 的繼續資料。
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import { useMemo, useState } from 'react'
import type { WorkflowDataPart } from '@mastra/ai-sdk'
type WorkflowData = WorkflowDataPart['data']
export function ApprovalWorkflow() {
const [requestId, setRequestId] = useState('')
const [summary, setSummary] = useState('')
const { messages, sendMessage, setMessages, status } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/workflow/approvalWorkflow',
prepareSendMessagesRequest: ({ messages }) => {
const lastMessage = messages[messages.length - 1]
const text = lastMessage.parts.find(p => p.type === 'text')?.text
const metadata = lastMessage.metadata as Record
// Resuming: send runId, step, and resumeData
if (text === 'Approve' || text === 'Reject') {
return {
body: {
runId: metadata.runId,
step: 'request-approval',
resumeData: { approved: text === 'Approve' },
},
}
}
// Starting: send inputData
return {
body: { inputData: { requestId: metadata.requestId, summary: metadata.summary } },
}
},
}),
})
// Find suspended workflow
const suspended = useMemo(() => {
for (const m of messages) {
for (const p of m.parts) {
if (p.type === 'data-workflow' && (p.data as WorkflowData).status === 'suspended') {
return { data: p.data as WorkflowData, runId: p.id }
}
}
}
return null
}, [messages])
const handleApprove = () => {
setMessages([])
sendMessage({ text: 'Approve', metadata: { runId: suspended?.runId } })
}
const handleReject = () => {
setMessages([])
sendMessage({ text: 'Reject', metadata: { runId: suspended?.runId } })
}
return (
{!suspended ? (
) : (
{
(suspended.data.steps['request-approval']?.suspendPayload as { message: string })
?.message
}
)}
)
}
```
重點:
- 可透過 `step.suspendPayload` 存取暫停 payload
- 若要繼續,請在 request body 中傳送 `runId`、`step`(step ID)及 `resumeData`
- 必須設定儲存空間,暫停/繼續才能持久保存 Workflow 狀態
如需完整實作,請參閱 UI Dojo 中的 [workflow-suspend-resume 範例](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/workflow-suspend-resume.tsx)。
### Tool 中的巢狀 Agent 串流
Tool 可以在內部呼叫 Agent,並將 Agent 輸出串流回 UI。當巢狀 step 仍在執行時,這會建立精簡的 `data-tool-agent` 快照;巢狀 step 完成時,會建立 `data-tool-agent-step` part;巢狀執行完成時,則會建立一份完整的 `data-tool-agent` 快照。
此模式會使用:
- `context.mastra.getAgent()`:從 Tool 內取得 Agent 執行個體
- `agent.stream()`:串流 Agent 回應
- `stream.fullStream.pipeTo(context.writer)`:將 Agent 串流 pipe 至 Tool 的 writer
**後端**:
建立會呼叫 Agent,並將其串流 pipe 至 Tool writer 的 Tool。
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const nestedAgentTool = createTool({
id: 'nested-agent-stream',
description: 'Analyze weather using a nested agent',
inputSchema: z.object({
city: z.string().describe('The city to analyze'),
}),
outputSchema: z.object({
summary: z.string(),
}),
execute: async (inputData, context) => {
const agent = context?.mastra?.getAgent('weatherAgent')
if (!agent) {
return { summary: 'Weather agent not available' }
}
const stream = await agent.stream(
`Analyze the weather in ${inputData.city} and provide a summary.`,
)
// Pipe the agent's stream to emit data-tool-agent parts
await stream.fullStream.pipeTo(context!.writer!)
return { summary: (await stream.text) ?? 'No summary available' }
},
})
```
建立使用此 Tool 的 Agent。
```typescript
import { Agent } from '@mastra/core/agent'
import { nestedAgentTool } from '../tools/nested-agent-tool'
export const forecastAgent = new Agent({
id: 'forecast-agent',
instructions: 'Use the nested-agent-stream tool when asked about weather.',
model: 'openai/gpt-5.6-sol',
tools: { nestedAgentTool },
})
```
**前端**:
使用 `data-tool-agent` part 處理即時快照,並使用 `data-tool-agent-step` part 處理已完成巢狀 step 的 payload。
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import { useState } from 'react'
import type { AgentDataPart, AgentStepDataPart } from '@mastra/ai-sdk'
export function NestedAgentChat() {
const [input, setInput] = useState('')
const { messages, sendMessage, status } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat/forecastAgent',
}),
})
return (
{messages.map(message => (
{message.parts.map((part, index) => {
if (part.type === 'text') {
return
{part.text}
}
if (part.type === 'data-tool-agent') {
const { id, data } = part as AgentDataPart
return (
Nested Agent: {id}
{data.text &&
{data.text}
}
)
}
if (part.type === 'data-tool-agent-step') {
const { data } = part as AgentStepDataPart
return (
Completed nested step {data.stepIndex + 1}
{data.step.text &&
{data.step.text}
}
)
}
return null
})}
))}
)
}
```
重點:
- 將 `fullStream` pipe 至 `context.writer` 會建立 `data-tool-agent` part
- 需要剛完成之巢狀 step 的完整 payload 時,請讀取 `data-tool-agent-step`
- `AgentDataPart` 包含 `id`(位於 part 上)及 `data.text`(目前巢狀 Agent 的文字快照)
- 串流完成後,Tool 仍會傳回自己的輸出
如需完整實作,請參閱 UI Dojo 中的 [tool-nested-streams 範例](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/tool-nested-streams.tsx)。
### 從 Workflow step 串流 Agent 文字
Workflow step 可以將 Agent 串流 pipe 至 step 的 `writer`,即時串流 Agent 的文字輸出。如此一來,使用者就能在 Workflow 執行期間看到 Agent 的「思考」內容,而不必等待 step 完成。
此模式會使用:
- Workflow step 中的 `writer`:將 Agent 的 `fullStream` pipe 至 step 的 writer
- `text` 與 `data-workflow` part:前端在接收 step 進度的同時,也會接收串流文字
**後端**:
建立 Workflow step,透過 pipe 至 step 的 `writer` 來串流 Agent 回應。
```typescript
import { createStep, createWorkflow } from '@mastra/core/workflows'
import { z } from 'zod'
import { weatherAgent } from '../agents/weather-agent'
const analyzeWeather = createStep({
id: 'analyze-weather',
inputSchema: z.object({ location: z.string() }),
outputSchema: z.object({ analysis: z.string(), location: z.string() }),
execute: async ({ inputData, writer }) => {
const response = await weatherAgent.stream(
`Analyze the weather in ${inputData.location} and provide insights.`,
)
// Pipe agent stream to step writer for real-time text streaming
await response.fullStream.pipeTo(writer)
return {
analysis: await response.text,
location: inputData.location,
}
},
})
const calculateScore = createStep({
id: 'calculate-score',
inputSchema: z.object({ analysis: z.string(), location: z.string() }),
outputSchema: z.object({ score: z.number(), summary: z.string() }),
execute: async ({ inputData }) => {
const score = inputData.analysis.includes('sunny') ? 85 : 50
return { score, summary: `Comfort score for ${inputData.location}: ${score}/100` }
},
})
export const weatherWorkflow = createWorkflow({
id: 'weather-workflow',
inputSchema: z.object({ location: z.string() }),
outputSchema: z.object({ score: z.number(), summary: z.string() }),
})
.then(analyzeWeather)
.then(calculateScore)
weatherWorkflow.commit()
```
使用 `workflowRoute()` 註冊 Workflow。文字串流預設為啟用。
```typescript
import { Mastra } from '@mastra/core'
import { workflowRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
agents: { weatherAgent },
workflows: { weatherWorkflow },
server: {
apiRoutes: [workflowRoute({ path: '/workflow/weather', workflow: 'weatherWorkflow' })],
},
})
```
**前端**:
同時轉譯 `text` part(串流 Agent 輸出)與 `data-workflow` part(step 進度)。
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import { useState } from 'react'
import type { WorkflowDataPart } from '@mastra/ai-sdk'
type WorkflowData = WorkflowDataPart['data']
export function WeatherWorkflow() {
const [location, setLocation] = useState('')
const { messages, sendMessage, status } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/workflow/weather',
prepareSendMessagesRequest: ({ messages }) => ({
body: {
inputData: {
location: messages[messages.length - 1].parts.find(p => p.type === 'text')?.text,
},
},
}),
}),
})
return (
{messages.map(message => (
{message.parts.map((part, index) => {
// Streaming agent text
if (part.type === 'text' && message.role === 'assistant') {
return (
{status === 'streaming' && (
Agent analyzing...
)}
{part.text}
)
}
// Workflow step progress
if (part.type === 'data-workflow') {
const workflow = part.data as WorkflowData
return (
{Object.entries(workflow.steps).map(([stepId, step]) => (
{stepId}: {step.status}
))}
)
}
return null
})}
))}
)
}
```
重點:
- step 的 `writer` 可在 `execute` 函式中使用(不是透過 `context`)
- `workflowRoute()` 上的 `includeTextStreamParts` 預設為 `true`,因此文字預設會進行串流
- 文字 part 會即時串流,而 `data-workflow` part 則會隨 step 狀態更新
如需完整實作,請參閱 UI Dojo 中的 [workflow-agent-text-stream 範例](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/workflow-agent-text-stream.tsx)。
### 分支 Workflow 的多階段進度
對於具有條件式分支的 Workflow(例如快遞與標準配送),可以在自訂事件中加入識別碼,以追蹤不同分支的進度。
UI Dojo 範例會在事件資料中使用 `stage` 欄位,識別正在執行的分支(例如 `"validation"`、`"standard-processing"`、`"express-processing"`)。前端會依此欄位將事件分組,顯示管線式進度 UI。
請參閱 UI Dojo 中的 [branching-workflow.ts](https://github.com/mastra-ai/ui-dojo/blob/main/src/mastra/workflows/branching-workflow.ts)(後端)與 [workflow-custom-events.tsx](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/workflow-custom-events.tsx)(前端)。
### Agent network 中的進度指示器
使用 Agent network 時,可以從子 Agent 所使用的 Tool 發出自訂進度事件,以顯示目前使用中的 Agent。
UI Dojo 範例在事件資料中包含 `stage` 欄位,用來識別正在執行的子 Agent(例如 `"report-generation"`、`"report-review"`)。前端會依此欄位將事件分組,並顯示各組的最新狀態。
請參閱 UI Dojo 中的 [report-generation-tool.ts](https://github.com/mastra-ai/ui-dojo/blob/main/src/mastra/tools/report-generation-tool.ts)(後端)與 [agent-network-custom-events.tsx](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/agent-network-custom-events.tsx)(前端)。