> Discover all available pages from the documentation index: https://mastra.zisheng.pro/ja/llms.txt
# AI SDK UI を使用する
[AI SDK UI](https://sdk.vercel.ai) は、AI を活用したインターフェースを構築するための React ユーティリティとコンポーネントのライブラリです。このガイドでは、`@mastra/ai-sdk` を使用して Mastra の出力を AI SDK 互換形式に変換し、フロントエンドで AI SDK のフックとコンポーネントを使用する方法を説明します。
> **注記:** AI SDK v4 から v5 に移行する場合は、[移行ガイド](https://mastra.zisheng.pro/ja/guides/migrations/ai-sdk-v4-to-v5)を参照してください。
> **ヒント:** さらに例を確認するには、Mastra の [**UI Dojo**](https://ui-dojo.mastra.ai/) または [Next.js クイックスタートガイド](https://mastra.zisheng.pro/ja/guides/getting-started/next-js)を参照してください。
## はじめに
Mastra と AI SDK UI を併用するには、`@mastra/ai-sdk` パッケージをインストールします。`@mastra/ai-sdk` は、Mastra Agent を AI SDK 互換形式でストリーミングするためのカスタム API Route とユーティリティを提供します。これには、チャット、Workflow、Network の Route Handler、UI 統合用のユーティリティとエクスポートされた型が含まれます。
`@mastra/ai-sdk` は、AI SDK UI の 3 つの主要なフック、[`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
```
これで、以下の統合ガイドとレシピを実行できます。
## 統合ガイド
通常は、Mastra のコンテンツを AI SDK 互換形式でストリーミングする API Route を設定し、その Route を `useChat()` などの AI SDK UI フックで使用します。次のいずれかの方法を選択してください。
- [Mastra サーバー](#mastras-server)
- [フレームワーク非依存](#framework-agnostic)
API Route の設定後、[`useChat()`](#usechat) フックで使用できます。
### Mastra サーバー
Mastra をスタンドアロンサーバーとして実行し、フロントエンド(Vite + React など)を API エンドポイントに接続します。ここでは Mastra の[カスタム API Route](https://mastra.zisheng.pro/ja/docs/server/custom-api-routes) 機能を使用します。
> **情報:** Mastra の [**UI Dojo**](https://ui-dojo.mastra.ai/) は、この構成の例です。
[`chatRoute()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/chat-route)、[`workflowRoute()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/workflow-route)、[`networkRoute()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/network-route) を使用して、Mastra のコンテンツを AI SDK 互換形式でストリーミングする API Route を作成できます。実装後、これらの API Route を [`useChat()`](#usechat) で使用できます。
**chatRoute()**:
この例では、ID が `weatherAgent` の Agent を使用するチャットルートを `/chat` エンドポイントに設定します。
```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/ja/reference/ai-sdk/chat-route)を参照してください。
**workflowRoute()**:
この例では、ID が `weatherWorkflow` の Workflow を使用する Workflow Route を `/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/ja/reference/ai-sdk/workflow-route)を参照してください。
> **Workflow 内の Agent ストリーミング:** Workflow Step が Agent のストリームを Workflow Writer にパイプすると(`await response.fullStream.pipeTo(writer)` など)、Agent が Workflow Step 内で実行されている場合でも、Agent のテキストチャンクと Tool 呼び出しが UI ストリームへリアルタイムで転送されます。
>
> 詳しくは [Workflow ストリーミング](https://mastra.zisheng.pro/ja/docs/workflows/overview)を参照してください。
**networkRoute()**:
この例では、ID が `weatherAgent` の Agent を使用する Network Route を `/network` エンドポイントに設定します。
```typescript
import { Mastra } from '@mastra/core'
import { networkRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
networkRoute({
path: '/network',
agent: 'weatherAgent',
}),
],
},
})
```
動的な Network ルーティングも使用できます。詳しくは [`networkRoute()` リファレンス](https://mastra.zisheng.pro/ja/reference/ai-sdk/network-route)を参照してください。
### フレームワーク非依存
Mastra サーバーを実行せず、Next.js や Express などのフレームワークを使用する場合は、独自の API Route Handler で [`handleChatStream()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/handle-chat-stream)、[`handleWorkflowStream()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/handle-workflow-stream)、[`handleNetworkStream()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/handle-network-stream) 関数を使用できます。
これらは [`createUIMessageStreamResponse()`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream-response) でラップできる `ReadableStream` を返します。
> **AI SDK v6 との互換性:** フレームワーク非依存の Handler は、既存の AI SDK v5 またはデフォルトの動作を維持します。アプリが AI SDK v6 に対して型付けされている場合は、`version: 'v6'` を渡します。`handleChatStream()` と `handleNetworkStream()` で最適な TypeScript 型推論を得るには、インストール済みの `ai` バージョンの `UIMessage[]` として `messages` を渡してください。
以下の例では、Next.js App Router での使用方法を示します。
**handleChatStream()**:
この例では、ID が `weatherAgent` の Agent を使用するチャットルートを `/chat` エンドポイントに設定します。
```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()**:
この例では、ID が `weatherWorkflow` の Workflow を使用する Workflow Route を `/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()**:
この例では、ID が `routingAgent` の Agent を使用する Network Route を `/network` エンドポイントに設定します。
```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 Route を作成した場合も、[任意のフレームワーク](#framework-agnostic)を使用した場合も、その API エンドポイントを `useChat()` フックで使用できます。
天気 Agent を使用する Route を `/chat` に設定した場合、次のように質問できます。正しい `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)}
)
}
```
Agent に追加設定を渡すなど、チャットルートへ送信するリクエストをカスタマイズするには、[`prepareSendMessagesRequest`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat#transport.default-chat-transport.prepare-send-messages-request) を使用します。
## Mastra Memory を使用する
Agent に [Memory](https://mastra.zisheng.pro/ja/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 Param、Auth Context、データベースなど、アプリ独自の状態から値を設定します。
Mastra Memory がメッセージを読み込み、保存する仕組みについては、[メッセージ履歴](https://mastra.zisheng.pro/ja/docs/memory/message-history)を参照してください。
[`chatRoute()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/chat-route) と [`handleChatStream()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/handle-chat-stream) は、すでに Memory に対応しています。新しいメッセージだけを送信し、Thread と Resource の識別子を含めるようにクライアントを設定してください。
### `useCompletion()`
`useCompletion()` フックは、フロントエンドと Mastra Agent 間の単一 Turn の Completion を処理し、プロンプトを送信して HTTP 経由でストリーミングレスポンスを受信できるようにします。
フロントエンドは次のように実装できます。
```typescript
import { useCompletion } from '@ai-sdk/react'
export default function Page() {
const { completion, input, handleInputChange, handleSubmit } = useCompletion({
api: '/api/completion',
})
return (
)
}
```
バックエンドの実装を選択します。
**Mastra Server**:
```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(Generative UI とも呼ばれます)では、Mastra からストリーミングされるデータに基づいて、カスタム React コンポーネントをレンダリングできます。生のテキストや JSON を表示する代わりに、Tool の出力や Workflow の進行状況(Agent Network の実行やカスタムイベントを含む)を視覚化するコンポーネントを作成できます。
次のような場合にカスタム UI を使用します。
- Tool の出力を視覚的なコンポーネントとしてレンダリングする(JSON の代わりに天気カードを表示するなど)
- Workflow Step の進行状況をステータスインジケーターで表示する
- Agent Network の実行を Step ごとの更新で視覚化する
- 長時間実行される処理中に、進行状況やステータスの更新を表示する
### Data Part のタイプ
Mastra は、メッセージ内の「Part」としてデータをフロントエンドにストリーミングします。各 Part には、レンダリング方法を決める `type` があります。`@mastra/ai-sdk` パッケージは Mastra ストリームを AI SDK 互換の [UI Message DataParts](https://ai-sdk.dev/docs/reference/ai-sdk-core/ui-message#datauipart) に変換します。
| Data Part のタイプ | ソース | 説明 |
| ---------------------- | ---------------------- | -------------------------------------------------------------------- |
| `tool-{toolKey}` | AI SDK 組み込み | `input-available`、`output-available`、`output-error` の状態を持つ Tool 呼び出し |
| `data-workflow` | `workflowRoute()` | Step のステータスと最終出力を含む Workflow 実行状態の Snapshot |
| `data-workflow-step` | `workflowRoute()` | 変更された Step の完全な Payload を持つ Workflow Step の差分 |
| `data-network` | `networkRoute()` | 順序付けられた Step と出力を含む Agent Network の実行 |
| `data-tool-agent` | Tool 内のネストされた Agent | 現在の Step の実行中に送出される、ネストされた Agent のコンパクトな Snapshot |
| `data-tool-agent-step` | Tool 内のネストされた Agent | ネストされた Step の完了時に送出される、その 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` コンポーネントとしてレンダリングする例を示します。
**Backend**:
```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,
}
},
})
```
**Frontend**:
メッセージ内の `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 を登録するときに使用したキーです。たとえば `tools: { weatherTool }` として登録すると、Part のタイプは `tool-weatherTool` になります。
### Workflow データをレンダリングする
`workflowRoute()` または `handleWorkflowStream()` を使用すると、Mastra は Workflow 状態の Snapshot を `data-workflow` Part として、変更された Step の完全な Payload を `data-workflow-step` Part として送出します。これにより、長時間実行される Workflow で、完了済みの全 Step の出力が中間 Snapshot ごとに繰り返されることを防ぎます。
**Backend**:
実行中に `data-workflow` Part と `data-workflow-step` Part を送出する、複数 Step の Workflow を定義します。
```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()
```
Workflow を Mastra に登録し、`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',
}),
],
},
})
```
**Frontend**:
`data-workflow` Part を確認し、Workflow のステータス Snapshot をレンダリングします。直前に変更された 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/ja/docs/workflows/overview)を参照してください。
### Network データをレンダリングする
`networkRoute()` または `handleNetworkStream()` を使用すると、Mastra は呼び出された Agent とその出力を含む、Agent Network の実行状態を保持した `data-network` Part を送出します。
**Backend**:
Agent を Mastra に登録し、`networkRoute()` で Routing 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',
}),
],
},
})
```
**Frontend**:
`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/ja/docs/agents/networks) を参照してください。
### カスタムイベント
Tool の `execute()` 関数内で `writer.custom()` を使用して、カスタム Data Part を送出します。Tool の実行中に進行状況、ステータス更新、その他のカスタム UI 更新を表示する場合に便利です。
Data Part として認識されるには、カスタムイベントのタイプを `data-` で始める必要があります。
> **警告:** `writer.custom()` の呼び出しは必ず `await` してください。そうしないと `WritableStream is locked` エラーが発生する可能性があります。
**Backend**:
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',
}
},
})
```
**Frontend**:
メッセージの 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 のストリームを Tool の Writer に直接パイプしたりできます。詳しくは [Tool ストリーミング](https://mastra.zisheng.pro/ja/docs/agents/using-tools)を参照してください。
### 例
カスタム UI パターンの動作例は、[Mastra の UI Dojo](https://ui-dojo.mastra.ai/) で確認できます。リポジトリには次の実装が含まれます。
- [Generative 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/ja/reference/ai-sdk/to-ai-sdk-stream) ユーティリティを使用します。具体的な使用パターンは[例](https://mastra.zisheng.pro/ja/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' })
```
### 過去のメッセージを読み込む
チャット UI に表示するために Mastra Memory からメッセージを読み込む場合は、[`toAISdkV5Messages()`](https://mastra.zisheng.pro/ja/reference/ai-sdk/to-ai-sdk-v5-messages) または [`toAISdkV4Messages()`](https://mastra.zisheng.pro/ja/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/ja/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 Server**:
前述のように、Mastra 設定に `chatRoute()` を追加します。次に、サーバーレベルの 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/ja/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 を早期終了する(ユーザーが拒否した場合など)
**Backend**:
承認のために中断する 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' }),
],
},
})
```
**Frontend**:
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
}
)}
)
}
```
重要なポイントは次のとおりです。
- 中断 Payload には `step.suspendPayload` からアクセスできます
- 再開するには、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 を呼び出し、その出力を UI にストリーミングできます。ネストされた Step の実行中はコンパクトな `data-tool-agent` Snapshot、Step の完了時は `data-tool-agent-step` Part、ネストされた Run の完了時は完全な `data-tool-agent` Snapshot が作成されます。
このパターンでは次の要素を使用します。
- `context.mastra.getAgent()`:Tool 内から Agent インスタンスを取得する
- `agent.stream()`:Agent のレスポンスをストリーミングする
- `stream.fullStream.pipeTo(context.writer)`:Agent のストリームを Tool の Writer にパイプする
**Backend**:
Agent を呼び出し、そのストリームを 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 },
})
```
**Frontend**:
実行中の Snapshot には `data-tool-agent` Part を、完了したネスト Step の Payload には `data-tool-agent-step` Part を使用します。
```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` を `context.writer` にパイプすると、`data-tool-agent` Part が作成されます
- 直前に完了したネスト Step の完全な Payload が必要な場合は、`data-tool-agent-step` を読み取ります
- `AgentDataPart` には `id`(Part 上)と `data.text`(現在のネスト Agent テキストの Snapshot)があります
- ストリームの完了後も、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 のストリームを Step の `writer` にパイプして、Agent のテキスト出力をリアルタイムでストリーミングできます。これにより、Step の完了を待つのではなく、Workflow の実行中に Agent が「思考」する様子をユーザーに表示できます。
このパターンでは次の要素を使用します。
- Workflow Step の `writer`:Agent の `fullStream` を Step の Writer にパイプする
- `text` Part と `data-workflow` Part:フロントエンドが Step の進行状況とともにストリーミングテキストを受信する
**Backend**:
Agent のレスポンスを Step の `writer` にパイプしてストリーミングする Workflow Step を作成します。
```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' })],
},
})
```
**Frontend**:
`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` のため、テキストはデフォルトでストリーミングされます
- `data-workflow` Part で Step のステータスが更新される間、テキスト Part はリアルタイムでストリーミングされます
完全な実装については、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"` など)を識別します。フロントエンドはこのフィールドでイベントをグループ化し、Pipeline 形式の進行状況 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 を使用する場合、Subagent が使用する Tool からカスタムの進行状況イベントを送出して、現在アクティブな Agent を表示できます。
UI Dojo の例では、イベントデータの `stage` フィールドを使用して、実行中の Subagent(`"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)(フロントエンド)を参照してください。 フロントエンドがレンダリングするデータの構造を認識できるように、`outputSchema` を持つ Tool を定義します。