uxskill
Star on GitHub
Launch · v3.0.0 · 2026-05-28

ux-skill v3.0 — 我们发布了 The Brain。品牌规范现在是训练数据,不是模板。

v2 从目录里挑,v3 从目录里蒸馏。同样的 160 份品牌规范,角色完全不同。原来的推荐器会对 1,182 条目录打分,返回最匹配的那一条。新的合成器把这些条目当作词汇来读,从一份 brief 编译出一种崭新的设计语言。每次调用,返回的都是那次调用之前不曾存在过的系统。

品牌规范是训练数据,
不是模板。

用一段话讲清这次转向

v2 是一个推荐器。你给它一份 brief,它从精心整理的目录里 — 84 种风格、176 套调色板、160 份品牌规范、70 组字体搭配 — 返回最相近的一条。输出永远是数据库里的某一行。v3 保留了整份目录,但把它的角色翻转了:这 1,182 条条目现在是引擎用来蒸馏的词汇。一份 brief 不再是去命中一行,而是被编译成 7 个轴的数值,这些数值再合成出全新的 token。目录教会引擎"通透 + 企业"是什么形状;引擎据此生成一个全新的、未曾出现过的"通透 + 企业"系统。同样的数据,完全不同的角色。

v2 里坏掉的、v3 修好的东西

三个真实的 bug,加上一个设计上的空洞。bug 不是那种喧哗的,而是安静的那种 — 引擎一直在返回看起来合理的输出,我们也一直在发布。直到坐下来写升级说明、被一次外部 review 逼着诚实地看一眼,我们才意识到。

1. 靠文件系统顺序的"伪"决定论

推荐器流水线里有几处排序,会按相似度给候选打分,用 Python 的 os.listdir 返回顺序来打破并列。macOS 上是字母序,Linux 上是文件系统日志里的插入顺序。新克隆和老克隆,打平后的结果就可能反过来。我们一直说引擎是确定性的。它在分数不打平的范围内确实是确定性的,一旦打平,就变成"操作系统恰好记得的那个顺序"。

v3 给每一次排序都加上了明确的并列处理 — 按品牌 id 的字母序。同一份 brief、同一组轴、同一份输出,跨机器、跨文件系统、跨 Python 版本都相同。有一个测试会在三个临时目录里把合成器跑 200 次,断言输出按字节完全一致。

2. 轴的冲突全靠"碰巧"解决

在 v2 中,如果 brief 同时含有两个相反方向的标签 — 比如 densecorporate — 推荐器的打分会被某个隐藏系数偏向其中一边,输出就会朝那一边倾斜。没有文档化的规则。事后能解释结果,事前没法预测。

v3 把这件事抬到了一个轴的相互作用矩阵里。每一处文档化的冲突都有明示的解决方式和理由。密度和正式度争抢 spacing — 密集型 brief 让密度赢(彭博风),通透型 brief 让正式度赢(奢华风)。这套矩阵有测试,可读,提交在仓库里。

3. 评估器在批改合成器自己的作业

这一个最难为情。能发现还是因为 ChatGPT 在 v2.1 规范的外部 review 里指出来的 — 他们叫它自我参照漂移 (self-referential drift)。流程看上去很干净:合成器从 brief 编译出一套系统,然后 score_tone_match 评估这个系统和 brief 的语气是不是一致。问题在于,score_tone_match 用的是和合成器一模一样的逻辑,从 brief 推出一组轴,再把合成器的轴和自己推出的轴比对。比较的两边都过同一个函数。当然分数会很高。合成器是用自己的评分标准在批改自己。

v3 把两边解耦了。score_tone_match 现在通过一条独立的、绝不会碰到合成器轴逻辑的映射,直接把合成结果和 brief 的原始语气标签warmboldminimal 这些字符串 — 做对比。两条路径再也抄不到对方了。这一观察归功于外部 review,我们在此公开承认。

4. 决策日志只写、从不读

v2 在每次 recommend 之后会向 .ux/decisions.jsonl 追加一行,但没有任何东西会再把它读回来。它只是我们自己调试用的日志,并不是反馈信号。推荐器第 1 次调用和第 1,000 次调用行为完全一样。没有学习,仓库有记忆,引擎选择忽略。

