SpecStream
定义
SpecStream 是 json-render 内置的流式 JSON Spec 编译与处理能力,用于接收 LLM 的分块输出,并把这些片段增量解析为可被渲染器使用的界面描述。
在 json-render 的设计中,AI 不直接写 JSX、Vue 组件或任意前端代码,而是在开发者预先定义的 组件目录(Catalog) 与 schema 护栏内生成结构化 JSON Spec。SpecStream 负责处理“模型还没有完整输出整个 JSON Spec,但已经陆续吐出若干块内容”的场景,使系统可以边接收、边解析、边尝试渲染。
一句话说,SpecStream 让 生成式 UI 从“等模型完整想完再一次性出界面”,变成“模型每生成一段合法结构,界面就可以渐进出现”。
在本文档中的语境
SpecStream 出现在介绍 json-render 核心功能的“流式渲染:SpecStream 边生成边画”部分。原文把它描述为 json-render 的内置能力:模型每吐出一块 JSON,界面就渐进渲染一块,体验类似 ChatGPT 的打字机式输出。
这里的“打字机式输出”不是指简单地把文字一字一字显示出来,而是指 AI 输出的对象变成了可渲染的界面结构。也就是说,流式传来的不是普通 Markdown 或纯文本,而是 JSON Spec 的片段;这些片段经过增量编译后,会被 Renderer 用来生成真实 UI。
它解决的问题
直接让模型完整生成界面描述再渲染,主要会遇到两个问题:
- 体验卡顿:如果必须等 LLM 完整生成整个界面 JSON、系统完整解析、再交给渲染器,用户只能长时间看空白或 loading。界面越复杂、模型输出越长,等待感越明显。
- 结构不可预测导致难以安全流式渲染:如果不加约束地让模型直接输出 UI 代码、JSX 或任意结构,系统无法可靠判断当前片段是否安全、是否能渲染、是否会幻觉出不存在的组件,甚至可能引入 XSS 等风险。
- 生成式 UI 既要灵活又要确定:模型擅长“涌现”和自由组合,但界面工程要求组件、属性、动作和数据绑定都可预测。SpecStream 的价值就在于把流式体验和 schema 约束结合起来。
因此,SpecStream 并不是单纯为了“更快显示文字”,而是为了让 AI 生成的界面可以在受控结构中渐进出现,降低等待感,同时避免把不可预测的模型输出直接交给前端执行。
关键 API
原文明确提到的流式编译 API 是:
createSpecStreamCompiler().push(chunk)
其中:
createSpecStreamCompiler()用于创建 SpecStream 编译器;push(chunk)用于把模型流式输出的一块 JSON 文本片段推入编译器;- 编译器会在每次收到 chunk 后尝试增量解析;
- 当已有内容形成合法、可理解的 Spec 结构时,Renderer 就可以根据当前可用的部分渐进渲染界面。
这个 API 的重点是“逐块解析模型输出”,而不是等待 LLM 结束后再把完整 JSON 一次性 JSON.parse。
工作方式
SpecStream 的典型工作流程可以分为五步:
- 开发者先用 组件目录(Catalog) 定义 AI 能使用哪些组件、能绑定哪些数据、能触发哪些动作,并用 schema 描述合法 JSON Spec 的结构。
- 系统通过 catalog 生成提示词,要求模型只在该目录和 schema 允许的范围内输出 JSON Spec。
- LLM 开始流式输出 JSON。输出不是一次性完成,而是分成多个 chunk 陆续返回。
- 每当模型吐出一块 JSON,调用
createSpecStreamCompiler().push(chunk)把该片段送入 SpecStream 编译器。 - 编译器尝试增量解析当前累计内容;当某些部分已经形成合法 spec,Renderer 就可以据此渐进渲染界面。
在这个过程中,界面可以逐步从无到有:例如先出现外层布局,再出现卡片、指标、按钮等组件,最后补齐数据绑定、条件显示或交互动作。用户看到的是一个逐渐成形的 UI,而不是长时间等待后突然出现完整界面。
与 Catalog 和 schema 的关系
SpecStream 不是绕过 组件目录(Catalog) 或 schema 校验的捷径。相反,它是在流式过程中继续服从 json-render 护栏设计的一部分。
在 json-render 的整体架构中,开发者会把 AI 可用的组件、动作、数据绑定能力登记到 Catalog,并用基于 zod 的 schema 定义 JSON Spec 的合法结构。模型只能在这个白名单内生成结构化描述,Renderer 再把 JSON Spec 映射到真实组件。
SpecStream 的边界是:
- 它允许 LLM 分块输出 JSON Spec;
- 它允许系统对这些分块做增量解析;
- 它允许 Renderer 基于已经形成的合法部分渐进渲染;
- 但它不允许最终结果偏离开发者定义的 schema;
- 它也不意味着模型可以生成任意 UI 代码或调用未登记组件。
原文在功能表中把 SpecStream 的一致性描述为“输出永远严格匹配你定义的 schema”。这说明流式渲染能力与 schema 约束是绑定的:流式只是改变输出和渲染的时机,不改变安全边界。
与普通文本流式输出的区别
SpecStream 的体验可以类比 ChatGPT 打字机式输出,但两者处理对象不同:
| 对比项 | ChatGPT 文本流式输出 | SpecStream |
|---|---|---|
| 输出对象 | 普通文本、Markdown 或代码文本 | JSON Spec 的分块片段 |
| 用户看到的结果 | 文本逐字或逐段出现 | UI 结构逐步成形 |
| 解析目标 | 文本展示为主 | 增量编译为可渲染界面描述 |
| 约束方式 | 通常依赖提示词和后处理 | 仍要求匹配 Catalog/schema |
| 风险控制 | 文本显示风险较低,但代码执行需额外防护 | 通过组件白名单和 schema 限制可渲染内容 |
因此,SpecStream 更接近“结构化界面的流式编译器”,而不是普通的 token 流展示组件。
细节与边界
- SpecStream 面向的是 AI 输出的 JSON Spec,不是让 AI 直接输出并执行 JSX、HTML 或脚本。
- 它解决的是生成式 UI 的流式体验问题:避免用户等待模型完整输出界面描述后才能看到结果。
- 它仍然依赖 json-render 的 Renderer 将 JSON Spec 转换为真实 UI;SpecStream 本身不是组件库,也不是最终渲染器。
- 它与开发者定义的 Catalog/schema 配套使用;没有受控组件目录和结构约束,流式解析就难以保证安全和可预测。
- 它适用于任意支持流式输出的 LLM 接入路径。原文说明开发者可以调用
catalog.prompt()生成系统提示词,把任意 LLM 的流式输出喂给 SpecStream,并不需要绑定 Vercel 自家服务。 - 它的“渐进渲染”并不等于每个 chunk 都一定立刻变成可见 UI;只有当片段已经形成合法、可理解的 spec 部分时,Renderer 才能据此更新界面。
在 json-render 架构中的位置
SpecStream 位于“AI 生成 JSON Spec”和“Renderer 渲染 UI”之间,是连接模型流式输出与受控界面渲染的中间层。
整体链路可以表示为:
用户 Prompt → AI + Catalog → 流式 JSON chunk → SpecStream 编译器 → 合法 JSON Spec → Renderer → 真实 UI
其中 Catalog 决定 AI 能生成什么,SpecStream 决定这些生成内容如何被流式接收和增量编译,Renderer 决定如何把合法 spec 映射到 React、Vue、Svelte、React Native、Ink 等具体运行环境中的真实组件。