news 2026/9/21 3:19:59

json-render 的 Jev 组合实验:用受限候选集与评估器协议驱动生成式 UI 树

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
json-render 的 Jev 组合实验:用受限候选集与评估器协议驱动生成式 UI 树

json-render 的 Jev 组合实验:用受限候选集与评估器协议驱动生成式 UI 树

【免费下载链接】json-renderThe Generative UI framework项目地址: https://gitcode.com/GitHub_Trending/js/json-render

本文是 json-render(Generative UI framework)仓库内apps/web(官网与 Playground)中Jev 组合实验(Jev composition experiment)的完整技术指南。该实验在 Playground 的/playground页面提供 "default / jev" 双模型切换,让 Jev 模型通过 Vercel AI Gateway 从组件目录中挑选原子候选、组合出一棵可交互的 UI 树,并在后续请求中以 add / replace / remove / move 的顺序编辑协议持续迭代。读完本文,你将掌握该实验的运行方式、密钥与限额配置、两阶段批处理组合流程、顺序编辑协议,以及其底层experimental_*公共 API 的调用契约与实现原理。

实验定位:一个受目录约束的 UI 组合器

apps/web/README.md中明确说明,Playground 的/playground页面内置一个 "default / jev" 切换开关:default对应常规的生成式模型(通过AI_GATEWAY_API_KEY调用),而jev是一个实验选项,鼠标悬停或聚焦其信息图标即可看到实验性状态说明。Jev 选项是@json-render/core中可复用 APIexperimental_composeSpecexperimental_createEvaluator参考消费方(reference consumer)

  • Jev 从 Playground 的组件目录(catalog)与允许的动作绑定(action bindings)中组合并编辑 UI 树;
  • 服务端通过 Vercel AI Gateway 完成评估调用,Jev 专用密钥为JEV_AI_GATEWAY_API_KEY,默认模型继续使用AI_GATEWAY_API_KEY
  • 后续追问(follow-up)以所选版本作为initialSpec,可以对树执行新增、替换、删除、移动,且早前版本保持原样不变(不可变版本历史);
  • 与 default 模型共用同一套提示输入、版本历史、spec/流式检查器与功能预览;
  • 共享的/api/generate端点负责流式输出 spec 补丁(spec patches)与决策元数据。

实验的完整设计文档位于 apps/web/lib/jev/README.md,其中包含运行方式、架构与限制的权威说明,是本文的骨架来源;文中所有源码引用均来自当前仓库。

运行实验:密钥、启动与试玩路径

密钥配置

apps/web/.env.local或服务器环境中设置:

JEV_AI_GATEWAY_API_KEY=<你的 Vercel AI Gateway 密钥>

要点(来自 apps/web/lib/jev/README.md):

  • Playground 为 Jev 使用这个专用 Gateway 密钥;default 模型继续使用AI_GATEWAY_API_KEY
  • Jev 不会回退到 default 模型的密钥,未配置时服务端会返回 503(见下文);
  • Gateway 团队必须放行typesafe-aiprovider;无需单独的 TypeSafe API key
  • 该密钥只应存在于服务端。核心包中的 experimental-evaluator.ts 对apiKey的注释明确写着:"Server-side Vercel AI Gateway key. Never expose this in browser code.",并在空密钥时直接抛出 "A Vercel AI Gateway API key is required."。

启动开发服务器

从仓库根目录执行:

pnpm --filter web dev

apps/web/package.jsondev脚本为portless json-render next dev --turbopack,依赖全局工具portless(未安装时会提示先执行npm i -g portless)。使用命令打印的 portless URL 并追加/playground访问;在 HTTPS 代理开启时即为https://json-render.localhost/playground

推荐的试玩流程

文档给出的端到端验证路径是:选择 Jev → 选择Create account settings示例 → 发送请求 → 编辑姓名、打开通知开关、点击Save changes、再点Reset。需要特别注意的语义约束:

  • 动作处理器只在用户交互时运行:Jev 只负责把动作绑定写入 spec,绝不会在生成阶段执行它们;
  • 表单提交仅做校验并弹出 toast 演示:它不会认证用户,也不会真的发送消息;
  • 所有业务数据均为合成数据(synthetic)。

