从「规范写了没人看」到「机器守住字典引用」

0 评论 43 浏览 0 收藏 19 分钟

设计规范写满文档,前端两周后走查才发现有人自创红色,字典里根本没注册。规范防不住不看规范的人——这不是人的问题,是机制的问题。语义令牌与字典让规范从人盯变成机查,机器防线在PR阶段自动阻断非法引用。本文从码本定义到三层验证,完整呈现如何让语义一致性从靠自觉变成靠系统。

设计规范写在文档里,前端工程师两周后走查才发现:有人自创了 error 直接用红色,字典里根本没注册。规范防不住不看规范的人——这不是某个人的问题,是机制的问题。

语义令牌与字典,是 Schema-As-Code 治理框架中让”规范从人盯变成机查”的核心机制。

它不是给颜色起更好的名字。status.critical 不是 #EF4444 的别名,而是一个语义指针——编译时自动展开为:这个场景下用什么视觉、用户该做什么、AI 不能说什么、跨层禁止哪些误用。所有契约必须引用字典中的已定义项,禁止自创;机器防线在契约入库前自动校验:引用未注册?阻断。结构层级错位?阻断。版本不一致?警告。

本文从码本定义(语义令牌表与语义字典)、本质差异(语义令牌与 Design Token 不是同一层东西)、到机器防线(三层验证如何让非法引用在 PR 阶段就被拦住),完整呈现这套机制如何让语义一致性从”靠自觉”变成”靠系统”。

一、从一个真实的坑开始

前端工程师的原话是这样的:

“规范里写着『不许自创 Token』,但两周后走查才发现有人加了 error 直接用红色,字典里根本没注册。规范防不住不看规范的人。

这句话暴露了一个所有设计系统都会踩的坑:规范写在文档里,约束写在人心里,但执行落在 CI 之外。

《语义令牌表》《语义字典》解决的是”码本有没有”的问题——它们定义了语义域、语义令牌、场景映射,让设计意图有了机器可读的注册表。但注册表存在,不等于引用关系被守住。前者回答了”语义令牌和 Design Token 到底有什么不同”,后者回答的是最关键的问题:

契约写完后,怎么验证非法引用真的被拦截、引用关系真的在持续对账?

本文以这个坑为线索,先快速过一遍码本(占 20%),然后重点展开 Token 层差异和机器防线——这两部分才是让”规范不被当空气”的关键。

二、码本与注册表

2.1 语义令牌表:契约的最小语义单元

语义令牌(semantic_tokens)是 YAML 契约中描述组件语义特征的最小单元。以 error_severity 为例:

semantic_tokens:

error_severity:

fatal:

description: “致命错误:数据可能丢失或会话不可恢复”

visual_mapping:

color_token: status.critical # ← 引用字典绑定,非色值

motion_token: pulse.red.urgent # ← 语义描述,非具体 CSS

icon_token: octagon-alert

user_action:

– label: “刷新页面”

action: refresh_page

priority: 1

– label: “导出历史”

action: export_history

priority: 2

llm_constraints:

– “文案必须说明’对话可能已丢失'”

– “禁止仅显示’出错了’等模糊文案”

– “禁止显示纯技术错误码”

immutable_boundaries:

– boundary_type: semantic

rule: “禁止把 fatal 级错误渲染为普通文字提示”

violation_action: block

– boundary_type: semantic

rule: “status.critical 不可用于 observational 域”

violation_action: block

一个完整的语义令牌包含五个维度:

2.2 语义字典:组织级语义注册表

语义字典是组织级的语义码本,所有 YAML 契约必须引用字典中的已定义项,不可自创。它覆盖三层:

  • 字段级定义:如 error_severity 的四级(fatal / transient / retryable / degraded)、action_type 的两级(destructive / primary)
  • 场景映射:如 SCN-001 删除账户,直接给出覆盖层、语义绑定、组件组合、文案约束的完整方案
  • 版本管理:字典版本演进时,旧契约可继续引用旧版本(多版本共存),弃用项标记 deprecated 保留至少 90 天

《语义令牌表》和《语义字典》共同解决了”语义有没有标准定义”的问题。但标准定义存在,只是第一步。接下来要回答的是:标准定义和工程实现之间,到底差了什么?

