使用 AI SDK UI
AI SDK UI 是一个用于构建 AI 驱动界面的 React 工具和组件库。本指南将介绍如何使用 @mastra/ai-sdk 将 Mastra 输出转换为 AI SDK 兼容格式,以便在前端使用 AI SDK UI 的 hook 和组件。
要从 AI SDK v4 迁移到 v5?请参阅迁移指南。
想查看更多示例?请访问 Mastra 的 UI Dojo 或 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()、useCompletion() 和 useObject()。
安装所需包以开始使用:
- npm
- pnpm
- Yarn
- Bun
npm install @mastra/ai-sdk@latest @ai-sdk/react ai
pnpm add @mastra/ai-sdk@latest @ai-sdk/react ai
yarn add @mastra/ai-sdk@latest @ai-sdk/react ai
bun add @mastra/ai-sdk@latest @ai-sdk/react ai
现在可以按照下方的集成指南和方案继续操作了。
集成指南集成指南的直接链接
通常,你需要设置以 AI SDK 兼容格式流式传输 Mastra 内容的 API 路由,然后在 useChat() 等 AI SDK UI hook 中使用这些路由。请选择以下方式之一:
设置好 API 路由后,就可以在 useChat() hook 中使用它们。
Mastra ServerMastra Server的直接链接
将 Mastra 作为独立 Server 运行,并将前端(例如使用 Vite + React)连接到其 API 端点。此方式会使用 Mastra 的自定义 API 路由功能。
Mastra 的 UI Dojo 就采用了这种设置。
你可以使用 chatRoute()、workflowRoute() 和 networkRoute() 创建以 AI SDK 兼容格式流式传输 Mastra 内容的 API 路由。实现后,即可在 useChat() 中使用这些 API 路由。
- chatRoute()
- workflowRoute()
- networkRoute()
此示例展示如何在 /chat 端点设置聊天路由,并使用 ID 为 weatherAgent 的 Agent。
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() Reference 文档。
此示例展示如何在 /workflow 端点设置 Workflow 路由,并使用 ID 为 weatherWorkflow 的 Workflow。
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() Reference 文档。
当 Workflow 步骤将 Agent 流传送到 Workflow writer(例如 await response.fullStream.pipeTo(writer))时,即使 Agent 在 Workflow 步骤内部运行,其文本块和 Tool 调用也会实时转发到 UI 流。
详情请参阅 Workflow 流式传输。
此示例展示如何在 /network 端点设置网络路由,并使用 ID 为 weatherAgent 的 Agent。
import { Mastra } from '@mastra/core'
import { networkRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
networkRoute({
path: '/network',
agent: 'weatherAgent',
}),
],
},
})
你也可以使用动态网络路由,详情请参阅 networkRoute() Reference 文档。
与框架无关与框架无关的直接链接
如果你不想运行 Mastra Server,而是使用 Next.js 或 Express 等框架,可以在自己的 API 路由 handler 中使用 handleChatStream()、handleWorkflowStream() 和 handleNetworkStream() 函数。
这些函数返回一个 ReadableStream,你可以使用 createUIMessageStreamResponse() 对其进行封装。
与框架无关的 handler 会保留现有 AI SDK v5 默认行为。如果应用针对 AI SDK v6 进行类型定义,请传入 version: 'v6'。为使 handleChatStream() 和 handleNetworkStream() 获得最佳 TypeScript 类型推断,请将 messages 作为已安装 ai 版本中的 UIMessage[] 传入。
以下示例展示如何将这些函数与 Next.js App Router 配合使用。
- handleChatStream()
- handleWorkflowStream()
- handleNetworkStream()
此示例展示如何在 /chat 端点设置聊天路由,并使用 ID 为 weatherAgent 的 Agent。
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 })
}
此示例展示如何在 /workflow 端点设置 Workflow 路由,并使用 ID 为 weatherWorkflow 的 Workflow。
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 })
}
此示例展示如何在 /network 端点设置网络路由,并使用 ID 为 routingAgent 的 Agent。
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()usechat的直接链接
无论是通过 Mastra Server 创建 API 路由,还是使用自行选择的框架,现在都可以在 useChat() hook 中使用这些 API 端点。
假设你已在 /chat 设置使用天气 Agent 的路由,就可以如下向其提问。请务必设置正确的 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>
)
}
使用 prepareSendMessagesRequest 自定义发送到聊天路由的请求,例如向 Agent 传递额外配置。
使用 Mastra Memory使用 Mastra Memory的直接链接
为 Agent 配置 Memory 后,Mastra 会在 Server 上从存储中加载对话历史。客户端只需发送新消息,而不要发送完整对话历史。
发送完整历史不仅多余,还可能导致消息顺序错误,因为客户端时间戳可能与数据库中存储的时间戳冲突。
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 参数、身份验证上下文或数据库中的值。
有关 Mastra Memory 如何加载和存储消息的更多信息,请参阅消息历史。
chatRoute() 和 handleChatStream() 已支持 Memory。请配置客户端,使其只发送新消息,并包含线程和资源标识符。
useCompletion()usecompletion的直接链接
useCompletion() hook 处理前端与 Mastra Agent 之间的单轮补全,让你可以发送提示词并通过 HTTP 接收流式响应。
前端可以如下实现:
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>
)
}
请选择一种后端实现:
- Mastra Server
- Next.js
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 })
},
}),
],
},
})
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(也称为生成式 UI)允许你根据 Mastra 流式传输的数据渲染自定义 React 组件。你可以为 Tool 输出和 Workflow 进度创建可视化组件,而不是显示原始文本或 JSON;这也包括 Agent 网络执行和自定义事件。
以下场景适合使用自定义 UI:
- 将 Tool 输出渲染为可视化组件(例如用天气卡片替代 JSON)
- 使用状态指示器显示 Workflow 步骤进度
- 通过逐步更新将 Agent 网络执行可视化
- 在长时运行的操作期间显示进度指示器或状态更新
数据 part 类型数据 part 类型的直接链接
Mastra 将数据作为消息中的“part”流式传输到前端。每个 part 都有一个 type,用于决定如何进行渲染。@mastra/ai-sdk 包将 Mastra 流转换为 AI SDK 兼容的 UI Message DataPart。
| 数据 part 类型 | 来源 | 说明 |
|---|---|---|
tool-{toolKey} | AI SDK 内置 | Tool 调用及其状态:input-available、output-available、output-error |
data-workflow | workflowRoute() | 包含步骤状态和最终输出的 Workflow 执行状态快照 |
data-workflow-step | workflowRoute() | Workflow 步骤增量,包含发生变化步骤的完整 payload |
data-network | networkRoute() | 包含有序步骤和输出的 Agent 网络执行数据 |
data-tool-agent | Tool 中的嵌套 Agent | 当前步骤仍在运行时的精简嵌套 Agent 快照 |
data-tool-agent-step | Tool 中的嵌套 Agent | 嵌套步骤结束时发出的完整嵌套 Agent 步骤 payload |
data-tool-workflow | Tool 中的嵌套 Workflow | 从 Tool 的 execute() 内部流式传输的 Workflow 输出 |
data-tool-network | Tool 中的嵌套网络 | 从 Tool 的 execute() 内部流式传输的网络输出 |
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 组件。
- 后端
- 前端
使用 outputSchema 定义 Tool,使前端了解要渲染的数据结构。
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 的状态和输出渲染自定义组件。
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 (
<div>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, index) => {
// Handle user text messages
if (part.type === 'text' && message.role === 'user') {
return <p key={index}>{part.text}</p>
}
// Handle weather tool output
if (part.type === 'tool-weatherTool') {
switch (part.state) {
case 'input-available':
return <Loader key={index} />
case 'output-available':
return <WeatherCard key={index} {...part.output} />
case 'output-error':
return <div key={index}>Error: {part.errorText}</div>
default:
return null
}
}
return null
})}
</div>
))}
</div>
)
}
Tool part 类型遵循 tool-{toolKey} 模式,其中 toolKey 是向 Agent 注册 Tool 时使用的 Key。例如,如果将 Tool 注册为 tools: { weatherTool },part 类型将是 tool-weatherTool。
渲染 Workflow 数据渲染 Workflow 数据的直接链接
使用 workflowRoute() 或 handleWorkflowStream() 时,Mastra 会发出表示 Workflow 状态快照的 data-workflow part,以及包含发生变化步骤完整 payload 的 data-workflow-step part。这样可以避免长时运行的 Workflow 在每个中间快照中重复所有已完成步骤的输出。
- 后端
- 前端
定义一个多步骤 Workflow,它会在执行过程中发出 data-workflow 和 data-workflow-step part。
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 事件流式传输到前端。
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 状态快照。如果需要刚发生变化步骤的完整 payload,还应读取 data-workflow-step part。
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 (
<div className="step">
<div className="step-header">
<span>{name}</span>
<span className={`status status-${status}`}>{status}</span>
</div>
{status === 'success' && output && <pre>{JSON.stringify(output, null, 2)}</pre>}
</div>
)
}
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 (
<div>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, index) => {
if (part.type === 'data-workflow') {
const workflowData = part.data as WorkflowData
const steps = Object.values(workflowData.steps)
return (
<div key={index} className="workflow-progress">
<h3>Workflow: {workflowData.name}</h3>
<p>Status: {workflowData.status}</p>
{steps.map(step => (
<StepIndicator
key={step.name}
name={step.name}
status={step.status}
output={step.output}
/>
))}
</div>
)
}
if (part.type === 'data-workflow-step') {
const stepData = part.data as WorkflowStepData
return (
<StepIndicator
key={index}
name={stepData.step.name}
status={stepData.step.status}
output={stepData.step.output}
/>
)
}
return null
})}
</div>
))}
</div>
)
}
有关 Workflow 流式传输的更多信息,请参阅 Workflow 流式传输。
渲染网络数据渲染网络数据的直接链接
使用 networkRoute() 或 handleNetworkStream() 时,Mastra 会发出包含 Agent 网络执行状态的 data-network part,其中包括调用了哪些 Agent 及其输出。
- 后端
- 前端
向 Mastra 注册 Agent,并通过 networkRoute() 暴露路由 Agent,以便将网络执行事件流式传输到前端。
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 的执行步骤,以确保类型安全。
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 (
<div className="agent-step">
<div className="step-header">
<span className="agent-name">{step.name}</span>
<span className={`status status-${step.status}`}>{step.status}</span>
</div>
{step.input && (
<div className="step-input">
<strong>Input:</strong>
<pre>{JSON.stringify(step.input, null, 2)}</pre>
</div>
)}
{step.output && (
<div className="step-output">
<strong>Output:</strong>
<pre>
{typeof step.output === 'string' ? step.output : JSON.stringify(step.output, null, 2)}
</pre>
</div>
)}
</div>
)
}
export function NetworkChat() {
const { messages, sendMessage, status } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/network',
}),
})
return (
<div>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, index) => {
if (part.type === 'data-network') {
const networkData = part.data as NetworkData
return (
<div key={index} className="network-execution">
<div className="network-header">
<h3>Agent Network: {networkData.name}</h3>
<span className={`status status-${networkData.status}`}>
{networkData.status}
</span>
</div>
<div className="network-steps">
{networkData.steps.map((step, stepIndex) => (
<AgentStep key={stepIndex} step={step} />
))}
</div>
</div>
)
}
return null
})}
</div>
))}
</div>
)
}
有关 Agent 网络的更多信息,请参阅 Agent 网络。
自定义事件自定义事件的直接链接
在 Tool 的 execute() 函数中使用 writer.custom() 发出自定义数据 part。这适用于进度指示器、状态更新或 Tool 执行期间的任何自定义 UI 更新。
自定义事件类型必须以 data- 开头,才能被识别为数据 part。
必须对 writer.custom() 调用使用 await,否则可能会遇到 WritableStream is locked 错误。
- 后端
- 前端
在 Tool 的 execute() 函数中使用 writer.custom(),在不同执行阶段发出以 data- 为前缀的自定义事件。
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,并渲染随新事件到达而更新的进度指示器。
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 (
<div className="progress-indicator">
{progress.status === 'in-progress' ? (
<span className="spinner" />
) : (
<span className="check-icon" />
)}
<span className={`status-${progress.status}`}>{progress.message}</span>
</div>
)
}
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 (
<div>
{latestProgress && <ProgressIndicator progress={latestProgress} />}
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, index) => {
if (part.type === 'text') {
return <p key={index}>{part.text}</p>
}
return null
})}
</div>
))}
</div>
)
}
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' })
加载历史消息加载历史消息的直接链接
从 Mastra Memory 加载消息并在聊天 UI 中显示时,请使用 toAISdkV5Messages() 或 toAISdkV4Messages(),将其转换为适合 useChat() 的 initialMessages 使用的相应 AI SDK 格式。
传递额外数据传递额外数据的直接链接
sendMessage() 允许你从前端向 Mastra 传递额外数据。随后,可以在 Server 上将这些数据用作 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 Server
- Next.js
如上所示,在 Mastra 配置中添加 chatRoute()。然后,添加 Server 级中间件:
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()
},
],
},
})
你可以通过 requestContext 参数在 Tool 中访问这些数据。详情请参阅 Request Context 文档。
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的直接链接
Workflow 可以暂停执行并等待用户输入,然后再继续运行。这适用于审批流、确认流程或任何人在回路场景。
该 Workflow 使用以下内容:
suspendSchema/resumeSchema:定义暂停 payload 和恢复输入的数据结构suspend():暂停 Workflow 并将暂停 payload 发送到 UIresumeData:包含 Workflow 恢复时的用户响应bail():提前退出 Workflow(例如用户拒绝时)
- 后端
- 前端
创建一个暂停并等待批准的 Workflow 步骤。该步骤检查 resumeData 以判断是否正在恢复,并在首次执行时调用 suspend()。
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。暂停或恢复需要使用 Storage 来持久化状态。
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 的恢复数据。
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<string, string>
// 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 (
<div>
{!suspended ? (
<form
onSubmit={e => {
e.preventDefault()
setMessages([])
sendMessage({ text: 'Start', metadata: { requestId, summary } })
}}
>
<input
value={requestId}
onChange={e => setRequestId(e.target.value)}
placeholder="Request ID"
/>
<input value={summary} onChange={e => setSummary(e.target.value)} placeholder="Summary" />
<button type="submit" disabled={status !== 'ready'}>
Submit
</button>
</form>
) : (
<div>
<p>
{
(suspended.data.steps['request-approval']?.suspendPayload as { message: string })
?.message
}
</p>
<button onClick={handleApprove}>Approve</button>
<button onClick={handleReject}>Reject</button>
</div>
)}
</div>
)
}
要点:
- 可通过
step.suspendPayload访问暂停 payload - 如需恢复,请在请求正文中发送
runId、step(步骤 ID)和resumeData - 必须配置 Storage,才能在暂停或恢复时持久化 Workflow 状态
完整实现请参阅 UI Dojo 中的 workflow-suspend-resume 示例。
Tool 中的嵌套 Agent 流Tool 中的嵌套 Agent 流的直接链接
Tool 可以在内部调用 Agent,并将 Agent 输出流式传回 UI。在嵌套步骤仍在运行时,这会创建精简的 data-tool-agent 快照;嵌套步骤结束时,会创建 data-tool-agent-step part;嵌套运行结束时,则会创建一份完整的 data-tool-agent 快照。
该模式使用以下内容:
context.mastra.getAgent():从 Tool 内部获取 Agent 实例agent.stream():流式传输 Agent 响应stream.fullStream.pipeTo(context.writer):将 Agent 流传送到 Tool 的 writer
- 后端
- 前端
创建一个调用 Agent 并将其流传送到 Tool writer 的 Tool。
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。
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,以及表示已完成嵌套步骤 payload 的 data-tool-agent-step part。
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 (
<div>
<form
onSubmit={e => {
e.preventDefault()
sendMessage({ text: input })
setInput('')
}}
>
<input value={input} onChange={e => setInput(e.target.value)} placeholder="Enter a city" />
<button type="submit" disabled={status !== 'ready'}>
Get Forecast
</button>
</form>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, index) => {
if (part.type === 'text') {
return <p key={index}>{part.text}</p>
}
if (part.type === 'data-tool-agent') {
const { id, data } = part as AgentDataPart
return (
<div key={index} className="nested-agent">
<strong>Nested Agent: {id}</strong>
{data.text && <p>{data.text}</p>}
</div>
)
}
if (part.type === 'data-tool-agent-step') {
const { data } = part as AgentStepDataPart
return (
<div key={index} className="nested-agent-step">
<strong>Completed nested step {data.stepIndex + 1}</strong>
{data.step.text && <p>{data.step.text}</p>}
</div>
)
}
return null
})}
</div>
))}
</div>
)
}
要点:
- 将
fullStream传送到context.writer会创建data-tool-agentpart - 需要刚完成的嵌套步骤的完整 payload 时,请读取
data-tool-agent-step AgentDataPart包含id(位于 part 上)和data.text(当前嵌套 Agent 文本快照)- 流结束后,Tool 仍会返回自己的输出
完整实现请参阅 UI Dojo 中的 tool-nested-streams 示例。
从 Workflow 步骤流式传输 Agent 文本从 Workflow 步骤流式传输 Agent 文本的直接链接
Workflow 步骤可以将 Agent 流传送到步骤的 writer,从而实时流式传输 Agent 的文本输出。这样,用户可以在 Workflow 执行期间看到 Agent 正在“思考”,而无需等待步骤完成。
该模式使用以下内容:
- Workflow 步骤中的
writer:将 Agent 的fullStream传送到步骤的 writer text和data-workflowpart:前端在接收步骤进度的同时,也会接收流式文本
- 后端
- 前端
创建一个 Workflow 步骤,通过将 Agent 响应传送到步骤的 writer 来进行流式传输。
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。默认启用文本流式传输。
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(步骤进度)。
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 (
<div>
<form
onSubmit={e => {
e.preventDefault()
sendMessage({ text: location })
setLocation('')
}}
>
<input
value={location}
onChange={e => setLocation(e.target.value)}
placeholder="Enter city"
/>
<button type="submit" disabled={status !== 'ready'}>
Analyze
</button>
</form>
{messages.map(message => (
<div key={message.id}>
{message.parts.map((part, index) => {
// Streaming agent text
if (part.type === 'text' && message.role === 'assistant') {
return (
<div key={index}>
{status === 'streaming' && (
<p>
<em>Agent analyzing...</em>
</p>
)}
<p>{part.text}</p>
</div>
)
}
// Workflow step progress
if (part.type === 'data-workflow') {
const workflow = part.data as WorkflowData
return (
<div key={index}>
{Object.entries(workflow.steps).map(([stepId, step]) => (
<div key={stepId}>
<strong>{stepId}</strong>: {step.status}
</div>
))}
</div>
)
}
return null
})}
</div>
))}
</div>
)
}
要点:
- 可以直接在
execute函数中使用步骤的writer(不通过context) workflowRoute()上的includeTextStreamParts默认为true,因此默认会流式传输文本- 文本 part 会实时流式传输,同时
data-workflowpart 会随着步骤状态更新
完整实现请参阅 UI Dojo 中的 workflow-agent-text-stream 示例。
分支 Workflow 的多阶段进度分支 Workflow 的多阶段进度的直接链接
对于包含条件分支的 Workflow(例如加急配送与标准配送),可以在自定义事件中加入标识符,以跟踪不同分支的进度。
UI Dojo 示例在事件数据中使用 stage 字段来标识正在执行的分支(例如 "validation"、"standard-processing"、"express-processing")。前端按该字段对事件分组,以显示管道式进度 UI。
请参阅 UI Dojo 中的 branching-workflow.ts(后端)和 workflow-custom-events.tsx(前端)。
Agent 网络中的进度指示器Agent 网络中的进度指示器的直接链接
使用 Agent 网络时,可以从子 Agent 所使用的 Tool 中发出自定义进度事件,以显示当前处于活动状态的 Agent。
UI Dojo 示例在事件数据中包含 stage 字段,用于标识正在运行的子 Agent(例如 "report-generation"、"report-review")。前端按该字段对事件分组,并显示每个阶段的最新状态。
请参阅 UI Dojo 中的 report-generation-tool.ts(后端)和 agent-network-custom-events.tsx(前端)。