v3 把这个回路闭合了。账本有锁定的 schema (_v: 1),推荐器按同一个 (industry, ui_type) 桶里的过往胜者给候选重排,只有分数够高 + 用户真的发布了的决策才算进重排。脑终于长了眼睛看自己的历史。第 6 节是完整说明。

七轴合成器

v3 的中心是一个决定性函数,它把一份 brief 映射到 7 个数值轴,再把这 7 个轴映射到一套完整的设计系统。轴没什么神秘 — 每个都是一个有文档化值域的标准化标量,指向一组特定的 token。在轴上跑(而不是从目录里挑一行)的意义在于:空间里任何组合都可以触达,包括目录里实际品牌从未用过的组合。目录定义空间的形状,合成器可以落到其中任何位置。

衡量什么 映射到
warmth 调色板的色温 — 从冷调炭黑到暖调赭色 调色板色相族,强调色饱和度曲线
contrast 视觉响度 — quiet / balanced / loud 排版比例 (1.200 / 1.250 / 1.333)、阴影深度、强调色强度
density 单屏信息密度 — airy 到 dense spacing 基数 (4/8/12/16px)、行高倍率
geometry 边缘人格 — sharp / balanced / soft radius 尺度 (2/8/18px)、转角拓扑、描边粗细
formality 语气定位 — playful / balanced / corporate 字重曲线、字距、从 caption 到 hero 的跨度
motion 动效预算 — restrained / balanced / cinematic 持续时间梯度 (120/240/420ms)、缓动家族、滚动行为
type_personality 字体的声音 — humanist / neutral / geometric / expressive display 字体家族、字重梯度、斜体策略

Python 的入口足够小,能塞进一个示例里。传一份 brief,得到一个合成系统,里面挂着轴的数值、选好的调色板、字体设置、spacing 尺度、radius 尺度、动效预设。

# 从一份 brief 到一个系统,一次调用
from engine.synthesizer import synthesize

brief = Brief(
  industry="fintech-payments",
  tone=["bold", "serious"],
  audience=["merchants", "developers"],
)

sys = synthesize(brief)
# sys.mode == "pure_synthesis"
# sys.axes == {warmth: 0.32, contrast: 0.72, density: 0.58, ...}
# sys.palette, sys.type, sys.spacing, sys.radius, sys.motion

同样的 brief 进来,同样的轴出来,同样的 token 出来。永远如此。合成器没有随机种子,因为没有可种的随机。

三种模式,一套判定逻辑

合成器根据 brief 自动路由到三种模式之一。大多数用法都不需要手动选模式 — brief 的形状自己决定。逻辑刻意简单:是否指定了参考品牌、是否打了 strict 标志,这两件事就决定了路径。

strict_brand · 最快

100% 指定品牌

reference_brands=[stripe]strict=True,直接返回 Stripe 规范的原样。不解读、不混合、不合成。适合品牌已存在、规范有权威、brief 只想要 token 的场景。当你手上有一份 Figma 文件和一套写好的系统、需要不即兴的代码时,走这条路。

brand_anchor · 70 / 30

以品牌为锚,按 brief 的轴做调适

reference_brands=[stripe] 但不带 strict=True,返回 70% 的 Stripe token 与按轴距挑选出的 4 个姐妹品牌 30% 的轴差融合。输出仍然一眼就是 Stripe,只是朝 brief 的语气微微弯曲 — 给消费者功能时是更俏皮一点的 Stripe,给企业级仪表盘时是更正式一点的 Stripe。锚定品牌在所有冲突里都胜出。

pure_synthesis · 无穷空间

未指定品牌 — 每次调用都是新的语言

brief 里没有 reference_brands 时,合成器从 brief 给 7 个轴打分,在目录里按轴距找到 8 个最近的范例,蒸馏它们共有的结构,产出全新的 palette + type + spacing + radius + motion 一整套。不同 brief 落在同一空间的不同区域,每个输出都内部自洽、且具有可识别的自我。

