Skip to content

30 · 整库 reseed 合并轮 —— 四个载荷一次做完

物理仓库位置:~/reading-browser(RB)。对端 RVH 在 ~/reading_vocab_helper。 创建:2026-08-26 · 状态:RB 侧已完成(2026-08-26,见 §9 实施记录)—— 载荷 ①②③ 落盘 + 一项额外收获(存量专名补 name 释义);载荷 ④ 例句译文未开工(§9.6)。 待 RVH 新会话跟进(§9.5),两边同轮发版。 本文件是这一轮的唯一范围真相源。它合并了三个此前分散的来源,逐个指向原文,不复制内容。

0. 这一轮为什么是「合并轮」

预装库 lampio_dict.db 是双端 byte-equal 资产(🔒 红线 #10),改它必须整库 reseed + sync-rvh-vocabulary.sh 整包同步 + 双端同轮发版。贵的从来不是 LLM,是这套协调。 所以四件本可各自开轮的事压成一轮。

#载荷来源性质
RVH prune —— 收掉 v26/v27 遗留的死 headword~/reading_vocab_helper/docs/cross-end/29-rvh-prune-reseed-request.md删行
RVH 补词 —— 90 个缺失的正确词形同上(29-missing-headwords.txt加行
专名治理 L-C —— 补 proper_noun 标签 + 清露骨/垃圾词~/reading-browser/docs/plans/proper-noun-governance-plan.md §6改标签 + 删行
例句中文译文 —— 86,569 条本文件 §5(此前无文档)加字段

①② 是硬需求且改的是行集 —— 所以整库 reseed 这轮绕不开;③④ 搭车,边际成本只有 LLM 时间。

1. 必读清单(按顺序)

  1. ~/reading_vocab_helper/docs/cross-end/29-rvh-prune-reseed-request.md —— RVH 的诉求全文。数据:同目录 29-prune-candidates.tsv(94 行带判据字段)、 29-missing-headwords.txt(90 个)。
  2. ~/reading-browser/docs/plans/proper-noun-governance-plan.md —— 专名治理四层方案。重点 §6 的 L-C 与其中 2026-08-26 两段更正
  3. ~/reading-browser/docs/cross-end/24-rb-lemmatizer-asset-v26-reseed-handoff.md §6 —— 本轮的由来:当时为省一次 LLM 重译只重烤 lemma_* 三表,写明「两者都在下次 整库 reseed 时自然收敛」。那笔知情取舍现在到期。
  4. ~/reading-browser/src-tauri/src/db/vocab_scope.rs 模块注释 —— L-A 已落地的运行时判据。本轮产出要能被它接住。

2. 三方交叠处:补词清单里的专名

RVH 的 90 个补词里约三分之一是专名(cincinnati / carmen / colossians / corinthians / thessalonians / reuters / vegas / wales / kansas / massachusetts / netherlands / philippines / lakers / ellis / perkins / sears / jesus …)。RVH 明确不替 RB 决定。

🔴 必须卡住的点:这些词若补进 vocabulary不打 proper_noun 标签, 等于把 L-A 刚清掉的 584 词问题重新引进来 —— 它们会立刻回到发现候选池、快速分级、 难度直方图。决定补哪些的同时就要决定 word_tags,不能分两步。

判据现成(vocab_scope.rs):打 proper_noun 标签 noun / proper noun / name 以外的词性 → 排除。注意它对带 adj 的国籍/文化词(american / chinese / latin) 是保留的。

3. 两个必须带上的陷阱

3.1 删词会让查词体验变差

删掉 = 变 OOV → 双击走 backfill_word → 缓冲池 → Wiktionary 小写页,而小写页往往是 另一个词条(实测:kyiv=「chicken Kiev 的异体」、texas=「蒸汽船顶层舱面」、 paris=「pari 的复数」,见 plan §3.2)。删词不消除垃圾释义,只是把它从预装库挪到缓冲池。

  • 专名 → 保留词条 + 打标 优于删除(L-D 落地前)
  • 露骨词 → 删除是对的,它们没有「查得到」的价值
  • RVH 的 prune 清单 → 那些是死词形(alway / rath / lat),没人会去查,删除安全

3.2 RVH 的清单明确不能照单执行(他们自己标了)

62 个强候选里混着真词:rend(A2 真动词) · lade · wale · emirate · grenadine · congratulation(单数) · crossroad(单数) · outskirt · ourself · oversea · instal(异体) · soothcomic strip 是方法假阳性(词频表查不到短语,已加 is_phrase 列)。 uninterest / unconcern / overjoy 按 RB doc 28 §7 的既有裁定明确不删。 补词那 90 个同理:s/d/ka/cha/si/ya 是噪声,本就该在停用词表。

逐条人工过,别按阈值批量执行。

4. 翻译成本已验:从 ~12k 词压到 ~90 词(2026-08-26 实测)

RVH 单子按「~12k 词 LLM 重译($5-15)」估算成本,那个数不用付

bin/enrich_translations.dart--resume(word, lang) 跳过已完成项,判据是输出文件 output/translated_vocabulary.jsonl 里该词的 pos_definitions任一 definition 带 translations.zh。实测:把现有 src-tauri/assets/lampio_dict.dbpos_definitions 原样导成该 JSONL(SELECT word, pos_definitions 即可,_entryFromJson 只强制 word 字段), 18,898 行全部满足判据,覆盖率 100%target_languages 只有 zh,无其它语言拖尾。

⇒ 载荷 ①②③ 真正要调 LLM 的只有那 90 个补词(还要先剔掉噪声与专名)。导出 27.2MB,几秒钟。

🔴 但 --resume 有个会静默出错的地方

merge 是按下标对齐的:

dart
for (var i = 0; i < new.definitions.length && i < old.definitions.length; i++)
  new.definitions[i].translations.addAll(old.definitions[i].translations);

新一轮抽取若产出的义项顺序或数量与旧库不同,译文会贴到错误的义项上 —— 不报错、不校验。bank 的「基金 / 钱 /(血、数据等)库」错位一格就是自信的胡说。 这正是本项目在消歧那里踩过并写进 docs/vocabulary-domain-knowledge.md §4 的同一个坑: 「存的是义项的 gloss 文本(不是 sense_index)—— 规避预装库 reseed 后义项顺序漂移导致自信标错」

Kaikki dump 未变(同一个 457MB 文件),抽取理论上确定性;但 v26/v27 之后 pipeline 代码动过, 义项顺序是否逐字一致没人验过

开工顺序(先花一次扫描的时间,可能省掉全部改造)

  • 第 0 步:只跑抽取,先验义项顺序。 dart bin/extract_kaikki.dart(不花 LLM 钱, 只花一次 457MB 扫描),然后把 output/cleaned_vocabulary.jsonl 与现有 db 逐词对 (pos → definitions[].gloss) 序列。
    • 逐字一致 → 按下标 merge 本来就安全,--resume 直接用,不改任何代码。
    • 有出入 → 走防线①,并把差异清单留档(那本身就是 pipeline 行为变了的证据,值得单记一笔)。
  • 防线①(首选):导出 JSONL 时按 gloss 文本建索引,merge 按 gloss 匹配而非下标。 db 里每条 definition 都带 gloss(实测可用),导出脚本本来就要写,多带一个键的事。
  • 防线②(兜底):跑完做一次全量复核 —— 新库每个 (word, gloss) 的 zh 是否与旧库一致。 ⚠️ 别只看「翻译总数对得上」—— 错位的总数永远是对的。

✅ 第 0 步已执行(2026-08-26)—— 结论:必须走防线①,且它不是「可选优化」

跑法(全程无 LLM、无网络,约 4 分钟):build_wordlistextract_kaikkimerge_data, 再把 output/cleaned_vocabulary.jsonl 与现有 lampio_dict.db 逐词对 (pos → definitions[].gloss)

结果分两层,方向相反,别只看第一层就下结论:

层面实测含义
gloss 内容逐字命中 53,829 / 54,826 = 98.2%;再归一大小写+空白+尾点又命中 990(99.99%);真正找不到 7 条抽取本身没有漂移,dump 同一个文件,确定性成立
gloss 顺序12,072 个交集词里只有 **4,143(34.3%)**序列完全一致;5,740 词乱序或缺项、2,189 词是有序子集、2,190 词 POS 集合都不同按下标 merge 不安全

按下标 merge 的实际损伤(这是决定性的数字):

旧库带 zh 的义项          54,612 条
  按下标会贴对            30,804  (56.4%)
  🔴 按下标会贴到错义项   23,808  (43.6%)   波及 6,343 个词
  越界丢失                     0

43.6% 的中文释义会被静默挂到错误的义项上,正是 §4 预警的那种「自信的胡说」。 差异清单已留档:30-sense-order-diff.tsv(8,103 行, 列 would_misplace / recoverable_by_gloss_key)。

🔴 根因不是「pipeline 行为变了」,别去追一个不存在的回归

§4 原文推测「v26/v27 之后 pipeline 代码动过,义项顺序是否逐字一致没人验过」。 实测抽取是一致的,乱序另有来源,而且是结构性的、每轮必然发生

lib/quality_governor.dart::_governPosSenses(Step 5.5,跑在 Step 4 翻译之后)会 ① 按 _senseScore 重排、② 每 POS 封顶 5 条kMaxSensesPerPos)、③ 丢 archaic 义项、④ 近义去重、⑤ 裁剪 gloss 的前置括注((often followed by up) To stop sleeping.To stop sleeping. —— 那 7 条「找不到」全部是它,不是丢数据)。

于是链条是:

cleaned_vocabulary.jsonl  (Step 3,未治理,义项全、原序)
      ↓ Step 4 翻译 → translated_vocabulary.jsonl   ← --resume 真正的对照物,**已丢失**
      ↓ Step 5.5 治理(重排 + 封顶 + 丢弃 + 改写 gloss)
   lampio_dict.db         (post-governance,**唯一还在的旧数据**)

⇒ 我们能拿来喂 --resume 的只有 db,而 db 与 Step 3 产物天然不同序这与抽取是否确定性无关 —— 即使 dump 和 pipeline 一个字节都没动,按下标 merge 照样错 43.6%。 §4 那个「逐字一致 → --resume 直接用」的分支,在本项目的现有产物结构下永远不可能成立

落到实施上的三条

  1. 防线① 是必做项,不是「有出入才做」。导出 db → JSONL 时必须带 gloss, enrich_translations.dart 的 merge 循环按 gloss 匹配取代 definitions[i]
  2. gloss 键必须归一小写 + 空白折叠 + 去尾点。不归一就白丢 798 个词的 990 条译文 (hopefulFeeling hope vs feeling hope 是典型)。前置括注那 7 条可另做 「去前导 (...)」再匹配,或直接认了(占比 0.01%)。
  3. ⚠️ 一个 §4 没提到、但同样静默的坑loadProgress 判定「该词已完成」的条件是 任一 definition 带 zh。新抽取给出的义项数普遍多于旧库(治理封顶 5 的直接后果, 见 abbreviation 5→8、abide 4→8),这些新多出来的义项永远拿不到译文, 而该词又因为「有一条带 zh」被整词跳过。⇒ 覆盖率复核要按义项算,不能按词算; 或把跳过判据改成「全部 definition 都有 zh」。

4.5 🔴 第 0 步顺带查出的更大问题:db 是多份 LLM 产物的唯一幸存副本

§4 只盯着「翻译会不会贴错义项」。实测发现同一个根因(output/ 丢失)还埋着三份 只存在于 lampio_dict.db 里、仓库中没有任何缓存文件的 LLM 产物。默认重跑 pipeline 会把它们一起抹掉,而且全部静默

只在 db 里的东西规模产自抹掉的后果
translations.zh54,612 条义项Step 4(无缓存,§4 已述)要重付 LLM
cefr_source='llm' 的分级5,164 词Step 5(opt-in,默认跳过)分级回落到词频反推的伪 CEFR
word 级 word_tags1,099 词proper_noun 871 / abbreviation 246 / symbol 23)Step 5 同一步见下 🔴

