メインコンテンツへ移動

AI SDK UI を使用する

AI SDK UI は、AI を活用したインターフェースを構築するための React ユーティリティとコンポーネントのライブラリです。このガイドでは、@mastra/ai-sdk を使用して Mastra の出力を AI SDK 互換形式に変換し、フロントエンドで AI SDK のフックとコンポーネントを使用する方法を説明します。

注記

AI SDK v4 から v5 に移行する場合は、移行ガイドを参照してください。

ヒント

さらに例を確認するには、Mastra の UI Dojo または 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()useCompletion()useObject() と統合できます。

必要なパッケージをインストールします。

npm install @mastra/ai-sdk@latest @ai-sdk/react ai

これで、以下の統合ガイドとレシピを実行できます。

統合ガイド
統合ガイドへの直接リンク

通常は、Mastra のコンテンツを AI SDK 互換形式でストリーミングする API Route を設定し、その Route を useChat() などの AI SDK UI フックで使用します。次のいずれかの方法を選択してください。

API Route の設定後、useChat() フックで使用できます。

Mastra サーバー
Mastra サーバーへの直接リンク

Mastra をスタンドアロンサーバーとして実行し、フロントエンド(Vite + React など)を API エンドポイントに接続します。ここでは Mastra のカスタム API Route 機能を使用します。

情報

Mastra の UI Dojo は、この構成の例です。

chatRoute()workflowRoute()networkRoute() を使用して、Mastra のコンテンツを AI SDK 互換形式でストリーミングする API Route を作成できます。実装後、これらの API Route を useChat() で使用できます。

この例では、ID が weatherAgent の Agent を使用するチャットルートを /chat エンドポイントに設定します。

src/mastra/index.ts
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() リファレンスを参照してください。

フレームワーク非依存
フレームワーク非依存への直接リンク

Mastra サーバーを実行せず、Next.js や Express などのフレームワークを使用する場合は、独自の API Route Handler で handleChatStream()handleWorkflowStream()handleNetworkStream() 関数を使用できます。

これらは createUIMessageStreamResponse() でラップできる ReadableStream を返します。

AI SDK v6 との互換性

フレームワーク非依存の Handler は、既存の AI SDK v5 またはデフォルトの動作を維持します。アプリが AI SDK v6 に対して型付けされている場合は、version: 'v6' を渡します。handleChatStream()handleNetworkStream() で最適な TypeScript 型推論を得るには、インストール済みの ai バージョンの UIMessage[] として messages を渡してください。

以下の例では、Next.js App Router での使用方法を示します。

この例では、ID が weatherAgent の Agent を使用するチャットルートを /chat エンドポイントに設定します。

app/chat/route.ts
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 })
}

useChat()
usechatへの直接リンク

Mastra サーバーで API Route を作成した場合も、任意のフレームワークを使用した場合も、その API エンドポイントを useChat() フックで使用できます。

天気 Agent を使用する Route を /chat に設定した場合、次のように質問できます。正しい api URL を設定することが重要です。

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 (
<div>
<pre>{JSON.stringify(messages, null, 2)}</pre>
<form onSubmit={handleFormSubmit}>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="Name of the city"
/>
</form>
</div>
)
}

Agent に追加設定を渡すなど、チャットルートへ送信するリクエストをカスタマイズするには、prepareSendMessagesRequest を使用します。

Mastra Memory を使用する
Mastra Memory を使用するへの直接リンク

Agent に Memory が設定されている場合、Mastra はサーバー上のストレージから会話履歴を読み込みます。クライアントからは、会話履歴全体ではなく新しいメッセージだけを送信してください。

履歴全体の送信は冗長であり、クライアント側のタイムスタンプがデータベースに保存されたタイムスタンプと競合して、メッセージの順序に問題が生じることがあります。

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.threadmemory.resource には、URL Param、Auth Context、データベースなど、アプリ独自の状態から値を設定します。