从 CLI 看,三条路径是这样的:

# strict — 100% 指定品牌
$ uxskill synthesize --brand stripe --strict

# anchor — 70/30
$ uxskill synthesize --brand stripe

# pure synthesis — 无品牌
$ uxskill synthesize --industry fintech-payments --tone bold

一个合成器,一套判定逻辑,三种输出人格。用户不切换实现 — brief 自己挑路径。

轴的相互作用矩阵

轴会争抢同一批 token。density 想要紧凑的 spacing,formality 在奢华语境里想要更宽裕的 spacing。geometry 为编辑感想要锐利的转角,formality 在金融仪表盘里想要柔和的转角。在 v2 里,谁的系数恰好更大谁就赢。在 v3 里,每一个文档化的冲突都有声明的结果,以及它指向的设计流派 — 名字都列出来了,你读了之后想反对完全可以。

四个典型案例,都作为测试 fixture 提交了:

dense + corporate
spacing 基数:4px

密度赢。彭博学派的答案。当 brief 要求在企业语境里塞密集的数据仪表盘,信息密度就是最高的美德 — 正式度在"密度下的可读性"面前让位。

airy + corporate
spacing 基数:12px

正式度赢。奢华金融的答案。airy + corporate 是给高净值银行、私人财富、高管仪表盘的 brief — 元素之间的空气,本身就是"严肃"的信号。

sharp + corporate
radius 尺度:2px

几何赢。NYT 学派的答案。sharp + corporate 是编辑型 brief — 直角加上 2px 的微小 radius,读起来是机构感、深思熟虑、对开报纸式的语气。

soft + playful
radius 尺度:18px

两个轴方向一致。Glossier 学派的答案。柔和几何加俏皮语气会收敛到宽裕的圆角 — 18px 是矩形开始读作"鹅卵石"的阈值。

为什么这件事要紧:v2 里,同一份 brief 这个月可能落 4px,下个月可能落 12px,看打分时哪个系数赢。v3 里,dense + corporate 永远是 4px,airy + corporate 永远是 12px。规则可读、可测、可争论。如果你不认同某个结论,可以对矩阵开 issue,讨论的是品味,而不是某个藏起来的常量。

脑会学习

这才是这次发布对项目而言真正新的部分:引擎会从你本地的决策里学习。机制小、确定、完全离线。没有遥测、没有账号、不会上云。你的安装从你的安装里学。不同的仓库会长出不同的脑。

账本

每一次 /ux-recommend/ux-synthesize 调用,都会向 .ux/decisions.jsonl 写入一行。schema 锁定为 _v: 1:

{
  "_v": 1,
  "ts": "2026-05-28T14:22:09Z",
  "frame": { "industry": "fintech-payments", "ui_type": "dashboard", ... },
  "system": { "style_id": "editorial-calm-dark", "palette_id": "charcoal-amber", ... },
  "axes": { "warmth": 0.31, "contrast": 0.72, ... },
  "lint_score": 88,
  "user_accepted": true
}

重排

下一次调用时,推荐器会按 (industry, ui_type) 给历史记录分组。每个候选只要命中同桶里的一条先验,就拿到 +5 的加成。只有 lint_score >= 80user_accepted = true 的先验才会被计算 — 被拒绝的输出不会进入学习。在任何重排发生之前,同一桶里至少需要 3 条合格先验,否则引擎按冷启动跑,行为和全新安装完全一致。

承诺

这里能给出的承诺很窄、值得说清楚。确定性保留。同一份 brief + 同一份账本永远产生同一份输出。+5 加成在任何 tie-break 之前应用,品牌 id 字母序的规则仍在下层把所有打平拆开。冷启动安全。新仓库的行为与其他所有新仓库相同。学习是本地的。你的决策从不离开你的机器。

你随时可以查看自己的安装学到了什么:

$ uxskill stats --html
[OK] Wrote .ux/stats.html
[OK] Open in browser to see your install's learned priors

