LobeHub 入职引导模块系统化 UX 审计实战:一个可复用的 worked example 全解
这篇技术指南以 LobeHub 仓库中 ux-audit 技能的官方范例文档(.agents/skills/ux-audit/references/example/onboarding.md)为核心主体,完整还原一次针对整个 Onboarding(首次引导)模块的系统性(systemic)UX 审计:从审计框架与覆盖分层、审计范围界定、模式清单盘点,到亮点沉淀、10 项分级缺陷的定位与定级、规则回灌与工单落地。读完你将掌握一套"以代码静态审计为主、以模式目录为基准、以规则回灌为闭环"的可重复 UX 审计方法论,并看清它在 LobeHub 真实代码库中如何落地。
一、这是一次什么样的审计:系统性(systemic)而非单屏
ux-audit 技能对一次审计只处理一个 surface 有着严格约束(见 SKILL.md),但当目标不是单个页面、而是"一批强相关的相关界面"时,就会升级为 systemic pass。onboarding.md 记录的就是这样一次 2026-07 对整个 Onboarding 模块(首次运行设置,first-run setup)的真实运行。
审计采用三层结构,每层对应独立的程序文件,且有一个铁律:结论必须来自能看见它的那一层:
| 层级 | 程序文件 | 干什么 | 能捕获什么 |
|---|---|---|---|
| L1 静态 | layer-1-static.md | 读代码 | 缺失的状态/分支(empty/error/retry)、草稿未持久化、模式缺失、结构性问题 |
| L2 视觉 | layer-2-visual.md | 对渲染结果截图 | 真实视觉层级与主导控件、间距/对比/对齐、截断/溢出、empty/loading/error 的真实观感、断点、明暗模式 |
| L3 动态 | layer-3-dynamic.md | 驱动真实用户旅程 + 插桩 | 进行中/锁定态、强制的错误/空态、step N→N+1 是否成立、焦点/键盘、量化的 CLS/LCP/INP |
本次 Onboarding 审计只运行了 L1(静态/代码层),L2/L3 均未执行(本次没有渲染与运行环境)。因此,所有涉及真实按钮主导性、进度条是否渲染的视觉结论都被打上了 pending L2 标签——这正是"一个结论必须由能看到它的层来下"这一 ground rule 的直接体现,详见 coverage matrix。
二、审计范围:两个入口背后的三条并行流程
审计首先要做的不是埋头读代码,而是界定 surface 的"类"(class)并列出该类别的应有能力。Onboarding 的表面类对应模式目录中 Getting started 家族(Welcome / Guided Tour / Empty-state-as-onboarding),被审计的是同一族小表面:
| 流程 | 路由 | 屏幕(按顺序) | 编排器 |
|---|---|---|---|
| 公共前缀 | /onboarding(?step=1|2) |
Telemetry → ResponseLanguage | features/Onboarding/Common/index.tsx |
| Classic(web/mobile) | /onboarding/classic |
FullName → Interests → [ProSettings] → AgentPicker | features/Onboarding/Classic/index.tsx |
| Agent(web/mobile) | /onboarding/agent |
单步对话式 | features/Onboarding/Agent/index.tsx |
| Desktop(Electron) | /desktop-onboarding(?screen=) |
Welcome → [Permissions·mac] → DataMode → Login | routes/(desktop)/desktop-onboarding/index.tsx |
在当前仓库中可对应定位到:公共前缀与分支切换收敛于 src/features/Onboarding/index.tsx(依据 ?step 查询参数渲染 TelemetryStep 或 ResponseLanguageStep,前缀完成后内联渲染 Classic 分支);Classic 分支的步骤机集中在 Classic/index.tsx(步骤 1=FullName、2=Interests、3=ProSettings、4=AgentPicker);桌面流程则在 DesktopOnboarding 与 src/routes/(desktop)/desktop-onboarding/index.tsx 对应的桌面路由下。
审计快照记录的编排关系如下:
- 公共前缀通过
deriveOnboardingBranchPath(branch.ts:18)汇入 Classic 或 Agent 分支; - Agent 分支受构建开关
AGENT_ONBOARDING_ENABLED+ 运行时enableAgentOnboarding+!isDesktop共同门控; - 步骤外壳(step chrome)在 web 端由
routes/onboarding/_layout、桌面端由routes/(desktop)/desktop-onboarding/_layout共享。
持久化 / 恢复(健康区): Classic 步骤是服务端持久化的(onboarding.currentStep + finishedAt,见 store/user/slices/onboarding/action.ts),上层叠加了乐观的 localOnboardingStep 与一个合并写队列(coalescing update queue);桌面端通过 resolveInitialScreen(URL → saved → everCompleted → Welcome,见 DesktopOnboarding/resolveInitialScreen.ts)恢复;老用户通过 needsOnboarding(selectors.ts)整段跳过。回跳 URL 参数在整个流程中被一路传递(见 src/utils/onboardingRedirect.ts 及其测试 onboardingRedirect.test.ts)。
三、模式清单:用了什么、用得怎么样
审计的基准是 Jenifer Tidwell《Designing Interfaces》的模式语言 + LobeHub ux 技能的执行清单(见 pattern-catalog.md 与 ux skill)。对照下表可以快速看出模式分布的"强区"与"真空区":
| 模式(家族) | 位置 | 评级 | 备注 |
|---|---|---|---|
| Welcome / Sign-on | Telemetry(TelemetryStep.tsx)、桌面 Welcome |
✅ | 有目的性的首屏、打字机开场 |
| Guided Tour / Onboarding(分步) | Common + Classic 线性步骤 | ⚠️ | 有步骤但无进度/Sequence Map(缺陷③) |
| Sequence Map / 进度 | — | 缺 | 最多 6 个 classic / 4 个 desktop 屏幕,却无 "N of M"(缺陷③) |
| Escape Hatch(跳过) | AgentPicker 跳过;agent 分支模式的布局跳过 | ⚠️ | 纯 classic 流程直到最后一步前都无法跳过(缺陷⑤) |
| Deep-linking | ?step(web)、?screen(desktop)恢复位置 |
✅ | 规范化、可恢复 |
| Empty-state as onboarding | AgentPicker 区分空态与错误态(index.tsx:162-167) |
✅ | 做得好——但错误态无重试(缺陷④) |
| Loading Skeleton | AgentPicker 骨架、Agent 品牌加载器、桌面 Suspense | ✅ | 项目级加载器,未用 antd Spin |
| Failure + Retry | 桌面 LoginStep 完整 idle/loading/success/error + retry+cancel | ✅ | 典范——其他处应以此为模板 |
| Failure + Retry | web 写入步骤 + AgentPicker install/load | 缺 | 缺陷①②④⑥ |
| Progress Indicator / Cancelability | 桌面 LoginStep 认证倒计时 + 取消(LoginStep.tsx:293) |
✅ | |
| Prominent "Done" Button | 每步一个主按钮贯穿全流程 | ✅ | 主导性待 L2 验证 |
| Illustrated Choices | Interests 网格、桌面 DataMode | ✅ |
阅读结论: 状态机类工作(桌面 Login、恢复、乐观队列、回调贯穿、Agent bootstrap→classic 降级)已成熟;薄弱点集中在 Feedback(web 写入步骤的失败/重试) 与一个 Navigation 类规范缺口(无进度指示 / Escape Hatch 偏弱)。
四、亮点沉淀:"不可回归清单"
该模块最强的地方恰恰是"找缺陷"最容易漏掉的部分——状态机与恢复管道,它们是审计"回灌闭环"里 ✅ 的那一半,也是下一次 onboarding 改动时的"don't regress"清单:
- ✅ 亮点 —— 桌面
LoginStep状态机(→ Feedback / Failure+Retry 的 ✅ 范例)。 完整覆盖 idle / loading / success / error,且带 retry + cancel + 认证倒计时,由主进程广播(authorizationSuccessful/Failed/Progress)驱动,并与真实远端配置对齐(见 DesktopOnboarding/steps/LoginStep.tsx)。web 写入步骤(缺陷①②④⑥)应拷贝这个失败重试样板。 - ✅ 亮点 —— 恢复 / 持久化。 Classic 步骤服务端持久化(
onboarding.currentStep+finishedAt),底层是乐观localOnboardingStep加一个能扛住快速连续点击的合并写队列(见 action.ts 中通过#enqueueOnboardingWrite串行化的写入链);桌面经resolveInitialScreen(URL → saved → everCompleted → Welcome)恢复;老用户由needsOnboarding(selectors.ts)整段跳过。被中断的引导会在正确步骤重新打开。 - ✅ 亮点 —— Agent bootstrap 失败时降级到 Classic 而非白屏。 bootstrap 查询失败会重定向进确定性的 classic 流程,而不是把用户困在坏掉的对话里(
features/Onboarding/Agent/index.tsx:330-335)——优雅降级到可靠基线。(其姊妹ErrorBoundary fallbackRender={() => null}位于:372,是唯一弱项,见 §5 缺陷⑩。) - ✅ 亮点 —— WrapUp 是一个带
finally的"确认 → 进行中 → 完成"。WrapUpHint在结束前跑confirmModal,并在finally中重置loading(WrapUpHint.tsx:35-52)——这正是语言门写入(缺陷①)缺失的形状。 - ✅ 亮点 —— Callback-URL 贯穿。 注册目标被暂存、历经整个多步流程、结束时消费,并经过安全重定向守卫(
utils/onboardingRedirect、AgentPickerStep/index.tsx:114)——首次绕行后把用户送回原目的地。当前仓库中该能力可从 onboardingRedirect.ts 与 Layout/index.tsx 的stashOnboardingCallbackUrl(search)调用看到。 - AgentPicker 空态-错误态之分。
failedToLoad与empty分别渲染(AgentPickerStep/index.tsx:162-167)——"错误≠空"做对了(缺失的重试是缺陷④,而非对该区分的否定)。可恢复、规范化的深链(?step/?screen)。
五、体验缺陷(已排序分级)
① ResponseLanguage —— 公共前缀门写入无失败路径 → 永久卡死步骤 🔴
handleNext 先置 isNavigating=true,然后 await setSettings({ general: { responseLanguage } }) 无 try/catch/finally(审计快照的 ResponseLanguageStep.tsx:37-43)。而这次写入正是 commonStepsCompleted 的唯一信号(selectors.ts 中 commonStepsCompleted 只检查 responseLanguage 是否已写入)。若写入被拒(如网络抖动),onNext 永不触发、isNavigating 永不复位 → Send 与 Back 永远 disabled,无错误、无重试:用户被锁死在语言屏,进不了产品。门控整个流程的那一次写入,恰恰是零失败处理的那一次。
值得补充的仓库复核:当前代码中 ResponseLanguageStep.tsx 已将
setSettings写入包进try/catch,失败时置hasError、复位isNavigatingRef/isNavigating,并渲染responseLanguage.saveFailed错误文案——说明该缺陷在审计后已得到修复,这也正是审计落地价值的一个直接证据。
② AgentPicker —— agent 安装失败被吞掉,引导却照常完成 🔴
handleContinue 把 installMarketplaceAgents 包在 catch { console.error } 里,然后照样走 finish('continue') → finishOnboarding() → navigate 离开(AgentPickerStep/index.tsx:135-140)。用户亲手挑选的 agents 静默安装失败,最终落在一个缺少这些 agents 的应用里却毫无提示——最后一步的全部意义可能不可见地失效。仓库复核:steps/AgentPickerStep/index.tsx 中该 catch-and-continue 逻辑仍然存在。
③ 任何流程都没有进度 / Sequence Map —— surface-class 基准(Navigation)🟠
Classic 最多连续 6 屏(telemetry→language→fullname→interests→[prosettings]→agentpicker),desktop 也有 3–4 屏;两者都不显示 "Step N of M" 或进度条——模块内唯一的 <Steps> 只是 Telemetry/Welcome 里的装饰性功能列表(current={null},TelemetryStep.tsx:82、WelcomeStep.tsx:72)。而同类成熟产品(Notion / Linear / Slack / Vercel 的设置向导)普遍展示总长度与当前位置。用户无法判断引导还要多久。(待 L2 确认真实渲染。)
④ AgentPicker 错误态无重试 🟠
模板加载失败时只渲染一段裸的 agentMarketplace.picker.failedToLoad 文案(AgentPickerStep/index.tsx:160-167),没有 Reload。空态与错误态确实被正确区分(好),但最后一步的核心能力丢失后,只能靠 Skip 放弃来恢复。
⑤ FullName 必填、classic 流程到最后一步前无 Escape Hatch 🟠
FullNameStep 唯一的向前控件是 SendButton,非空名字前一直 disabled(FullNameStep.tsx:74),无 Skip / 无名下一步;布局层的 Skip 链接只在 agent 分支模式下渲染(_layout/index.tsx:45-50)。因此纯 classic 流程强迫用户走完 telemetry/language/fullname/interests/prosettings 直到 AgentPicker 都无跳过。类规范:可选资料步骤应可跳过,身份设置不应硬阻塞首次进入。
⑥ 资料写入是 fire-and-forget,无失败反馈 🟡
FullName(FullNameStep.tsx:34)、Interests(InterestsStep.tsx:72)、Telemetry(TelemetryStep.tsx:35)调用 updateFullName/updateInterests/updateGeneralConfig 时不 await、不 catch,随即 onNext()。store action 是异步的(common/action.ts:54,59),但各步骤无视了这个 promise:服务端持久化失败是静默的,值丢失而流程继续前进。(乐观 store 只是软化显示,不保证持久性。)仓库复核:FullNameStep.tsx 当前 handleNext 仍是"调用 updateFullName(value.trim()) 后立即 onNext()",未 await。
⑦ 步骤内草稿不跨刷新持久化 🟡
输入的姓名(FullName)与自定义兴趣(Interests)只存在于本地 useState,仅 Next 时才提交。步骤级恢复可用(服务端 currentStep),但步骤中途刷新会丢失未发送输入。
⑧ 步骤同步失败被吞掉 🟡
internal_processStepUpdateQueue 对服务端写入只 console.error(onboarding/action.ts:91-93)。若步骤持久化持续失败,恢复点会悄然不前且无人告知用户(低危:下一步时自愈)。
⑨ ComposioServerList 拉取无 loading/error 面 🟡
useFetchUserComposioConnections(true) 驱动各应用的连接状态,但网格始终渲染静态 COMPOSIO_APP_TYPES(ComposioServerList/index.tsx:14-37);连接拉取失败会静默地把所有集成显示为未连接——"加载失败"被读成"什么都没连"。非死路(可选步骤)。(逐项渲染待 L2。)
⑩ Agent 对话子树失败 → 白屏 🟡
对话被包在 <ErrorBoundary fallbackRender={() => null}> 中(Agent/index.tsx:372),chat 子树一旦抛出就渲染空——无错误、无重试。Agent 流程为换取丰富性付出了更大的单轮失败面;它值得一个可见错误 + 恢复(或复用上面那个 bootstrap-error → Classic 降级路径)。该问题在 Agent-vs-Classic 对比中被暴露。
六、技能反馈:审计如何反哺规则库(回灌闭环)
回灌闭环有两个半边——缺陷打磨某条清单项的 ❌ 例子,亮点打磨其 ✅ 例子。本次运行两半边都落了地:
- 新 ❌ 规则落地:Grow §5.2 —— 多步流程要显示进度并保持可跳过。 分步流程(>2 步)必须 (a) 展示进度/步骤指示器(位置 + 总数)、(b) 让非必需步骤可跳过并提供始终可见的 escape hatch;同时镜像进 SKILL.md 的 Quick review。❌ 例子即缺陷③ + ⑤(模块内唯一的
<Steps>是装饰性的,current={null})。对应规则现可在 ux/references/grow.md 的 §5.2 看到。 - 既有规则强化 + 新 ✅ 例子落地:Feedback §4.2 现在同时覆盖"被 await 的门控写入"——它必须在
finally中复位 in-progress 标记并在 catch 时提供重试,否则一次失败写入会永久禁用前进控件。❌ 例子:缺陷①。✅ 例子:带 retry + cancel 的桌面LoginStep状态机——它现在成为清单引用的正面模型。对应规则见 ux/references/feedback.md 的 §4.2。 - 既有规则得到验证(补充了新的 ❌ 引用例):§4.2(②④⑥⑧⑨⑩)、§3.5(②⑥)、Edit §2.1(⑦)、Read §1.1 error-as-empty(⑨)、Escape Hatch(⑤)。
- 方法论回灌:Agent-vs-Classic 之问催生了一条新的 ux-audit 基本规则——"比较两个变体时:胜者是一个结果性(outcome)判定,而非工艺性(craft)判定",并在覆盖率矩阵中新增 A/B-winner 行,用本模块自己的失误作为 ❌ 自省例。这条规则本身已写入 SKILL.md 的 ground rule:winner 判定必须由 L3/analytics 裁决,L1/L2 只能比较机制,不能宣布赢家。
七、待办:L2 视觉 + L3 动态验证清单
审计对任何无法由 L1 定论的结论都留了明确的后验任务,而非妄下结论:
- L2 —— 确认无进度指示器渲染(③);确认每一步主按钮都是主导控件(全步骤 pending-L2);AgentPicker 的 empty/error/loading 真实渲染可区分(④);ComposioServerList 逐项连接/错误渲染(⑨);暗色 + 窄屏。
- L3 —— 对 ResponseLanguage 的
setSettings写入强制断网以实况确认卡死步骤(①);强制installMarketplaceAgentsreject 并确认静默完成(②);强制 AgentPicker 模板加载失败并确认无重试死路(④);完整走一遍 classic 确认前进动能 + 无跳过(⑤);测量步骤切换的 INP / CLS。
八、结果落地:从发现到工单队列
发现最终落地为 LOBE-11138("Onboarding UX Audit",挂靠在 UX-audit 父任务 LOBE-11078 之下),拆分为以下子问题;类规范缺口(③⑤)同时回灌进 ux 的 Grow §5.2,await-write 规则(①)则回灌进 Feedback §4.2(见第六节):
| 子问题 | 发现 | 类型 |
|---|---|---|
| LOBE-11154 | ① language-gate 卡死步骤(加 finally + retry) |
bug 🔴 |
| LOBE-11155 | ②④ AgentPicker:安装失败静默完成 + 加载失败无重试 | bug 🔴 |
| LOBE-11156 | ③ 进度 / Sequence Map 缺失 | bug + 回灌 |
| LOBE-11157 | ⑤ FullName 必填 / 无 escape hatch | bug + 回灌 |
| LOBE-11158 | ⑥⑦⑧⑨ fire-and-forget 写入 / 草稿丢失 / 静默步骤同步 / composio 状态 | bug 🟡 |
| 未归档 | ⑩ Agent 对话 ErrorBoundary → 白屏(并入 LOBE-11155 或新建) |
bug 🟡 |
九、把这个方法论搬到你的模块
onboarding.md 的可复用之处不在于"Onboarding 有什么问题",而在于它的审计姿势,六步闭环可直接迁移:
- 定类:先命名 surface 的类与同类产品规范(Getting-started 家族、设置向导、OAuth 同意页……),写下"应有能力清单"再审计——纯代码审计对"从没构建过的能力"天然失明;
- 跑 L1:按模式目录逐项对照状态(empty/error/retry、进度、escape hatch、深链、骨架屏),每项结论都必须给
file:line证据; - 分层裁决:视觉/运行期结论一律标注 pending L2/L3,绝不拿 L1 去 tick L2 的结论;
- 沉淀 ✅:把状态机、持久化、降级路径等强项写进"don't regress"清单;
- 给缺陷定级排序:按能否困住用户(🔴 卡死/不可见失败)→ 类规范缺失(🟠)→ 静默降级(🟡)排序,每条都要能说出"用户在哪个界面、点哪个按钮、看到什么";
- 回灌 + 建工单:好例子磨亮清单的 ✅,缺陷磨亮 ❌,并同步到 issue 队列。
本仓库的 ux-audit 技能目录还提供了一整组其他模块的同类 worked example(settings、chat、home、profile、tasks、topic、stats 等,见 .agents/skills/ux-audit/references/example),以及 layer-1/2/3 三层程序文件。若你在 LobeHub 中负责某一屏的体验质量,完全可以直接对照这份清单与模式目录复跑一遍——这就是仓库文档化该技能想让你获得的标准化审计能力。
文中涉及 Agent 分支与部分
_layout的细节来自 2026-07 审计快照;若与当前src/features/Onboarding目录结构存在出入(如公共前缀现收敛于 index.tsx、桌面引导集中于 src/features/DesktopOnboarding),请以仓库最新代码为准——审计记录的价值正在于它给出了可复核的起点与可跟踪的修复轨迹。
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 StartedRust0630
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
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