这是系列第四篇,也是「规范篇」。前三篇分别讲了出码的根本难题、物料搭建的问题地图、控制 AI 的 harness 设计。现在把它们收口成一个可落地的问题:要让这套架构完整表达各种复杂页面、逼近生产表现,我到底需要设计哪些协议和 Schema? 我会负责任地把这套协议族穷举出来——结构、字段、含义,一个不落;也会诚实标注它的边界。
一句话主线
完整表达一个生产级页面,靠的不是一个大 Schema,而是一套分层、共享同一个 Envelope、彼此用 id 交叉引用的协议族——覆盖「词汇 → 物料 → 结构 → 数据与逻辑 → 控制 → 产物」六层。哪一层的协议缺失或表达力不足,页面就会在那一层退回成「死页面」或「不可控产物」。
所以判断这套协议族「够不够」的标准,不是字段多不多,而是:上一篇讲的「活页面 5 层」(视觉 / 布局 / 数据 / 逻辑 / 边界)有没有被无缝覆盖。 文末我会用这个标准回过头验收。
先立一个共享地基:Envelope(信封)
穷举协议之前,先解决一件让整套协议族「成为一族」而不是「一堆散 JSON」的事——每一个协议实例都包在同一个 Envelope 里。 它承载所有跨协议通用的元信息,尤其是前一篇反复强调的 confidence 和 provenance。
// Envelope —— 所有协议实例的统一外壳
{
"id": "cmp_7f3a...", // 全局唯一 id,跨协议引用的锚点
"kind": "ir.composition", // 协议类型,形如 <层>.<名>,见下方目录
"specVersion": "1.4.0", // 该协议的语义化版本(semver)
"confidence": 0.82, // 0~1,此实例的可信度;人工确认后=1.0
"provenance": "model", // 来源:model|human|api|design|rule|mixed
"createdBy": "material-matcher", // 产出它的 agent / 工具 / 人
"refs": ["blk_12", "mat_card_v2"], // 它引用了哪些其它实例的 id
"extensions": {}, // 逃生舱:厂商/业务自定义扩展,不污染主 schema
"payload": { } // 具体协议内容,各协议在此展开
}
三个设计承诺,贯穿全族:
id+refs= 用引用代替嵌套。 协议之间不深度内联彼此,而是互相引用 id。这让每个产物可独立校验、独立回退、独立缓存——正是上一篇「可回退存档点」的物理实现。confidence+provenance= 不确定性是一等公民。 每一条信息都知道自己「有多可信、从哪来」,人机门和防幻觉才有据可依。extensions= 演进的逃生舱。 任何未预见的业务字段先进extensions,稳定后再提升为一等字段。没有逃生舱的 schema 一定会在真实业务里被撑爆。
下面按六层展开 23 个协议。每个给:用途、结构、关键字段含义。
L0 · 词汇层:所有协议共享的「名词表」
页面里一切引用的最底层符号。它们不描述页面,而是定义「说页面时能用哪些词」。
P1 · Design Token Protocol(设计令牌)
用途:把颜色、间距、字体等设计原子符号化,让所有上层协议引用 token 而非硬编码值——这是「高还原」和「可换肤」的共同前提。
{ "kind": "token.set", "payload": {
"color": { "brand.primary": "#2F6BFF", "text.body": "#1A1A1A" },
"space": { "sm": 8, "md": 16, "lg": 24 }, // 间距刻度
"font": { "body": { "family": "Inter", "size": 14, "line": 22 } },
"radius": { "card": 12 }, "shadow": { "card": "0 2 8 rgba(0,0,0,.08)" },
"zIndex": { "modal": 1000 }, "motion": { "fast": "150ms ease-out" },
"breakpoint": { "sm": 640, "md": 1024, "lg": 1440 } // 响应式断点
}}
关键点:token 是命名引用(color.brand.primary),不是值。上层所有颜色/间距都写 token 名,换主题只换这一张表。
P2 · Semantic Ontology(语义本体 / 类型注册表)
用途:定义一套受控的语义标签词汇(「商品卡片」「主操作按钮」「筛选区」),供物料声明自己「是什么」、供匹配器检索。没有受控词汇,语义匹配就是各说各话。
{ "kind": "onto.registry", "payload": {
"categories": ["navigation","dataDisplay","dataEntry","feedback","layout","business"],
"semanticTags": {
"product.card": { "extends": "dataDisplay", "expects": ["title","price","image"] },
"action.primary": { "extends": "dataEntry" }
}
}}
expects 声明这个语义通常携带哪些字段——它同时是匹配的线索和数据绑定的提示。
L1 · 物料层:整套架构的核心资产
物料是搭建的原子。Material Manifest 是整个协议族里最重的一个协议——物料对外的一切能力、约束、可配置性,都在这里声明。
P3 · Material Manifest Protocol(物料契约)★核心
用途:一个物料对「人」和「搭建系统/AI」的完整自我描述。它决定了这个物料能不能被检索、被配置、被合法组合、被数据驱动。
{ "kind": "material.manifest", "id": "mat_product_card", "specVersion": "2.1.0",
"payload": {
"name": "ProductCard",
"semanticTags": ["product.card"], // ← 引用 P2,供匹配
"category": "business",
"granularity": "molecule", // atom|molecule|block|template,粒度
"props": { // 可配置面(对外暴露的参数)
"title": { "type": "string", "required": true, "bindable": true },
"price": { "type": "number", "bindable": true, "format": "currency" },
"variant": { "type": "enum", "options": ["default","compact"], "default": "default" }
},
"slots": { // 插槽:能放哪些子物料
"footer": { "accepts": ["action.primary","action.secondary"], "max": 2 }
},
"events": { // 对外抛出的事件(逻辑层的挂载点)
"onClick": { "payload": "{ id: string }" },
"onFavorite": { "payload": "{ id: string, next: boolean }" }
},
"states": ["default","hover","loading","disabled","empty","error"], // 状态覆盖
"variants": ["default","compact"],
"composition": { // 组合契约:谁能嵌我 / 我能嵌谁
"allowedParents": ["list.grid","layout.section"],
"forbiddenParents": ["dataEntry.form"]
},
"responsive": { "ownsBreakpoints": false }, // 响应式责任归属(自己管/外层管)
"tokensUsed": ["color.brand.primary","space.md","radius.card"], // 消费的 token
"a11y": { "role": "article", "focusable": true },
"dataContract": { // 该物料期望的数据形状(对接数据绑定)
"shape": { "id":"string","title":"string","price":"number","image":"url" }
},
"deprecations": [], // 废弃/迁移信息,支撑版本演进
"preview": "asset://mat_product_card/thumb.png"
}}
含义要点:props.bindable 标记哪些字段可被数据绑定(P10 会用);slots.accepts 引用语义标签而非具体物料 id,保持解耦;composition 就是上一篇「组合契约」的落地;dataContract 是物料与数据层握手的接口。这一个协议同时服务了匹配、组合、绑定、逻辑挂载、版本治理五件事。
P4 · Catalog / Registry Protocol(物料目录)
用途:把成百上千个 Manifest 组织成可检索、可版本化的目录,供 catalog_search 工具(上一篇)查询。
{ "kind": "catalog.index", "payload": {
"materials": [ { "id":"mat_product_card", "latest":"2.1.0",
"versions":["1.0.0","2.0.0","2.1.0"],
"visualEmbedding":"vec://...", "semanticTags":["product.card"] } ],
"coverageStats": { "byCategory": { "business": 142, "layout": 30 } }
}}
visualEmbedding 支撑视觉检索,coverageStats 支撑上一篇的「覆盖率度量」。
L2 · 结构还原层:从设计稿到页面骨架
这一层是流水线的 IR 链(上一篇的 artifact 1→4),把「一张图」逐级精化成「一棵带布局的组件树」。
P5 · Design Source Protocol(设计输入)
用途:把设计来源归一化,屏蔽「截图 / Figma / Sketch」的差异。
{ "kind": "ir.designSource", "payload": {
"type": "figma", // figma|screenshot|sketch
"artboards": [ { "id":"ab_1", "viewport":{"w":1440,"h":900},
"raster":"asset://ab_1.png",
"nativeTree": { } } ] // 若有源文件,携带原生层级(信息量远大于截图)
}}
诚实提示:type=screenshot 时 nativeTree 为空,后续全靠视觉推断,保真度天花板明显更低。输入的信息量,从这里就决定了下限。
P6 · Layout Tree Protocol(视觉分解树)
用途:分块器的产物——把设计切成带 bbox、嵌套、语义猜测的块。
{ "kind": "ir.layoutTree", "payload": {
"blocks": [ { "id":"blk_12", "bbox":[24,80,392,520], "parent":"blk_root",
"children":["blk_13"], "ocrText":["iPhone 15","¥5999"],
"semanticGuess":"product.card", "confidence":0.78 } ] }}
P7 · Binding Map Protocol(选料图)
用途:匹配器产物——每个块 → 物料候选 + 置信度 + 是否需人工。
{ "kind": "ir.bindingMap", "payload": {
"bindings": [ { "block":"blk_12",
"candidates":[ {"material":"mat_product_card","version":"2.1.0","score":0.86},
{"material":"mat_media_card","score":0.61} ],
"chosen":"mat_product_card", "confidence":0.86, "needsHuman":false } ] }}
P8 · Composition Tree Protocol(组合树 / 页面结构 DSL)★核心
用途:这是页面的静态骨架的最终形态——组件实例的树,含解析后的布局模型和 props。L3 的所有数据/逻辑都挂在它的节点上。
{ "kind": "ir.composition", "id":"page_home", "payload": {
"root": {
"node":"n_root", "material":"layout.section",
"layout": { "model":"flex", "direction":"column", "gap":"space.lg" }, // 引用 token
"children": [
{ "node":"n_grid", "material":"list.grid",
"layout": { "model":"grid", "cols":{"sm":1,"md":3}, "gap":"space.md" }, // 响应式
"repeat": { "over":"$.products", "as":"item" }, // ← 列表渲染,绑数据源
"children": [
{ "node":"n_card", "material":"mat_product_card", "version":"2.1.0",
"props": { "title":"$item.title", "price":"$item.price" }, // 绑定表达式
"slots": { "footer":[ {"node":"n_btn","material":"action.primary"} ] } }
] } ] }
}}
关键设计:repeat.over 用表达式引用数据(解决「长列表」——上一篇 pix2code 的老大难);props 值可以是字面量或 $ 绑定表达式,把静态结构和动态数据在同一棵树上缝合;layout.cols 按断点取值,把响应式编码进结构。这棵树是整个页面的「主干」,其余协议都是挂在它上面的枝叶。
L3 · 数据与逻辑层:让页面「活」起来(第三输入的落地)
这是上一篇反复强调的命门——信息不在设计稿也不在物料,必须由专门协议显式承载。这一层最关键,也最难。
P9 · Data Contract Protocol(接口契约)
用途:把后端接口归一化(通常从 OpenAPI/Swagger 经 MCP 导入),供数据绑定引用。
{ "kind": "data.contract", "payload": {
"endpoints": [ { "id":"ep_products", "method":"GET", "path":"/api/products",
"query": { "page":"number", "keyword":"string" },
"response": { "shape": { "list":[{"id":"string","title":"string","price":"number"}],
"total":"number" } },
"errors": [ {"code":401,"meaning":"unauth"}, {"code":500,"meaning":"server"} ] } ] }}
errors 是常被忽略却对边界态至关重要的字段——P14 要靠它。
P10 · Data Binding Protocol(数据绑定)
用途:把组合树节点的 bindable props / repeat 源,映射到接口字段,含转换。
{ "kind": "logic.dataBinding", "payload": {
"sources": [ { "id":"src_products", "endpoint":"ep_products",
"params": { "page":"$state.page", "keyword":"$state.kw" },
"trigger":"onMount|onParamChange" } ],
"bindings": [ { "target":"n_grid.repeat.over", "expr":"$src_products.list" },
{ "target":"n_card.props.price", "expr":"item.price",
"transform":"currency('CNY')" } ] }}
transform 处理格式化/单位换算等最常见的「字段对不上」问题。
P11 · State Model Protocol(状态模型)★核心
用途:定义页面的状态原子、派生状态、作用域。跨物料联动的本质是共享状态,所以这个协议是联动能成立的基础。
{ "kind": "logic.state", "payload": {
"atoms": [ { "id":"page", "scope":"page", "initial":1 },
{ "id":"kw", "scope":"page", "initial":"" },
{ "id":"selectedCity", "scope":"page", "initial":null } ],
"derived": [ { "id":"canSubmit", "expr":"form.valid && !submitting" } ], // 派生态
"scopes": ["global","page","subtree","local"] }}
P12 · Interaction Spec Protocol(交互规格)★核心 / 最难
用途:把 PRD 里的业务规则结构化成「触发-守卫-动作」条目。由 intake agent 抽取、人工确认后生效(上一篇的第三输入入口二)。
{ "kind": "logic.interaction", "payload": {
"rules": [ {
"id":"r_submit",
"trigger": { "node":"n_submitBtn", "event":"onClick" },
"guard": "$derived.canSubmit", // 守卫条件
"actions": [
{ "type":"call", "endpoint":"ep_createOrder", "payload":"$form.values" },
{ "type":"onError", "do":[ {"type":"keepDraft"}, {"type":"toast","msg":"提交失败"} ] },
{ "type":"onSuccess", "do":[ {"type":"navigate","to":"/order/$resp.id"} ] }
],
"provenance":"prd#3.2", "confidence":0.6, "needsHuman":true } ] }}
每条规则携带 provenance(追溯到 PRD 第几节)与 confidence——这是让「AI 从散文里抽出来的逻辑」可审计的关键。
P13 · Logic Graph Protocol(逻辑编排图)
用途:把散落的交互规则、数据源、状态编译成一张可执行、可分析的有向图(事件→动作→状态变更→再触发)。它是 P10~P12 的「编译产物」,用于检测环、竞态、悬空引用。
{ "kind":"logic.graph", "payload": {
"nodes":[ {"id":"g_click","type":"event"}, {"id":"g_call","type":"effect"},
{"id":"g_page","type":"state"} ],
"edges":[ {"from":"g_click","to":"g_call"}, {"from":"g_call","to":"g_page"},
{"from":"g_page","to":"src_products.refetch"} ], // 联动在图上显式成边
"diagnostics": { "cycles":[], "danglingRefs":[] } }}
把「联动」画成图上的边——这样跨物料联动就从「隐式约定」变成了「可检查的显式结构」。
P14 · Boundary & Lifecycle Protocol(边界态与生命周期)
用途:为每个数据依赖区域声明 loading/empty/error/权限的兜底——上一篇说的「占一半工作量却没人画」的部分。
{ "kind":"logic.boundary", "payload": {
"regions": [ { "target":"n_grid", "source":"src_products",
"loading": { "material":"feedback.skeleton" },
"empty": { "when":"$src_products.list.length===0", "material":"feedback.empty" },
"error": { "byCode": { "401":{"action":"redirectLogin"},
"default":{"material":"feedback.errorRetry"} } },
"timeoutMs": 8000 } ] }}
error.byCode 直接消费 P9 的 errors——协议间的引用闭环在这里体现。
P15 · Routing / Navigation Protocol(路由导航)
用途:多页流转、传参、进入守卫(权限)。
{ "kind":"nav.routing", "payload": {
"routes":[ { "path":"/order/:id", "page":"page_orderDetail",
"params":{"id":"string"}, "guard":"$auth.isLogin" } ],
"transitions":[ { "from":"page_home", "to":"/order/:id", "carry":["selectedItem"] } ] }}
L4 · 控制层:Harness 如何被配置与约束
这一层不描述页面,而是描述「AI 怎么被组织去生产页面」——上一篇 harness 的协议化。
P16 · Agent / Skill Manifest(SKILL.md 的形式化)
{ "kind":"harness.skill", "payload": {
"name":"material-matcher", "role":"把 block 匹配到物料",
"inputs":["ir.layoutTree"], "output":"ir.bindingMap",
"tools":["catalog_search","visual_embed","token_resolver"],
"constraints":["material_id 必须来自工具返回","confidence<0.75 → needsHuman"],
"model":{"tier":"vision","effort":"medium"} }}
P17 · Tool Contract(工具契约)
即上一篇的 function-calling schema,标准化 name/description/input_schema/output_schema/sideEffects。sideEffects 标记工具是否有副作用(只读 vs 会改状态),供编排层决定能否并发/重试。
P18 · Confidence & Provenance(可信度与溯源规范)
用途:跨切面统一「可信度怎么算、来源怎么标、阈值怎么定」。它不单独产出实例,而是规定 Envelope 里那两个字段的取值语义与合成规则(如:一个产物的 confidence = 其依赖产物 confidence 的下界)。把它单列为协议,是因为「不确定性怎么传播」必须全族统一,否则门就形同虚设。
P19 · Gate / HITL Protocol(校验门与人机门)
{ "kind":"harness.gate", "payload": {
"gates":[ { "after":"ir.bindingMap", "type":"confidence",
"rule":"confidence < 0.75 || needsHuman",
"onFail":"routeToHuman", "ui":"materialPicker" },
{ "after":"logic.interaction", "type":"human", "rule":"always" } ] }}
逻辑规格默认 always 过人工——因为它的幻觉代价最高。
P20 · Verification Report(多维校验报告)
用途:呼应 Design2Code——分维度报,绝不合成总分。
{ "kind":"harness.verifyReport", "payload": {
"visual": { "score":0.94, "method":"clip+ssim" },
"element": { "recall":0.88, "missing":["blk_31"], "misplaced":["blk_12"] },
"logic": { "passed":7, "failed":1,
"failures":[ {"rule":"r_submit","reason":"未发出 POST /order"} ] },
"verdict": "fail", "blame":"logic.interaction#r_submit" }} // 定位坏环,供回修
logic 维度靠可执行断言得出(点提交是否真的发请求)——逻辑靠执行来验,不靠观看来验。
P21 · Feedback / Correction Protocol(反馈回流)
用途:把人工在门处的每次修正,结构化记录,回灌物料目录与 few-shot 样本库,让系统越用越准。
{ "kind":"harness.correction", "payload": {
"at":"ir.bindingMap", "block":"blk_12",
"modelChose":"mat_media_card", "humanChose":"mat_product_card",
"signal":"negative", "reuseAs":"fewshot" }}
L5 · 产物层:可运行与可回改
P22 · Page Runtime Schema(页面运行时 Schema)
用途:把 L2~L3 的所有 IR 编译成一个自洽、可被运行时直接渲染的序列化页面(若走 schema-driven 运行时路线)。它是 Composition Tree + DataBinding + Logic + Boundary + Routing 的合并投影,去掉了所有 confidence/候选等「过程信息」,只保留「运行所需」。它是「过程 IR」与「运行产物」的分水岭。
P23 · Source Map / Round-trip Protocol(溯源映射 / 可回改)
用途:解决上一篇点名的低代码头号顽疾——产物(代码或 schema)与 IR 之间的双向映射。每段产出的代码/schema 片段,记录它源自哪个 IR 节点(node id),使得「手改了代码能否同步回搭建器」成为可能。
{ "kind":"output.sourceMap", "payload": {
"mappings":[ { "outputRange":"OrderPage.tsx:40-58", "irNode":"n_card",
"editable":true, "roundTrip":"structural" } ] }}
roundTrip 诚实标注每处的回改能力:structural(结构改动能回流)/ props-only(只能回流属性)/ none(手改后脱管)。把「哪些能回改、哪些不能」显式化,比假装全都能回改更负责任。
用「活页面 5 层」验收覆盖度
回到主线定的验收标准——这套协议族到底盖没盖全?
| 活页面的层 | 由哪些协议承载 | 覆盖 |
|---|---|---|
| ① 视觉物料 | P1 Token · P3 Manifest · P4 Catalog | ✅ |
| ② 布局关系 | P6 LayoutTree · P8 Composition(layout/响应式) | ✅ |
| ③ 数据 | P9 Contract · P10 Binding · P11 State | ✅ |
| ④ 逻辑/交互 | P12 Interaction · P13 LogicGraph · P15 Routing | ✅ |
| ⑤ 状态/边界 | P14 Boundary · P11 State(派生态) | ✅ |
| (横切)不确定性与控制 | P16–P21 + Envelope | ✅ |
| (横切)可运行 & 可回改 | P22 Runtime · P23 SourceMap | ✅ |
五层都有归属,且横切的「可控」与「可维护」也各有协议。这才敢说「完整表达」。
负责任地说:这套协议族的边界
穷举了 23 个协议,但我不想给你一个「银弹」的错觉。诚实标注四条边界:
- 这是一套自洽的设计提案,不是经过生产打磨的标准。 真实落地一定会在 P8/P12/P13(结构与逻辑)反复迭代——那是复杂度真正聚集的地方。字段会增、语义会变,
extensions逃生舱就是为此准备的。 - 有几块我刻意留成了「接口占位」而非展开:动效/过渡(设计稿天然缺失,需独立的 motion 协议)、实时/推送(WebSocket 状态流)、离线/乐观更新、埋点与实验分流。它们都能作为新协议挂进这个 Envelope 体系,但每一块都值得单独一篇。
- 协议能表达 ≠ AI 能自动填对。 协议只是「能被正确表达」的容器;L3 逻辑层的内容能否被自动生成,取决于第三输入的质量——协议解决的是「表达力」,不解决「信息从哪来」。这两件事不能混为一谈。
- 完备性是渐近的,不是一次到位的。 判断这套东西成不成熟,不看协议数量,看它能不能在不破坏已有页面的前提下,通过加协议/加字段吸收新场景。可演进性,才是生产级的真正门槛。
三条可迁移的设计律
抽掉具体字段,真正能带走、用到任何复杂系统建模上的,是这三条:
- 先定 Envelope,再定协议。 让「跨协议通用的东西」(id、版本、可信度、溯源、扩展位)只定义一次。共享地基决定了这堆 schema 是「一族」还是「一堆」。
- 用
id引用代替深度嵌套。 引用式结构换来的是可独立校验、可回退、可缓存、可定位坏环——这些工程属性远比「一个大 JSON 看起来完整」值钱。 - 把不确定性和可回改性做成一等协议(P18、P23),而不是事后打补丁。一个系统对「我不确定」和「我这里能不能改回去」的表达能力,决定了它离生产有多远。
写在最后:四篇连起来是一条完整的路——出码的根本难题 → 搭建的问题地图 → 控制 AI 的 harness → 用协议族把它完整表达出来。而所有协议归根结底在做同一件事:把一个「活页面」里那些看不见的信息——组合约束、数据来源、业务逻辑、边界兜底、不确定性——从「藏在人脑和 PRD 里」变成「显式、可校验、可演进的结构」。 页面从设计稿到生产的距离,本质就是这些隐性信息被显式化的程度。协议,就是显式化的语言。