设计系统 AGI 化的前置工作:规则的形式化表达与可计算约束
一、"这个对话框不能有超过两个按钮"——一条无法自动检查的规则
设计师在规范文档中写了:"模态对话框最多包含两个操作按钮(确认 + 取消),超过两个应使用下拉菜单。"这是合理的 UX 规则——避免给用户太多选择。但当前的 Stylelint/ESLint 规则引擎无法检查这条规则。因为它不是"属性值"层面的约束,而是"组件结构"层面的语义约束。
要让 AI(包括未来的 AGI)能够自动执行设计系统的规则,前置工作是把自然语言的设计规范转换为形式化、可计算的约束表达式。这个转换过程不是 AI 能自动完成的——它需要人类设计系统维护者明确回答"这条规则到底在约束什么"。
二、设计规则的形式化层次
设计规则的形式化遵循特定的层次结构。从设计规范文档出发,经过规则类型判定,规则被划分为以下五种约束类型,并最终统一为可计算约束 JSON:
- 属性约束:针对单一组件的视觉属性,例如
button.border-radius = 4px,这类规则可直接转换为 Stylelint 规则。 - 结构约束:涉及组件内部元素的数量或组合,例如"Modal 子元素中 button 数量 ≤ 2",这需要结合 ESLint 规则与 AST 计数来实现。
- 关系约束:定义组件之间的依赖关系,例如"若存在 ErrorMessage,则必存在 role=alert",通常通过自定义 Lint 规则处理。
- 语义约束:关注业务含义与视觉表现的对应,例如"primary action 必须用 highest contrast 颜色",这需要 AI 语义分析配合 Token 校验。
- 流程约束:涉及用户交互行为序列,例如"删除操作必须有确认步骤",这是最难自动化的部分,需要行为检测机制。
三、设计规则的形式化表达
// design-rules/formal-rules.schema.ts // 设计规则的形式化表达 Schema /** * 约束运算符 */ type ConstraintOperator =| 'eq' // 等于
| 'neq' // 不等于
| 'gt' // 大于
| 'gte' // 大于等于
| 'lt' // 小于
| 'lte' // 小于等于
| 'in' // 在枚举值中
| 'notIn' // 不在枚举值中
| 'matches' // 正则匹配
| 'between'; // 在区间内
/**
- 约束目标:约束应用在什么上
*/
type ConstraintTarget =
| 'css-property' // CSS 属性
| 'jsx-prop' // JSX Props
| 'dom-structure' // DOM 结构(子元素数量/类型)
| 'component-hierarchy' // 组件层级关系
| 'user-flow'; // 用户操作流
/**
- 一条形式化的设计规则
- 设计意图:每一条规则都有完整的三要素:
- 适用条件(when)
- 约束内容(constraint)
- 违规后果(violation)
/
interface FormalRule {
/* 规则 ID(唯一标识) */
id: string;
- 违规后果(violation)
/** 规则分类 */
category: 'visual' | 'structural' | 'behavioral' | 'accessibility';
/** 严重程度 */
severity: 'error' | 'warning' | 'suggestion';
/**
- 适用条件:什么情况下这条规则生效
- 使用类似 MongoDB 查询的语法:
- { "component": "Modal", "props.variant": "confirm" }
*/
when: RuleCondition;
/**
- 约束内容:这条规则要求什么
- 可以是一个简单约束(单条规则)或复合约束(多条规则的逻辑组合)
*/
constraint: RuleConstraint;
/**
- 违规信息模板
- 使用 {target}、{expected}、{actual} 占位符
*/
violationMessage: string;
/** 规则的原始文档引用(可追溯) */
source: string;
/** 是否可自动修复 */
autoFixable: boolean;
/** 自动修复的 AST 操作(如果 autoFixable) */
fix?: FixAction[];
}
interface RuleCondition {
/** 适用的组件名/
component?: string;
/* Props 条件/
props?: Record<string, any>;
/* 父组件条件/
parent?: string;
/* 自定义 JavaScript 表达式(用于复杂条件) */
expression?: string;
}
interface RuleConstraint {
/** 约束目标/
target: ConstraintTarget;
/* 目标属性路径/
property?: string;
/* 运算符/
operator: ConstraintOperator;
/* 期望值 */
expected: any;
}
interface FixAction {
type: 'replace' | 'insert' | 'remove' | 'modify-ast';
target: string;
value?: any;
}
```typescript // design-rules/example-rules.ts // 设计规则实例——从自然语言到形式化表达的转换 import { FormalRule } from './formal-rules.schema'; /** * 示例 1:按钮圆角规则 * * 自然语言:"所有主要按钮的圆角为 4px" * ↓ 形式化 */ const buttonRadiusRule: FormalRule = { id: 'DS-BTN-001', category: 'visual', severity: 'error', when: { component: 'Button', props: { type: 'primary' } }, constraint: { target: 'css-property', property: 'border-radius', operator: 'eq', expected: '4px' }, violationMessage: '主按钮的圆角必须为 4px,当前为 {actual}', source: '设计规范 v2.3 § 3.1.2', autoFixable: true, fix: [ { type: 'replace', target: 'border-radius', value: '4px' } ] }; /** * 示例 2:对话框按钮数量规则 * * 自然语言:"模态对话框最多包含两个操作按钮(确认+取消),超过两个应使用下拉菜单" * ↓ 形式化 */ const modalButtonsRule: FormalRule = { id: 'DS-MODAL-001', category: 'structural', severity: 'warning', when: { component: 'Modal', expression: 'true' // 所有 Modal 都检查 }, constraint: { target: 'dom-structure', property: 'children.filter(c => c.type === Button && c.props.isAction).length', operator: 'lte', expected: 2 }, violationMessage: '模态对话框的操作按钮不应超过 2 个,当前有 {actual} 个。多余的按钮建议使用 Dropdown 菜单', source: '设计规范 v2.3 § 5.4.1', autoFixable: false // 需要人工判断哪些按钮应移入下拉菜单 }; /** * 示例 3:错误信息关联规则 * * 自然语言:"表单错误信息必须通过 aria-describedby 关联到对应输入框" * ↓ 形式化 */ const errorMessageAssocRule: FormalRule = { id: 'DS-FORM-003', category: 'accessibility', severity: 'error', when: { component: 'AccessibleInput', props: { error: { $exists: true } } }, constraint: { target: 'jsx-prop', property: 'aria-describedby', operator: 'neq', expected: undefined }, violationMessage: '存在错误信息的输入框必须有 aria-describedby 属性', source: 'WCAG 2.1 成功准则 3.3.1', autoFixable: true, fix: [ { type: 'modify-ast', target: 'aria-describedby', value: 'automatically-generated-id' } ] }; /** * 示例 4:主操作颜色规则(语义约束——最难形式化) * * 自然语言:"页面中的主要操作按钮必须使用所有可见按钮中对比度最高的颜色" * ↓ 形式化(无法完美表达,只能近似) */ const primaryActionColorRule: FormalRule = { id: 'DS-COLOR-005', category: 'visual', severity: 'warning', when: { component: 'Button', props: { type: 'primary' } }, constraint: { target: 'css-property', property: 'background-color', operator: 'in', expected: ['var(--color-brand-primary)', 'var(--color-action-primary)'] }, violationMessage: '主操作按钮应使用品牌主色,当前使用了非标准颜色 {actual}', source: '设计 Token 规范 § 2.1', autoFixable: false };// design-rules/rule-engine.ts // 设计规则执行引擎 import { FormalRule } from './formal-rules.schema'; /** * 规则执行引擎 * * 加载形式化规则库,对代码做合规检查 * * 设计意图:规则引擎不关心规则是怎么来的(人写的还是 AI 蒸馏的), * 只关心:这条规则能否在当前代码片段上执行? */ class DesignRuleEngine { private rules: FormalRule[] = []; loadRules(rules: FormalRule[]): void { this.rules = rules; } /** * 对代码片段执行所有适用的规则 * * @param ast - 组件的 AST 表示 * @param context - 执行上下文(文件路径、组件名等) * @returns 违规列表 */ check(ast: any, context: any): Violation[] { const violations: Violation[] = []; for (const rule of this.rules) { if (!this.isApplicable(rule, ast, context)) continue; const result = this.evaluateConstraint(rule.constraint, ast, context); if (!result.passed) { violations.push({ ruleId: rule.id, severity: rule.severity, message: this.formatMessage( rule.violationMessage, result.actual ), autoFixable: rule.autoFixable, fix: rule.fix }); } } return violations; } /** * 判断规则是否适用于当前上下文 */ private isApplicable( rule: FormalRule, ast: any, context: any ): boolean { const { when } = rule; // 组件名匹配 if (when.component && context.componentName !== when.component) { return false; } // Props 条件匹配 if (when.props) { for (const [key, value] of Object.entries(when.props)) { if (context.props?.[key] !== value) return false; } } // 自定义表达式 if (when.expression) { // 在沙箱中执行表达式 // const fn = new Function('ast', 'context', `return ${when.expression}`); // if (!fn(ast, context)) return false; } return true; } /** * 执行约束检查 */ private evaluateConstraint( constraint: any, ast: any, context: any ): { passed: boolean; actual?: any } { // 获取目标值 let actual: any; // CSS 属性检查 if (constraint.target === 'css-property') { actual = this.getCSSPropertyValue(ast, constraint.property); } // JSX Prop 检查 else if (constraint.target === 'jsx-prop') { actual = context.props?.[constraint.property]; } // DOM 结构检查 else if (constraint.target === 'dom-structure') { actual = this.evaluateDOMExpression(ast, constraint.property); } // 执行运算符判断 const passed = this.applyOperator( actual, constraint.operator, constraint.expected ); return { passed, actual }; } private applyOperator( actual: any, operator: string, expected: any ): boolean { switch (operator) { case 'eq': return actual === expected; case 'neq': return actual !== expected; case 'gt': return actual > expected; case 'gte': return actual >= expected; case 'lt': return actual < expected; case 'lte': return actual <= expected; case 'in': return Array.isArray(expected) && expected.includes(actual); case 'notIn': return Array.isArray(expected) && !expected.includes(actual); default: return false; } } private getCSSPropertyValue(ast: any, property: string): any { return null; } private evaluateDOMExpression(ast: any, expr: string): any { return null; } private formatMessage(template: string, actual: any): string { return template.replace('{actual}', String(actual)); } } interface Violation { ruleId: string; severity: 'error' | 'warning' | 'suggestion'; message: string; autoFixable: boolean; fix?: FixAction[]; }四、形式化的边界——什么规则永远无法形式化
审美规则。"这个对话框看起来不够优雅"——这是真人的审美判断,任何形式化表达都会丢失关键信息。
上下文相关的用户体验规则。"在电商场景中,促销标签的颜色应该用暖色系"——这条规则依赖于"电商场景"这个语义上下文,而代码中无法自动提取"当前页面是否为电商场景"。
创新的例外。"正常情况下按钮圆角 4px,但如果设计团队决定在特定营销活动页使用更大的圆角来传递'活泼'的氛围,4px 规则可以被打破。"——所有规则都有例外,而自动规则引擎要么过于死板(不允许例外),要么过于宽松(允许太多例外导致规则失效)。
五、总结
设计系统 AGI 化的前置工作是将自然语言的设计规范转换为形式化的规则表达式。这不是 AI 的工作——是人类设计系统维护者的工作。
三条核心原则:
- 规则必须可计算——给定任意代码片段,计算结果是"通过"或"不通过"
- 从简单规则开始——先形式化"按钮圆角"这类确定性规则,再逐步处理"语义约束"
- 保留人工判断出口——规则引擎的
severity: 'warning'和autoFixable: false就是为那些"不确定"的规则保留的空间
当规则库覆盖了设计规范的 70%(属性的、结构的、可计算的),AI 就能在剩下的 30%(语义的、审美的、需要判断的)上发挥真正的推理价值。