LobeHub UX Audit 实践:用三层证据法审计"自学习建域"页面的完整拆解
本文以 LobeHub 仓库内 UX 审计范例报告 为主体,完整还原一次基于 ux-audit 技能 的界面体验审计:先讲清楚这套三层(静态 / 视觉 / 动态)审计方法论与判定规则,再逐条对照报告中引用的源码证据,说明每个"模式命中 / 亮点 / 体验缺口"是如何从代码里找出来并落成结论的,最后给出读者在自己的项目中复现这套审计的可行路径。
一、UX Audit 是什么:一次只审一个界面、以证据为准
ux-audit 是 LobeHub 仓库 .agents/skills/ 目录下定义的一个 Agent 技能,目标是对单个页面(surface)做可重复、有基准的体验评审。它回答两个问题:
- 这个界面用了哪些交互模式,用得怎么样——基准是 Jenifer Tidwell《Designing Interfaces》的"模式语言",清单化后存放在 pattern-catalog.md,按导航、布局、输入、命令与动作、复杂数据展示、反馈、入门引导、视觉风格等家族分组;
- 体验在哪里薄弱——基准是 ux 技能 的行为检查清单(如
edit.md的草稿保护、act.md的动线完整性),每个缺口都必须挂到某条清单项上。
调用约定写在技能 frontmatter 中:<page-or-surface> [--l1 | --l2 | --l3],即一次只审一个页面,可用层级参数限定范围。默认跑 L1(有截图时加 L2),"持续审计"体现在随产品迭代逐页重跑。
三层审计:结论必须来自"看得见它"的那一层
| 层级 | 程序文件 | 做什么 | 能抓什么 | 成本 |
|---|---|---|---|---|
| L1 静态 | layer-1-static.md | 读代码 | 缺失的空态/错误/重试分支、无草稿持久化、缺失的模式、结构性问题 | 低,离线,每次必跑 |
| L2 视觉 | layer-2-visual.md | 渲染截图 | 真实视觉层级与主导控件、间距/对比度/对齐、截断溢出、深浅色模式、响应式断点 | 中,需要渲染环境 |
| L3 动态 | layer-3-dynamic.md | 用 acceptance 自动化驱动真实用户旅程 | 进行中/锁定态、强制错误/空态、步骤衔接、焦点/键盘可达性、量化 CLS / LCP / INP | 高,需要运行环境 + 鉴权 |
配套一张覆盖矩阵,核心规则是"判定必须来自能看见它的层":视觉层级、间距对比这类结论只能由 L2 确认,L1 对它们是"会误导"的;动线衔接、键盘可达性、性能指标只有 L3 能下结论;而缺失分支、草稿未持久化这类结构问题 L1 就能确认。SKILL.md 里特别点名的"常见陷阱"就是:不能因为代码里有一个 variant 属性就勾掉"只有一个主按钮"——那是 L2 级别的判定。
几条硬性判定规则
技能文件里还有四条值得单独说的 Ground rule:
- Evidence, not vibes(证据而非感觉):每个发现都要引用证据——L1 是
file:line,L2 是"用 Read 工具核验过的截图",L3 是捕获值/快照。错误的"它缺失了"比没有发现更糟。 - 对标的是"界面类别"的惯例,而不只是自家产物:读代码只能暴露"已实现部分的缺陷",对"根本没实现的能力"是结构性盲的。审计前要先把这个界面所属类别的行业惯例列出来,对着找缺口。
- 对比两个变体时,赢家是结果判定而不是做工判定:对引导页、同意页、付费墙这类"挡在用户和目标之间"的门面,往往"最少的那版"最好;判定赢家必须挂在 L3/分析数据上,L1/L2 只能比较机制,不能宣布胜者。
- 报告好的一面,而不只列缺口:做得好的状态机、失败后仍保留草稿的输入框,都是与缺口同级的一等发现,要标出
file:line并打 ✅ 亮点,因为它们既是下一轮重构的"不要回归"清单,也是回灌进ux清单的正面示例。
严重度分三级:🔴 破坏信任(数据/输入丢失、卡死态、误导性空态、静默发送失败);🟠 死路或误导(无前进路径、状态含糊、缺进行中反馈);🟡 摩擦/不一致/错失的愉悦感——且明确提示这一级最容易被漏报。
二、审计案例全文:Create expertise modal(自学习建域界面)
example/self-learning-create.md 是一份典型的"worked example"——技能要求每份审计报告保存为 references/example/<page>.md,供下一轮运行当模板。它审计的对象是"自学习"模块中新建专家域(expertise domain)的创建界面。报告开头声明了运行层级:
Layers: L1 ✅ · L2 desktop dark ✅ · L3 forward journey partial.
即跑了完整的静态层与桌面端深色模式视觉层,动态层只覆盖了前向旅程的一部分。
1. 该界面命中的模式(Patterns in use)
报告用一张表逐条对照模式目录给出评级与证据:
| 模式 | 评级 | 证据 |
|---|---|---|
| Modal Panel(模态面板) | ✅ | 命令式的 base-ui 模态框(当时实现 CreateDomainModal.tsx:97-105) |
| Forgiving Input(宽容输入) | ✅ | 一句自然语言简述即可变成可编辑草稿(:26-89) |
| Preview(预览) | ✅ | 解析出的名称/过滤器在"创建"之前先行呈现(:53-70) |
| Prominent Done(显眼完成按钮) | ✅ | Continue/Create 是唯一主操作(:80-89) |
| Draft safety(草稿安全) | —(缺席) | 状态仅存内存,且 maskClosable 为 true(:29-31、:101) |
注意最后一行:草稿安全这一条评级是"—"(缺席),它直接对应下面第一条体验缺口。这体现覆盖矩阵的分层思想——草稿持久化是 L1 就能确认的结构问题,不需要截图或运行环境。
2. 亮点 / 好的案例(Strengths / good cases)
- ✅ 亮点 — 先给松散输入,再要结构(Loose input before structure)。 Forgiving Format 与 Preview 组合良好:用户先写一段话,系统解析成结构,用户再修正结构——而不是反过来逼用户先填一堆表单。
- ✅ 亮点 — 失败保留字段(Failure preserves fields)。
try/catch/finally上报错误后草稿原样保留(:33-45),生成失败不会让用户白输。
这两条正是"报告好的一面"规则的落地:每条都带 file:line 证据,并说明它为什么重要(load-bearing)。
3. 体验缺口(Experience gaps,按严重度排序)
- 🔴 点遮罩关闭 / 刷新会丢失草稿。草稿状态仅在内存中、且遮罩可点击关闭,一次误点或刷新就会把用户输入蒸发。补救方案:持久化草稿 + 脏状态关闭确认。
- 🟠 收录边界只以文本预览,没有用样例验证。"什么算属于这个域"的过滤器是一段文字,但用户在承诺之前看不到"哪些 Topic 会命中 / 哪些不会"。补救:在落库前展示匹配/不匹配的 Topic 示例。
- 🟡 解析步骤没有显式的"返回"。用户可以编辑解析出来的结果,但在流程模型里无法回头修改原始那句话。
🔴 级落在"输入丢失"上,符合严重度量表的定义(数据/输入丢失=破坏信任);🟠 级落在"边界不可验证"上,属于"误导/无确认手段";🟡 级是流程摩擦。
4. 技能反馈(Skill feedback)
报告末尾一行:"Validates Edit §2.1 and Act §3.1; no new generic rule landed."——即本次审计是 ux 技能 edit 模块 §2.1「保护进行中的编辑」 与 act 模块 §3.1 既有清单项的又一次实例化验证,没有沉淀出新的通用规则。技能要求:即使本轮没有新规则,也必须在报告中显式说明,沉默不是允许的收尾方式。
三、逐条对照源码:报告中的每个证据指认什么
报告引用的是当时名为 CreateDomainModal.tsx 的模态框实现;当前仓库中该界面已演进为整页组件 CreateDomainPage.tsx,由路由 agent/self-learning/new/index.tsx/agent/self-learning/new/index.tsx) 挂载。下面把报告里的每个结论映射回现有源码,验证审计证据链是否成立。
宽容输入 + 预览:一句简述 → 可编辑草稿
CreateDomainPage 的注释直接描述了这套"两步建域"模型(与 createGoal 交互对齐):
① 一段话说清方向 → ② 检查它读出来的锚。锚不只是名字和过滤器:分层决定经验挂在哪一层、经典依据决定「覆盖」意味着什么——所以 step 2 把整个锚候选摊开。
对应实现是 generate()(CreateDomainPage.tsx#L234-L244):调用 expertiseService.draftDomain({ agentId, brief }) 把自然语言简述变成一份 ExpertiseDomainDraft,成功后 setStep('review') 进入审阅步;失败则 toast.error 并回退——若已有草稿就回到 review,否则回到 describe。"失败保留字段"的亮点在此处得到印证:catch 分支只改步骤、不碰 draft 与 brief,已解析的结构完好无损。
审阅步的"预览"体现为整块摊开的可编辑字段:标题(maxLength=80)、domainFilter(域过滤器)、outOfScope(范围外)、rationale(依据)、经典条目列表(canonEntries,E1/E2 编号,可增删改标题/来源/陈述)与分层列表(layers,L1/L2 编号)。创建按钮的启用条件是明确的 canCreate(L270):
const canCreate = !!draft && !!draft.title.trim() && !!draft.domainFilter.trim() && !creating;
即必须有草稿、标题与域过滤器都非空才允许落库——"解析结果先呈现、再提交"的 Preview 模式在代码层面成立。审阅步底部只有唯一的主操作按钮(Confirm),且支持 ⌘/Ctrl+Enter 快捷提交(L298-L304),对应 Prominent Done 评级。
逐块精修:Adjustment 机制
审阅步里每个字段旁有一个"✨ 调整"按钮,点开 Popover 用自然语言要求局部重生成(如"把过滤器写得更严格")。其数据结构是一个五元组(createDomainAdjustment.ts):
export type AdjustmentTarget =
'canonEntries' | 'domainFilter' | 'layers' | 'outOfScope' | 'rationale';
export const mergeAdjustedBlock = (
current: ExpertiseDomainDraft,
adjusted: ExpertiseDomainDraft,
target: AdjustmentTarget,
): ExpertiseDomainDraft => ({ ...current, [target]: adjusted[target] });
refine()(CreateDomainPage.tsx#L246-L268)把当前草稿连同调整指令一起发给 draftDomain,返回后只合并被调整的那一个块,其余字段不受影响——这避免了整稿重生覆盖用户已手工修改的内容。页面上还有一处细节注释值得注意:精修请求在途期间对应字段被 disabled,因为"在途调整回答的是请求发出时的那份草稿,期间的编辑会在响应合并时被静默覆盖"。
草稿安全:从"🔴 缺口"到已落地的持久化
这是本报告最有价值的演进证据。审计时刻的评级是"Draft safety —(缺席):状态仅内存、遮罩可关",对应 🔴 级缺口 1(丢草稿)。当前代码中,这一缺口已有专门的 Hook 承接:useCreateDomainDraft.ts。
- 按 Agent 维度作用域的持久化:
storageKey = 'self-learning:create:' + agentId,草稿(brief+ 结构化draft)在每次变化时写入localStorage,两者皆空时删除键(L48-L63)。草稿作用域限定到具体 Agent,不会跨实体串稿——这正是 ux 技能 edit §2.1 检查单里"draft scoped to its target id"的要求。 - 旧格式迁移:早期模态框把简述以纯文本形式存在同一个键下,
parseStoredCreateDraft(L10-L21)兼容解析——JSON 失败就按纯文本兜底,JSON.parse出来是字符串也按{ brief }处理。 - 脏状态退出守卫:
CreateDomainPage额外挂了beforeunload监听,brief非空时event.preventDefault()(CreateDomainPage.tsx#L225-L232),补上了"刷新丢稿"这一半。 - 创建成功后清理:
create()成功落库后localStorage.removeItem(storageKey)再跳转(L287-L288);"返回总览"走clearDraft()同时清内存与存储。
测试用例 useCreateDomainDraft.test.ts 对这三点都有断言:
it('migrates the legacy plain-text brief', () => {
expect(parseStoredCreateDraft('Improve production incident response')).toEqual({
brief: 'Improve production incident response',
});
});
以及"路由 Agent 就绪时恢复草稿而不删除它""clearDraft 同时清内存与持久化副本"两个用例。换句话说,报告当年 🔴 级指认的"丢草稿"问题,后续被一条"回灌 → 修复 → 测试固化"的链路解决了,审计范例与修复后代码可以在同一个仓库里对读。
四、从报告到闭环:审计发现的"落地"机制
SKILL.md 强调:审计在发现被落地之前没有结束。一次运行的收尾有三步必做动作:
- 具体 Bug → 修掉最高 🔴,或以子任务形式建单;
- 可泛化的缺口 → 回灌
ux技能(强制):把缺口变成清单项的规则 + ❌ 示例,让检查清单每次运行后都比运行前更锋利;好的案例同理回灌 ✅ 示例,目标是"提炼出当前规则文本还没说清的子规则",而不只是装饰; - 报告本身 → 存为
references/example/<page>.md。
ux 是被审计的基准,审计是让 ux 保持诚实的机制,二者构成闭环。本例的 Skill feedback 一节("Validates Edit §2.1 and Act §3.1; no new generic rule landed")就是这一闭环的收尾记录:本轮没有新规则,但既有规则又被一个真实界面实例验证了一次。
对想在自己的项目里借鉴这套方法的读者,几个可直接搬走的要点:
- 先定层,再下结论:把"视觉层级对不对""键盘能不能到"这类问题分别钉死在能看见它们的层级上,避免"读代码打勾"式的虚假覆盖;
- 缺口必须能引用证据:
file:line(静态)、核验过的截图(视觉)、捕获值(动态)三选一,引用不出证据的结论不写进报告; - 好案例与缺口同级:每次审计强制产出 "Strengths / good cases" 一节,作为下一轮重构的"不要回归"清单;
- 报告即模板:把每份报告按统一结构(Patterns in use 表 → 亮点 → 排序缺口 → 技能反馈)存档,让第 N 次审计比第 1 次更快更准。
另外,pattern-catalog.md 结尾还基于历次审计沉淀了一张"本代码库最常薄弱的模式家族"提示:集中在 Feedback(缺失败/重试)与 Input(草稿安全、placeholder 误用)两族——审计时优先查这两族。这份范例报告中"草稿安全缺席 + 失败保留字段"恰好分别踩中这两族的一正一反两面,是这一提示的缩影。
五、小结
example/self-learning-create.md 这份不到 30 行的范例报告,完整演示了 LobeHub UX 审计方法论的最小闭环:以《Designing Interfaces》模式目录和 ux 检查清单双基准打分,L1 静态层确认"宽容输入 + 预览 + 唯一主操作"三项命中、草稿安全缺席,L2 桌面深色层确认视觉结论,L3 部分覆盖前向旅程;三条缺口按 🔴/🟠/🟡 定级并各给一行补救,最后以"验证既有清单项、无新规则"收尾。对照当前仓库源码可以看到,报告中标记的 🔴 草稿丢失缺口已由 useCreateDomainDraft.ts 的 localStorage 持久化、beforeunload 守卫与配套测试用例实际解决——这正是"审计让检查清单保持诚实、检查清单让下一次审计更快"这一闭环的实例化。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00