🔴 最要命的是第三行vocab_scope.rs 的判据第一句就是「打了 proper_noun 标签」。 这 871 个标签没了,它就查不到任何行 → 排除集从 584 掉到 0 → L-A 刚做完的 8 个步骤 整体变成 no-op,而且不报错、不红测试 —— 表现只是「发现模式里人名又回来了」。

对照:短语的 6,610 条 word_tags(idiomaticity)不受影响,因为它们有 data/phrase_idiomaticity.json 提交在仓里。差别就是有没有落缓存文件。

本轮必须把「从旧 db 回捞」当成一等公民,回捞面不止 §4 说的译文一项: pos_definitions[].translations + primary_cefr_level/cefr_source/cefr_inferred + word_tags。回捞同样按 gloss 文本键(理由同 §4),词级字段按 word 键。 建议顺手把回捞产物写成 data/llm_cefr_tags.json 提交进仓, 和 generated_examples.json 一样变成确定性缓存 —— 这样下一轮才不会再踩第三次。


4.6 载荷 ①②:重建 wordlist 已自动解决大半,但引入一类新的孤儿

用修好的 lemmatizer 资产重跑 Step 1(build_wordlist,3 秒、无 LLM)实测:

RVH 诉求自动解决仍需人工
① prune 94 个候选83 个自动消失(新 wordlist 里已无该 headword)剩 11 个:chang clothe hi opus out pant philippine rout ski sometime vega
② 补 90 个词形66 个自动纳入剩 24 个:s d ka cha si ya(噪声)· his us yes(停用词)· cara dali fila flinders colossians philippians(专名)· bioethics cheerleading confetti impending insignia measles ourselves undersigned unqualified(真词,需查为什么没进)

