> Discover all available pages from the documentation index: https://mastra.zisheng.pro/zh-HK/llms.txt
# 使用 OpenUI
[OpenUI](https://openui.com) 是生成式 UI 的開放標準。它結合精簡、以串流為先的語言(OpenUI Lang)、React 執行環境和內置元件庫,讓模型輸出可在串流期間呈現為結構化 UI。
OpenUI 透過 [AG-UI 協定](https://docs.ag-ui.com)連接至 Mastra。`@ag-ui/mastra` 轉接器會封裝 Mastra `Agent` 並發出 AG-UI 事件,而 OpenUI 的 `agUIAdapter()` 會在用戶端解析這些事件。
> **提示:** 如需完整的可運作範例,請參閱 OpenUI 程式碼庫中的 [`mastra-chat`](https://github.com/thesysdev/openui/tree/main/examples/mastra-chat) 範例。
## 整合指南
將 Mastra 嵌入 Next.js API 路由,並透過 AG-UI 協定把 OpenUI `` 聊天介面連接至該路由。
1. 建立新的 OpenUI 應用程式基本結構:
**npm**:
```bash
npx @openuidev/cli@latest create --name openui-mastra-chat
```
**pnpm**:
```bash
pnpm dlx @openuidev/cli@latest create --name openui-mastra-chat
```
**Yarn**:
```bash
yarn dlx @openuidev/cli@latest create --name openui-mastra-chat
```
**Bun**:
```bash
bun x @openuidev/cli@latest create --name openui-mastra-chat
```
前往新建立的項目目錄:
```bash
cd openui-mastra-chat
```
建立好的應用程式是採用以下結構的 Next.js 項目:
```bash
openui-mastra-chat
└── src
├── app
│ ├── api
│ │ └── chat
│ │ └── route.ts
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── generated
│ └── system-prompt.txt
└── library.ts
```
聊天路由位於 `src/app/api/chat/route.ts`,聊天介面位於 `src/app/page.tsx`,元件庫則位於 `src/library.ts`。OpenUI CLI 會根據你的元件庫寫入 `src/generated/system-prompt.txt`;每當元件庫有變更時,都要重新產生此檔案。
將你的 OpenAI API 金鑰加入 `.env.local`:
```bash
OPENAI_API_KEY=sk-...
```
> **備註:** OpenUI 需要模型 Provider 的 API 金鑰。你可以使用 Mastra 支援的任何 Provider,並在下一步調整 Agent 設定。
2. 安裝 Mastra 依賴套件及適用於 Mastra 的 AG-UI 轉接器:
**npm**:
```bash
npm install @mastra/core @ag-ui/mastra @ag-ui/core zod
```
**pnpm**:
```bash
pnpm add @mastra/core @ag-ui/mastra @ag-ui/core zod
```
**Yarn**:
```bash
yarn add @mastra/core @ag-ui/mastra @ag-ui/core zod
```
**Bun**:
```bash
bun add @mastra/core @ag-ui/mastra @ag-ui/core zod
```
`@ag-ui/mastra` 會把 Mastra `Agent` 封裝在會發出 [AG-UI 協定](https://docs.ag-ui.com)事件的 `MastraAgent` 中。OpenUI 的 `agUIAdapter()` 會在用戶端接收這些事件。
3. 開啟 `src/app/api/chat/route.ts`。使用來自 `@mastra/core/tools` 的 `createTool`,定義 Agent 所需的任何 Tool:
```typescript
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
const getWeather = createTool({
id: 'get_weather',
description: 'Get current weather for a city.',
inputSchema: z.object({ location: z.string().describe('City name') }),
execute: async ({ location }) => {
return { location, temperature_celsius: 22, condition: 'Clear' }
},
})
```
將 Mastra `Agent` 封裝在 `MastraAgent` 中。注入產生的系統提示,讓 Agent 知道如何使用 OpenUI 元件庫:
```typescript
import { MastraAgent } from '@ag-ui/mastra'
import { Agent } from '@mastra/core/agent'
import { readFileSync } from 'fs'
import { join } from 'path'
const systemPrompt = readFileSync(join(process.cwd(), 'src/generated/system-prompt.txt'), 'utf-8')
const agent = new MastraAgent({
agent: new Agent({
id: 'openui-agent',
name: 'OpenUI Agent',
instructions: `You are a helpful assistant. Use tools when relevant.\n\n${systemPrompt}`,
model: {
id: 'openai/gpt-5.6-sol',
apiKey: process.env.OPENAI_API_KEY,
},
tools: { getWeather },
}),
resourceId: 'chat-user',
})
```
匯出 `POST` 處理常式,以 Server-Sent Events (SSE) 串流傳送 Agent 的 AG-UI 事件:
```typescript
import type { Message } from '@ag-ui/core'
import { NextRequest } from 'next/server'
export async function POST(req: NextRequest) {
const { messages, threadId }: { messages: Message[]; threadId: string } = await req.json()
const encoder = new TextEncoder()
const stream = new ReadableStream({
start(controller) {
const subscription = agent
.run({ messages, threadId, runId: crypto.randomUUID(), tools: [], context: [] })
.subscribe({
next: event => {
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`))
},
complete: () => {
controller.enqueue(encoder.encode('data: [DONE]\n\n'))
controller.close()
},
error: error => {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify({ error: error.message })}\n\n`),
)
controller.close()
},
})
req.signal.addEventListener('abort', () => subscription.unsubscribe())
},
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
Connection: 'keep-alive',
},
})
}
```
4. 將 OpenUI `` 聊天介面連接至路由。使用 `fetchLLM()` 建立 `llm` 轉接器,並將 `streamAdapter` 設為 `agUIAdapter()`,讓 OpenUI 知道要解析 AG-UI 事件。
```tsx
'use client'
import '@openuidev/react-ui/components.css'
import { AgentInterface, agUIAdapter, fetchLLM } from '@openuidev/react-ui'
import { openuiChatLibrary } from '@openuidev/react-ui/genui-lib'
const llm = fetchLLM({
url: '/api/chat',
streamAdapter: agUIAdapter(),
})
export default function Page() {
return (
)
}
```
`componentLibrary` 屬性控制模型可產生哪些元件。你可以把 `openuiChatLibrary` 換成自己的元件庫,以限制或擴充輸出。
5. 啟動開發伺服器:
**npm**:
```bash
npm run dev
```
**pnpm**:
```bash
pnpm run dev
```
**Yarn**:
```bash
yarn dev
```
**Bun**:
```bash
bun run dev
```
開啟 。你現在可以透過 OpenUI 聊天介面與 Mastra Agent 對話,而模型在串流傳送內容時,結構化 UI 亦會逐步呈現。
## 使用 AG-UI 進行串流傳送
OpenUI 會接收 [AG-UI 協定](https://docs.ag-ui.com),這是一種不受傳輸方式限制、適用於 AI Agent 的已定義類型事件串流。`@ag-ui/mastra` 會將 Mastra `Agent` 轉換成此協定:
- 伺服器會呼叫 `agent.run({ messages, threadId, runId, ... })`,並將每個發出的事件序列化為 SSE 訊息。
- 用戶端會將 `fetchLLM({ streamAdapter: agUIAdapter() })` 傳遞至 ``,由後者把 SSE 串流解析成驅動 OpenUI Lang 呈現的內部事件。
`threadId` 會把跨請求的對話連繫起來,而 `runId` 則識別單次執行。請為每個請求產生新的 `runId`,並在用戶端保存 `threadId`。
## 元件庫
OpenUI 會根據元件庫產生 UI。元件庫定義可使用的元件、其屬性,以及如何指示模型使用這些元件。
### 內置元件庫
`@openuidev/react-ui` 提供兩個可直接使用的元件庫:
- `openuiChatLibrary`:聊天介面元件(卡片、表單、表格、圖表)。
- `openuiDashboardLibrary`:儀表板和資料密集型介面的元件。
將元件庫傳遞至 ``,即可讓模型使用其中的元件。
### 自訂元件庫
如要限制或擴充輸出,請在 `src/library.ts` 定義自己的元件庫,並匯出部分元件。將該元件庫傳遞至 ``,並在元件庫每次有變更時重新產生系統提示:
**npm**:
```bash
npx @openuidev/cli generate src/library.ts --out src/generated/system-prompt.txt
```
**pnpm**:
```bash
pnpm dlx @openuidev/cli generate src/library.ts --out src/generated/system-prompt.txt
```
**Yarn**:
```bash
yarn dlx @openuidev/cli generate src/library.ts --out src/generated/system-prompt.txt
```
**Bun**:
```bash
bun x @openuidev/cli generate src/library.ts --out src/generated/system-prompt.txt
```
產生的提示會由 API 路由讀取並合併至 Agent 的 `instructions`,讓 Agent 確切知道可以發出哪些元件。