Jev 如何产出 spec:两次有限选择 + 顺序编辑协议

Jev 是"组合式"模型:它暴露 Choice、Boolean、Score 三类输出,不产出自由格式的 JSON 或散文。因此新 UI 的构建被表达为两批有限选择(batches of finite choices),完整流程共 5 步:

  1. 第一批(select 阶段):在一次评估中同时询问根元素(root)与各独立组件的成员资格问题。互斥的资源变体共享同一个问题(同一问题内二选一),可复用的配方(recipe)则给出有界的使用数量。候选值中包含应用自己拥有的状态/动作绑定。
  2. 组装并校验预览:选定内容立即组装、校验并流式输出预览。预览采用目录顺序(catalog order)与根的默认插槽(default slot)。当同一配方/资源同时被选为根时,根选择优先于推测性成员资格(对应 experimental-composition-batch.ts 中if (first.resource && first.resource === root.resource) continue;以及可重复配方计数中减去根占用的逻辑)。
  3. 第二批(layout 阶段):针对实际选中集合,在第二次评估中询问最终的父级插槽(parent slots)与同级位置(sibling positions)。组合树需整体校验(包括深度与环),再流式输出。相同位置保持目录顺序。若只有一个根、或只有一个子元素且只有单个插槽,则无需第二次调用;整个流程也不需要单独的 finish 调用。
  4. 后续追问:以当前选中 spec 走顺序编辑协议:add(新增)、replace(替换)、remove(删除)、move/reorder(移动/重排)。替换与移动先选择目标,第二次评估再选择合法配方或目标位置。未被要求改变的元素与早前版本保持不变。
  5. 追踪(trace):每次评估对应一条 trace;批处理 trace 使用select/layout作为操作名,独立决策放在answers字段中,计时与用量按每次调用计一次。provider 出错或组合布局非法时,保留最后一份有效预览并报告失败。

Batch 阶段的关键代码见 experimental-composition-batch.ts:composeBatch先并行发起select调用(root问题 + 按资源分组生成的select_N问题),立即组装出临时 spec 并yield snapshot()预热预览;随后并行发起layout调用(parent_<id>order_<id>问题),在私有克隆上完成挂载后整体validate再发布。批处理避免了每个组件一次网络往返。作为对比,公共 API 也支持strategy: "sequential"(每次只做一个操作)以及现有的自定义评估器(custom evaluators)。

值得强调的事实(README 原话):实验中没有完整的 UI 模板,也没有生成式模型调用。示例提示按钮只填充请求文本;具体包含哪些元素、顺序、分组、采用哪些动作绑定,全部由 Jev 决定;外观与行为由 registry 负责;序列化 JSON 也不是 Jev 写的,而是代码根据选择组装而成。

平台必须提供什么:原子候选与合成内容

组件目录约束了组件名、props 与事件,但字符串与数组 props 仍是开放取值。实验用"平台自有内容 + 绑定配方"来封堵这一剩余空间,具体由 apps/web/lib/jev/grammar.ts 提供:

  • 17 种组件类型:Card、Stack、Grid、Heading、Avatar、Badge、Input、Textarea、Select、Checkbox、Switch、Button、Text、Metric、BarGraph、Table、Separator;
  • 表单字段、校验规则、标签、合成个人资料与商务数据,以及两个允许的目录动作(formSubmitsetState)。个人资料候选包含头像、显示名、角色、简介、邮箱、所在地与会员徽章,全部绑定到平台提供的记录对象(platformState.profile,例如 "Maya Chen" / "Product designer");
  • 若干对布局 props 与按钮文案有用的取值;请求中的带引号标题会被复制进额外的 Heading 候选中(grammar.ts通过prompt.matchAll(/"“["”]/g)抽取引号内文本,与内置标题列表去重后生成heading_N候选)。

这些是原子元素候选(atomic element candidates),而非页面模板。宿主应用完全可以从自己的数据 schema、真实记录、本地化文案与允许的操作中构建同类候选——实验只是把这些值放在grammar.ts里,应用则向可复用的 core API 提供自己的候选。目前不支持的场景包括:同一字段在多个表单中重复出现、以及任意的全新文本/数据(例如要求模型凭空编造一段话或一条新数据)。