三、Token 层差异:语义令牌不是 Design Token

这是最容易踩的坑之一:把语义令牌当成 Design Token 来用。

3.2 为什么 color_token 不能写死色值?

看一个典型的错误写法:

# ❌ 错误:把语义令牌写成 Design Token

visual_mapping:

color: “#cf1322” # 硬编码色值

background: “#1e1e22” # 硬编码背景色

正确的写法:

# ✅ 正确:引用字典中的语义绑定

visual_mapping:

color_token: status.critical # 字典绑定,非色值

motion_token: pulse.red.urgent # 语义描述,非具体动画参数

icon_token: octagon-alert # 语义图标名,非 SVG 路径

差别在于:语义令牌是”意图的指针”,Design Token 是”实现的值”。

如果 color_token 写死为 #cf1322,当设计系统升级主题(如深色模式、品牌色调整)时,契约不会跟着变——它绑死在了某个具体的实现上。而 status.critical 作为字典绑定,可以在不同主题下解析为不同的色值,同时保持语义约束不变。

更重要的是:LLM 消费的是语义描述,不是 CSS。 当你给 AI 的 Prompt 前缀里写”禁止使用 status.critical 以外的红色”,AI 理解的是语义规则;如果你写”禁止使用 #cf1322 以外的红色”,AI 只会机械匹配字符串,完全丢失了”为什么这个颜色不能用”的语义上下文。

3.3 motion_token 为什么不是 CSS 动画?

# ❌ 错误:把语义描述写成实现细节

motion_token: “animation: pulse 0.8s ease-in-out infinite”

# ✅ 正确:语义描述,由前端按平台实现

motion_token: pulse.red.urgent # 红色脉冲:注意力强制的最高档

pulse.red.urgent 告诉消费方的是:这个动画的语义意图是”最高档注意力强制”,颜色语义是”危险/致命”,节奏语义是”紧急”。 至于是用 CSS animation、React Native Animated、还是 Flutter AnimationController 实现,由前端按平台自行决定。

这种”语义与实现解耦”的设计,让同一份契约可以编译为:

  • Web 前端的 CSS 变量 + Tailwind 类名
  • 移动端的设计 Token 映射表
  • AI 生成时的 Prompt 前缀约束
  • CI 校验时的正则规则

3.4 这个坑的代价

如果不区分语义令牌和 Design Token,会出现什么情况?

核心结论:语义令牌是”意图的指针”,不是”实现的值”。契约中所有 *_token 字段必须引用字典注册表,禁止硬编码任何平台相关的实现细节。

但”必须引用”写在规范里,不等于真的被守住了。接下来进入 :怎么让机器来守这条规则。

四、字典引用的机器防线:规范写了,怎么防住不看规范的人?

回到开头那个坑:前端两周后才发现有人自创了 error 用红色,字典里根本没注册。

这个问题的本质不是”字典不完善”,而是**”引用关系缺乏机器校验”**。设计目标,就是把”契约必须引用字典已定义项、不可自创”这条约束,从”写在文档里”变成”机器在 PR 提交时自动阻断”。

4.1 验证对象:字典引用的三类违规

先把”守住”翻译成可测试的命题:

三类违规的共同特征:它们都能通过 YAML 语法检查,只有对照字典注册表才能判定非法。

人眼看 YAML,语法全对;只有机器拿着注册表逐条对账,才能发现”这个词字典里没有”。这正是需要机器守护而不是人工审查的原因。

4.2 验证设计:三层防线

第一层:契约加载与解析验证——契约能被正确读入吗?

问题:验证的前提是契约被正确加载。机器读入契约时,语义令牌与不可变边界是否被完整解析?

