Skip to main content
OpenUI 是一个生成式 UI 库,它允许语言模型以一种名为 openui-lang 的声明式格式生成完整的交互式 UI。代理不是返回聊天消息,而是返回一个包含卡片、图表、表格、选项卡和表单的组件树,然后由 Renderer 将其转换为真实的 React UI。 这种集成非常适合数据丰富的输出,如报告、仪表板和数据探索器,其中模型既是数据分析师又是 UI 设计师。

工作原理

  1. 生成系统提示词: 在启动时调用一次 openuiLibrary.prompt();它会生成一个完整的 openui-lang 参考,模型使用它来编写有效的组件树
  2. 在第一条消息时注入: 在新对话开始时,将系统提示词作为初始系统消息发送
  3. 模型编写 openui-lang: 模型以类似 root = Stack([header, kpis, chart]) 的程序形式响应,而不是散文
  4. 使用 Renderer 渲染: 将文本传递给 OpenUI 的 Renderer 和组件库;它会解析并渲染该树

安装

OpenUI 需要 React 19+ 和 zustand。前端代码仅限 React;LangGraph 代理后端可以用 TypeScript 或 Python 编写。

导入组件样式

在你的 CSS 入口点或直接在根组件中导入 OpenUI 的捆绑样式:

生成系统提示词

OpenUI 提供了一个 openuiLibrary.prompt() 函数,用于生成完整的 openui-lang 参考,包含所有组件签名、语法规则、流式传输提示和示例。在模块加载时调用一次:
preamble 会覆盖默认的角色设定。添加 additionalRules 以注入特定任务的约束:

通过 useStream 注入系统提示词

将系统提示词作为每个新线程的第一条消息发送。检查 stream.messages.length === 0 以检测新线程,并在前面添加一条 system 消息:

使用 Renderer 渲染

将 AI 消息的文本内容直接传递给 Renderer 以及 openuiLibrary
在活动流期间传递 isStreaming={true},以便 Renderer 在定义到达时优雅地处理未解析的引用。

openui-lang 格式

模型编写的是一个程序,而不是 JSON 规范。每条语句都是一个赋值;root 是入口点。官方提示词会教模型这种格式,包括提升(hoisting)——先编写 root,这样 UI 外壳会立即显示:
启用提升(推荐)后,root 行会首先编写,这样页面结构会立即出现,并且每个部分会在模型定义它时逐步填充。

渐进式渲染工具

直接将 useStream 连接到 Renderer 会导致在每个流式令牌上重新渲染,并且每个响应会产生数百次无操作的重新解析。这会导致图表组件在其数据尚未到达时崩溃。以下工具解决了这些问题: 将完整代码块复制到你的项目中,并将 stable 传递给 <Renderer>

后续查询

OpenUI 的 Button 组件支持 continue_conversation 动作类型。当用户点击后续按钮时,Renderer 会触发 onAction,上面的 AIMessageView 会将按钮的标签作为下一条用户消息提交,这与在输入框中输入的代码路径完全相同。 通过系统提示词中的 additionalRules 为每个报告添加“进一步探索”部分:

最佳实践

  • 在模块加载时生成系统提示词: 不要在 React 组件内部;提示词有几千字节,应该只计算一次
  • 仅在新线程时注入系统提示词: 检查 stream.messages.length === 0,并在后续轮次跳过注入,以避免在线程历史记录中重复提示词
  • 使用提升顺序: 先编写 root = Stack([...]);UI 外壳会立即出现,并且各部分会在模型定义每个部分时逐步填充
  • 仅在完整语句时更新: 避免在每个令牌上重新渲染 Renderer;仅在完整语句(name = ComponentCall(...))到达时更新
  • 渲染前验证图表数据: 图表组件需要在包含在稳定快照中之前定义其 Series 和标签数组
  • 保持驼峰命名变量名: openui-lang 解析器仅接受驼峰命名标识符;在系统提示词的 additionalRules 中强化这一点