> Discover all available pages from the documentation index: https://mastra.zisheng.pro/ko/llms.txt
# AI SDK UI 사용
[AI SDK UI](https://sdk.vercel.ai)AI 기반 인터페이스를 구축하기 위한 React 유틸리티 및 구성 요소의 라이브러리입니다. 이 가이드에서는 다음을 사용하는 방법을 배웁니다.`@mastra/ai-sdk`Mastra의 출력을 AI SDK 호환 형식으로 변환하여 프런트엔드에서 후크와 구성 요소를 사용할 수 있도록 합니다.
:::참고 AI SDK v4에서 v5로 마이그레이션하시나요? 참조[migration guide](https://mastra.zisheng.pro/ko/guides/migrations/ai-sdk-v4-to-v5). :::
> **팁:** 더 많은 예를 보고 싶으십니까? 마스트라(Mastra)를 방문해 보세요[**UI Dojo**](https://ui-dojo.mastra.ai/) or the [Next.js quickstart guide](https://mastra.zisheng.pro/ko/guides/getting-started/next-js).
## 시작하기
Mastra와 AI SDK UI를 함께 설치하여 사용하세요.`@mastra/ai-sdk` package. `@mastra/ai-sdk` 는 AI SDK 호환 형식으로 Mastra 에이전트를 스트리밍하기 위한 사용자 지정 API 경로와 유틸리티를 제공합니다. 여기에는 채팅, Workflow, 네트워크 경로 핸들러와 UI 통합을 위한 유틸리티 및 내보낸 타입이 포함됩니다.
`@mastra/ai-sdk`AI SDK UI의 세 가지 주요 후크와 통합됩니다.[`useChat()`](https://ai-sdk.dev/docs/ai-sdk-ui/chatbot), [`useCompletion()`](https://ai-sdk.dev/docs/ai-sdk-ui/completion), and [`useObject()`](https://ai-sdk.dev/docs/ai-sdk-ui/object-generation).
시작하려면 필수 패키지를 설치하세요.
**npm**:
```bash
npm install @mastra/ai-sdk@latest @ai-sdk/react ai
```
**pnpm**:
```bash
pnpm add @mastra/ai-sdk@latest @ai-sdk/react ai
```
**Yarn**:
```bash
yarn add @mastra/ai-sdk@latest @ai-sdk/react ai
```
**Bun**:
```bash
bun add @mastra/ai-sdk@latest @ai-sdk/react ai
```
이제 아래 통합 가이드와 레시피를 따를 준비가 되었습니다!
## 통합 가이드
일반적으로 AI SDK 호환 형식으로 Mastra 콘텐츠를 스트리밍하는 API 경로를 설정한 다음 다음과 같은 AI SDK UI 후크에서 해당 경로를 사용합니다.`useChat()`. Choose one of these approaches:
- [마스트라의 서버](#mastras-server)
- [프레임워크에 구애받지 않음](#framework-agnostic)
API 경로를 설정한 후에는 다음에서 사용할 수 있습니다.[`useChat()`](#usechat) hook.
### 마스트라의 서버
Mastra를 독립형 서버로 실행하고 프런트엔드(예: Vite + React 사용)를 API 엔드포인트에 연결하세요. 마스트라(Mastra)를 사용하게 됩니다.[custom API routes](https://mastra.zisheng.pro/ko/docs/server/custom-api-routes) feature for this.
> **정보:** 마스트라의[**UI Dojo**](https://ui-dojo.mastra.ai/) is an example of this setup.
당신은 사용할 수 있습니다[`chatRoute()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/chat-route), [`workflowRoute()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/workflow-route), and [`networkRoute()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/network-route) 하여 Mastra 콘텐츠를 AI SDK 호환 형식으로 스트리밍하는 API 경로를 생성합니다. 구현한 후에는 이러한 API 경로를 [`useChat()`](#usechat).
**chatRoute()**:
이 예에서는 채팅 경로를 설정하는 방법을 보여줍니다.`/chat` endpoint that uses an agent with the ID `weatherAgent`.
```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()` reference documentation](https://mastra.zisheng.pro/ko/reference/ai-sdk/chat-route) for more details.
**workflowRoute()**:
이 예에서는 Workflow 경로를 설정하는 방법을 보여줍니다.`/workflow` endpoint that uses a workflow with the ID `weatherWorkflow`.
```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()` reference documentation](https://mastra.zisheng.pro/ko/reference/ai-sdk/workflow-route) for more details.
> **Workflow에서 Agent 스트리밍:** Workflow 단계에서 Agent의 스트림을 Workflow 작성자에게 파이프하는 경우(예:`await response.fullStream.pipeTo(writer)`), 에이전트가 Workflow 단계 내에서 실행되는 경우에도 에이전트의 텍스트 청크와 도구 호출이 실시간으로 UI 스트림에 전달됩니다.
>
> 보다[Workflow Streaming](https://mastra.zisheng.pro/ko/docs/workflows/overview) for more details.
**networkRoute()**:
이 예에서는 네트워크 경로를 설정하는 방법을 보여줍니다.`/network` endpoint that uses an agent with the ID `weatherAgent`.
```typescript
import { Mastra } from '@mastra/core'
import { networkRoute } from '@mastra/ai-sdk'
export const mastra = new Mastra({
server: {
apiRoutes: [
networkRoute({
path: '/network',
agent: 'weatherAgent',
}),
],
},
})
```
동적 네트워크 라우팅을 사용할 수도 있습니다.[`networkRoute()` reference documentation](https://mastra.zisheng.pro/ko/reference/ai-sdk/network-route) for more details.
### 프레임워크에 구애받지 않음
Mastra의 서버를 실행하지 않고 대신 Next.js 또는 Express와 같은 프레임워크를 사용하려는 경우 다음을 사용할 수 있습니다.[`handleChatStream()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/handle-chat-stream), [`handleWorkflowStream()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/handle-workflow-stream), and [`handleNetworkStream()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/handle-network-stream) functions in your own API route handlers.
그들은`ReadableStream` that you can wrap with [`createUIMessageStreamResponse()`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream-response).
> **AI SDK v6 호환성:** 프레임워크에 구애받지 않는 핸들러는 기존 AI SDK v5/기본 동작을 유지합니다. 앱이 AI SDK v6에 대해 입력된 경우 다음을 통과하세요.`version: 'v6'`. For best TypeScript inference with `handleChatStream()` and `handleNetworkStream()`, pass `messages` as `UIMessage[]` from your installed `ai` version.
아래 예는 Next.js App Router와 함께 사용하는 방법을 보여줍니다.
**handleChatStream()**:
이 예에서는 채팅 경로를 설정하는 방법을 보여줍니다.`/chat` endpoint that uses an agent with the ID `weatherAgent`.
```typescript
import { handleChatStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/src/mastra'
export async function POST(req: Request) {
const params = await req.json()
const stream = await handleChatStream({
mastra,
agentId: 'weatherAgent',
params,
})
return createUIMessageStreamResponse({ stream })
}
```
**handleWorkflowStream()**:
이 예에서는 Workflow 경로를 설정하는 방법을 보여줍니다.`/workflow` endpoint that uses a workflow with the ID `weatherWorkflow`.
```typescript
import { handleWorkflowStream } from '@mastra/ai-sdk'
import { createUIMessageStreamResponse } from 'ai'
import { mastra } from '@/src/mastra'
export async function POST(req: Request) {
const params = await req.json()
const stream = await handleWorkflowStream({
mastra,
workflowId: 'weatherWorkflow',
params,
})
return createUIMessageStreamResponse({ stream })
}
```
**handleNetworkStream()**:
이 예에서는 네트워크 경로를 설정하는 방법을 보여줍니다.`/network` endpoint that uses an agent with the ID `routingAgent`.
```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()`
API 경로를 생성했는지 여부[Mastra's server](#mastras-server) or used a [framework of your choice](#framework-agnostic), you can now use the API endpoints in the `useChat()` hook.
경로를 설정했다고 가정하면`/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)}
)
}
```
사용[`prepareSendMessagesRequest`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat#transport.default-chat-transport.prepare-send-messages-request) 하여 채팅 경로로 전송되는 요청을 사용자 지정할 수 있습니다. 예를 들어 에이전트에 추가 구성을 전달할 수 있습니다.
## 마스트라 Memory 사용
귀하의 대리인이[memory](https://mastra.zisheng.pro/ko/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` and `memory.resource` 를 URL 매개변수, 인증 컨텍스트 또는 데이터베이스와 같은 앱 자체 상태에서 가져옵니다.
보다[Message history](https://mastra.zisheng.pro/ko/docs/memory/message-history) 에서 Mastra Memory가 메시지를 불러오고 저장하는 방법을 자세히 알아보세요.
[`chatRoute()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/chat-route)그리고[`handleChatStream()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/handle-chat-stream) 는 이미 Memory와 함께 작동합니다. 새 메시지만 전송하고 스레드 및 리소스 식별자를 포함하도록 클라이언트를 구성하세요.
### `useCompletion()`
그만큼`useCompletion()` 훅은 프런트엔드와 Mastra 에이전트 간의 단일 턴 완성을 처리하여 Prompt를 전송하고 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을 표시하는 대신 Agent 네트워크 실행 및 사용자 정의 이벤트를 포함하여 Tool 출력 및 Workflow 진행을 위한 시각적 구성 요소를 생성할 수 있습니다.
다음과 같은 경우 맞춤 UI를 사용하세요.
- 시각적 구성 요소(예: JSON 대신 날씨 카드)로 Tool 출력 렌더링
- 상태 표시기로 Workflow 단계 진행 상황 표시
- 단계별 업데이트를 통해 Agent 네트워크 실행 시각화
- 장기 실행 작업 중에 진행률 표시기 또는 상태 업데이트 표시
### 데이터 부분 유형
Mastra는 메시지 내의 "부분"으로 데이터를 프런트엔드로 스트리밍합니다. 각 부분에는`type` that determines how to render it. The `@mastra/ai-sdk` package transforms Mastra streams into AI SDK-compatible [UI Message DataParts](https://ai-sdk.dev/docs/reference/ai-sdk-core/ui-message#datauipart).
| 데이터 부분 유형 | 소스 | 설명 |
| ---------------------- | ------------------ | ---------------------------------------------------------------------------------- |
| `tool-{toolKey}` | AI SDK built-in | Tool invocation with states: `input-available`, `output-available`, `output-error` |
| `data-workflow` | `workflowRoute()` | 단계 상태와 최종 출력이 포함된 Workflow 실행 상태 스냅샷 |
| `data-workflow-step` | `workflowRoute()` | 변경된 단계의 전체 페이로드가 포함된 Workflow 단계 델타 |
| `data-network` | `networkRoute()` | 순서가 지정된 단계와 출력이 포함된 Agent 네트워크 실행 |
| `data-tool-agent` | Tool 내 중첩 Agent | 현재 단계가 아직 실행 중일 때의 간결한 중첩 Agent 스냅샷 |
| `data-tool-agent-step` | Tool 내 중첩 Agent | 중첩 단계가 완료될 때 내보내는 전체 중첩 Agent 단계 페이로드 |
| `data-tool-workflow` | Tool 내 중첩 Workflow | Tool의 `execute()` |
| `data-tool-network` | Tool 내 중첩 네트워크 | Tool의 `execute()` |
| `data-{custom}` | `writer.custom()` | 진행률 표시기, 상태 업데이트 등을 위한 사용자 지정 이벤트 |
### 렌더링 Tool 출력
AI SDK가 자동으로 생성`tool-{toolKey}` 부분은 에이전트가 도구를 호출할 때 생성됩니다. 이러한 부분에는 도구의 상태와 출력이 포함되며, 이를 사용하여 사용자 지정 컴포넌트를 렌더링할 수 있습니다.
Tool 부분은 다음 상태를 순환합니다.
- `input-streaming`: Tool 입력이 스트리밍되고 있습니다. (Tool 호출 스트리밍이 활성화된 경우)
- `input-available`: Tool이 완전한 입력으로 호출되었으며 실행을 기다리고 있습니다.
- `output-available`: 출력과 함께 Tool 실행이 완료되었습니다.
- `output-error`: Tool 실행 실패
다음은 날씨 Tool의 출력을 사용자 정의로 렌더링하는 예입니다.`WeatherCard` component.
**Backend**:
다음을 사용하여 Tool을 정의합니다.`outputSchema` 하여 프런트엔드가 렌더링할 데이터의 구조를 알 수 있도록 합니다.
```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}` 부분을 메시지에서 찾아 도구의 상태와 출력에 따라 사용자 지정 컴포넌트를 렌더링합니다.
```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 부품 유형은 패턴을 따릅니다.`tool-{toolKey}`, where `toolKey` 는 에이전트에 도구를 등록할 때 사용하는 키입니다. 예를 들어 도구를 `tools: { weatherTool }`, the part type will be `tool-weatherTool`.
### Workflow 데이터 렌더링
사용시`workflowRoute()` or `handleWorkflowStream()`, Mastra emits `data-workflow` parts for workflow state snapshots and `data-workflow-step` 부분에서 변경된 단계의 전체 페이로드를 확인할 수 있습니다. 이렇게 하면 장시간 실행되는 Workflow가 모든 중간 스냅샷에서 완료된 모든 단계의 출력을 반복하지 않습니다.
**Backend**:
여러 단계를 내보내는 Workflow를 정의합니다.`data-workflow` and `data-workflow-step` parts as it executes.
```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()` to stream workflow events to the frontend.
```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` 부분을 사용하여 Workflow 상태 스냅샷을 렌더링합니다. 방금 변경된 단계의 전체 페이로드가 필요하다면 `data-workflow-step` parts.
```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 (
)
}
if (part.type === 'data-workflow-step') {
const stepData = part.data as WorkflowStepData
return (
)
}
return null
})}
))}
)
}
```
Workflow 스트리밍에 대한 자세한 내용은 다음을 참조하세요.[Workflow Streaming](https://mastra.zisheng.pro/ko/docs/workflows/overview).
### 네트워크 데이터 렌더링
사용시`networkRoute()` or `handleNetworkStream()`, Mastra emits `data-network` 부분에는 호출된 에이전트와 각 출력 등 Agent 네트워크의 실행 상태가 포함됩니다.
**Backend**:
Mastra에 Agent를 등록하고 다음을 통해 라우팅 Agent를 노출합니다.`networkRoute()` 하여 네트워크 실행 이벤트를 프런트엔드로 스트리밍합니다.
```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` 부분을 사용하고, `NetworkDataPart` type for type safety.
```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 (
)
}
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 네트워크에 대한 자세한 내용은 다음을 참조하세요.[Agent Networks](https://mastra.zisheng.pro/ko/docs/agents/networks).
### 맞춤 이벤트
사용`writer.custom()` within a tool's `execute()` 사용자 지정 데이터 파트를 내보내는 함수입니다. Tool 실행 중 진행률 표시기, 상태 업데이트 또는 사용자 지정 UI 업데이트에 유용합니다.
맞춤 이벤트 유형은 다음으로 시작해야 합니다.`data-` to be recognized as data parts.
> **경고:** 당신은해야합니다`await` the `writer.custom()` call, otherwise you may encounter a `WritableStream is locked` error.
**Backend**:
사용`writer.custom()` inside the tool's `execute()` function to emit custom `data-` prefixed events at different stages of execution.
```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**:
사용자 정의 이벤트 유형에 대한 메시지 부분을 필터링하고 새 이벤트가 도착할 때 업데이트되는 진행률 표시기를 렌더링합니다.
```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 (
{message.parts.map((part, index) => {
if (part.type === 'text') {
return
{part.text}
}
return null
})}
))}
)
}
```
### Tool 스트리밍
Tool은 다음을 사용하여 데이터를 스트리밍할 수도 있습니다.`context.writer.write()` 더 세밀하게 제어하거나 Agent의 스트림을 Tool의 writer로 직접 파이프할 수 있습니다. 자세한 내용은 [Tool Streaming](https://mastra.zisheng.pro/ko/docs/agents/using-tools).
### 예
사용자 정의 UI 패턴의 실제 예를 보려면 다음을 방문하세요.[Mastra's UI Dojo](https://ui-dojo.mastra.ai/). The repository includes implementations for:
- [생성 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 단계 시각화
- [Agent 네트워크](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/network.tsx): 네트워크 실행 화면
- [맞춤 이벤트](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/ko/reference/ai-sdk/to-ai-sdk-stream) utility. See the [examples](https://mastra.zisheng.pro/ko/reference/ai-sdk/to-ai-sdk-stream) for concrete usage patterns.
`toAISdkStream()`기존 AI SDK v5/기본 동작을 유지합니다. 앱이 AI SDK v6에 대해 입력된 경우 다음을 통과하세요.`version: 'v6'`.
```typescript
import { toAISdkStream } from '@mastra/ai-sdk'
const v5Stream = toAISdkStream(mastraStream, { from: 'agent' })
const v6Stream = toAISdkStream(mastraStream, { from: 'agent', version: 'v6' })
```
### 기록 메시지 로드 중
Mastra의 Memory에서 메시지를 로드하여 채팅 UI에 표시할 때 다음을 사용하세요.[`toAISdkV5Messages()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/to-ai-sdk-v5-messages) or [`toAISdkV4Messages()`](https://mastra.zisheng.pro/ko/reference/ai-sdk/to-ai-sdk-v4-messages) 이를 다음에 적합한 AI SDK 형식으로 변환하려면 `useChat()`'s `initialMessages`.
### 추가 데이터 전달
[`sendMessage()`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat#send-message)프런트엔드에서 Mastra로 추가 데이터를 전달할 수 있습니다. 이 데이터는 서버에서 다음과 같이 사용될 수 있습니다.[`RequestContext`](https://mastra.zisheng.pro/ko/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**:
추가`chatRoute()` 위에 표시된 것처럼 Mastra 구성에 추가합니다. 그런 다음 서버 수준 미들웨어를 추가합니다:
```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` parameter. See the [Request Context documentation](https://mastra.zisheng.pro/ko/docs/server/request-context) for more details.
**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는 실행을 일시 중단하고 계속하기 전에 사용자 입력을 기다릴 수 있습니다. 이는 승인 흐름, 확인 또는 인간 참여 시나리오에 유용합니다.
Workflow에서는 다음을 사용합니다.
- `suspendSchema` / `resumeSchema` - 일시 중단 페이로드와 재개 입력의 데이터 구조 정의
- `suspend()`- Workflow를 일시 중지하고 일시 중지 페이로드를 UI로 보냅니다.
- `resumeData`- Workflow가 재개될 때 사용자의 응답을 포함합니다.
- `bail()`- Workflow를 조기에 종료합니다(예: 사용자가 거부하는 경우).
**Backend**:
승인을 위해 일시 중지되는 Workflow 단계를 만듭니다. 단계 확인`resumeData` to determine if it's resuming, and calls `suspend()` on first execution.
```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`, and `resumeData`.
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import { useMemo, useState } from 'react'
import type { WorkflowDataPart } from '@mastra/ai-sdk'
type WorkflowData = WorkflowDataPart['data']
export function ApprovalWorkflow() {
const [requestId, setRequestId] = useState('')
const [summary, setSummary] = useState('')
const { messages, sendMessage, setMessages, status } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/workflow/approvalWorkflow',
prepareSendMessagesRequest: ({ messages }) => {
const lastMessage = messages[messages.length - 1]
const text = lastMessage.parts.find(p => p.type === 'text')?.text
const metadata = lastMessage.metadata as Record
// Resuming: send runId, step, and resumeData
if (text === 'Approve' || text === 'Reject') {
return {
body: {
runId: metadata.runId,
step: 'request-approval',
resumeData: { approved: text === 'Approve' },
},
}
}
// Starting: send inputData
return {
body: { inputData: { requestId: metadata.requestId, summary: metadata.summary } },
}
},
}),
})
// Find suspended workflow
const suspended = useMemo(() => {
for (const m of messages) {
for (const p of m.parts) {
if (p.type === 'data-workflow' && (p.data as WorkflowData).status === 'suspended') {
return { data: p.data as WorkflowData, runId: p.id }
}
}
}
return null
}, [messages])
const handleApprove = () => {
setMessages([])
sendMessage({ text: 'Approve', metadata: { runId: suspended?.runId } })
}
const handleReject = () => {
setMessages([])
sendMessage({ text: 'Reject', metadata: { runId: suspended?.runId } })
}
return (
{!suspended ? (
) : (
{
(suspended.data.steps['request-approval']?.suspendPayload as { message: string })
?.message
}
)}
)
}
```
핵심 사항:
- 일시 중지 페이로드는 다음을 통해 액세스할 수 있습니다.`step.suspendPayload`
- 재개하려면 다음을 보내세요.`runId`, `step` (the step ID), and `resumeData` in the request body
- Workflow 상태를 유지하려면 일시 중지/재개에 대한 스토리지를 구성해야 합니다.
전체 구현을 보려면 다음을 참조하세요.[workflow-suspend-resume example](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/workflow-suspend-resume.tsx) in UI Dojo.
### 중첩된 Agent 스트림 인 Tool
Tool은 내부적으로 Agent를 호출하고 Agent의 출력을 다시 UI로 스트리밍할 수 있습니다. 이렇게 하면 컴팩트한`data-tool-agent` 중첩된 단계가 아직 실행 중인 동안의 스냅샷, `data-tool-agent-step` 중첩된 단계가 완료될 때의 파트와 하나의 전체 `data-tool-agent` snapshot when the nested run finishes.
패턴은 다음을 사용합니다.
- `context.mastra.getAgent()`- Tool 내에서 Agent 인스턴스 가져오기
- `agent.stream()`- Agent의 응답을 스트리밍합니다.
- `stream.fullStream.pipeTo(context.writer)`- Agent의 스트림을 Tool 작성자에게 파이프합니다.
**Backend**:
Agent를 호출하고 해당 스트림을 Tool 작성자에게 파이프하는 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**:
핸들`data-tool-agent` parts for the live snapshot and `data-tool-agent-step` parts for the completed nested step payload.
```typescript
import { useChat } from '@ai-sdk/react'
import { DefaultChatTransport } from 'ai'
import { useState } from 'react'
import type { AgentDataPart, AgentStepDataPart } from '@mastra/ai-sdk'
export function NestedAgentChat() {
const [input, setInput] = useState('')
const { messages, sendMessage, status } = useChat({
transport: new DefaultChatTransport({
api: 'http://localhost:4111/chat/forecastAgent',
}),
})
return (
{messages.map(message => (
{message.parts.map((part, index) => {
if (part.type === 'text') {
return
{part.text}
}
if (part.type === 'data-tool-agent') {
const { id, data } = part as AgentDataPart
return (
Nested Agent: {id}
{data.text &&
{data.text}
}
)
}
if (part.type === 'data-tool-agent-step') {
const { data } = part as AgentStepDataPart
return (
)
}
```
핵심 사항:
- 단계의`writer` is available in the `execute` function (not via `context`)
- `includeTextStreamParts`기본값은`true` on `workflowRoute()`, so text streams by default
- 텍스트 부분은 실시간으로 스트리밍됩니다.`data-workflow` parts update with step status
전체 구현을 보려면 다음을 참조하세요.[workflow-agent-text-stream example](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/workflow-agent-text-stream.tsx) in UI Dojo.
### 분기 Workflow를 통한 다단계 진행
조건부 분기가 있는 Workflow(예: 빠른 배달과 표준 배달)의 경우 사용자 지정 이벤트에 식별자를 포함하여 여러 분기의 진행 상황을 추적할 수 있습니다.
UI Dojo 예제에서는 다음을 사용합니다.`stage` 이벤트 데이터의 필드를 사용하여 실행 중인 분기를 식별합니다(예: `"validation"`, `"standard-processing"`, `"express-processing"`). 프런트엔드는 이 필드를 기준으로 이벤트를 그룹화하여 파이프라인 스타일의 진행 상황 UI를 표시합니다.
참조[branching-workflow.ts](https://github.com/mastra-ai/ui-dojo/blob/main/src/mastra/workflows/branching-workflow.ts) (backend) and [workflow-custom-events.tsx](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/workflow-custom-events.tsx) (frontend) in UI Dojo.
### Agent 네트워크의 진행률 표시기
Agent 네트워크를 사용할 때 하위 Agent가 사용하는 Tool에서 사용자 정의 진행 이벤트를 내보내 현재 활성 상태인 Agent를 표시할 수 있습니다.
UI Dojo 예제에는 다음이 포함됩니다.`stage` 이벤트 데이터의 필드를 사용하여 실행 중인 하위 Agent를 식별합니다(예: `"report-generation"`, `"report-review"`). 프런트엔드는 이 필드를 기준으로 이벤트를 그룹화하고 각각의 최신 상태를 표시합니다.
참조[report-generation-tool.ts](https://github.com/mastra-ai/ui-dojo/blob/main/src/mastra/tools/report-generation-tool.ts) (backend) and [agent-network-custom-events.tsx](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/agent-network-custom-events.tsx) (frontend) in UI Dojo.