Files
qingjian/docs/design/nine-key.md
T
yenharvey 03c4074f59 docs: Android 用户文档、实现要点与移动端统一到 Rust 的计划
- 用户文档:Android 入门页,按键、快捷输入、统计与译词页补手机上的差异
- 设计与笔记:九宫格、语音与滑行、渲染与主题的手机部分,Android 实现要点与 crate 笔记
- 路线图与 contributing:记下 Android 进度,移动端键盘与面板将来统一到 Rust,共享规则不在壳里另写
2026-10-20 20:35:18 +08:00

3.7 KiB
Raw Blame History

九宫格拼音

手机上的 9 键拼音:2–9 每键三四个字母,64426 → 你好。它是一种输入方案(Engine::set_nine_key),和双拼、注音一样是设置项, 不是模式;歧义消解、排序、整句、学习全在 Core,壳只有一张键位表和一列「选拼音」。

为什么能复用全拼的管线

  • 每个位置多种写法本来就有。 词库的 lookup_pattern_alt 与整句词图 sentence::convert 的入参都是 &[Vec<SyllablePattern>]——每个位置若干写法,模糊音与敲错变体就是这么喂进去的。九宫格的一个位置 = 同一段数字码下的全部音节 (64 → ni mi),代价都是 0,由词频与语言模型在词和路径上分辨。
  • 一个字母一个数字。 任何一种读法与数字串等长、逐字符对齐,所以候选消耗多少输入直接按它的音节长度算,不需要对齐搜索; '(壳的分词键)照旧跳过。

切分

nine_key::segment(input) 只在边界上做动态规划(不展开读法,否则 4 个音节就上千种):一段数字是合法位置,当且仅当它是某个音节的码 (完整),或在末尾 / 简拼时是某个音节前缀的码。每个边界留最优的 12 种形状(BEAM;数字串的边界歧义比全拼多得多,留 4 种时 tian qi hen 会被 tian shi en 挤掉),排序键与全拼切分相同:音节少 > 不完整音节少 > 前面的音节长。 每种形状的每个位置带上这段码下的全部写法(Position::alternatives)。

缓冲区里可以混着字母:用户在拼音列里点了 ni,壳调 Engine::pin_nine_key("ni"),第一个还没定的位置的数字换成字母并接一个 ' (64426 → ni'426)。字母位置只有它自己一种写法。

查询(Engine::query_nine_key)

与形码一样是一条平行管线,在 query_inner 里进切分之前分岔:

  1. 前 3 种形状各自 lookup_all(整段)+ lookup_exact_all(各前缀,逐词上屏用),与全拼同一个 ranking::rank 和打分闭包。
  2. 整句:前 12 种形状各跑一次 sentence::convert,取分最高的(边界歧义只有语言模型分得清);规则同全拼(至少两个音节、路径不止一个词、没有占位音节)。
  3. 显示:Query::typed_display 给首选候选的读音(ni'hao),首选只覆盖前面一段时后面补各位置的第一种写法;decoded_keys = true。
  4. 拼音列:Query::pinyin_choices 是第一个未定位置上的全部写法,按「带这个首音节的最靠前候选」排序。

不走的:拼写纠错、模糊音、英文尾段、表达式 / 问字模式、辅码(数字键都被占了,这些以后按需再接)。

上屏与学习

consumed_by 在九宫格下按候选音节长度逐个消耗(跳过 ',不超过作用域);按输入串记的选择用候选自己的拼音当键 (nihao),和全拼共用一份学习数据,换键盘不丢。个人敲错表不记(数字对不上字母)。整句上屏时的词序列重算走同一套九宫格位置。

壳

键位:1 分词(')、2–9 字母;左侧一列是 pinyin_choices,点了就 pin,不在组句时放标点;右列退格、重输、0。英文模式下九宫格退回全键盘。 Android 的实现见 docs/notes/android.md。

验证

cargo run -p qingjian-cli -- --nine-key 64426(输入写数字键)。2026-09-21 在产品词库 + 语言模型上:今天天气很好、我们一起去开发、我想请你吃饭、 这个输入法真不错、明天上午开会、现在几点了 的数字串整句首选全对,单次查询约 2 ms(M 系列 Mac)。还没有九宫格的回放评测集。