🔴 但「自动消失」里混着 RVH §4.2 明令不许删的真词

rend lade wale sooth emirate grenadine congratulation crossroad outskirtourself oversea instal uninterest unconcern overjoy —— 全部不在新 wordlist 里。 也就是说「重跑 wordlist」这个动作本身就是 §3.2 警告的那种按阈值批量执行, 它会把 RVH 特意圈出来的误伤词一个不落地全删掉。

更糟的是其中 7 个(rend lade wale sooth uninterest unconcern overjoy仍然是 lemmatizer 的归一目标rends/rending → renduninterests → uninterest。 删掉词条但留着映射 = 用户双击 rending → 归一到 rend → 本地查不到 → OOV 兜底, 正是 doc 24 §6 代价 #2 那个缺陷的新实例,而本轮存在的意义就是消灭它。

全量算了一遍这类「有 surface 归一进来、却没有词条」的孤儿 base: 本轮会新引入 41 个(旧库有词条、新 wordlist 丢了、映射还在)。 清单见 30-orphan-bases.tsv(41 行,带 in_rvh_prune_list / rvh_marked_protect 两列便于逐条裁定)—— 里面既有该删的死词(rath lat upcome automat), 也有该留的真词(rend stave sear discus rebind handwrite)。必须逐条过。

不变式建议(值得固化成守门,而不是每轮靠人记): 凡是 lemmatizer 的归一目标,都必须在 vocabulary 里有词条。 这条一旦成立,doc 24 §6 代价 #2、RVH 的 P1 OCR 缺陷、本轮新引入的 41 个孤儿, 全是它的同一种违反。可在 generate_db 收尾处加断言。


4.7 载荷 ③:name POS 这条免费信号实测有效,但判据要写全

kaikki_parser.dart:41_irrelevantPos 里含 'name',确实在静默丢弃(L-C 原文属实)。 实测扫全 dump(1,454,988 行,约 4 分钟,无 LLM):

新 wordlist 命中 13,835 词
  带 name POS         5,088
  只有 name POS       1,543   ← 这批当前**根本不在 db 里**(被 _irrelevantPos 滤光了释义)

⚠️ L-C 原文 ① 那句「见过 name POS → 打 proper_noun」不能照做,实测会打到 fox march turkey bill rose mark june english french —— fox 的教训重演。

必须叠上 L-C 原文 ② 已经写对的那个守卫(cefr_inferred=1),再叠 vocab_scope 自带的第二半(无 noun/name 以外词性),三者合起来实测干净:

判据命中误伤权威分级真词
有 name POS5,088🔴 15+(fox/march/turkey/bill/rose/june…)
cefr_inferred=11,884🔴 15(rose/english/french/china/japan/pope/dean/mason…)
+ 无其它词性(vocab_scope 现有判据)1,0280

效果:vocab_scope 的实际排除集 584 → 1,028(净增 515); plan §0.4 实测的 C2 首屏 12 个人名命中 10/12(漏 ajax 有 prep 词性、austin 有 adj 词性 —— 属 §「刻意接受的假阴性」同类,不收紧)。

另注:现有 871 个 proper_noun 标签与 1,543 个「只有 name POS」的词交集为 0, 两个信号完全互补(LLM 标的是有名词释义、语义上是专名的词;name POS 标的是 Wiktionary 里纯粹当名字收的词)。所以 L-C 是叠加,不是替换 —— 这也再次说明 §4.5 那 871 个旧标签必须回捞,不能指望 name POS 重新生成出来。

🔴🔴 2026-08-26 二次更正:上表的「误伤 0」是错的,name POS 不能单独作判据

上表那一行「+ 无其它词性 → 1,028,误伤 0」是抽查 8 个手挑词得出的,不是全量审计。 实机跑完 517 词补标后全量复核,误伤约 40–50 个真词,即 ~8–10% 错误率:

china(瓷器) beverage bidder carrier cartridge cedar dragon finder genesis guardian
guild lotus manga manor marina matrix metabolism millennium node nylon oasis
petroleum plaza pointer prairie providence senate sender toner treasury tribune
trinity watt avatar babe atlas boulevard earl glen gnome jersey soma thong guinea

它们全部只有 noun 词性、且 cefr_inferred=1(从没被 Oxford/CEFR-J 权威分级过), 所以判据自带的那道安全网对它们不成立 —— plan §「为什么不误杀 fox」那段推理 只对「LLM 语义标注出来的 871 词」有效,对机械的 name POS 信号无效。

试过用 name 义项占比再收一刀,也不行(全量实测):

name 义项占比
真词(不该排除)中位 38%,但 prairie 90% · providence 87% · trinity 85% · marina/glen/watt 83%
专名(该排除)中位 88%,但 charlie 25% · andy 50% · richard/barbara 67%

两个分布重叠严重,不存在干净阈值。这与 plan §0.6「为什么光靠标签救不了」、 §3「L-B 已被实测推翻」是同一件事的第三次复现:Wiktionary 的 POS 结构里没有 「这个词在现代英语里主要当普通名词用」这一位信息。

L-C 原文 ① 那条(见过 name POS → 打标)作为最终判据不可用。 修正后的做法:

  1. name POS ∧ cefr_inferred=1 ∧ 无其它词性 → 只当候选过滤(517 词,高召回)
  2. 再过一遍 LLM 语义裁定决定谁真是专名 —— 这正是现有 871 个好标签的产生方式
  3. 结果落 data/round30_proper_nouns.json 提交进仓(确定性缓存,同 §5.2 先例)

成本与载荷 ② 的译文同量级(几百次调用),远小于它防住的伤害: 误伤一个 metabolism / petroleum 是把真词永久踢出学习池,而用户看不见、也删不掉 (fox 的教训原样重演)。


5. 载荷 ④:例句中文译文(本轮新增,此前无文档)

5.1 规模

例句总数 86,569 条(分布在 12,160 个词条上)
  来源:corpus 35,734 / llm 50,835
现有译文:0 条
现有结构:definition.examples[] + definition.example_source[] 两个平行数组

5.2 存储形状(开工前先定,定错要再付一轮

入库形状保持平行数组 example_translations: string[](与既有 example_source[] 同形) —— 不破坏任何消费方,双端 runtime 零改造成本。

但生成/回填路径必须按例句原文文本作键,不能按下标。 理由同 §4:下次 reseed 例句顺序一变, 按下标回填就会把译文贴到别的句子上。缓存文件照 generated_examples.json 的先例: data/example_translations.json键是英文原句,提交进仓(确定性、可复现、下轮免费)。

既有的 example_source[] 其实有同样的下标脆弱性,但错了只是 corpus/llm 标签错、不可见; 译文错了是用户可见的胡说。两者风险不对等,所以译文这条要按文本键。

5.3 🔴 复习卡挖空泄露 —— 这条最容易被漏掉

ClozeRevealCard.tsx:89 写明:cloze 卡的句子回落时也可能用预装库例句。 而 cloze.ts 现有的防泄露只做一件事 —— 把英文句里目标词的所有形态遮掉 (surface + word_forms + 朴素屈折 + canonical,见该文件头注释)。 它对中文译文一无所知。

⇒ 例句译文一旦入库,cloze 卡正面绝不能渲染它(背面/揭晓侧可以)。 中文译文里必然含有目标词的词义 = 直接把答案递给用户,而现有防泄露拦不住。 cloze.ts/build-check Step 6 的高危纯逻辑文件,改动要跑 pnpm test:run

5.4 数据进库 ≠ 默认显示

生成 86,569 条译文只是把数据备好。「例句底下要不要常驻中文」是独立的产品决策, 可以之后随时迭代(折叠 / 按需 / 常显),不需要再来一轮 reseed。 反过来说:把展示做成常显之前,值得先想清楚它会不会削掉例句的阅读价值 —— 学习者的眼睛会直接滑到中文。本项目在别处对这类取舍是克制的 (「词形对学习者噪声大、价值低,所以不进任何卡面」)。

运行时另有一条更轻的路可作对照:translate-batch edge function 已部署,收 texts: string[] 返回译文,可做「点一下翻译」。预装译文与它不冲突,但如果最终选了按需, 那 86k 条就只是提速而非必需 —— 这条写在这里是为了让展示决策有完整信息。

✅ 5.5 载荷 ④ 已完成(2026-08-26,与 ①②③ 同轮)

唯一例句      86,432 条  →  覆盖 86,817 个位置(同句在不同词条复用同一译文)
落在词条      12,163
覆盖率        100%(无空位;数组与 examples 严格等长)
词数          18,925 不变 · db 55→63MB · SHA c875b3db…→ae33c8a4…
成本          in 1.87M / out 2.55M tokens,高峰口径上界 $4.19
耗时          44 分钟(并发 12,批 25),1 批失败已断点续跑补齐

入口 bin/gen_example_translations.dart → 缓存 data/example_translations.json键 = 英文原句,提交进仓)→ apply_round30.dart --payloads examples 落库。

§5.3 的防泄露已落地并验证:Rust/TS 两侧 Senseexample_translations 字段 (注释写明约束),前端新增两条回归测试 cloze.test.ts::「例句译文不得泄露到卡面正面」。做了注入式反向验证 —— 把译文拼进卡面后两条测试立刻变红,还原后转绿,确认闸门真的会咬。

⚠️ 对端有同款风险:RVH 的 lib/features/vocabulary_notebook/domain/services/cloze_builder.dartbuildFallbackCloze(String word, List<String> examples, …) 是同构的例句回落路径, 它的防泄露同样只遮英文形态。译文进库后,只要有人把 example_translations 喂进那个 examples 列表、或在卡面正面并排渲染,就会泄露。已写进 doc 31 交接。

顺带记一笔(不在本轮范围):验收时扫到 4 条「译文里没有中文」的位置, 根因是源例句本身就是乱码或外语(Ocearium stæli · thrussy; clussy), 不是翻译失败。其中 eff you see kay why oh you(拼读脏话,挂在 see / why 下) 属症状 4 的例句层残留 —— 本轮的露骨词清理只做了释义层。


6. 🔴 RVH 侧前提:光 prune 库还不够,两边必须同轮发版

RVH 的预装库导入只 UPSERT、不删 preinstalled 行app_database.dart 里唯一的 DELETE 只清 source='backfilled')。所以新库拿掉 alway存量安装照样留着 —— doc 24 §6 那句 「下次 reseed 自然收敛」只对全新安装成立。