这份 HTML 面板会显示每个桶下的决策数量、平均 lint 分、最常出现的调色板、轴的分布。这就是自我学习的可见证据 — 随着你不断发布,你的安装的"品味轮廓"会随时间变得更浓。没有任何东西在服务器上。一切都在你的仓库里:明文 JSONL 和生成的 HTML 视图。

/ux-evolve 自动循环

v3 的新命令是 /ux-evolve。直到现在,打磨循环都是手动的:跑 lint、读结果、修、再 lint。Evolve 把这个循环关掉了。你把一份产物和一个目标分给它,它会跑 lint、应用 6 个幂等的打磨 pass、再 lint,要么停在目标、要么停在 plateau、要么停在 5 轮上限。

一轮的结构:

  1. Lint — 跨 A11y、内容、质量、排版的 145 条确定性正则规则。返回一个 0-100 的分数和一份 finding 列表。
  2. Polish — 6 个 pass,优先修复高严重度 finding。幂等:对已经打磨过的产物再跑一遍是 no-op。
  3. Re-lint — 给打磨后的产物重新打分。
  4. Decide — 新分数 ≥ 90,停;距上一轮的 delta 小于 5(plateau),停;到第 5 轮,停;否则继续循环。

质量门禁硬卡在 65。最终分数低于 65,引擎拒绝发布,除非传入 --force。理由是:65 以下的输出已经是能被识别出来的 AI slop,我们不允许自己的引擎安静地输出 slop。用户可以覆盖,但覆盖是显式的,并且会写进决策账本。

一个具体例子:一份金融科技仪表盘脚手架第一次 lint 拿了 72 分 — 大部分是焦点状态缺失、几个低对比度标签、一个"用 Inter 做 display"的 finding。第 1 轮把焦点状态打磨到 81 分,第 2 轮把对比度修到 86 分,第 3 轮把 Inter 换成 brief 实际指定的 display 字体并重新平衡 caption 尺寸 — 91 分,目标到达,循环结束。三轮,无拒绝,发布。

我们没有做的事(以及原因)

v2.1 极端化版本的规范里,有几样东西没进 v3。诚实地说出哪些没做、为什么没做,是讨论 scope 唯一靠谱的方式。

不让 LLM 进入回路

极端化方案里有一个由 LLM 判定的"主观美学轴" — "问问模型这是不是编辑风"。我们出于原则拒绝了。引擎的全部要点就是确定性:同一份 brief、同一份输出、明天早上不会有惊喜。一旦 LLM 进入合成器,每次调用都不可复现。v3 调用 LLM 零次。合成器是纯 Python 走 JSON,linter 是正则,评估器是规则驱动。

不做多候选遗传变异

方案里还有一个遗传算法步骤 — 每次调用产出 N 个候选,变异、打分、返回最优。我们在分支里试过,输出反而更差了,因为变异引入了噪声,打磨循环还要再清掉。v3 选择单候选合成 + 打磨循环。一个候选,六个幂等 pass。结果更好,代码面更小。

不改命令名

方案里把 /ux-recommend 改成 /generate:ui,把 /ux-polish 改成 /mutate:ui。我们保留了已有的全部 22 条命令的原名,把 /ux-evolve 作为第 23 条加进来。我们不希望一次从 v2 到 v3 的升级,把任何人的肌肉记忆或 shell alias 弄坏。

不"为了无穷空间烧掉目录"

最极端的版本认为目录是负担 — 合成器能独立到达整个设计空间,1,182 条条目应该删掉。我们把每一条都留下了。目录是教会合成器空间形状的东西 — 没有样本,轴就没有锚,输出就开始漂。v3 把目录当词汇来读,v3 需要这套词汇。

这些是品味判断,优先级不同的人会反对,没问题。把我们没做的事、为什么没做记录下来,这是我们对项目的本分。

v3 时的数字

23
Commands
18
MCP tools
1,182
Entries
145
Anti-patterns
160
Brand specs
17
IDEs
17
Locales
223
Tests pass

