> 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)}
setInputValue(e.target.value)} placeholder="Name of the city" />
) } ``` 사용[`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 (
{completion}
) } ``` 백엔드 구현을 선택하세요. **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 (
{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 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 (
{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 네트워크에 대한 자세한 내용은 다음을 참조하세요.[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 (
{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 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)}
setInputValue(e.target.value)} placeholder="Name of the city" />
) } ``` 다음 예제 중 하나를 사용하여 백엔드를 구현합니다. **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 ? (
{ e.preventDefault() setMessages([]) sendMessage({ text: 'Start', metadata: { requestId, summary } }) }} > setRequestId(e.target.value)} placeholder="Request ID" /> setSummary(e.target.value)} placeholder="Summary" />
) : (

{ (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 (
{ e.preventDefault() sendMessage({ text: input }) setInput('') }} > setInput(e.target.value)} placeholder="Enter a city" />
{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` to `context.writer` creates `data-tool-agent` parts - 읽다`data-tool-agent-step` 방금 완료된 중첩 단계의 전체 페이로드가 필요할 때 - 그만큼`AgentDataPart` has `id` (on the part) and `data.text` (the current nested-agent text snapshot) - 스트림이 완료된 후에도 Tool은 여전히 자체 출력을 반환합니다. 전체 구현을 보려면 다음을 참조하세요.[tool-nested-streams example](https://github.com/mastra-ai/ui-dojo/blob/main/src/pages/ai-sdk/tool-nested-streams.tsx) in UI Dojo. ### Workflow 단계의 스트리밍 Agent 텍스트 Workflow 단계에서는 Agent의 스트림을 단계의 스트림으로 파이프하여 Agent의 텍스트 출력을 실시간으로 스트리밍할 수 있습니다.`writer`. 이를 통해 사용자는 단계가 완료될 때까지 기다리지 않고 Workflow가 실행되는 동안 Agent가 "생각하는" 과정을 볼 수 있습니다. 패턴은 다음을 사용합니다. - `writer`Workflow 단계에서 - Agent의 파이프라인`fullStream` to the step's writer - `text`그리고`data-workflow` 파트 - 프런트엔드는 단계 진행 상황과 함께 스트리밍 텍스트를 수신합니다 **Backend**: 단계의 응답으로 파이프를 연결하여 Agent의 응답을 스트리밍하는 Workflow 단계를 만듭니다.`writer`. ```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() ``` Workflow를`workflowRoute()`. Text streaming is enabled by default. ```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` parts (streaming agent output) and `data-workflow` parts (step progress). ```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 (
{ e.preventDefault() sendMessage({ text: location }) setLocation('') }} > setLocation(e.target.value)} placeholder="Enter city" />
{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 })}
))}
) } ``` 핵심 사항: - 단계의`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.