每个候选同时固定了一种组件配置:预置好的 revenue BarGraph 可以被选中并移动,但想换成 LineGraph 就需要另一个候选。应用既可以把 props 绑定到自己的实时状态,也可以按请求动态构造候选,数据不必硬编码。Jev 只决定树、分组与区块顺序,且只能在这些被提供的配置范围内选择。

grammar.tssave候选为例,可见"候选 = 固定配置 + 动作绑定"的形态:

add( "save", "Button: Save changes. Bind press to setState to update the visible saved-status text. Local demo only.", "Button", { label: "Save changes", variant: "primary", disabled: false }, "action:save", { press: { action: "setState", params: { statePath: "/status", value: "Changes saved locally." }, }, }, );

候选的公共契约定义在 experimental-compose.ts 的Experimental_CompositionCandidateiddescriptionelement(仅允许type/props/on/visible,即"原子元素")、root(是否可作为根,默认 true)、maxUses(默认 1,可复用的布局元素可放大数量)、resource(共享同一 resource 的候选互斥)。validateCandidate会对每个候选做严格校验:组件必须存在于目录、props 必须通过对应 Zod schema、事件必须在该组件声明的事件列表内、动作必须命中目录actions或内置动作、回调(onSuccess/onError)也必须引用目录动作——这保证了"模型永远无法发明组件或动作"。

组合器公共 API 与限制:experimental_composeSpec

composeUI(apps/web/lib/jev/compose.ts)是 Playground 对公共 API 的薄封装,其调用即完整呈现了experimental_composeSpec的参数形态(核心选项见Experimental_ComposeSpecOptions):

experimental_composeSpec({ catalog: playgroundCatalog, candidates: buildCandidates(prompt), initialSpec, // 后续追问时传入所选版本 initialState: { ...platformState, ...initialSpec?.state }, elementDescriptions: /* 仅共享展示文案 */, prompt, signal, evaluate, // experimental_createEvaluator 默认实现 maxSteps: MAX_ELEMENTS, // 14 maxElements: MAX_ELEMENTS, // 14 maxDepth: 4, context: { platform: "Available: a synthetic user profile ..." }, instructions: { root, next, parent }, // 应用级构造指引 });

各参数的默认值与语义(源码注释与校验逻辑):

参数默认值说明
strategy"batch"新树默认走批处理 select/layout;"sequential"为一次一个操作
maxElements32批处理创建的元素预算(含根)
maxSteps32评估次数预算(含顺序化的收尾决策)
maxDepth8根深度为 1;Playground 收紧为 4
initialSpec编辑既有树;克隆并校验后才参与评估
elementDescriptions显式共享给评估器的既有元素描述,按元素 ID 键控
initialState写入 spec、绝不发给评估器
context显式共享给评估器的应用上下文
instructionsroot/next/parent三档应用级指引

三个数值参数都会先经过positiveInteger校验(必须是 ≥1 的安全整数),strategy只能是batchsequential,候选 ID 必须匹配/^[a-zA-Z][\w-]*$/且不得为finish/unavailable

值得展开的两个设计点:

  • 表达式子集:组合器 V1 刻意不提供 repeat 作用域、计算函数或自定义指令。checkExpressions只允许$state$bindState$and$or四种以$开头的键,且$state/$bindState的值必须是状态路径字符串。因此既有的可编辑 spec 必须使用受支持的表达式子集并构成合法树。
  • 隐私边界elementDescriptions只共享定位既有元素所需的展示文案(title/text/label/name/direction等字符串 props 与匹配候选的描述),绝不共享原始状态、字段输入值、绑定配方、动作参数或渲染器状态

组合器的事件流是一个异步生成器:每个step事件携带一棵分离拷贝(detached clone)的 spec 快照与单步决策(choice、parent、slot、confidence、elapsedMs、inputTokens),complete事件携带最终 spec、全部步骤、总耗时、总输入 token 与stopReasonfinish | limit | unavailable)。composeUI在 complete 事件上额外估算美元成本:inputTokens * 0.042 / 1e6(按每百万 token $0.042 估算),token 为 null 时成本也为 null。

限额一览

Jev 组合器把每次请求的边界写死为(README 原文):

  • 每个新批处理最多14 个元素
  • 每个请求最多14 次评估调用
  • 嵌套深度最多4 层
  • 单次 provider 请求最多10 秒experimental_createEvaluatortimeoutMs默认值 10000);
  • 整体上限55 秒(response.ts 中用AbortSignal.timeout(55000)实现,与 route 的maxDuration = 60呼应);
  • 所选 seed(初始 spec)最多含100 个元素previousSpecSchemaObject.keys(elements).length <= 100)。