Mastra Memory がメッセージを読み込み、保存する仕組みについては、メッセージ履歴を参照してください。

chatRoute()handleChatStream() は、すでに Memory に対応しています。新しいメッセージだけを送信し、Thread と Resource の識別子を含めるようにクライアントを設定してください。

useCompletion()
usecompletionへの直接リンク

useCompletion() フックは、フロントエンドと Mastra Agent 間の単一 Turn の Completion を処理し、プロンプトを送信して HTTP 経由でストリーミングレスポンスを受信できるようにします。

フロントエンドは次のように実装できます。

app/page.tsx
import { useCompletion } from '@ai-sdk/react'

export default function Page() {
const { completion, input, handleInputChange, handleSubmit } = useCompletion({
api: '/api/completion',
})

return (
<form onSubmit={handleSubmit}>
<input name="prompt" value={input} onChange={handleInputChange} id="input" />
<button type="submit">Submit</button>
<div>{completion}</div>
</form>
)
}

バックエンドの実装を選択します。

src/mastra/index.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 })
},
}),
],
},
})

カスタム UI
カスタム UIへの直接リンク

カスタム UI(Generative UI とも呼ばれます)では、Mastra からストリーミングされるデータに基づいて、カスタム React コンポーネントをレンダリングできます。生のテキストや JSON を表示する代わりに、Tool の出力や Workflow の進行状況(Agent Network の実行やカスタムイベントを含む)を視覚化するコンポーネントを作成できます。

次のような場合にカスタム UI を使用します。

  • Tool の出力を視覚的なコンポーネントとしてレンダリングする(JSON の代わりに天気カードを表示するなど)
  • Workflow Step の進行状況をステータスインジケーターで表示する
  • Agent Network の実行を Step ごとの更新で視覚化する
  • 長時間実行される処理中に、進行状況やステータスの更新を表示する

Data Part のタイプ
Data Part のタイプへの直接リンク

Mastra は、メッセージ内の「Part」としてデータをフロントエンドにストリーミングします。各 Part には、レンダリング方法を決める type があります。@mastra/ai-sdk パッケージは Mastra ストリームを AI SDK 互換の UI Message DataParts に変換します。

Data Part のタイプソース説明
tool-{toolKey}AI SDK 組み込みinput-availableoutput-availableoutput-error の状態を持つ Tool 呼び出し
data-workflowworkflowRoute()Step のステータスと最終出力を含む Workflow 実行状態の Snapshot
data-workflow-stepworkflowRoute()変更された Step の完全な Payload を持つ Workflow Step の差分
data-networknetworkRoute()順序付けられた Step と出力を含む Agent Network の実行
data-tool-agentTool 内のネストされた Agent現在の Step の実行中に送出される、ネストされた Agent のコンパクトな Snapshot
data-tool-agent-stepTool 内のネストされた Agentネストされた Step の完了時に送出される、その Step の完全な Payload
data-tool-workflowTool 内のネストされた WorkflowTool の execute() 内からストリーミングされる Workflow 出力
data-tool-networkTool 内のネストされた NetworkTool の execute() 内からストリーミングされる Network 出力
data-{custom}writer.custom()進行状況、ステータス更新などのカスタムイベント

Tool の出力をレンダリングする
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 コンポーネントとしてレンダリングする例を示します。

src/mastra/tools/weather-tool.ts
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 Part のタイプは tool-{toolKey} パターンに従います。toolKey は Agent に Tool を登録するときに使用したキーです。たとえば tools: { weatherTool } として登録すると、Part のタイプは tool-weatherTool になります。

Workflow データをレンダリングする
Workflow データをレンダリングするへの直接リンク

workflowRoute() または handleWorkflowStream() を使用すると、Mastra は Workflow 状態の Snapshot を data-workflow Part として、変更された Step の完全な Payload を data-workflow-step Part として送出します。これにより、長時間実行される Workflow で、完了済みの全 Step の出力が中間 Snapshot ごとに繰り返されることを防ぎます。

