> Discover all available pages from the documentation index: https://mastra.zisheng.pro/llms.txt
# 使用 OpenUI
[OpenUI](https://openui.com) 是生成式 UI 的开放标准。它将紧凑的流式优先语言(OpenUI Lang)与 React Runtime 和内置组件库相结合,使模型输出能在流式传输过程中渲染为结构化 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 Key 添加到 `.env.local`:
```bash
OPENAI_API_KEY=sk-...
```
> **备注:** OpenUI 需要模型 Provider Key。你可以使用 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` 封装在 `MastraAgent` 中,由后者发出 [AG-UI 协议](https://docs.ag-ui.com)事件。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' }
},
})
```
使用 `MastraAgent` 封装 Mastra `Agent`。注入生成的系统提示词,使 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` handler,将 Agent 的 AG-UI 事件作为 Server-Sent Events(SSE)进行流式传输:
```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` prop 控制模型可以生成哪些组件。将 `openuiChatLibrary` 替换为你自己的库,以限制或扩展输出。
5. 启动开发 Server:
**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` 转换为该协议:
- Server 调用 `agent.run({ messages, threadId, runId, ... })`,并将每个发出的事件序列化为 SSE 消息。
- 客户端将 `fetchLLM({ streamAdapter: agUIAdapter() })` 传给 ``,后者把 SSE 流解析为驱动 OpenUI Lang 渲染的内部事件。
`threadId` 将跨请求的对话关联起来,`runId` 则标识单次执行。请为每个请求生成新的 `runId`,并在客户端持久化 `threadId`。
## 组件库
OpenUI 根据组件库生成 UI。组件库定义可用组件、组件的 props,以及如何指示模型使用这些组件。
### 内置库
`@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 准确了解可以输出哪些组件。