达到限额、被取消或出错时,保留当前预览并标记为 partial(部分)。共享端点使用 web app 的请求频率限制器(minuteRateLimit/dailyRateLimit,见 api/generate/route.ts)。两个模型都编辑所选版本;Clear 重新开始。Stream 标签页会把构造决策与 spec 补丁并列展示。provider 调用与 spec 组装过程从不执行所选 UI 动作——动作只在真实用户交互时由运行时执行。

已知局限(诚实边界)

组合器校验树结构与候选值,但不保证 Jev 选择了正确的 UI。根选择、分组、何时停止都需要规划能力,而这是 Jev 有文档记载的弱点。置信度(confidence)会被展示,但没有质量门:多个布局选择可能都合理,且并未校准出一个通用阈值。

因此文档建议显式命名必需区块,例如:先要"顶部的订单表",再要"一行 revenue/orders/customer 指标",最后要"周营收图表"。过于简短的请求(如"带顶部表格的 dashboard")可能只选中一张表。后续追问可以移动既有表格而无需重建其数据。

推荐的追问示例:

  • 用户卡片:Design a user profile cardRemove the bioMake the avatar smaller
  • 设置页:Remove the email notifications switchChange the heading to "Account settings"Move the email field above the name field

服务端共享既有元素的展示标签与匹配候选的描述来识别编辑目标,同样不共享原始状态与已输入的字段值。编辑保留所选 spec 的状态(与 default 模型流程一致);交互式预览状态不会被保存进版本历史。

评估器传输层:Gateway v4 实验性 evaluation 协议

服务端使用 Gateway 的实验性 v4 evaluation 传输,模型标识为typesafe-ai/jev(experimental-evaluator.ts)。该实现已在@ai-sdk/gateway@4.0.85上验证通过。它用原生 fetch直连 Gateway 的 evaluation 端点,从而避免升级工作区内的 AI SDK 6 依赖、也不必绕过其最小发布年龄限制。协议可能变化;文档建议在适当时机迁移到符合资格的 AI SDK evaluation API 并用纯模型字符串(plain model string)调用。

实现要点:experimental_createEvaluator校验密钥与timeoutMs(1 ~ 2147483647 的整数)后返回一个满足Experimental_CompositionEvaluator签名的异步函数;请求体为{ state, questions },请求头携带Authorization: Bearer <apiKey>ai-gateway-protocol-version: 0.0.1ai-gateway-auth-method: api-keyai-evaluation-model-specification-version: 4ai-model-id: typesafe-ai/jev。响应经 Zod schema 严格解析:answers中每个问题必须是{ type: "choice", choice }且 choice 必须落在该问题的 criteria 键内,置信度来自providerMetadata.typesafe.confidence(0~1),usage.inputTokens为非负整数。任何越界选择都会抛错,从而保证评估器无法输出超出候选集的操作。组合器侧还有evaluateWithSignal兜底:即使自定义评估器忽略 abort 信号,也会用Promise.race强制中止。

评估器的自定义替换能力意味着应用可以不依赖 Gateway:只要实现(request: { state, questions, signal }) => Promise<{ answers, usage? }>即可接入任意后端或本地评估逻辑(测试即用 scripted 假评估器完成)。

文件地图与验证命令