RVH 已立项:reseed 落地那轮同步改导入逻辑加删除步骤。这绕不开一个 FK 决策 —— learning_entries.word → vocabulary(word)ON DELETE RESTRICT,且 RVH 的 PRAGMA foreign_keys 是真开的(不同于 RB 的 rusqlite 通道)。用户生词本里存着 rath 时 删该词条会被 FK 拒;选在 reseed 那轮做,是因为届时 rather 才第一次有词条、 「迁移到正确词形」才第一次可行(RVH doc 24 §9.2 已分析)。

产出前必须知会 RVH(新会话,CLAUDE.md §9 会话隔离),两边同轮发版。

7. 硬约束

  • 🔒 红线 #10lampio_dict.db 与 RVH byte-equal。产出后必须 scripts/sync-rvh-vocabulary.sh 整包同步,禁止任一端就地改。
  • /vocab-reseed skill,别手搓。
  • bump VOCABULARY_SEED_VERSION(RB)+ _preinstalledVocabVersion(RVH)。
  • 涉及 RVH 的改动必须新会话。
  • pipeline 现状(已核实,别照旧文档的旧说法):
    • data/kaikki_english.jsonl.gz完好 —— symlink → ~/Downloads/kaikki.org-dictionary-English.jsonl.gz,457MB,实测可解压。 ⚠️ 别对 symlink 用 ls -la 的 size 列判断(会看到 61 字节 = 目标路径长度),判据是权限位开头的 lreadlink这条已有人踩过
    • data/{generated_examples,differentiation,etymology}.json ✅ 在仓,确定性缓存,不用重付
    • 中文翻译 ❌ 无缓存文件(见 §4 的 --resume 方案)
    • output/ 空,可由现有输入重生成