実行中に data-workflow Part と data-workflow-step Part を送出する、複数 Step の Workflow を定義します。

src/mastra/workflows/activities-workflow.ts
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 イベントをフロントエンドにストリーミングします。

src/mastra/index.ts
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',
}),
],
},
})

Workflow ストリーミングについて詳しくは、Workflow ストリーミングを参照してください。

Network データをレンダリングする
Network データをレンダリングするへの直接リンク

networkRoute() または handleNetworkStream() を使用すると、Mastra は呼び出された Agent とその出力を含む、Agent Network の実行状態を保持した data-network Part を送出します。

Agent を Mastra に登録し、networkRoute() で Routing Agent を公開して、Network の実行イベントをフロントエンドにストリーミングします。

src/mastra/index.ts
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',
}),
],
},
})

Agent Network について詳しくは、Agent Network を参照してください。

カスタムイベント
カスタムイベントへの直接リンク

Tool の execute() 関数内で writer.custom() を使用して、カスタム Data Part を送出します。Tool の実行中に進行状況、ステータス更新、その他のカスタム UI 更新を表示する場合に便利です。

Data Part として認識されるには、カスタムイベントのタイプを data- で始める必要があります。

警告

writer.custom() の呼び出しは必ず await してください。そうしないと WritableStream is locked エラーが発生する可能性があります。

Tool の execute() 関数内で writer.custom() を使用し、実行の各段階で data- プレフィックス付きのカスタムイベントを送出します。

src/mastra/tools/task-tool.ts
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',
}
},
})

Tool ストリーミング
Tool ストリーミングへの直接リンク

Tool は、より低レベルの制御が必要な場合に context.writer.write() でデータをストリーミングしたり、Agent のストリームを Tool の Writer に直接パイプしたりできます。詳しくは Tool ストリーミングを参照してください。

例への直接リンク

カスタム UI パターンの動作例は、Mastra の UI Dojo で確認できます。リポジトリには次の実装が含まれます。

レシピ
レシピへの直接リンク

ストリーム変換
ストリーム変換への直接リンク

Mastra のストリームを手動で AI SDK 互換形式に変換するには、toAISdkStream() ユーティリティを使用します。具体的な使用パターンはを参照してください。

toAISdkStream() は、既存の AI SDK v5 またはデフォルトの動作を維持します。アプリが AI SDK v6 に対して型付けされている場合は、version: 'v6' を渡します。

import { toAISdkStream } from '@mastra/ai-sdk'

const v5Stream = toAISdkStream(mastraStream, { from: 'agent' })
const v6Stream = toAISdkStream(mastraStream, { from: 'agent', version: 'v6' })

過去のメッセージを読み込む
過去のメッセージを読み込むへの直接リンク

チャット UI に表示するために Mastra Memory からメッセージを読み込む場合は、toAISdkV5Messages() または toAISdkV4Messages() を使用し、useChat()initialMessages に適した AI SDK 形式へ変換します。

追加データを渡す
追加データを渡すへの直接リンク

sendMessage() を使用すると、フロントエンドから Mastra に追加データを渡せます。このデータは、サーバー上で RequestContext として使用できます。

フロントエンドコードの例を示します。

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 (
<div>
<pre>{JSON.stringify(messages, null, 2)}</pre>
<form onSubmit={handleFormSubmit}>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="Name of the city"
/>
</form>
</div>
)
}

次のいずれかの例でバックエンドを実装します。

前述のように、Mastra 設定に chatRoute() を追加します。次に、サーバーレベルの Middleware を追加します。

src/mastra/index.ts
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 ドキュメントを参照してください。

ユーザー承認による Workflow の中断と再開
ユーザー承認による Workflow の中断と再開への直接リンク

Workflow は実行を中断し、続行前にユーザー入力を待つことができます。承認フロー、確認、Human-in-the-loop のシナリオに便利です。

