跳到主要内容

使用 OpenUI

OpenUI 是生成式 UI 的开放标准。它将紧凑的流式优先语言(OpenUI Lang)与 React Runtime 和内置组件库相结合,使模型输出能在流式传输过程中渲染为结构化 UI。

OpenUI 通过 AG-UI 协议连接到 Mastra。@ag-ui/mastra 适配器会封装 Mastra Agent 并发出 AG-UI 事件,再由 OpenUI 的 agUIAdapter() 在客户端解析。

提示

完整的可运行示例请参阅 OpenUI 仓库中的 mastra-chat 示例。

集成指南
集成指南的直接链接

将 Mastra 嵌入 Next.js API 路由,并通过 AG-UI 协议将 OpenUI <AgentInterface /> 聊天界面连接到该路由。

  1. 搭建新的 OpenUI 应用:

    npx @openuidev/cli@latest create --name openui-mastra-chat

    进入新创建的项目目录:

    cd openui-mastra-chat

    搭建出的应用是一个具有如下结构的 Next.js 项目:

    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

    .env.local
    OPENAI_API_KEY=sk-...
    备注

    OpenUI 需要模型 Provider Key。你可以使用 Mastra 支持的任意 Provider,并在下一步中相应调整 Agent 配置。

  2. 安装 Mastra 包和适用于 Mastra 的 AG-UI 适配器:

    npm install @mastra/core @ag-ui/mastra @ag-ui/core zod

    @ag-ui/mastra 将 Mastra Agent 封装在 MastraAgent 中,由后者发出 AG-UI 协议事件。OpenUI 的 agUIAdapter() 在客户端使用这些事件。

  3. 打开 src/app/api/chat/route.ts。使用 @mastra/core/tools 中的 createTool 定义 Agent 所需的 Tool:

    src/app/api/chat/route.ts
    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 组件库:

    src/app/api/chat/route.ts
    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)进行流式传输:

    src/app/api/chat/route.ts
    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 <AgentInterface /> 聊天界面连接到该路由。使用 fetchLLM() 构建 llm 适配器,并将 streamAdapter 设置为 agUIAdapter(),以便 OpenUI 解析 AG-UI 事件。

    src/app/page.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 (
    <div className="relative h-screen w-screen overflow-hidden">
    <AgentInterface llm={llm} componentLibrary={openuiChatLibrary} />
    </div>
    )
    }

    componentLibrary prop 控制模型可以生成哪些组件。将 openuiChatLibrary 替换为你自己的库,以限制或扩展输出。

  5. 启动开发 Server:

    npm run dev

    打开 http://localhost:3000。现在,你可以通过 OpenUI 聊天界面与 Mastra Agent 聊天;随着模型进行流式传输,结构化 UI 会逐步渲染。

使用 AG-UI 进行流式传输
使用 AG-UI 进行流式传输的直接链接

OpenUI 使用 AG-UI 协议,这是一种与传输方式无关的 AI Agent 类型化事件流。@ag-ui/mastra 将 Mastra Agent 转换为该协议:

  • Server 调用 agent.run({ messages, threadId, runId, ... }),并将每个发出的事件序列化为 SSE 消息。
  • 客户端将 fetchLLM({ streamAdapter: agUIAdapter() }) 传给 <AgentInterface />,后者把 SSE 流解析为驱动 OpenUI Lang 渲染的内部事件。

threadId 将跨请求的对话关联起来,runId 则标识单次执行。请为每个请求生成新的 runId,并在客户端持久化 threadId

组件库
组件库的直接链接

OpenUI 根据组件库生成 UI。组件库定义可用组件、组件的 props,以及如何指示模型使用这些组件。

内置库
内置库的直接链接

@openuidev/react-ui 提供两个可以直接使用的库:

  • openuiChatLibrary:用于聊天界面的组件(卡片、表单、表格、图表)。
  • openuiDashboardLibrary:用于仪表盘和数据密集型界面的组件。

将库传给 <AgentInterface componentLibrary={...} />,使其中的组件可供模型使用。

自定义组件库
自定义组件库的直接链接

如需限制或扩展输出,请在 src/library.ts 中定义自己的库,并导出一部分组件。将该库传给 <AgentInterface />,并在组件库发生变化时重新生成系统提示词:

npx @openuidev/cli generate src/library.ts --out src/generated/system-prompt.txt

API 路由会读取生成的提示词,并将其合并到 Agent 的 instructions 中,使 Agent 准确了解可以输出哪些组件。