设计

  • 契约加载接口:输入 contracts/*.yaml,解析 semantic_tokens(语义令牌)和 immutable_boundaries(不可变边界),生成内存中的规则树。
  • 引用对账:规则树生成时,逐条核对契约引用的覆盖层与绑定是否都在字典注册项内。

演示环境证明:在演示环境中,上传 ERR-001.yaml 后,系统正确解析了 error_severity 下的四个级别(fatal / transient / retryable / degraded),并生成了对应的校验规则树。这证明了契约加载逻辑在单点是成立的。

推演条件:接入生产环境后,需支持多文件批量加载与版本管理(Git 版本控制),可用 Git Diff 自动触发契约重编译,实时同步到所有下游工具。

第二层:拦截强度测试——非法引用能被拦住吗?

问题:当契约试图引用字典里不存在的东西时,校验能不能拦住它?

设计:编译前置校验(契约入库前,任一不过即阻断,输出具体错误项),对三类违规逐项验证:

  1. 覆盖层存在性:semantic_domain 的值是否在字典预定义列表中;
  2. 绑定存在性:契约引用的语义绑定是否都在字典中已定义;
  3. 场景一致性:场景映射是否指向字典中已定义的覆盖层;
  4. 字段完整性:7 个顶层字段是否齐全、version 是否符合 SemVer;
  5. 结构合法性:YAML 语法、缩进、类型是否符合 schema/intent-schema.json。

校验失败返回 { code, message, location },错误定位到具体字段路径(如 semantic_tokens.error_severity.fatal.llm_constraints);违反不可变边界按 violation_action 三档执行:

演示环境证明:当前演示环境覆盖契约加载与解析(见第一层);前置校验五项属编译管线 v1 的交付范围(对应工程里程碑 M3),其对抗用例的批量验证随管线一并落地。

推演条件:接入生产环境后,前置校验由 /api/contracts/validate(POST,只校验不产出)在 CI 中自动执行;参照对抗用例库的工程口径,需自动生成 100+ 组用例覆盖所有注册项与边界条件,目标拦截率 ≥ 95%。

第三层:引用关系追踪——入库之后,引用会持续对账吗?

问题:契约通过校验入库只是第一时刻。字典版本演进后,契约的引用关系会不会悄悄失效?规则”写了”不等于”用了”,引用”当时合法”不等于”一直合法”。

设计

  • 契约消费追踪(Observability):追踪每份契约被哪些 Prompt 前缀引用、被哪些组件校验规则消费;追踪指标包括契约文件版本号、下游消费点清单、最后同步时间戳。
  • 版本兼容与弃用:旧契约可继续引用旧版本字典(多版本共存,编译时按契约声明的字典版本解析);弃用项标记 deprecated 保留至少 90 天,编译时对引用方输出 warning 并附迁移指引;所有变更经 Git Diff 审查留痕,可回滚、可归因。
  • 失败判定:任何节点超时未消费;契约版本与下游不一致;消费日志缺失或断裂。

演示环境证明:在演示环境中,手动模拟了契约提交 → 解析 → 生成 Prompt 前缀的完整链路,单文件链路可跑通。

推演条件:接入生产环境后,需配置 Git Webhook + CI 流水线 + 消费状态数据库,实现自动化追踪;告警规则为——提交后 5 分钟未触发 CI、编译通过后 10 分钟未注入、注入后 1 小时未同步 DesignOps。

4.3 运行逻辑:它一直在工作吗?

机器防线不是一次性检查,而是一条持续运行的链路:

加载契约 → 编译为内存规则树 → 执行校验 → 产出 4 种格式

↑___________________________________________↓

版本同步闭环(Git Webhook 触发)

  • 执行链路:每个产出文件头部嵌入版本声明(源契约、版本、编译时间),验收结论与拦截记录均可追溯到具体契约版本。
  • 版本同步闭环:提交 YAML 新版本 → Git 钩子触发编译管线 → 自动生成 4 种格式新版本 → 自动通知下游(前端换 Prompt 前缀、DesignOps 换 Checklist、CI 规则下次提交生效)。
  • 拦截统计与归因:CI 与推演引擎的拦截次数按模式 ID 归因;收益换算按返工成本模型推演,并标注推演口径。

4.4 调整前后对比:同样的坑,不同的结局

回到开头那条前端反馈。机器防线就位后,同样的”新增 error 用红色但字典没注册”场景,走向完全不同:

本篇定义”守住字典引用应该测什么、通过标准是什么”,工程团队负责”怎么自动化跑、怎么接入生产环境”。

本文由 @阿基拉de_Akir 原创发布于人人都是产品经理。未经作者许可,禁止转载

题图来自Unsplash,基于CC0协议

更多精彩内容,请关注人人都是产品经理微信公众号或下载App
评论
评论请登录
  1. 目前还没评论,等你发挥!