语义规范体系:YAML 里写的不是颜色值,是语义令牌
本文是阶段二《设计师作为"语义翻译者"》的语义规范体系专题。阶段二论证了设计规范从文档转向代码的必要性:传统规范给"人"看,机器无法读取,AI生成内容时不受约束。核心方案是Schema-As-Code——将设计意图翻译成YAML语义契约,让机器在生成前读取并执行约束。
需要首先澄清:YAML里写的不是颜色值,不是文案,是语义令牌。这不是"技术实现",是"语义翻译"。
本文在此基础上回答"代码格式的规范体系长什么样":YAML格式标准、语义令牌命名空间、语义域分类、契约库管理,以及到Prompt前缀/Checklist/CI规则的编译输出机制。
快速阅读:
阶段一:组件语义快照与模式诊断:AI 生成界面的第一道检查
方法论总纲与开源仓库:把设计规范写成代码格式,是所有 AI 工具的上游约束方法论
一、引言:YAML 里到底写的是什么?
我第一次给别人看我的 YAML 契约时,对方看了一眼说:“这不就是 Design Token 吗?把颜色、字体写成代码格式。”
我说不是。他反问:“那你写的color_token: status.critical和 Design Token 的color: #EF4444有什么区别?”
这个问题让我意识到:如果我不先讲清楚"语义令牌"和"Design Token"的区别,所有人都会把 Schema-As-Code 理解成"另一种 Design Token 写法",然后整个方法论就被矮化了。
所以这篇文章必须放在 YAML 格式之前——先理解语义规范体系,再看 YAML 怎么写,顺序不能反。
二、Design Token vs 语义令牌:两个完全不同的东西
Design Token 解决什么问题?
Design Token 管的是"视觉层的一致性":
# Design Token(Figma / Tokens Studio / Style Dictionary)color:primary:"#1677FF"danger:"#EF4444"warning:"#F59E0B"font:family:"Inter"size:14pxDesign Token 回答的问题:“这个按钮用什么颜色、什么字体?”
它的作用是:让设计稿和代码里的颜色值保持一致,设计系统更新时,改一处全局生效。
语义令牌解决什么问题?
语义令牌管的是"语义层的一致性":
# 语义令牌(Schema-As-Code)semantic_tokens:error_severity:fatal:visual_mapping:color_token:"status.critical"# ← 不是 #EF4444,是 "致命状态"motion_token:"pulse.red.urgent"# ← 不是 pulse 2s,是 "紧急脉冲"icon_token:"alert.octagon"# ← 不是 warning.svg,是 "八边形警告"语义令牌回答的问题:“这个错误在这个场景下代表什么后果?用户应该怎么理解?”
核心区别:一层 vs 两层
| 维度 | Design Token | 语义令牌(Semantic Token) |
|---|---|---|
| 管什么 | 视觉层(长什么样) | 语义层(意味着什么) |
| 写什么 | 色值、字号、间距 | 状态级别、认知阶段、边界强度 |
| 谁消费 | 前端(CSS/组件库) | AI / 机器(Prompt / 校验规则) |
| 变更时 | 改色值,全局变色 | 改语义映射,全局改变"理解方式" |
| 例子 | color: #EF4444 | color_token: status.critical |
关键洞察:Design Token 是"视觉字典",语义令牌是"语义字典"。两者互补,不是替代。
用户看到的界面 ↓ Design Token(管颜色、字体、间距) ↓ 语义令牌(管颜色代表什么、文案传递什么) ↓ 机器理解的规则没有语义令牌,AI 知道"用红色",但不知道"红色代表致命"。
三、语义令牌的四大命名空间
我设计语义令牌时,把常见的语义场景归纳为四大类,每类一个命名空间:
1.status.*—— 状态级别
回答的问题:“这件事有多严重?”
| 令牌 | 含义 | 典型场景 | 视觉映射(示例) |
|---|---|---|---|
status.critical | 致命 | 系统故障、数据丢失 | 红色脉冲 + 八边形警告 |
status.warning | 警告 | 限流、降级、可恢复错误 | 黄色提示 + 时钟图标 |
status.info | 信息 | 提示、说明、部分可用 | 蓝色静态 + 信息图标 |
status.success | 成功 | 保存完成、操作成功 | 绿色静态 + 对勾图标 |
status.neutral | 中性 | 加载中、等待中 | 灰色动画 + 旋转图标 |
为什么不是直接写颜色?
因为同一个status.critical在不同设计系统里可以映射到不同的色值:
- 设计系统 A:
#EF4444(Tailwind 红) - 设计系统 B:
#F5222D(Ant Design 红) - 设计系统 C:
#FF4D4F(DevUI 红)
语义令牌不关心具体色值,只关心语义映射关系。设计系统更新时,改映射表,语义令牌不变。
2.phase.*—— 认知阶段
回答的问题:“AI 现在处于什么阶段?用户在等什么?”
| 令牌 | 含义 | 典型场景 | 视觉映射(示例) |
|---|---|---|---|
phase.research | 检索 | 搜索信息、查找来源 | 蓝色 + 放大镜图标 + 来源计数 |
phase.analysis | 综合 | 对比多源、识别分歧 | 黄色 + 大脑图标 + 共识度 |
phase.check | 验证 | 核对链接、验证事实 | 绿色 + 盾牌图标 + 验证状态 |
phase.output | 生成 | 生成答案、输出结果 | 紫色 + 文档图标 + 引用索引 |
为什么需要阶段令牌?
Perplexity 的 “Searching…” “Reading…” “Wrapping up…” 让用户困惑——不知道 AI 是在查资料还是开始编答案。
有了phase.*,系统可以显化认知阶段:"现在处于检索阶段,已找到 5 个来源,其中 4 个一致。“用户知道"这是查出来的,不是猜出来的”。
3.boundary.*—— 边界强度
回答的问题:“系统拒绝用户时,权利边界在哪里?”
| 令牌 | 含义 | 典型场景 | 视觉映射(示例) |
|---|---|---|---|
boundary.soft | 软性拒绝 | 拒绝请求但保留会话 | 黄色提示条 + 保留输入框 |
boundary.hard | 强制终止 | 终止会话清空上下文 | 红色退出面板 + 数据政策说明 |
boundary.review | 升级审核 | 提交人工审核 | 蓝色提示 + 预计审核时间 |
为什么需要边界令牌?
Claude 的 “I can’t help with that” 和 “Claude has ended this chat” 在界面上都是"拒绝"——用户不知道对话还在不在、数据丢没丢。
有了boundary.*,系统区分:软性拒绝(继续聊)vs 强制终止(开新会话)。用户明确知道自己的权利状态。
4.action.*—— 操作性质
回答的问题:“用户点击这个按钮后,后果是什么?”
| 令牌 | 含义 | 典型场景 | 视觉映射(示例) |
|---|---|---|---|
action.destructive | 破坏性 | 删除、清空、不可逆 | 红色空心 + 二次确认 + 输入验证 |
action.constructive | 建设性 | 保存、提交、创建 | 蓝色实心 + 成功反馈 |
action.neutral | 中性 | 取消、关闭、返回 | 灰色描边 + 无后果 |
为什么需要操作令牌?
AI 生成"删除账户"按钮时,默认给蓝色实心"确认"——因为 Prompt 里只说"生成一个删除按钮",没有说"这是不可逆操作"。
有了action.destructive,系统知道:这个按钮后果是破坏性的,必须红色空心、必须二次确认、必须输入账户名验证。
四、语义域:组件在什么场景下?
语义令牌定义了"是什么",但同一个组件在不同场景下,语义可能完全不同。
我设计了三个语义域(Semantic Domain):
observational—— 观察型
特征:用户被动接收信息,不需要操作。
典型组件:错误提示、状态通知、告警卡片。
约束:
- 禁止要求用户输入
- 必须提供明确的退出路径
- 文案必须说明后果(不能只说"出错了")
transactional—— 交易型
特征:用户主动触发操作,系统必须反馈结果。
典型组件:提交按钮、表单、确认弹窗。
约束:
- 必须提供操作结果反馈
- 成功/失败状态必须明确
- 不可逆操作必须有二次确认
conversational—— 对话型
特征:用户和系统双向对话,上下文持续累积。
典型组件:聊天界面、问答系统、AI 助手。
约束:
- 必须保留对话上下文
- 边界动作必须说明会话状态
- 拒绝时必须提供替代建议
为什么需要语义域?
同一个 Alert 组件:
- 在
observational域(系统故障)→ 用户被动看,不能要求输入 - 在
transactional域(表单验证)→ 用户主动填,必须给反馈 - 在
conversational域(对话中断)→ 上下文要保留,不能说断就断
没有语义域,系统不知道"这个组件在用户流程中扮演什么角色",约束就会错位。
五、不可变边界:红线
语义令牌定义"应该是什么",不可变边界定义"绝对不能是什么"。
我把它理解为"语义红线"——一旦触碰,系统必须阻断。
示例:ERR-001 的不可变边界
immutable_boundaries:-boundary_type:"safety"rule:"禁止直接执行删除操作而不显示二次确认"violation_action:"block"-boundary_type:"clarity"rule:"禁止在错误状态文案中使用模糊词汇(如'出错了'、'Something went wrong')"violation_action:"block"-boundary_type:"consistency"rule:"禁止将不同严重级别的错误使用相同视觉表达"violation_action:"block"为什么叫"不可变"?
这些边界不是"建议",是"强制规则":
- block:一旦触发,直接阻断生成,返回修改建议
- warn:触发后警告,但允许人工确认后放行
- escalate:触发后升级人工审核
与语义令牌的区别:
- 语义令牌 = " fatal 应该用什么颜色"(推荐性映射)
- 不可变边界 = “fatal 绝对不能和 retryable 用同一种颜色”(强制性约束)
六、完整示例:ERR-001 的语义规范拆解
把上面的概念串起来,看 ERR-001 是怎么从"语义规范"翻译成"YAML 契约"的:
语义规范层(设计师的意图)
设计意图: - 错误状态必须按后果严重程度分级 - 用户必须知道"多严重"和"该做什么" - 系统绝对不能让不同级别的错误长得一样 语义规范: - 状态级别:fatal / transient / retryable / degraded(status.*) - 语义域:observational(用户被动接收) - 不可变边界:不同级别不能用相同颜色语义令牌层(机器可读的标识)
# 语义令牌映射status.critical → 致命错误 → 红色脉冲 status.neutral → 可恢复错误 → 灰色加载 status.warning → 限流提示 → 黄色倒计时 status.info → 部分可用 → 蓝色提示YAML 契约层(可执行的规则)
intent_id:"ERR-001"semantic_domain:"observational"semantic_tokens:error_severity:fatal:description:"系统级故障,对话上下文可能丢失"visual_mapping:color_token:"status.critical"# ← 语义令牌motion_token:"pulse.red.urgent"# ← 语义令牌icon_token:"alert.octagon"# ← 语义令牌user_action:-label:"刷新页面"action:"refresh"-label:"导出历史"action:"export_history"llm_constraints:-"必须明确告知用户对话上下文可能已丢失"transient:description:"网络抖动,系统可自动恢复"visual_mapping:color_token:"status.neutral"# ← 语义令牌motion_token:"spinner"# ← 语义令牌icon_token:"loader"# ← 语义令牌user_action:-label:"等待自动恢复"action:"wait"llm_constraints:-"禁止红色背景(避免情绪过载)"immutable_boundaries:-boundary_type:"consistency"rule:"禁止将不同严重级别的错误使用相同视觉表达"violation_action:"block"三层关系:
- 语义规范层 = 设计师的意图(人话)
- 语义令牌层 = 机器可读的标识(机器话)
- YAML 契约层 = 可执行的规则(代码话)
七、语义规范与 YAML 契约的关系
不是先有 YAML 格式,再往里填内容。
是先有语义规范体系,再选择 YAML 作为载体。
语义规范(理论层) ├── 语义令牌命名空间(status / phase / boundary / action) ├── 语义域分类(observational / transactional / conversational) └── 不可变边界原则(安全 / 清晰 / 一致) ↓ YAML 契约(实现层) ├── semantic_tokens 字段(填语义令牌) ├── context 字段(填语义域) └── immutable_boundaries 字段(填红线)理解了这个关系,就不会把 Schema-As-Code 当成"另一种 Design Token",而是"设计意图的语义翻译层"。
八、Before/After:加了语义规范后,YAML 契约长什么样?
Before(没有语义规范,YAML 只写视觉值)
# 错误状态规范(没有语义层)error:color:"#EF4444"bg:"solid"icon:"warning.svg"text:"Something went wrong"button:"重试"问题:
- 前端看了知道"用红色",但不知道"红色代表什么"
- AI 看了知道"生成红色按钮",但不知道"什么场景下该用什么红色"
- 设计系统更新红色色值时,YAML 要全部改一遍
After(有了语义规范,YAML 写语义令牌)
# 错误状态规范(有语义层)semantic_tokens:error_severity:fatal:visual_mapping:color_token:"status.critical"# ← 语义令牌,不是色值motion_token:"pulse.red.urgent"icon_token:"alert.octagon"user_action:["刷新页面","导出历史"]transient:visual_mapping:color_token:"status.neutral"# ← 同一组件,不同语义,不同令牌motion_token:"spinner"icon_token:"loader"user_action:["等待自动恢复"]改变:
- 前端看了知道"fatal 用 status.critical",去映射表查具体色值
- AI 看了知道"fatal 必须红色脉冲 + 刷新/导出按钮"
- 设计系统更新色值时,改映射表,YAML 完全不变
九、总结
Schema-As-Code 的 YAML 契约里写的不是颜色值、不是文案、不是 Design Token。
写的是语义令牌——机器可读的语义标识,定义"这个界面元素在这个场景下代表什么"。
四大命名空间:
status.*—— 状态级别(多严重)phase.*—— 认知阶段(在干什么)boundary.*—— 边界强度(权利在哪)action.*—— 操作性质(后果是什么)
三层语义域:
observational—— 用户被动看transactional—— 用户主动点conversational—— 用户和系统聊
一条红线:
immutable_boundaries—— 绝对不能做什么
理解了这个语义规范体系,再看 YAML 格式,就知道每个字段填的不是视觉值,是语义映射。