让 Agent 写界面,现在的结果通常是"看起来对":布局像那么回事,颜色也挑得不错,但你用键盘 Tab 一遍就发现焦点丢了,下拉菜单按 Esc 不关,屏幕阅读器读出来是三个没有名字的按钮。模型会写组件,却不太懂交互行为。
Vercel Labs 开源的 UI SDK,换了一个供给方式:不给 Agent 成品组件,给它一组交互行为。项目在 11 月 12 日把 6 个包发到 npm,版本 0.1.0-beta.1,Apache-2.0;仓库 vercel-labs/ui 截至 2026-11-14 约 4,200 星、113 个 fork、454 次提交。
它把 Select 拆成了零件
官方的例子很好懂:你不再丢给 Agent 一个写好的 Select,而是给它一个弹层(popup)、一个选择模型(selection)和一个键入跳转(typeahead)。这三块可以拼成下拉框,也可以拼成自动补全、组合框,或者一个还没人设计过的控件。
代码层面分两步。definePrimitive 定义一个"原语",声明状态、API 和插槽(slot);插槽把属性、事件和行为挂到具体的 DOM 部件上。仓库 README 的例子是一个搜索框,它的 Clear 按钮只在有文字时出现:
import { createFocusRecovery, definePrimitive } from "@ui-sdk/core"
const searchField = definePrimitive({
name: "search-field",
state: () => ({ value: "" }),
api: (context) => ({
set: (value: string) => context.store.set({ value }),
recovery: createFocusRecovery({ fallback: () => context.element("input") }),
}),
slots: (slot) => ({
input: slot({
attrs: (state) => ({ value: state.value }),
on: {
input: (event, { api }) =>
api.set((event.target as HTMLInputElement).value),
},
}),
clear: slot({
present: (state) => state.value !== "",
attrs: () => ({ type: "button" }),
press: ({ api }) => api.set(""),
bind: (el, part) => part.api.recovery.bind(el, part),
}),
}),
})
然后用 createParts 在各自框架里渲染。官方说五个适配器里的 parts 是同一份——一个行为定义,React 和 Svelte 都能用。适配器覆盖 React 19+、Vue 3.5+、Svelte 5.33+、Solid 2 RC 和原生 Web Components。
行为清单包括:createRovingFocus(方向键在列表里移动,整组只占一个 Tab 位)、createTypeahead(敲几个字母跳到对应项)、createPopup(贴着触发元素打开并留在屏幕内)、createSelection(单选多选,支持 Shift 和 Ctrl)、createReorder、createResize、createViewport、createVirtualizer。文档把它们分成 10 类,从"焦点与按键"到"画布"。预置组件里有 Select、Combobox、Date Picker、Menu、Tree、Color Picker。
这层抽象真正省掉的是重复实现键盘和 ARIA:焦点陷阱、roving tabindex、Esc 关闭、点击外部关闭、屏幕阅读器需要的属性,这些每次让 Agent 从零写,都会漏。
更值得看的是另一半:让 Agent 直接操作 UI
UI SDK 还有一条 agent bridge。在页面渲染前调用 createAgent(),渲染后 Agent 可以 agent.snapshot() 拿到组件清单——每个组件有引用、类型、标签、当前值和可用动作:
{
"ref": "c1",
"component": "input",
"label": "Project name",
"value": "Apollo",
"actions": ["setValue", "clear"]
}
然后 agent.act() 直接调用动作,比如把项目名改成 Atlas。这条路绕开了截图和坐标:Agent 不用先"看见"输入框在哪,再算点击位置。
权限控制也留了口子。应用可以传一个 allow 回调,批准或拒绝 Agent 请求的动作;变更回调会收到 source: "agent",可以撤销这次改动。文档里另有历史与撤销、以及通过 WebMCP 把工具注册给支持该协议的浏览器的做法。
这个思路比"让 Agent 更会截图"更靠谱:截图是给人看的输出格式,机器操作 UI 最该拿到的是一张带动作的状态表。
那个 69%,该信一半
Vercel 自己做了一个项目设置页的基准:Agent 要改项目名、改成员、改通知设置,然后保存。对照组用浏览器工具,实验组用 UI SDK 加 agent bridge,两边都是 Sonnet、经 Claude Code 跑,同一个任务和系统提示词。
| 指标 | 浏览器工具 | UI SDK + bridge | 变化 |
|---|---|---|---|
| 工具调用 | 16 | 5 | −69% |
| 读取文本 | 39.8k 字符 | 6.8k 字符 | −83% |
| 耗时 | 41 秒 | 16 秒 | −61% |
| 成本 | $0.073 | $0.032 | −56% |
数字是真的,但样本很小:一个任务、每种方法三次运行、取中位数。官方也承认,对照组那个页面是用另一套组件库做的——也就是说,差异里有一部分来自"两边的组件实现不一样",而不全是"bridge 比截图强"。方向可以信,幅度不要拿去写进容量规划。
和 Radix、React Aria 差在哪
都是 headless,都把外观留给你,区别在"谁来做拼接"和"有没有给 Agent 留接口"。
| 项目 | 模型 | 框架 | 给 Agent 的层 |
|---|---|---|---|
| UI SDK | 行为,由 Agent 组合成组件 | React / Vue / Svelte / Solid / Web Components | 有(技能、llms.txt、createAgent、WebMCP) |
| Radix Primitives | 无样式组件 | React | 未公开 |
| React Aria Components | hooks 与组件 | React | 未公开 |
| Base UI | 无样式组件 | React | 未公开 |
| Ark UI | 基于 Zag.js 状态机的组件 | React / Vue / Solid / Svelte | 未公开 |
公平地说,React Aria 的 hooks 和 Ark UI 的 Zag.js 早就在做"行为与标记分离",UI SDK 的差异不在这个理念,而在它是按"Agent 是主要使用者"来设计的:技能文件、安装进 AGENTS.md 的指引、包内的 llms.txt、以及运行时的 bridge。
想试的话,顺序是这样
- 装包:
npm install @ui-sdk/react @ui-sdk/core(框架换成对应适配器)。 - 给 Agent 装技能:
npx skills add vercel-labs/ui --skill ui-sdk,它会在项目里加一份说明,指向包内的llms.txt。 - 从
definePrimitive写一个最小原语开始,别一上来就重写整套设计系统。 - 如果要接 bridge,记得在页面渲染前创建,并且先把
allow回调写出来——默认放行等于把整个应用的操作权交给模型。
该信多少
- 本文读了仓库 README 与官方文档的 Agents 页面,没有安装、没有跑基准;四个数字都来自 Vercel 自测。
- 项目处于 beta,受 Vercel 公测条款约束,框架、API、文档都可能变;
0.1.0-beta.1这个版本号本身就是提醒。 - 4,200 星不算多,社区案例还很少,"Agent 用起来到底顺不顺"目前只有厂商演示。
- 对等依赖很硬:React 19、Vue 3.5、Svelte 5.33 起步,老项目接入前先看自己的版本。
一句话判断
UI SDK 押的是一个方向性判断:Agent 生成界面的瓶颈已经从"会不会写组件"移到"懂不懂交互行为",而 Agent 操作自己生成的界面,也不该靠截图和坐标。方向我同意,但 0.1.0-beta.1 的东西适合拿来试水一个内部页面,不适合这个月就压在一套对外产品上。
来源
- vercel-labs/ui(README、安装方式、适配器与许可;约 4,200 星,Apache-2.0,2026-11-14 读取)
- UI SDK 文档:Built for Agents(primitive 示例、agent bridge、基准表)
- MarkTechPost:Vercel Releases UI SDK(版本、npm 发布日期、与同类库对比)
说明:本文为官方文档解读,未安装或运行 UI SDK;性能数字为 Vercel 自测,样本为一个任务、每种方法三次运行。
——诺谱