实验相关文件(相对仓库根目录):

  • apps/web/lib/jev/grammar.ts:Playground 自有的取值与原子候选;
  • packages/core/src/experimental-compose.ts:公共、与 provider 无关的组合器;
  • packages/core/src/experimental-composition-batch.ts:新树的并行成员资格与布局决策;
  • packages/core/src/experimental-composition-tree.ts:内部 seed 校验与树编辑辅助(attach/detach/replaceElement/indexTree 等);
  • packages/core/src/experimental-evaluator.ts:公共 Gateway 评估器适配器;
  • apps/web/lib/jev/compose.ts:带 Playground 指令与成本展示的公共 API 消费方;
  • apps/web/app/api/generate/route.ts:共享的频率受限端点,按所选模型分发(model === "typesafe-ai/jev"时走createCompositionResponse,否则走 default 模型);
  • apps/web/lib/jev/response.ts:把组合快照适配为 Playground 的 JSONL spec 补丁与决策元数据(__meta: "decision"/__meta: "composition"/__meta: "error");
  • apps/web/components/playground.tsx:共享的模型切换、实验信息 tooltip、提示输入、版本历史、实时预览与检查器;
  • apps/web/lib/jev/compose.test.ts:覆盖结构、动作边界、未知用量、取消与限额的测试。

运行验证(仓库根目录):

pnpm exec vitest run packages/core/src/experimental-compose.test.ts packages/core/src/experimental-evaluator.test.ts apps/web/lib/jev/compose.test.ts pnpm type-check

compose.test.tsscripted假评估器按脚本依次返回next/parent选择,既验证了批处理路径(root/select_*/order_*/parent_*问题的应答组装),也验证了顺序编辑路径(add/replace/remove/move 的决策消耗)。

对应用接入的启示

Jev 组合实验示范了一条与"自由生成 JSON spec"截然不同的路线:把生成问题转化为有限选择问题。应用若要把这类组合器接入自己的产品,核心要点可归纳为:

  1. 目录是硬边界:组件、props、事件、动作必须全部在 catalog 中声明,模型无法越界;
  2. 候选是内容载体:真实数据、本地化文案与允许操作都应预构造成原子候选(可绑定 live state,也可按请求动态生成),模型不发明文本与数据;
  3. 两阶段批处理省往返:select 并行决定成员资格,layout 并行决定挂载点与顺序,均按一次调用计费;
  4. 编辑协议是顺序的:add 之外,replace/move 采用"先选目标、再选方案"的两段式决策,remove 直接删除子树,未触达元素保持原样;
  5. 安全与隐私默认成立:provider 调用与组装过程不执行任何 UI 动作;状态不进评估器;共享给模型的只有描述性文案;
  6. experimental_API 可在任何版本中变化:文档明确要求接入方锁定精确版本(pin exact versions)。

对希望从源码继续深入研究的读者,建议从 packages/core/src/experimental-compose.ts 的experimental_composeSpec入口读起,依次对照 experimental-composition-batch.ts 与 experimental-composition-tree.ts,最后用 apps/web/lib/jev/compose.test.ts 中的脚本化评估器反推每一步的状态机行为。

【免费下载链接】json-renderThe Generative UI framework项目地址: https://gitcode.com/GitHub_Trending/js/json-render

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 3:19:54

MXNet Clojure KVStore API 实战:掌握多设备梯度聚合与键值对管理

MXNet Clojure KVStore API 实战&#xff1a;掌握多设备梯度聚合与键值对管理 【免费下载链接】mxnet Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript…

作者头像 李华
网站建设 2026/9/21 3:19:14

Hydra 源码深度解析:配置管理与实验调度机制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/21 3:17:44

CANoe SOME/IP实战:ARXML语义映射与VCODM故障定位

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/21 3:09:19

@ice/plugin-rax-compat 使用指南:将 rax-app 项目平滑迁移到 ice.js

前端Web框架SSR前端构建插件系统微前端跨平台 【免费下载链接】ice &#x1f680; ice.js: The Progressive App Framework Based On React&#xff08;基于 React 的渐进式应用框架&#xff09; 项目地址&#xff1a; https://gitcode.com/gh_mirrors/ice1/ice 点击查看 免费下…

作者头像 李华