Commit Graph
26 Commits
Author SHA1 Message Date
fanlusky cf07b77df6 feat(engine): 等音节数字面切分按整句打分裁决,预编辑跟随首选
最少音节切分平局时固定取较短首音节,切分又在查词之前定死,yibananzhao 只会切成
yi'ba'nan'zhao 出「一把难找」,「一般按照」「这个方案很好」这类 n/ng 边界歧义的整句没有机会。

- arbitrate_literal_segmentations:合法输入的等音节数字面切分各解一遍词格,路径分最高的领衔,
  同分仍取原切分;输方分差在 3.0 以内时它的整句占第 2 位,更远不出。任一切分在词库里有整词时
  不介入(单词级仍按词频),整句来源全关时与原来一致,用户调过频的读音不让出领衔
- 合法输入纠错的原读法改为领衔的字面切分:纠错胜出时第 2 位是得分最高的字面读法
- 首选是另一种等音节数字面切分时预编辑跟着它分(yi'ban'an'zhao、fang'an)
- 胜出切分的前缀词替换原切分前缀区间扫出的同名行,选「一般」按 yi'ban 消耗
- 词格跨度查询与最佳路径加缓存,随 reset_cache 清空;普通输入每键 p90 也随之下降
- 新增 eval_quanpin_sentence 与句子级评测集 tests/data/quanpin_sentence_cases.tsv
2026-10-12 03:05:44 +08:00
chr331 36ab259c43 fix(engine): 保留原样混拼候选优先级 2026-10-11 13:56:14 +08:00
Ouchihao 29c8917e8c fix(engine): preserve leading-initial mixed jianpin 2026-10-10 14:46:13 +08:00
Lu Fan(SonnyCalcr) 8a524aa498 feat(engine): 全拼纠错扩展到多字/漏字,贵档读法按整句分领衔 (#623)
* feat(engine): 合法输入纠错扩展到多字、漏字,纠错读音按原始区间消耗字母

jiuzheeyang 切成 jiu'zhe'e'yang 全合法,多打的 e 原本纠不回「就这样」;多字纠错的
读音又与原始字母不等长,buuhui 选「不会」后会剩下一个 i。

- legal_input_correction_cuts(原 legal_input_transposition_cuts)认换位、多字、漏字
  三类静态表读法,不认邻键与生成式空间;末尾音节按漏字读出的(nia -> nian)视为
  还没打完,跳过;k-best 先取 9 条再过滤,长句里纠正不被别处的读法挤掉
- 改变字母数的纠正代价 3.0(换位 2.0);字面切分本身是真词时不认改变字母数的读法,
  少一个音节的句子在语言模型里天然分高(邪恶 -> 些、答案 -> 但)
- 新增 correction_cut_for_reading / corrected_reading_raw_length:选词按纠错切分
  记录的原始区间消耗字母,预编辑也按它分隔(jiu'zhee'yang)
- AutocorrectCut 恢复 has_generated,供合法输入路径排除生成式读法

* fix(engine): 被裁掉的合法输入纠错读法不再误标原读法的候选

合法输入上的纠错读法登记进了全局纠错标记表,即使裁决时被丢掉也照样参与按读音字母
的标记匹配:xiee 按多一个 e 读成 xie 被丢掉后,邪恶后面的前缀单字 写/些/血 读音
也是 xie,全被标成纠错。

改为不登记,只在 arbitrate_legal_corrections 里给真正用上的那条纠错读法的整读音
候选打标,标记随结果进缓存。

* feat(engine): 结构性贵档纠错读法可按整句分领衔

shiideya 的换位读法 shi + ide->die(权重 10,是爹呀)比多字读法 shii->shi(权重 12,
是的呀)便宜一档。#592 只在同档内做上下文消解,贵档读法一律排在最后,「是的呀」
连首页都进不了。

- 上下文消解把前 2 条贵档读法也解码,不要求词库有整词;总分 = 路径分 - 手误代价,
  代价按纠错表权重换算(权重 x 0.2:换位 2.0、漏字 2.2、多字 2.4)
- 只收由结构性手误(换位、漏字、多字)构成的贵档读法:邻键只比漏字贵 0.4 分,
  常用词的语言模型优势会把 zhowei 的「周围」挤成「作为」
- AutocorrectCutSegment 记录每段的纠正权重,用来区分纠正类型
- 预编辑跟随领衔读法分隔也覆盖切不成合法音节的输入(shii'de'ya)
- eval_quanpin_autocorrect 新增 --word-lattice,量产品默认开词格时的排序

评测(--word-lattice,300 样本,seed 42)各档 R@1/R@3 不低于改动前:mixed R@1
79.7/76.7/78.3/70.3 -> 82.3/81.7/81.3/78.0,insertion 83.3/74.7/72.7/52.0 ->
91.3/89.3/90.0/83.7,deletion 持平或上升,ambiguous 持平。

* fix(engine): 纠错上下文消解在任一整句来源开启时生效

上下文消解只认 word_lattice 开关。只开万象重排、不显示 Trigram 整句时(词格在内部
照样解码),整段被跳过,shiideya 退回静态排序,「是爹呀」又领衔。

- 新增 SentenceAssociationOptions::any_sentence_source,门控改看它;全关时仍是纯
  静态排序,#592 的「关联关闭 = 静态路径」不变
- 贵档胜者没有整词也没有整句行时(重排模型未载入),由它的最长前缀词领衔,不让
  主切的行顶回首位
2026-10-05 01:41:37 +08:00
Lu Fan(SonnyCalcr) adde5c188c feat(engine): 合法输入的换位手误按整句打分纠错 (#622)
ziazheliya、jioa、liazheli、nia 能整串切成合法音节(zi'a'zhe'li'ya、ji'o'a),
纠错入口按「切不成合法音节」设闸,原本完全不进纠错。

- legal_input_transposition_cuts 列出合法输入上的换位读法:纠正段须盖住字面
  切分里被挤出的非首位零声母音节(排除 dao'lu 读成「大楼」这类跨边界重切),
  另有罕见音节小表(lia->lai、dia->dai),只用于多音节输入
- arbitrate_legal_corrections 按噪声信道整句打分:词格最佳路径分 + 每处换位
  -2.0,不看整句候选显示开关;纠错领衔时原读法固定第 2 位,原读法领衔时纠错
  在 3.0 分差内占第 2 位,否则不出
- 字面整词保住首位:候选里有读音字母与输入相同的词库词时,改写字母的读法
  (换位纠错,以及已有的辅音复制别名 yongan -> 勇敢)最多排第 2
- 纠错领衔时预编辑按领衔读法分隔(zia'zhe'li'ya)
2026-10-04 23:54:18 +08:00
Lu Fan(SonnyCalcr) 5208753ce8 feat: 句中辅助码新增大写字母触发方式 (#620)
* feat(engine): add uppercase trigger for shuangpin mid-sentence helpcode

With the uppercase trigger on, an uppercase letter right after a complete
two-key syllable opens a mid-sentence helpcode block by itself, as if the
backtick had been typed before it: ulpbXihfa is ulpb`xihfa, and a following
uppercase letter is still the second code (ulpbXYih is ulpb`xYih). The raw
input keeps the typed letters; the block rules live in
contracts/mid_sentence_helpcode.h so TSF, Server and engine parse alike,
including the semicolon final, which no longer counts uppercase blocks.

The scheme now hands the parsed layout (source index and decorations) to the
request, so selection advance and preedit no longer re-parse the raw input.
Adds the MidSentenceHelpcodeUppercaseChanged worker opcode (31).

* feat(windows): count uppercase helpcode blocks before backtick and semicolon

With MidSentenceHelpcodeUppercaseChanged on, TSF uses the shared block rules
when deciding whether '`' opens a block and whether ';' is the ing final, in
both the synchronous and the deferred key classification.

* feat(server): wire the mid-sentence helpcode uppercase trigger

New helpcode.shuangpin_mid_sentence_helpcode_uppercase (default off) in both
config templates, both settings hosts and the settings page as a third
trigger checkbox. It takes effect only with mid-sentence helpcode on and
direct helpcode off; the Server applies it to the semicolon final rule and
broadcasts MidSentenceHelpcodeUppercaseChanged to TSF.
2026-10-04 21:00:41 +08:00
Lu Fan(SonnyCalcr) 22fb1e2569 Merge pull request #619 from fanlusky/feat/shuangpin-direct-helpcode
feat: 双拼直接辅助码(万象式,无需引导键)
2026-10-04 19:32:10 +08:00
fanlusky 45c473158a feat(engine,server,windows,ui-html): 双拼预编辑可显示转换后的全拼
外观里行内预编辑与候选窗预编辑各加一个「双拼显示转换后的全拼」开关,默认关闭。
开启后在双拼方案下:行内原始按键显示不带分词符号的全拼,拼音分词显示带分词符号的
全拼;候选窗显示带分词符号的全拼。

- 引擎新增 get_shuangpin_preedit_forms,同时给出原串切分与全拼切分,不受
  shuangpin_preedit_mode 影响。
- 原始按键样式下 TSF 不等 Server 回包,开关开启且为双拼时 Server 下发的实际样式
  改为 pinyin,回包里再按用户样式去掉分词符号;换方案时重新下发。
- 转换后字母数与按键不再对应,Server 在预编辑末尾附光标映射表
  (engine/contracts/preedit_caret_map.h),DLL 用它定位光标和处理方向键,
  无表或长度不符时回落到原有字母映射。
2026-10-02 16:14:50 +08:00
fanlusky fae80660c1 feat(engine,server,windows,ui-html): 双拼句中辅助码
在完整音节后敲反引号,接着的字母筛这个音节上的字:第一码大小写均可,
紧跟的大写字母是第二码,之后可以接着打,一个输入里可以有多段。开关
helpcode.shuangpin_mid_sentence_helpcode 默认关闭。

约束在整句解码时生效,而不是解完再筛:
- Google 解码器在 add_char_qwerty 里送 extend_mtrx_nd 之前按约束过滤词条,
  锁住的字带着上下文重新整句解码;约束变化时整段重解,不污染增量搜索。
- kenlm 词格建图时按约束筛边,受约束的跨度不截断地查词库;神经重排只在
  词格 n-best 里挑,自动满足约束。
- 普通词条、云和 AI 候选按同一规则收尾校验;满足不了就不出整句。

反引号段在请求里换成手动分隔符,原串留给宿主回读与预编辑显示;选词推进
时剩余音节上的约束随原串保留。输入形状规则放在 contracts/mid_sentence_helpcode.h,
TSF、Server、引擎共用,新增 Worker opcode MidSentenceHelpcodeChanged = 28。
googlepinyinime-rev 已与上游解绑,AGENTS 与 UPSTREAM.md 同步说明。
2026-10-02 02:20:37 +08:00
fanlusky e502201fd9 fix(engine): 全拼别名改写后的 preedit 保留合法切分与手动分隔符
#565 让合法键入在被别名改写后退回无分隔的原始字母,多打一个字母就会丢掉
已显示的全部分隔符(hao'yong -> haoyonga)。现在按键入字母自身的合法读音
重新切分(hao'yong'a、dong'an)。BFS 无法解释时的退路改为原样显示键入,
不再吞掉用户手打的分隔符(haoyonga' 不再显示为 haoyonga)。

测试拆成两组:走简拼守卫的合法拼写,与走退路的输入。dongua、zhonguo 不经
过守卫,归入退路组,此前它们在修复前也能通过,不构成守卫分支的回归用例。
2026-10-01 22:57:51 +08:00
Jia Sui efdea2c14f fix(engine): 全拼合法键入被别名改写后 preedit 不再照搬改写串 2026-10-01 15:09:32 +08:00
fanlusky c99fb8d585 fix(engine): 英文调频不再以中文权重为基准,并按输入记英文槽位
混输列表里英文调频把「全表最高权重 + 1000」写成新权重,中文候选也算在内。
在 zai 下选一次 zaire,它的权重就变成 19,196,987,超过「在」,并且对所有能查出它的输入永久生效。

- adjust_english_candidate_ranking 只用英文候选的权重当基准
- 新增 english_slots 表和 english_slot / set_english_slot /
  learn_english_slot_selection / learn_english_slot_bypass:按输入记录首个英文候选在列表中的位置,
  选中时按调频名次前移(可以升到首位),越过它时后退一位,最多到首页末位
- 新增 InputSession::reads_as_pinyin:整串能否读成拼音(允许简拼声母和打到一半的音节)
2026-09-30 22:49:27 +08:00
fanlusky e365a8d1b5 perf(engine): 大模型改为只读映射并按需载入,缩短每键查询路径
- sc.lm 改用 kenlm 的 LAZY 载入:Windows 上默认的 POPULATE_OR_READ 会退成 READ,
  把 33 MB 整份拷进私有堆且永不释放;现在是 MapViewOfFile 只读映射,可被系统回收
- dict_japanese.dat 在 Windows 上同样改为只读映射,与 POSIX 分支一致
- 神经整句模型改为开关首次生效时才载入(LazySentenceModel),默认关闭的桌面模型
  不再占用约 24 MB
- 新增 reset_sentence_cache():重排结果与上文只影响词格整句,只清 series 一层,
  逐前缀查库与切分缓存保持热;set_rescoring_context 也改用它
- CircularBuffer 新增 find():命中时返回指针而不复制整份候选列表,并改为真正的 LRU;
  全拼、双拼每键的命中路径改用它
- Google 解码器去掉每次调用前的 im_reset_search,沿用上游的公共前缀增量搜索
- 测试:出货 sc.lm 能映射载入并正常打分;增量搜索与从头搜结果逐步一致
2026-09-30 19:58:18 +08:00
Jia Sui 456fdb819c feat(engine,server,ui-html,installer): 五笔拼音混输改为同时混排并拆出独立开关
Signed-off-by: Jia Sui <jsfaint@gmail.com>
2026-09-28 17:55:36 +08:00
fanlusky e7f7addd4f feat(engine): 神经网络整句联想与词格 n-best 重排
- 新增 engine/neural:移植 chinese-ime-lm 的字符级 Transformer 推理,在词格解出的最多 12 条
  n-best 里重排挑选整句,desktop / keyboard 两档模型各出一条首选,后台 worker 异步打分
- 词格支持 n-best 输出与重排(quanpin/lattice_rerank),全拼、双拼共用
- 新增 SentenceAssociationOptions,经 QueryRequest 传给各 provider;四个来源默认全关
- 资源契约登记两个 safetensors 与 NOTICE;模型二进制不进仓库,由
  scripts/fetch-neural-model.ps1 按 neural-model/lock.json 下载并校验 SHA256
- 新增 neural_sentence 测试
2026-09-24 14:03:39 +08:00
Jia Sui 5b0be4e71d Merge branch 'develop' into feat/caret-driven-resegmentation 2026-09-24 08:45:54 +08:00
Jia Sui 54c41d34b2 fix(engine): reset_cache 连带失效 caret 前缀候选缓存 2026-09-23 21:32:59 +08:00
Jia Sui 536d0a95ef feat(windows): 五笔四码后继续输入不丢字(顶字)
四码(不论是否唯一)之后继续敲字母时,先上屏该码的首选候选,再把这个字母
保留为下一次组合的开头。根因是两侧对「组合里有几个字母」认知不一致:TSF 的
本地按键缓冲跟着敲键涨(raw 预编辑样式下 preedit 就是它),服务端的五笔 raw
被引擎裁在四码;空格提交时组合文本被整段替换成候选并清掉本地缓冲,多敲的
字母随之丢失。

- 新增 worker 消息 CommitCandidateAndContinue(27),payload "<consumed>\t<text>":
  TSF 按服务端给出的消费数裁**自己**的组合缓冲,而不是套用服务端按旧视图算好
  的剩余串——快打时用户已多敲的字母因此不会被覆盖
- TSF 处理序列全部复用既有原语:_InsertTextToComposition + _HandleCompleteCommitFirst
  真提交,_StartComposition + AddVirtualKey + _HandleCompositionInputWorker(NO_REQUEST_ID)
  重建余下字母的组合;无组合时退化为直写
- 服务端顶字不看自动上屏开关:开关只决定唯一码要不要多敲一键才上屏,
  不决定丢不丢输入;自动上屏那条路也改用同一消息,顺带收掉快打竞态
- 引擎新增 wubi_four_code_is_complete(完整四码、码表作答、不要求唯一;
  空码不算——没有候选可提交时不触发顶字)

已知限制:光标在组合中间时的改码行为维持现状;空码继续敲字母仍会被忽略。
2026-09-22 09:23:25 +08:00
Jia Sui 5d1a9a6cc2 feat(server): 五笔四码唯一自动上屏
五笔敲满四码且码表只答一个候选时不再需要空格:第四个字母落下即上屏,下一个
字母直接开新组合。行为无条件生效,不设配置开关:业界默认开启(微软五笔、
搜狗五笔、极点、Rime 主流五笔方案),唯一候选没有第二选择,确认空格是纯冗余;
而一条开关要牵动配置五处同步,换不来可形成预期的行为差异,不值。

- engine 只报告事实(wubi_unique_four_code:完整四码、码表作答、候选恰好一个),
  是否上屏由 Server 决定;判定收在 input_key_policy.h 的纯函数里,随 portable
  目标进 ASan/UBSan;组词(creating_word)激活时组合保持打开,唯一四码不会把
  正在拼的词截断上屏
- 提交复用 ProcessSelectionKey,与空格上屏同一套记账;但只能走 worker 管道推送
  CommitCandidate:字母键在默认 raw 预编辑样式下不读请求-回复管道,回一帧 Normal
  既不会上屏,还会在 TSF 侧留下永不释放的回复帧,而 Server 已按 Normal 清掉组合
- 上屏前按当前组合同步重建候选页:候选页异步发布,提交路径读的 ui.items /
  page_words 否则可能还停在第三码那一拍
- 非唯一四码不做顶字上屏:一个按键只能回一条管道消息,真顶字需要扩 IPC 协议
2026-09-22 09:23:24 +08:00
fanlusky 837af70b7e fix(engine): 全拼云查询带上手动分词而非去边界的裸串
手动把 qi'e'huan 分成三段,云端却返回二段的「切换」。因为全拼云查询的 query_text
取自 normalized_input(已去掉撇号的裸串),云端拿不到音节边界只能自行贪心断句。改用
保留手动撇号的 normalized_segmentation 构造 query_text,与双拼分支一致;也让
to_google_spelling 能逐音节把 nve/lve 换成云端认的 nue/lue 写法。
2026-09-22 01:13:11 +08:00
fanlusky 01c5bca325 fix(engine): 选中的整句候选落成用户词组
词格(CandidateSource::Generated)和 Google 解码器(CandidateSource::Fallback)
猜出来的整句在词库里没有对应的行,而调频走的是
update_weight_by_pinyin_and_word——它只改 SQLite 里已经存在的行,找不到行就什么
也不做。于是用户选中这种整句多少次,下次输入同样的拼音,它仍然是猜出来的,仍然
排在词库里那条同音短语后面,看起来就是"永远提不上来"。

选中即落库才是对的处理:存成用户词组之后,下次它以 UserDatabase 候选出现,按
quanpin::generated_sentence_insert_position 的约定排在两条整句之前,之后再选还能
走正常调频。

落库不受 frequency_adjustment_ 约束(那是候选词频策略,不是造词),也不跳过
index == 0——首位整句同样没有落库,下次仍要靠猜。长度上限对标
quanpin::WordLatticeOptions::max_phrase_syllables,再长的整句只是这一次输入的
产物,落库除了撑大用户词库没有别的作用。
2026-09-19 13:48:17 +08:00
fanlusky ccbaa3a631 fix(engine): 送进 Google 解码器前把 ü 换成 nue/lue/ju 写法
小鹤双拼 ntdddswu 出的是「女蛾黛动物」而不是「虐待动物」:nt 转成全拼后
是 nve(shuangpin_pinyin_set 把 nue 抹掉了,词库与词格那一侧统一用 v 形),
而本地 googlepinyinime-rev 的拼写表和 inputtools 云输入都不认 nve/lve/jv,
只有 nue/lue/ju/jue,也不做 v→ü 的转写。带分隔符的串照样送过去了,解码器
却把 nve 重新切成 nv + e,于是有了「女蛾」。

加一个 quanpin::to_google_spelling,按音节整体改写 nve→nue、lve→lue、
jv→ju 这一类拼写,只在两个 Google 解码器的边界上用:云输入的 query_text、
双拼与全拼的整句回退。词库查询和词格用的仍然是 canonical_syllable 那一侧的
v 形写法,没有动。nv'er、lv'se 这些 Google 也认的写法原样通过。
2026-09-19 12:29:01 +08:00
Jia Sui e64195731e feat(server): Backspace 删空剩余拼音后退选已选分词(#35)
已选分词是造词中间态:删空剩余拼音时不再整条 composition 丢弃,
而是退选最后一段并恢复其原始拼音与候选。

engine:
- SelectionTransition 暴露本次选择消费掉的原始输入(保留大小写与双拼按键)
engine/contracts:
- 新增回复类型 CompositionRestored=14 与能力位 CompositionRestore;
  MaxKnown 同步抬高,Server 按协商能力位门控发送,旧 DLL 不会收到未知帧
server:
- 造词中间态压 (consumed_raw, previous_creating_word) 快照,边界 Backspace 弹栈退选
- 任何清空 raw 的路径同时清 creating_word 与快照历史,避免跨 composition 泄漏
windows:
- _HandleCompositionBackspace 提示退选时读回包重建 keystroke buffer / word / 预编辑 / 光标
- NeedToCreateWord 与 CompositionRestored 共用载荷解析;旧 Server 与 UiLess 回退旧行为

Closes #35
2026-09-17 18:45:01 +08:00
Jia Sui 94abc08e7c feat(engine): ü 系拼写别名归一,非标准拼法候选带轻标记
nue/lue 是词库标准键 nve/lve 的混拼别名,此前按原样查库必然落空。
把别名归一收敛到 resolve_segments 唯一咽喉点(jqxy 系 jv→ju 既有行为
一并收敛),查询、调频、造词键全部落到标准拼法上;别名改写同步进
pinyin_segmentation_,mark_autocorrect_candidates 据此给非标准拼法的
候选写 corrected_from,前端以词尾 * 轻标记提示(对标搜狗纠错星标),
提交、调频、置顶、造词不受影响。

契约翻转:fold_autocorrect_letters 不再把 v 折叠成 u——旧契约
「别名永不标记」按产品决策废弃,别名即标记;nu/nv、lu/lv 是真实
不同音节,归一按段精确匹配 [nl]ue,绝不互串,nu'e 手动分隔符不受
影响。composition 层显示折叠保留(职责是分隔符重建的等价分类),
注释改写说明与字典侧标记契约的分工。
2026-09-13 15:56:29 +08:00
Jia Sui 3c2f3ae374 feat(engine): 全拼纠错补齐漏字/多字,k-best 切分按词频消解歧义
此前纠错只有交换(sahng -> shang)与 QWERTY 邻键(shabg -> shang)两类,
生成器在离线阶段就把「一个错串对应多个音节」的变体整个丢掉,运行时也只
取代价最低的一条切分。三类输入因此没有出路:漏字的(shng)、多敲一键的
(shangg、sjhang)、漏声母连基础切分都切不出来的(uanli 想打 quan'li)。

本次按专利 CN 101133411 B 的思路重排整条链路:错串可能对应的读音全部
保留到运行时,先由 k-best 切分带出平行读音,再让词典词频决定谁排前面。

- 纠错类型从两类扩到四类:新增漏字(shng -> shang)与多字(shangg 双打、
  sjhang 邻键 -> shang)。两个新位跟随既有 autocorrect_transposition /
  autocorrect_neighbor 开关联动(任一打开即生效),定义保持独立,留待
  将来的独立配置键。查询与 preedit 重建共用同一映射,两侧纠正空间一致。
- 生成器从两表扩到四表并保留歧义变体(ahan -> {shan, zhan});同一条
  (错串, 正确) 对被两条规则产出时在两张表各留一份,保证每个类型开关
  单独有效。正确音节收敛进 kCorrectSyllables 单表、行内改存 16-bit
  下标,一万七千多行不再各自重复音节字符串,--check 拦截过期表头。
- 切分层新增 autocorrect_cut_kbest:按位置自左向右做 top-k 假设传播,
  排序键是(纠错边数,边权之和,生成序)——最少纠错优先的契约不变,
  权重只在同边数内打破平局,引擎绝不用一条额外纠错换更低权重。边权取
  专利 [0052] 允许的估常量(交换 10 / 漏字 11 / 多字 12 / 邻键 13),
  标定路径是挖掘用户日志。漏字/多字边不等长,切分段改按 raw 长度推进
  位置,preedit 分隔跟着原始字母的区间走。
- 词典层固定 k=9,为离线评测定值:漏声母键 uan 有 10-15 个等权漏字
  目标,k=3 按表序截断够不到 quan'li 的正确读音;k=9 提升 R@1/R@3
  且 p95 不变。主切分仍是查询键,其余读音交给
  merge_alternative_segmentations 与主读法在词频下竞争;备选读音在
  缓存命中路径同样发布,corrected_from 标记对全部纠错读音生效;纠错
  读音排在音形别名读音之前。结尾单插入(shangg = shang + g)天然是
  简拼尾形状,仍被简拼形守卫拦下——多字纠错的可达面在中间/开头插入
  与多音节输入。
- 纠错门放宽:!segments.empty() 从门里去掉。BFS 工作在原始字母上,
  基础切分为空的丢声母输入恰是最需要纠错的一批,jian -> ian 现在能
  走到纠错候选并按词频合并到首位。简拼形守卫保留:合法音节加至多一个
  尾字母(zheg)是用户意图,任何纠错表都不得改写。
- preedit 补两道防线:zheg 在显式三位掩码下仍走简拼守卫,不被漏字表
  重排为 zheng;只有 BFS 切分的读音与 scheme 别名层完全一致时才按
  BFS 位置重画分隔符,否则 sahng 会被切成 sa'hng 而候选实际来自
  别名读音 shang。

新增独立评测工具 eval_quanpin_autocorrect:从真实 msime.db 采样高频
词条,按交换/邻键/漏字/多字模型注错,测 R@1/R@3、读音还原率与温态
p50/p95,固定种子可复现。它是评测工具不是测试,永远不进 CI,作为
纠错阈值与门逻辑改动的零回归标尺;注错规则与生成器逐键一致,改任一
侧须同一提交同步另一侧。

表格测试改为按 (错串, 正确) 对断言唯一并加覆盖门(合计 >= 4700 条),
新增歧义保留、漏字/多字变体、k-best 排序与权重定序、preedit 守卫等
用例;engine/tests 的 README 与 AGENTS.md 同步写清 imetest 与评测
工具两个产物的边界。
2026-09-13 11:04:34 +08:00
fanlusky a7779d2b05 refactor(engine): 将 engine 从 submodule 内嵌为仓内一等组件
`vendor/MetasequoiaImeEngine` submodule 改为 `engine/` 目录,导入的是
product-lock.json 原本钉住的提交 c810d201,逐文件核对过 blob,因此这是一次
搬迁而不是引擎升级。引擎自己的嵌套 submodule(googlepinyinime-rev、utfcpp、
miniaudio)按各自 gitlink 一并展开,来源与提交见 engine/UPSTREAM.md。

Windows 专门化裁掉了不属于本产品的部分:whisper.cpp 与本地推理 provider、
macOS 示例宿主、整个 dictionary/ 建库流水线、上游 CI 与 release-please 配置。
词库仍按 product-lock.json 在构建时取发布产物。

引擎不再进 product-lock.json:没有 gitlink 就没有可比对的东西,锁里写一个上游
commit 等于宣称这棵树仍等于上游,而第一个 Windows 专门化改动落地时这句话就假了。
verify-contracts / verify-checkout 随之移除,verify-published 保留。

CI 与工具链:引擎源码并入根 scripts/format.sh(删掉引擎自己的那份,避免两套排除
列表分叉);引擎 voice.yml 里的 silero_vad.onnx 摘要校验移到本仓 lock job;
engine-bump-triage 工作流与其测试删除;gitsubmodule 的 dependabot 频率由 daily
改为 monthly。

README、README.en、AGENTS.md、CONTRIBUTING、docs/product-release.md 及各子目录
文档同步说明新的位置与边界。
2026-09-12 03:40:48 +08:00