组件目录(Catalog)
组件目录(Catalog)
定义
组件目录(Catalog)是 json-render 架构中由开发者预先声明的“AI 可用界面能力白名单”。它规定大模型在生成 生成式 UI 时可以使用哪些组件、每类组件有哪些字段、Spec 应该采用什么结构、以及组件可以触发哪些动作。
Catalog 不是最终渲染出来的 UI,也不是某个 React、Vue 或移动端组件实现本身。它更接近一份 schema 契约:AI 只负责在这份契约内产出结构化的 JSON Spec,真正把界面画出来的是后续的渲染器和开发者注册的真实组件。
在来源文章对 json-render 的描述中,核心设计可以概括为:AI 不写 UI,AI 只生成受 Catalog 约束的 JSON Spec;真正渲染像素的是开发者定义和注册的组件。Catalog 因此是 json-render “护栏式生成”的关键边界。
在 json-render 架构中的语境
在 json-render 的流水线中,用户 Prompt 会交给 AI;AI 结合 Catalog 生成 JSON Spec;Renderer 再把 Spec 渲染成真实 UI。这个过程可写成:
- 用户 Prompt → AI + Catalog → JSON Spec → Renderer。
Catalog 位于 AI 生成环节之前,用来告诉模型:
- 允许生成哪些组件类型;
- 每种组件的字段名和字段类型是什么;
- Spec 的根结构、元素集合和子节点引用关系应该如何组织;
- 哪些数据绑定和表达式可以使用;
- 哪些动作可以被组件触发。
这使 json-render 和“直接让 LLM 生成 JSX/HTML/前端代码”的路线不同。直接生成代码时,模型可能发明不存在的组件、输出不可预测结构,甚至带来注入脚本等安全风险;使用 Catalog 时,模型的自由度被限制在开发者定义的 schema 内,不确定性留在模型侧,确定性由开发者掌握。
Catalog 声明的内容
Catalog 通常会声明一组可由 AI 生成的组件类型。例如来源文章中的示例包含:
Card:卡片类组件;Metric:指标类组件;Button:按钮类组件。
它还会声明组件字段。例如:
Card可带title字段;Metric可带value字段;Button可带label字段。
Catalog 还会定义 JSON Spec 的组织结构。来源示例中出现了 root、children、elements 等关键结构:
root表示界面根节点配置;root.children是一个字符串数组,用于引用根节点下的子元素;elements是元素表,用于保存各个具体元素的定义;- 每个元素通过
type字段声明自身组件类型,例如Card、Metric或Button。
动作边界也属于 Catalog 的声明范围。来源示例把 setState 放入动作白名单,表示 AI 生成的界面可以触发该动作,但不能任意调用未登记的动作。
一个简化示例如下:
import { defineCatalog } from '@json-render/core';
import { z } from 'zod';
const catalog = defineCatalog(
z.object({
root: z.object({ children: z.array(z.string()) }),
elements: z.record(z.union([
z.object({ type: z.literal('Card'), title: z.string() }),
z.object({ type: z.literal('Metric'), value: z.number() }),
z.object({ type: z.literal('Button'), label: z.string() }),
])),
}),
{ components: ['Card', 'Metric', 'Button'], actions: ['setState'] }
);
这个示例表达了几个边界:AI 可以输出 Card、Metric、Button 三类元素;Card.title 必须是字符串;Metric.value 必须是数字;Button.label 必须是字符串;根节点只能通过 children 引用子元素;动作白名单中只有 setState。
defineCatalog 与 zod 的作用
zod 在 Catalog 中用于固化 JSON Spec 的结构和字段类型。开发者通过 zod schema 明确规定 Spec 应满足的形状,例如对象层级、数组字段、记录表、联合类型、字面量组件类型,以及字段的字符串或数字类型。
defineCatalog 则把这份 zod schema 与组件、动作等元信息组织成模型可用的契约。换句话说:
zod负责“结构与类型校验”:规定什么 JSON Spec 合法;defineCatalog负责“目录化与契约化”:把 schema、组件白名单、动作白名单组织成 json-render 能使用、也能提示给模型的 Catalog。
因此,Catalog 并不是随口写给模型看的提示词,而是可被程序校验的契约。模型生成的 JSON 必须严格匹配这个契约,才能继续交给渲染器处理。
安全机制与边界
Catalog 的安全性来自“白名单 + schema 校验”。所有 AI 可生成的组件、动作、数据绑定都必须先在 Catalog 中登记。模型如果越界,应该被 schema 校验挡回,而不是直接进入渲染阶段。
典型应被拦截的情况包括:
- 使用未登记组件:例如 Catalog 只登记了
Card、Metric、Button,模型却输出Chart或ScriptBlock; - 字段类型错误:例如
Metric.value需要数字,模型却输出字符串; - 字段结构错误:例如缺少必要的
type,或root.children不是字符串数组; - 越权动作:例如 Catalog 只允许
setState,模型却生成删除数据、发起未授权请求或其他未登记动作; - 不符合整体 JSON Spec 结构:例如
elements不是合法记录表,或元素定义不属于 schema 中允许的联合类型。
这种机制降低了几类风险:
- 幻觉组件风险:模型不能随意编造前端组件并假定系统支持;
- 注入风险:模型不能绕过组件白名单直接输出任意脚本或危险结构;
- 不可预测输出风险:渲染器面对的是已校验 Spec,而不是任意自然语言或任意代码;
- 工程维护风险:界面能力沉淀为可审计、可复用的契约,而不是散落在提示词中的隐性规则。
需要注意的是,Catalog 只定义 AI 能输出什么 Spec,并不自动保证每个真实组件实现本身没有漏洞。真实组件如何处理文本、数据、样式和副作用,仍需要开发者在组件实现和 registry 层面负责。
catalog.prompt() 的用途
catalog.prompt() 用于从 Catalog 自动生成系统提示词,提供给任意 LLM。它的作用是把 Catalog 中可用的组件、字段、动作和输出格式说明转化为模型可理解的提示,让模型知道自己应当输出怎样的 JSON Spec。
来源文章强调,接入自己的大模型时,可以调用 catalog.prompt() 拿到系统提示词,再把任意 LLM 的流式输出喂给 SpecStream。这意味着 Catalog 不绑定某一家模型服务,也不要求必须使用 Vercel 自家的模型能力;开发者可以接本地或私有化模型,例如文章提到的 Ollama / vLLM 场景。
与手写提示词相比,catalog.prompt() 的价值在于:提示词从同一份 Catalog 契约自动生成,减少“schema 已变但提示词没改”的漂移问题。模型看到的输出规则和程序校验使用的规则来自同一处定义。
与 registry 的区别
Catalog 和 registry 经常一起出现,但职责不同。
Catalog 定义 AI 能输出什么 Spec。它回答的问题是:
- AI 可以使用哪些组件类型?
- 每种组件可以有哪些字段?
- 字段类型是什么?
- Spec 的结构是什么?
- 可触发哪些动作?
registry 则把 Spec 中的组件类型映射到真实组件实现。它回答的问题是:
Card这个 Spec 类型最终由哪个真实组件渲染?Metric对应MyMetric还是另一个指标组件?Button对应哪个按钮实现?
来源文章中的 React 示例展示了这种分工:
import { Renderer } from '@json-render/react';
import { defineRegistry } from '@json-render/core';
const registry = defineRegistry(catalog, {
components: { Card: MyCard, Metric: MyMetric, Button: MyButton },
});
<Renderer spec={spec} registry={registry} />;
在这个例子中,Catalog 允许 AI 生成 Card、Metric、Button 类型的 JSON Spec;registry 则把这些类型分别映射为真实组件 MyCard、MyMetric、MyButton。如果没有 Catalog,AI 的输出边界不清晰;如果没有 registry,合法 Spec 也无法落到具体界面实现。
与流式渲染和跨平台的关系
Catalog 也服务于 json-render 的流式渲染能力。来源文章提到,SpecStream 可以在模型逐块吐出 JSON 时逐步解析和渲染;而 Catalog 的 schema 约束使这些逐步到来的片段最终仍要收敛到合法 Spec。
Catalog 本身不等于某个前端平台的组件库。json-render 的设计是“一套 Catalog,多端渲染”:同一份 JSON Spec 可以通过不同渲染器落到 React / Next.js、Vue 3、Svelte 5、SolidJS、React Native、Ink 终端 TUI 等目标,也可以扩展到 Remotion 视频、React PDF 文档、React Email 邮件、Satori SVG/PNG 社交卡片、React Three Fiber 3D 场景等非传统 UI 输出。
这种跨平台能力的前提是:AI 输出的不是平台代码,而是受 Catalog 约束的中间 Spec;不同平台通过各自 registry 和 renderer 把同一类 Spec 映射到本端实现。
细节与边界
- Catalog 是 AI 可用能力的白名单,不是最终 UI,也不是组件实现源码。
- Catalog 约束的是 JSON Spec 的结构、类型和动作边界;真实组件渲染逻辑仍由开发者实现和维护。
defineCatalog组织 schema、组件元信息和动作元信息;zod负责把 JSON Spec 的结构与字段类型变成可校验规则。- 模型输出不应因为“看起来合理”就直接渲染,必须经过 schema 校验。
- 未登记组件、错误字段类型、越权动作、不符合
root/children/elements等结构约定的 Spec,都应被挡回。 catalog.prompt()是把 Catalog 自动转成系统提示词的工具,用于指导任意 LLM 生成合法 Spec。- registry 不是 Catalog:registry 负责把 Spec 类型映射到真实组件,例如
Card → MyCard、Metric → MyMetric、Button → MyButton。
相关条目
- json-render
- 生成式 UI
- SpecStream
- Vercel Labs
- Ollama
- vLLM
- json-render 1.7万+ 的生成式UI框架,让AI在开发者护栏内造界面 - 今日头条 摘要