8. 完成判据

  • RVH:OCR 拍 "always" 不再被纠错器改回 alway(他们的 P1 真机缺陷,实测同类 36 个词受影响)
  • 专名:C2 档首屏 20 词的人名数(当前 12 个)显著下降;露骨词不再出现在 get_discoverable_words / get_triage_candidates
  • 补词里的专名已带 proper_noun 标签(用 vocab_scope 判据自查一遍)
  • 例句译文:86,569 条覆盖率达标;抽样复核 (例句原文 → zh) 对齐正确; cloze 卡正面确认不渲染译文pnpm test:run + 实机各一遍)
  • 确认可解开 lib/calibration.ts::CALIBRATION_MAX_LEVEL 后再改,并复验探针词质量
  • 跨端 phase0 byte-equal 复验
  • CHANGELOG + 本文件回执(RVH 侧写 31-rvh-*-confirmation.md

9. ✅ 实施记录(2026-08-26 完成,RB 侧)

做法与本文件 §0 的设想不同:没有重跑 pipeline,而是就地改库。 改路线的依据是 第 0 步实测(§4–§4.7):重跑 pipeline 才是本轮最大的风险源 —— 它会丢掉只存在于 db 的 5,164 词 llm 分级与 1,099 条 word 级 word_tags(含全部 871 个 proper_noun, 没了 vocab_scope 就查不到任何行),并且译文回捞有 43.6% 的错位面。 而四个载荷本质都是行/列级改动,就地做全部绕开这两条。

入口:tools/vocabulary_builder_v3/bin/apply_round30.dart (红线 #10 的第二个就地改库豁免,先例 rebake_lemma_tables.dart)。

9.1 结果

删除 53 · 补标 155 · 补 name 释义 157 · 去露骨义项 6 · 新增 80
vocabulary   18898 → 18925
proper_noun    871 → 1055
vocab_scope 排除集 584 → 769
SHA  2880c504… → c875b3db…      lemma_* 表 byte-equal 不变
载荷落点
① prune46 个 RVH 裁定的死词形 + 7 个露骨/垃圾词(bangbus hentai sexo porno vaginas + 两个拼写错误 masterbating/masterbation)。herpes/nudity/underpants 是正当词汇,保留
② 补词80 行(32 个带 proper_noun),375 条义项 100% 有 zh。s/d/ka/cha/si/ya 噪声与 his/us/yes 停用词按 §3.2 不补;redskins(族群蔑称)不补
③ 补标155 词。判据不是 §4.7 原写的机械规则 —— 机械规则误伤 8–10%,最终走 LLM 语义裁定(517 候选 → proper 155 / common 362),china/metabolism/petroleum/senate 等全部正确判回可学
④ 额外给 157 个存量专名折入 name POS 释义(468 条)。见 9.2

9.2 本轮的额外收获:存量专名的释义一直是坏的

kaikki_parser.dart:41_irrelevantPos'name',于是纯专名的真实含义从来 没进过库。实测库里:

virginia   = 「Vagina」                (且 zh 配的是「弗吉尼亚(美国州名)」——
                                        义项与译文对不上,是**已发版数据里**就有的
                                        「译文贴错义项」实例,非本轮引入)
johnson    = 「Penis.」
manchester = 「Household linen」

把这些词排出学习池(载荷 ③)却不补释义,就成了「不让学、查了还是垃圾」—— 正是 §3.1 反对的状态:保留专名词条的全部理由就是「查得到」。现已归位为 州名 / 姓氏 / 城市,并去掉 6 个专名仅剩的粗俗义项(gary johnson virginiacorey cory gina)。

9.3 验收

  • 真词误杀 0fox march turkey china metabolism petroleum senateprairie matrix node cedar dragon beverage carrier guardian 全部仍可学
  • C2 首屏 12 人名排除 10/12(漏 ajax 有 prep、austin 有 adj —— 属 vocab_scope 模块注释里「刻意接受的假阴性」同类,不收紧)
  • 死词形 / 露骨词清零;全库已无「全部义项都露骨」的词条
  • zh 覆盖 12,181 / 12,181(100%)
  • 行数守卫、4 表白名单守卫、排除集不得反向缩小守卫 —— 全过
  • cargo check 绿(唯一 warning 在 progress.rs,与本轮无关)
  • cargo test --lib 207/207pnpm build 绿

⚠️ §8 的「C2 首屏人名显著下降」只部分达成:12 → 10

改前 20:abba abby abigail acetate acme ajax alba alec alison alma alprazolam
         amos anon apoptosis assay astrophysics austin balm bangbus barbados
改后 20:acetate acme ajax alba alprazolam annals anon apoptosis assay
         astrophysics austin balm barbados barney barr barry bate beck benedict benny

abba/abby/abigail/alec/alison/alma/amos 清掉了,但下一批人名顶了上来。 诊断:barney barr barry beck benedict benny ajax austin都不在候选集里,因为它们在库里带 verb / adj / prep 词性:

db POSWiktionary 给的动词/形容词义
becknoun, verbto beck = 招手示意
barneynoun, adj, verbto barney = 争吵(英式俚语)
barr bennynoun, verb
barry benedictnoun, adj

也就是说瓶颈已经不是标签覆盖率,而是 vocab_scope 判据的第二半 (「无 noun/name 以外词性」)。那一半正是 fox/march/turkey 不被误杀的安全网, 模块注释里已把这类漏网列为「刻意接受的假阴性」(原文 93 个)。本轮把标签从 871 补到 1055 之后,剩下的人名基本都落在这个刻意的口子里。

想再往下压,得换一层机制,不是继续补标

  • 短期:CALIBRATION_MAX_LEVEL 的解封要慎重 —— §8 把「清完才谈解开」当作前提, 而现在 C2 首屏仍有一半是人名,条件不满足,本轮不解封
  • 中期:L-D 的 token 级信号(「非句首 + 首字母大写」降权)才是治 barney/beck 这类同形碰撞的正确层,plan §6 L-D 已有分析。

9.4 LLM 花费

$0.012 合计(专名裁定 $0.008 + 译文 $0.004+$0.0055)。 对照 RVH 单子按整库重跑估的 $5–15 —— 差三个量级,因为没有重译任何存量词

9.5 🔴 RVH 侧待办(新会话,见 §6)

预装库已 scripts/sync-rvh-vocabulary.sh 同步到 ~/reading_vocab_helper/assets/databases/lampio_dict.db(byte-equal,SHA c875b3db…), 但故意没有在 RVH 仓提交 —— 按 CLAUDE.md §9 会话隔离,RVH 的改动归 RVH 会话。

RVH 那边这一轮要做的:

  1. 提交这个资产git add assets/databases/lampio_dict.db
  2. 落 A:导入逻辑加删除步骤(doc 29 §5)—— 现在的导入只 UPSERT、不删 preinstalled 行,所以光换库存量安装照样留着 alway,RVH 的 P1 OCR 缺陷不会好
  3. FK 决策learning_entries.word → vocabulary(word)ON DELETE RESTRICT 且 RVH 的 PRAGMA foreign_keys 真开着。本轮删掉的 53 个词若在用户生词本里, 删词条会被 FK 拒。「迁移到正确词形」这条路现在第一次可行 —— always/rather 等已经有词条了(这正是 doc 29 §5 选择等到本轮的原因)
  4. bump _preinstalledVocabVersion(doc 25 §7)
  5. 复验:拍/分享含 always 的文本,确认不再被纠错器改回 alway
  6. 回执写 31-rvh-*-confirmation.md,并更新 docs/cross-end/README.md 总表

被删的 53 个词清单见 30-prune-adjudication.tsvrec=DELETE 行(迁移映射可参考同表 inbound_surfaces 列)。

9.6 本轮没做的:载荷 ④ 例句译文

§5 的 86,569 条例句译文未开工。它与 ①②③ 解耦(§5.4「数据进库 ≠ 默认显示」), 不做也不影响 RVH 解锁 P1。真要做时注意 §5.3 那条:cloze 卡正面绝不能渲染译文

9.7 遗留

  • 19 条悬挂 lemma 映射:本轮删的 46 个死词形里有 14 个仍是归一目标 (automats→automatrathest/raths→rathupstairrer→upstair 等)。 这些 surface 本身也都是构形垃圾,没人会读,故不动 lemma 资产(动它要重烤 + 再同步一轮)。清单可由 30-orphan-bases.tsv 复算。
  • ajax / austin 因带 prep / adj 词性漏出专名排除,同 vocab_scope 既有的 93 个刻意假阴性,不收紧。
  • jesus 打了 proper_noun 但标签是惰性的(它有真实的 intj 感叹用法 → 判据第二半保留它)。这是刻意的:保留感叹用法比排除专名更重要。