Workflow では次の要素を使用します。

  • suspendSchema / resumeSchema:中断 Payload と再開入力のデータ構造を定義する
  • suspend():Workflow を一時停止し、中断 Payload を UI に送信する
  • resumeData:Workflow の再開時にユーザーの応答を保持する
  • bail():Workflow を早期終了する(ユーザーが拒否した場合など)

承認のために中断する Workflow Step を作成します。Step は resumeData を確認して再開時の実行かどうかを判断し、初回実行時に suspend() を呼び出します。

src/mastra/workflows/approval-workflow.ts
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 を登録します。中断と再開の状態を永続化するにはストレージが必要です。

src/mastra/index.ts
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' }),
],
},
})

重要なポイントは次のとおりです。

  • 中断 Payload には step.suspendPayload からアクセスできます
  • 再開するには、Request Body に runIdstep(Step ID)、resumeData を含めます
  • 中断と再開の Workflow 状態を永続化するには、ストレージの設定が必要です

完全な実装については、UI Dojo の workflow-suspend-resume の例を参照してください。

Tool 内でネストされた Agent のストリーム
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 にパイプする

Agent を呼び出し、そのストリームを Tool の Writer にパイプする Tool を作成します。

src/mastra/tools/nested-agent-tool.ts
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 を作成します。

src/mastra/agents/forecast-agent.ts
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 },
})

重要なポイントは次のとおりです。

  • fullStreamcontext.writer にパイプすると、data-tool-agent Part が作成されます
  • 直前に完了したネスト Step の完全な Payload が必要な場合は、data-tool-agent-step を読み取ります
  • AgentDataPart には id(Part 上)と data.text(現在のネスト Agent テキストの Snapshot)があります
  • ストリームの完了後も、Tool は独自の出力を返します

完全な実装については、UI Dojo の tool-nested-streams の例を参照してください。

Workflow Step から Agent テキストをストリーミングする
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 の進行状況とともにストリーミングテキストを受信する

Agent のレスポンスを Step の writer にパイプしてストリーミングする Workflow Step を作成します。

src/mastra/workflows/weather-workflow.ts
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 を登録します。テキストストリーミングはデフォルトで有効です。

src/mastra/index.ts
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' })],
},
})

重要なポイントは次のとおりです。

  • Step の writerexecute 関数で使用できます(context 経由ではありません)
  • workflowRoute()includeTextStreamParts はデフォルトで true のため、テキストはデフォルトでストリーミングされます
  • data-workflow Part で Step のステータスが更新される間、テキスト Part はリアルタイムでストリーミングされます

完全な実装については、UI Dojo の workflow-agent-text-stream の例を参照してください。

分岐 Workflow で複数段階の進行状況を表示する
分岐 Workflow で複数段階の進行状況を表示するへの直接リンク

条件分岐を持つ Workflow(速達配送と通常配送など)では、カスタムイベントに識別子を含めることで、異なる分岐の進行状況を追跡できます。

UI Dojo の例では、イベントデータの stage フィールドを使用して、実行中の分岐("validation""standard-processing""express-processing" など)を識別します。フロントエンドはこのフィールドでイベントをグループ化し、Pipeline 形式の進行状況 UI を表示します。

UI Dojo の branching-workflow.ts(バックエンド)と workflow-custom-events.tsx(フロントエンド)を参照してください。

Agent Network の進行状況インジケーター
Agent Network の進行状況インジケーターへの直接リンク

Agent Network を使用する場合、Subagent が使用する Tool からカスタムの進行状況イベントを送出して、現在アクティブな Agent を表示できます。

UI Dojo の例では、イベントデータの stage フィールドを使用して、実行中の Subagent("report-generation""report-review" など)を識別します。フロントエンドはこのフィールドでイベントをグループ化し、それぞれの最新ステータスを表示します。

UI Dojo の report-generation-tool.ts(バックエンド)と agent-network-custom-events.tsx(フロントエンド)を参照してください。 フロントエンドがレンダリングするデータの構造を認識できるように、outputSchema を持つ Tool を定義します。