LobeHub ux-audit 实战范例:以自学习成长画像页为例的三层 UX 审计方法
本文以 LobeHub 仓库内 ux-audit 技能的一份参考范例——针对 Agent「自学习/成长画像」(self-learning overview)页面的 UX 审计报告——为骨架,完整拆解「三层审计 + 模式目录 + 严重度分级 + 回灌闭环」的方法论,并逐条对照当前仓库源码验证报告中的每条结论。读完你可以掌握:如何对单个页面(surface)做一次可复现、证据驱动的 UX 审查,如何把发现落地回 ux 检查清单,以及如何在源码层面复核审计结论是否仍然成立。
这份文档是什么:ux-audit 技能的一份参考范例
该文件位于 .agents/skills/ux-audit/references/example/self-learning-overview.md,是 LobeHub 为 Agent 准备的 ux-audit 技能(见 .agents/skills/ux-audit/SKILL.md)下的一份已完成的审计范例(worked example)。它的开篇第一行就交代了审计对象与覆盖层次:
Surface:
/agent/:aid/self-learning。 Layers: L1 ✅ · L2 desktop/760px dark ✅ · L3 keyboard/navigation ✅。
ux-audit 技能的目标是对一个界面一次(one surface per run)做标准化 UX 审查,其基准是两套东西:
- 模式语言(pattern catalog)——「好界面由什么构成」,见 references/pattern-catalog.md;
- ux 技能的执行检查清单(.agents/skills/ux/SKILL.md)——「一个流程应当如何表现」。
审计要回答两个问题:这个 surface 用了哪些模式(用得如何),以及体验在哪里薄弱(每个 gap 都要挂到某条检查清单项上)。反复出现的 gap 会作为新的检查项回灌进 ux 技能,而审计本身则沉淀为 references/example/<page>.md 范例——本文引用的文档正是这样一份范例,仓库内同目录还有 home.md、fleet.md、task-detail.md 等一批姊妹范例。
三层方法:L1 静态 / L2 视觉 / L3 动态
ux-audit 技能把审计拆成三层,核心理则是「结论必须来自能够看见它的层」(a verdict must come from a layer that can see it),并配有一张覆盖矩阵规定每类发现只能由哪一层下结论:
| 层次 | 做什么 | 能捕获什么 | 成本 |
|---|---|---|---|
| L1 Static | 读代码 | 缺失的状态/分支(empty/error/retry)、草稿未持久化、模式缺失、结构问题 | 低,离线,每次必跑 |
| L2 Visual | 对渲染结果截图 | 真实视觉层级与主导控件、间距/对比/对齐、截断溢出、empty/loading/error 实际长什么样、响应式断点、深浅色 | 中,需要可渲染环境 |
| L3 Dynamic | 通过 acceptance 自动化驱动真实用户旅程 + 插桩测量 | 进行中/锁定状态、强制触发的错误/空态、步骤衔接、焦点/键盘可达性、量化的 CLS / LCP / INP / 长任务 | 高,需要运行环境与登录态 |
几个关键规则值得单独强调,因为本文档的每条发现都受制于这些规则:
- 视觉类结论不许从代码里勾选。例如「只有一个主按钮」「空态是一个真正的页面」都是 L2 结论,不能因为代码里有
variantprop 就在 L1 打勾。 - 严重度分级(Severity rubric):🔴 破坏信任(数据丢失、卡死状态、误导性空态);🟠 死胡同或误导(无前进路径、无进行中反馈);🟡 摩擦/不一致/错失 delight——并且明确提示 🟡 层最容易被漏报,审计者需要单独切一次「interface-details」镜头。
- 报告好的部分:只列问题的审计会退化成 bug 报告。亮点(✅ 亮点)是一等发现,要写行为、证据和「为什么它承重」,因为它们既教人、又保护(下轮重构知道哪些行为不许回退)、还能校准 gap 的严重度。
本文档声明三层全部跑过:L1 代码、L2 桌面端 760px 深色截图、L3 键盘/导航旅程。下面按文档自身的章节结构逐段拆解。
审计对象:self-learning「成长画像」页面
在被审计的界面上,Agent 的自学习(self-evolving)概览页展示某个 Agent 在各「方向」(domain)上的学习状态:判断句标题(这个方向现在靠不靠谱)、习惯清单、成长曲线(累计学到 / 做对率)、方向清单,以及「教它新东西」的输入框。路由入口在 src/routes/(main)/agent/self-learning/index.tsx(目录名含括号,此处以代码路径文本形式给出),它只是薄薄地渲染核心组件:
import SelfLearning from '@/features/SelfLearning';
const AgentSelfLearningPage = memo(() => <SelfLearning />);
核心实现在 src/features/SelfLearning/index.tsx,其组件注释直接点明了页面的产品语义:
成长画像 —— 自进化的 L0,也是单方向时的全部。感知单位是「习惯 + 它靠不靠谱」,不是「学到几条」:判断句、按层画像、习惯分组、做对率曲线全部由 hits.outcome 折出来。这里没有任何必办事项 —— 教学台,不是审批台。带 :domainId 进来时就是同一张画像收窄到一个方向。
几个与审计结论直接相关的实现细节(均为当前源码可确认的事实):
- 两段式数据获取与轮询:先用
useExpertiseOverview读一次以统计已学条数,再交给 Portrait/useHistoryWarmup.ts 的 warm-up hook 决定轮询节奏——phase === 'running'时以WARMUP_POLL_MS作为 SWR 的refreshInterval持续刷新画像(该文件 L119 附近),否则停止轮询。 - 判断句生成:
sentenceFor(index.tsx L89-L116)按优先级给方向排序打分——有「反复出现」的习惯 > 有不稳定的习惯 > 从未实践 > 全部稳定——并据此生成一句具体的判断句(如「反复在同一步出错」而非「已学 N 条」)。 - 空态即首屏:
AsyncBoundary的 empty 分支(index.tsx L275-L290)渲染一个带DnaIcon、标题、说明文案和**主按钮「新建方向」**的空页面;empty、loading、error 三种状态互斥且各有归属。 - Create 入口的归属:头部工具栏里的「新建方向」按钮被显式限定在概览态:
{/* Starting a new direction belongs to the overview, not to one direction's page. */}
{!domainId && (
<Button type={'text'} onClick={openCreate}>
{t('nav.newDomain')}
</Button>
)}
(index.tsx L249-L254)注释说明得很直白:新建方向属于概览,不属于某个方向的页面。
报告逐段解读:一份范例审计长什么样
Patterns in use:模式评级表
文档的第一节是一张「模式 × 评级 × 证据」表,每个模式给出 ✅ / ⚠️ / — 评级,并用 file:line 定位证据(下表完整继承原文档):
| 模式 | 评级 | 证据 |
|---|---|---|
| Empty-state as onboarding(空态即引导) | ✅ | 空/加载/错误三种状态互相区分,且 Create 是主 CTA(SelfLearning/index.tsx:154-187)。 |
| Overview + Detail(总览 + 详情) | ⚠️ | 曲线/列表支持下钻,但「仅一个方向就自动跳转」会毁掉枢纽(:95-100)。 |
| Data Spotlight(数据聚光) | ✅ | 只有三条高练习量曲线被强调(:107-125)。 |
| Titled Sections(带标题的分节) | ✅ | Trend、insights、domains 各自成组。 |
| Clear Entry Point(清晰入口) | — | 有数据的状态下没有 Create 动作。 |
| Responsive Disclosure(响应式折叠) | ⚠️ | 760px 下宽侧边栏仍然保留,图表标签变得极小。 |
读这张表的方法正是 ux-audit 技能要求的:不是给模式打勾,而是每个评级都挂一条可复核的证据。⚠️ 和 — 两行就是这张表贡献的增量信息——Overview+Detail 的下钻能力是好的,但跳转行为把它从「枢纽」变成了「死胡同」;「Clear Entry Point」之所以是 —(无法评级),因为该状态根本不存在 Create 入口,评级无从谈起。
Strengths / good cases:三个 ✅ 亮点
第二节列出「不许回退」的亮点,每个都写明行为、证据位置和为什么承重(完整继承原文档):
- ✅ 亮点 — 诚实的无数据表现(Honest no-data projection):在有意义的序列出现之前,图表根本不出(
SelfLearning/index.tsx:91-93)。不画一条假曲线去「看起来在学习」,是数据页面最基本的诚实。 - ✅ 亮点 — 聚焦的对比(Focused comparison):只有三条被强调的曲线避免了「彩虹图」(
SelfLearning/index.tsx:107-125)。 - ✅ 亮点 — 真实的首次运行页面(Real first-run page):empty / failed / loading 三态分离;empty 既解释价值又提供 Create(
SelfLearning/index.tsx:154-187)。
在当前的 src/features/SelfLearning/Portrait/GrowthCharts.tsx 中可以看到「聚焦对比」的具体实现:图表是固定 380×96 的自绘 SVG(const W = 380; const H = 96;),只画两条默认曲线——累计学到(它懂了多少)与做对率(它可靠了没),两者同向走高,一眼读出「在变成专家」;多方向时按实践序号对齐后求和/求均值。而「无数据不画图」这条约束在 index.tsx L339 依然是显式的:
{runs > 0 && <GrowthCharts domains={scoped} />}
即只有 runs > 0(有过实践)才渲染曲线区块——与审计的「Honest no-data projection」完全吻合。
Experience gaps:五条按严重度排序的发现
第三节是审计报告的核心,按 🔴 → 🟠 → 🟡 排序,每条包含「发现 + 违反的清单项 + 修复方向」(完整继承原文档):
- 🔴 有了第一条数据后,枢纽和 Create 一起消失。 无条件的「仅一项则跳转」加上 Create 只存在于空态,导致面包屑变成循环。修复方向:保持概览可达,并在概览头部暴露 Create。
- 🟠 方向行与 insight 行是「纯鼠标伪控件」。 运行时的行是
DIV、无 role、tabIndex=-1、不出现在交互快照里(:243-264、:313-322)。修复方向:换成真正的 Link/规范行,并带 focus-visible 样式。 - 🟠 占据中心舞台(Center Stage)的是模型输出,而不是注意力。 曲线填满首屏,可操作的 insight 却在首屏之下。修复方向:先放「需要关注的」与「下一步动作」,证据放到下面。
- 🟡 窄桌面下可读性崩塌。 760px 时标题折行到四行、图表标签缩小,而侧边栏仍占约 280px。修复方向:响应式外壳,以及在「足够画图的宽度」以下切换到「列表优先」的替代形态。
- 🟡「学完了」隐藏了拟合的不确定性。 概览把由阈值推导出来的形态转译成了确定性,而详情页的置信度又不可用/是推测性的。
前两条(🔴🟠)属于「破坏信任 / 死胡同」级:键盘用户完全无法进入方向详情,而首次创建者创建完第一条数据后反而失去了枢纽页。这正是 ux-audit 技能里说的「一个错误结论比没有发现更糟」的反面教材——每条 gap 都标了来源层(键盘可达性来自 L3,视觉层级来自 L2),并给出可执行的一行修复方向,而不是抽象建议。
Skill feedback:回灌闭环
文档最后一节回答「这次审计让检查清单变强了没有」:
- Landed(已落地):
ux技能的 Read §1.6 现在要求「数据快捷方式(data shortcuts)必须保留稳定的枢纽与集合级动作」——这正是 gap 1 泛化后的规则。 - Validated(验证了既有规则):Act §3.4、键盘语义、Responsive Disclosure 三条既有检查项被本次审计实例化验证过。
这对应 ux-audit 技能中的「回灌(loop-back)」机制:ux 是审计的基准,审计是让 ux 保持诚实的机制——每条可泛化的 gap 都要回写成检查项的 ❌ 例子,每个做得好的模式则回写为 ✅ 例子或提炼出新的子规则;跳过回灌,审计就退化成一锤子买卖。
源码复核:审计结论放在当前代码上还对吗
范例文档的价值在于「证据可复核」。以下用当前仓库源码逐条核对关键结论(注意:文档中的行号对应审计当时的修订版,当前文件已经演进,行号有漂移,但结构级结论大多仍可直接对照)。
🔴 gap 1 的修复已在源码中体现
审计时「仅一个方向就无条件跳转」的逻辑,在当前 index.tsx L69-L74 中已演变为同页收窄而非跳转:
const scoped = useMemo(
() => (domainId ? allDomains.filter((d) => d.id === domainId) : allDomains),
[allDomains, domainId],
);
const single = scoped.length === 1;
从源码结构看,「带 :domainId 进来时就是同一张画像收窄到一个方向」意味着:概览不再被破坏,DomainList 也仅在「多方向且不在某方向页内」时渲染(index.tsx L361-L369),面包屑在单方向态下会变成指向概览的 Link(index.tsx L233-L239,domainId && current 时渲染回链)——「breadcrumb 变成循环」的问题被正面解决。再看「Create 只在空态」这一点:当前头部工具栏在 !domainId 且已有方向时也提供「新建方向」(前文 L249-L254 片段),且空态的主按钮(L283-L287)与头部入口指向同一个 openCreate。也就是说,gap 1 提出的两条修复方向(保持概览可达 + 概览头部暴露 Create)在当前代码中都已成立——这正是「审计 → 修复 → 范例留存」闭环的鲜活样本。
另外可观察到:当前代码的导航目标路径段已从 self-learning 更名为 self-evolving(openCreate 与删除后的回跳都使用 urlJoin('/agent', activeAgentId, 'self-evolving', …)),而 src/routes/(main)/agent/self-learning/ 目录仍保留,其中 legacy.tsx 导出 LegacyRouteRedirect 做旧路径兼容。审计范例里写的 Surface /agent/:aid/self-learning 即更名前的路径。
🟠 gap 2:伪控件行的现状
Portrait/DomainList.tsx 中的方向行当前仍是命令式导航而非规范链接:
<Flexbox
horizontal
align={'center'}
as={'button'}
className={styles.row}
...
onClick={() => onOpen(d.id)}
>
行是一个被渲染成 button 样式的 Flexbox,点击后通过 onOpen(id) 回调回到 index.tsx L362-L368 再 navigate(...)——即审计所指的「伪控件」形态(非 <a href> 的规范路由行)。组件注释也自陈了产品语义:「多个方向时的方向清单:一根可靠度条 + 一个词。单方向时不渲染——方向就是判断句的主语。」因此 gap 2 的修复(真实 Link + focus-visible)在当前结构上仍然成立,这也是该范例作为 ❌ 例子继续有效的部分。
🟡 gap 4:固定尺寸 SVG 与窄桌面可读性
GrowthCharts.tsx 中图表坐标系是硬编码的 380 × 96,坐标轴字号 10px。在 760px 这类窄桌面布局中,侧边栏占去约 280px 后,绘图区被进一步压缩——「图表标签变小、标题折四行」的 L2 发现与这个固定坐标系完全自洽。从源码结构看,该组件没有随容器宽度变化的响应式分支,修复方向(响应式外壳 + 低于「可用绘图宽度」时切换列表优先)依然是正确的。
数据聚光与两阶段轮询:✅ 亮点仍然成立
- Data Spotlight(三条强调曲线)对应
GrowthCharts只渲染两条聚合曲线 + 有限强调点的做法,与「避免彩虹图」的评级一致; - 空态三分离与 Create 主 CTA 对应
AsyncBoundary的isEmpty/errorVariant={'page'}/onRetry三参数(index.tsx L270-L291),empty 分支文案与动作齐备; - warm-up 轮询(
useHistoryWarmup返回refreshInterval供 SWR 使用,见 index.tsx L57-L67)保证了「候选历史挖掘中」这类进行中状态有真实的加载/推进语义,而不是假进度条。
如何复现这样一次审计
把范例抽象成流程,对任意一个 LobeHub 页面执行同等级别的 UX 审计只需五步:
- 选一个 surface,声明要跑哪几层。L1 永远跑;涉及布局/层级/响应式加 L2(需要截图并用 Read 工具核验);涉及旅程衔接、键盘可达性、CLS/LCP/INP 量化加 L3(依赖 acceptance 自动化框架与登录态)。参考层定义文件 layer-1-static.md、layer-2-visual.md、layer-3-dynamic.md。
- 按覆盖矩阵下结论:每条发现只挂在能看见它的层上;「只有一个主按钮」这类视觉结论必须截图验证,不许从代码里的
variantprop 反推。 - 先命名 surface 的类别(class)再审计。读自己的代码只能暴露「已造出来的东西」的瑕疵,对「压根没造的能力」是结构盲的——先按同类成熟产品的惯例列出该类别应有的能力清单,再对照找 gap。
- 按固定结构输出:① Patterns in use 评级表(每行带
file:line证据);② 独立的 Strengths / good cases 章节(✅ 亮点 + 为什么承重);③ 按 🔴/🟠/🟡 排序的 Experience gaps(每条含违反项、来源层、一行修复方向);④ Skill feedback(落地了哪条规则、验证了哪条既有规则)。 - 落地(Land)才算完成:修复最高 🔴 或拆成子问题;把可泛化 gap 回灌进
ux检查清单(含 Quick review 对应行);把做得好的模式回写为 ✅ 例子或提炼子规则;最后把报告存为references/example/<page>.md供下一次审计当模板。
这套流程的要点可以浓缩成三句:结论必须来自能看见它的层;亮点与缺陷同权(亮点是「不许回退」清单,也是校准严重度的基线);审计不是终点,回灌才是——每一轮审计都应该让检查清单比开始之前更锋利。本文档(self-learning-overview.md)连同 pattern-catalog.md 与 SKILL.md,构成了一份从「读代码」到「改规则」的完整可执行样板。
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