22 条已有的斜杠命令名字保持不动,新增的第 23 条是 /ux-evolve。15 个 MCP 工具保持不动,新增三个 — ux_synthesizeux_decisions_queryux_decisions_stats — 给想驱动合成器、或想查询脑学到了什么的 agent 使用。目录和 linter 不动。17 个 IDE 的安装器不动。17 份本地化首页和 README 也不变。v3 在表层是加法,在内核是改写。

v2 是从目录里挑的推荐器。v3 是从目录里蒸馏的编译器。同样的数据,在做相反的工作。

60 秒安装

v3 走和 v2 一样的三条发布路径 — pip、npm、IDE 插件市场。init 步骤会自动检测你的 IDE,把正确的配置文件写到正确的位置。synthesize 步骤接受一份 brief 或一组 industry + tone,产出一套完整的设计系统到当前仓库的 .ux/last-system.json

$ pip install uxskill
$ uxskill init                     # 自动检测你的 IDE
$ uxskill synthesize --industry fintech-payments --tone bold

[OK] Mode: pure_synthesis
[OK] Axes: warmth=0.31, contrast=0.74, density=0.58, geometry=0.42, ...
[OK] Wrote .ux/last-system.json (palette, type, spacing, radius, motion)
[OK] Wrote .ux/decisions.jsonl (1 entry, schema _v: 1)

所有支持的 IDE 都是同一条安装路径:Claude Code、Cursor、Windsurf、GitHub Copilot、Gemini CLI、Codex、Kiro、Cline、Continue、Aider、Zed、JetBrains AI、Pieces、Tabby、Tabnine、CodeWhisperer、Roo Cline。MCP stdio 服务器是所有 IDE 的唯一真源。

坦白边界

v3 是地基,不是终点。

七轴合成器是我们已经放出证据的工作。第 8、第 9 个轴 (saturation_strategy、surface_depth) 草案在,未发布 — 它们会让 dark / light 分流和渐变策略更深一层。决策账本目前只被推荐器的重排消费,我们预计 v3.1 会让评估器和 lint 打分也开始读它。我们拒掉的那份极端化方案并没有消失 — 当我们能证明某些点确实有帮助时,它们会在以后慢慢进来。

如果你发现某份 brief 的输出让你不满意,或者矩阵里某个结论和你的品味打架,请开 issue。矩阵在仓库里,测试在仓库里,脑也在你的仓库里。从现在起,争论都发生在一个你可以读得到的基底上。

这对你意味着什么变化

在 v2 的人:什么都不会坏。你已经在用的 22 条命令行为不变。lint 还是一样的速度。MCP 服务器用原名暴露 18 个工具里的 15 个,再加三个新工具。如果你升级后从不调用 /ux-synthesize/ux-evolve,你的日常工作流毫无变化。

一直在等引擎从推荐走到生成的人:v3 就是这一步。合成器是新的中心,/ux-recommend 还在,而且现在会读你的账本来重排;/ux-evolve 关上了打磨循环。品牌规范是训练数据,不是模板。同样的 1,182 条条目,在做完全不同的工作。

我们把 v3 叫做 The Brain,是因为做出来的就是这个东西 — 一个带闭合反馈回路的、受约束的生成式设计编译器。完全离线,LLM 调用 0 次。同 brief 同轴同输出,异 brief 异区域,无穷输出。你的安装从你的仓库里学,不同仓库长出不同的脑。整个东西是 MIT 协议、普通机器上 sub-300ms、pip 可装。

找一份你真正在意的 brief 跑一下,看返回的是什么。如果输出和你的品味打架,开 issue — 冲突进矩阵,规则会被命名,下一次安装就会表现得更好。这就是脑的复利方式。

在你的项目上跑一下

一个引擎,三条安装路径

驱动 v3 的合成器和 linter、recommender 在同一个包里发布。145 条反模式。160 份品牌规范。1,182 条条目。17 个 IDE。MIT 协议、无遥测、无账号。

$ /plugin marketplace add Laith0003/ux-skill
$ /plugin install ux@ux-skill
— 或 —
$ pip install uxskill
— 或 —
$ npx uxskill@latest init

GitHub 源码:Laith0003/ux-skill · npm:uxskill · PyPI:uxskill · 文档:uxskill.laithjunaidy.com