[!TIP] 受众:做过本地处理流水线、想看懂"多一个交互层"会带来什么的人。 核心目标:讲清精读为什么必须自带阅读器,以及取证、契约、门禁是怎么兜住三层结构的。 问题-方案映射

  • 打开大书卡死 → 先取证量出 470ms/419ms,再按目录缓存
  • 三层结构不散架 → contracts/ 一份 JSON Schema,三端 conformance 同时变红
  • 文档里混着估算和实测 → 每条陈述标可信度,动手前先用 spike 填掉估算

这是我在本地做的第三个流水线,但和前两个有本质区别。字幕(subgen)和漫画(comic-gen)都是翻完就完了:字幕交给播放器加载,漫画交给看图软件打开。精读没有现成载体——把一本书翻译讲解完,你总得有个地方读它、听它、查词、收生词。所以它比前两个多出一整个阅读器和状态层,这是这一批三个项目最值得对比的地方。

1. 它做什么

丢一本 EPUB / PDF / TXT 进去,全自动跑完分词 → 翻译 → 分档讲解 → 高质量朗读 → 逐词时间轴,得到一个可读的"译本";生词经 Worker 同步回已有的扩展和手机端复习。全程本地推理,不需要 API Key。

阅读器是"三模式 × 双节奏":显示模式是先答后核 / 静默正文 / 对照台,节奏是通篇 / 逐句跟读,词级高亮做逐词卡拉 OK。先答后核模式下,一句先自己讲一遍再核对,已核对的句子折叠成细痕,跨会话记住。生词有个闭环纪律:AI 可以补全词典,但只有你主动点"加入"才进生词本——机器不替你做复习决定。

2. 三层结构,和一份契约

架构是三段:Python 侧车(备料,一本书跑一次的离线批处理)、Rust/Tauri 外壳(窗口 / SQLite / 子进程治理 / 同步)、纯静态前端(从原扩展剥离)。跨端字段的唯一真相源是 contracts/ 里的 JSON Schema——改字段时三端的 conformance 测试同时变红。这是三层结构不散架的关键:不是靠纪律,是靠"三端各自对同一份 schema 校验,漂移当场暴露"。

3. 原书 / 译本两层模型

这是用户实测反馈驱动的一次重构。原书只是来源,不可直接阅读,创建译本时才定学习档案 / 语言 / LLM / TTS。同一文件重复导入只登记一条,二次进入 skipped;相同参数重跑覆盖原译本且保留稳定 id,不同参数生成多个译本;删原书级联删它的全部译本。每一步都有配套的 Rust 测试锁住。

4. 打开大书卡死的取证

用户反馈三件事:导入疑似重复 / “开始阅读准备"边界混乱 / 打开大书卡死。处理方式是先不猜,取证。

用真实书包 23.88MB(8007 句、46 章)实测:load_bookpack / load_bookpack_chapter 每次请求都整文件读 + 全量 JSON 解析——单次 open ≈470ms、每切一章 ≈419ms。46 章逐个切就是 46 次全量解析,翻页和搜索跨章跳转时明显卡顿甚至像死掉。

修复是按包目录缓存解析结果,删译本 / 原书时失效。顺带补的一课:加载失败要有可读错误 + 重试 + 返回书库,后端失败不能落一张空白页。同类的问题还有常驻词典服务:模型加载一次多次复用,查词从 5–8s 降到 ~1s,空闲自动回收显存。

失败也不只是加载层面:书库卡片直接显示"N 句失败”,备料台能只看失败句重跑,不重跑已完成的结果——用户不会等读到某句才发现它是空的。

5. 文档的可信度分级

这一节是给同行看的方法。设计文档里每条陈述都标可信度:✅ 实测事实 / 📐 估算(未在本机验证)/ ❓ 待验证假设 / 🎯 已确认的设计决策。混用这四类会出事——比如"40–60 分钟跑完一本"当时是估算不是实测,它由三个都没量过的数相乘得出,误差可能很大。所以动手前先跑 spike,把估算格子换成实测数字。

三个参考项目的记录里反复出现同一条教训:读过代码 ≠ 验证过运行时行为。真实 exe 冷启动验收抓到 2 个只有真跑才会暴露的前端 bug——一个是新模块没注册进入口页,一个是重构时误删了导航根节点。跑测试全绿不等于 exe 能用

小结

门禁是一条命令跑 11 项:Rust 150 个测试 + 前端 85 个 + DOM smoke + CSS lint + 对比度校验。主题的 5 套配色和自定义强调色全部经 WCAG AA 对比度门禁校验,不是肉眼看着还行。可以自嘲一句:项目初期没上版本控制,交付了 3 本书之后才建仓,第一个 tag 就叫 v0.1.0-working-3books。这个 tag 名本身就是这段经历的诚实注脚。