跳转至

语境预测 (User Predict):上下文联想与候选调频

user_predict.lua 由 Processor、Translator 和 Filter 三部分协同工作:记录用户实际输入形成的上下文关联,在移动端按需生成上屏后联想,在桌面端或移动端 reorder 模式中根据上文重新调整当前候选顺序。

当前版本采用稳定记录模型:

稳定 code + 候选文本
c 值记录有效学习强度
负 c 表示删除墓碑
同一层级按 c 值排名

因此,当前文档应以 c 值计数、负值墓碑、上下文隔离、分支裁剪和事务回滚 为核心理解 User Predict。


一、User Predict 现在负责什么?

当前模块主要承担三类用户可感知功能:

  1. 上屏后联想(Post Predict)
  2. 输入中的上下文调频(Context Reorder)
  3. 数字后的量词提权(Classifier Boost)

此外还包含:

  • 回头码兜底交换
  • S / 2 / 1 / P 四级关联查询
  • S-Gram 长上文前缀桥接
  • 关联学习与分支裁剪
  • 上屏后立即退格的数据库回滚
  • 预测候选主动删除
  • 导入 / 导出
  • 多端数据合并所需的稳定记录与墓碑状态

二、先用一个简单例子理解“学习”

一次有效的连续上下文即可建立关联。

例如测试:

家族
族长

注意两次上屏之间不要超过当前:

context_timeout: 5000

也就是默认约 5 秒的上下文窗口。

完成这次连续输入后,数据库已经可以形成:

家族 → 族长

的有效关联。

之后再次输入并上屏:

家族

如果开启了预测总开关:

  • 移动端 post 模式可以直接出现“族长”联想;
  • reorder 模式下,再输入“族长”对应编码时,“族长”可以根据上下文被提前。

第一次有效学习会把对应关联写成:

c=1

后续再次学习同一关联时继续递增:

c=2
c=3
...

三、总开关:prediction

User Predict 受 Rime Option:

prediction

统一控制。

当前 Base 方案中:

- name: prediction
  states: [预测关, 预测开]

如果这个 Option 关闭:

不生成 Post 联想
不执行上下文 Reorder

量词调频也位于同一个 Filter 中,并随整体预测 Filter 的运行状态共同工作。


四、桌面端与移动端并不是同一种显示方式

桌面端

当前 Lua 在非移动设备上会直接指定:

PREDICT_STYLE = reorder

因此桌面端开启 prediction 后,默认工作方式就是:

输入中的上下文候选调频

而不是上屏后再弹出 联想候选。

换句话说:

PC
prediction = on
自动使用 reorder

mobile_predict_style 在 PC 上不会改变这一点。

移动端

移动端才读取:

user_predict/mobile_predict_style

支持:

post
reorder
off

当前 Base 方案配置:

mobile_predict_style: post

因此移动端默认使用上屏后联想。


五、Post:上屏后的联想预测

当移动端使用:

mobile_predict_style: post

并且当前上文存在可用预测时,Lua 会在输入区压入:

作为预测占位符,然后由 User Predict Translator 生成:

type = predict

的候选。

例如:

家族

上屏后可能出现:


1. 族长
2. 成员
...

选择其中一个预测候选后,它会正常上屏,并继续作为新的语境参与后续预测。

普通按键会结束当前联想界面

如果正在显示 预测,此时开始正常输入:

z
a
...

Lua 会关闭当前预测候选,清掉占位符,再让新的按键继续交给后续 Rime 输入流程。

因此 Post 模式不会要求先手动关闭预测才能继续打字。

数字键

在非 T9 环境下,预测状态中直接按数字会结束联想并提交对应数字。

T9 中数字本身属于编码,因此会退出预测状态后把数字继续交还给 T9 输入流程。


六、Reorder:输入中的上下文调频

reorder 不会生成 占位符。

它先根据刚刚上屏的上下文查询预测记录,再在你开始输入下一个词时扫描当前正常候选。

如果某个正常候选正好存在于预测结果中,就根据预测排名将它提前。

例如已经形成:

家族 → 族长

的关联。

之后:

上屏:家族
输入:zuzhang

原始候选可能是:

1. 组长
2. 族长

上下文调频后可以变成:

1. 族长
2. 组长

Reorder 只重新组织正常候选

当前 Filter 主要处理:

user_phrase
phrase

两类正常候选。

它不是对候选列表中的所有特殊类型进行无条件排序。


七、量词提权

User Predict Filter 还包含独立的数字后量词提权。

例如已经直接输入:

1

随后输入某个与量词同码的拼音时,只要当前候选文字存在于量词表中,就可以被优先提升。

例如:

1 + jin

可以优先得到:

而不是普通语境下更高频的其他同码字。

默认量词表

当前方案通过:

user_predict/custom_classifiers

提供自定义列表,例如:

custom_classifiers:
  - 个只名位口人头匹条群批伙多
  - 张把件台部块根颗粒滴片朵面扇顶栋座所辆艘架盏支枝杆
  - 双对副套打串束排阵堆叠摞扎
  - 杯瓶盒包份碗锅盆桶袋罐盘
  - 次场局回趟顿番遍声项宗桩款步招
  - 年月天周岁秒分刻代期届任夜季
  - 本册篇首句段卷幅节堂门帖字行
  - 米寸尺里斤两吨克升元角毛笔百千万亿

Lua 会把列表内容合并成快速查找表。

关闭量词提权

如果不需要,可以把列表清空:

patch:
  "user_predict/custom_classifiers": []

这样量词查找表为空,就不会执行数字后的分类提权。


八、回头码兜底交换

当前版本仍然保留:

user_predict/enable_fallback_reorder

这个功能不是正常的 N-Gram 上下文预测,而是针对“删掉当前编码后重新输入同一编码”的回头场景。

简单理解:

输入一串编码
退格回删
再次输入相同编码
首二候选尝试交换

它主要用于同码候选首次顺序不合预期时的快速兜底。

当前 Base 配置

当前方案中:

enable_fallback_reorder: false

也就是说,代码功能存在,但当前 Base 默认没有开启。

需要使用时:

patch:
  "user_predict/enable_fallback_reorder": true

Fallback 与正常上下文 Reorder 相互独立

当回头码兜底实际触发时,Lua 会暂时关闭:

正常上下文 reorder
量词提权

然后只执行首二候选的兜底交换,避免多套调序同时干预。


九、当前真正的防污染机制

当前版本通过输入语境过滤减少无效学习。

1. 上下文超时

默认:

context_timeout: 5000

两次上屏之间超过这个时间,就切断当前记忆链。

因此:

家族

过很久之后再输入:

族长

不会被强行视为同一段即时语境。

2. 单次上屏超过 4 字不进行普通关联学习

当前基础规则:

单次上屏 > 4 个汉字
→ 不记录本次普通上下文关联

防止超长整句直接大量污染短上下文模型。

3. 连续重复不学习

例如:

你好 → 你好

不会继续制造自循环关联。

4. ABA 折返过滤

类似:

A → B → A

的快速折返会被识别,避免把短时间来回修改误学成高价值关联。

5. 非中文内容切断语境

当前普通学习主要接受:

中文字符
部分允许的语气标点

如果上屏内容属于其他不适合学习的文本,会重置当前语境链。

6. 标点隔离

普通句末标点通常意味着语境结束。

但对于:

...

这类常见句末助词,Lua 保留了白名单,使:

助词 + 标点

能够正常完成句尾处理,而不是在助词上屏时提前产生异常关联。


十、四字词还有一条直接学习通道

当前 v12 对恰好四个汉字的最终上屏文本提供专门学习逻辑。

例如一次上屏:

家族族长

如果它正好由四个汉字构成,会自动按:

前 2 字 → 后 2 字

建立 1-Gram:

家族 → 族长

这条记录不要求该四字文本原本在候选列表里处于什么位置。

也就是说,四字纯中文内容可以在第一次有效上屏后直接建立:

2 + 2

关联。


十一、S / 2 / 1 / P 四级查询

当前查询仍然使用四层模型:

S-Gram
2-Gram
1-Gram
P-Gram

不同层级使用不同的基础倍率:

S : × 1,000,000
2 : × 10,000
1 : × 100
P : × 1

候选基础权重则直接来自记录中的正 c 值。


十二、S-Gram:手工维护的静态关联

S-Gram 不是运行时自动学习层。

它专门用于:

手工导入的固定续句
诗词
专有表达
预设长句

当前运行时:

  • 只读取 S 记录
  • 不自动学习 S
  • 不自动增加 S 的 c
  • 不参与普通分支淘汰
  • 删除预测候选时也不会给 S 写删除墓碑

因此它属于长期静态记忆

权重

查询时:

S c 值 × 1,000,000

因此相对于动态 2 / 1 / P 层拥有最高优先级。

但它并不是“某个不可修改的隐藏后门”,而是一类明确的数据记录。


十三、v12 新增:S-Gram 长上文前缀桥接

假设手工导入:

S;床前明月光    疑是地上霜

完整关联本来是:

床前明月光 → 疑是地上霜

但用户可能只先输入:

床前

v12 会扫描同一个 S 家族,发现存在更长的静态上文:

床前明月光

于是可以先给出桥接候选:

明月光

选择这个桥接候选后,Lua 会保留完整强上下文:

床前明月光

然后优先查询:

S;床前明月光

继续得到:

疑是地上霜

可以理解为:

床前
桥接:明月光
完整静态上文:床前明月光
续句:疑是地上霜

桥接限制

当前源码内部限制包括:

最短前缀:2 字
最大剩余桥接长度:7 字
单次 S 家族扫描:20 条
纯桥接最多:3 条
普通候选已存在时最多插入:2 条

这些属于当前内部策略,暂时没有对应 YAML 参数。


十四、2-Gram:双上文关联

2-Gram 使用最近两个上屏单元作为上下文:

u0 + u1 → 下一个词

例如:

但是 + 你 → 不能

查询倍率:

× 10,000

为了限制上下文规模,当前只有满足:

u1 长度 ≤ 4
u0 + u1 总长度 ≤ 5

时才写入 / 查询 2-Gram。


十五、1-Gram:主要动态关联

1-Gram 是最直接的:

上一个词 → 下一个词

例如:

家族 → 族长

查询倍率:

× 100

当上文长度较长时,查询会从末尾最多 4 个字开始逐级回退:

4 字后缀
→ 3 字后缀
→ 2 字后缀

一旦当前回退层已经找到结果,就停止继续向更短后缀下降。


十六、P-Gram:长上文后缀兜底

P-Gram 用于保存较长上文的部分后缀关系。

例如一个较长上文无法直接作为普通 1-Gram 使用时,可以将其后部若干字作为:

P;后缀

继续查找下一词。

查询倍率最低:

× 1

因此它主要承担:

普通关联没有命中时的抗抖动兜底。

当一个 P 候选实际被用户继续上屏后,正常的 commit 学习流程又会根据真实上下文重新写入 1 / 2 等动态关联。

所以不需要再把它描述成一个独立的“P-Gram 手动转正按钮”。


十七、当前排名方式

当前预测权重可以简化为:

Score = c × Gram Multiplier

其中:

S → 1,000,000
2 → 10,000
1 → 100
P → 1

记录本身只要:

c > 0

就是有效预测记录。


十八、现在如何控制数据库规模?

当前主要通过:

max_memory_branches: 15

控制同一上下文前缀下保留的动态分支数量。

查询某一个动态 Gram 时:

  1. 读取当前前缀下记录
  2. c 降序
  3. 保留前 max_memory_branches
  4. 超出的动态记录写入负 c 墓碑

S-Gram 属于静态数据,不参与这一步动态分支裁剪。

因此因此,同一上下文下只有排名较高的动态分支继续保持有效状态,超出分支上限的记录会被标记删除。


十九、c 值与负墓碑

当前数据库记录采用稳定形式:

code + 候选

记录尾部为:

c=数字 d=0 t=0

正 c

表示当前关联有效:

c=1
c=2
c=3

每次再次学习同一条关联时,绝对值继续增加。

负 c

表示这条关联已经被删除:

c=-4

删除时不会简单抹掉 raw key,而是:

当前 |c|
→ +1
→ 写成负数

例如:

c=3

删除后:

c=-4

如果以后再次重新学习该关联,则会从当前绝对值继续增加,并恢复为正值:

c=5

这样可以避免:

删除
→ 旧设备同步回来
→ 旧记录重新复活

这类状态折返。


二十、上屏后立即退格:事务回滚

当前每次上屏学习时,Lua 会记录本次真正改动了哪些数据库键,以及:

写入前状态
写入后状态

如果上屏后没有进行其他正常输入,并在:

context_timeout

规定的时间窗口内立刻按 Backspace,User Predict 会尝试撤销刚才这次上屏造成的数据库学习

例如:

家族
→ 误选了“组长”
→ 立即 Backspace

对应的刚刚新增 / 增强的预测关联可以一起回滚。

回滚的是预测数据库事务

User Predict 负责恢复它自己刚才写入的数据库状态。

屏幕上文字究竟怎样被 Backspace 删除,仍由当前 Rime 前端和编辑环境共同处理。

防止误回滚旧事务

只要上屏后又进行了其他普通按键操作:

新的学习事务失效

后面的 Backspace 不会继续把更早以前的预测学习一起撤销。


二十一、主动删除预测候选

当当前菜单选中:

type = predict

的预测候选时,模块可以响应前端删除通知,也支持带 Control / Shift 的 Delete 或 BackSpace 类按键删除预测关联。

常见操作可以继续使用:

Ctrl + Delete
Shift + Delete

但数据库层的实际行为不是“物理销毁”,而是:

写入负 c 墓碑

并同时处理与当前上文相关的 P-Gram 兜底记录。

S-Gram 不允许这样删除

如果当前预测来自:

S-Gram

或者只是 S-Gram 生成的虚拟桥接候选,删除逻辑会直接放行,不给它写墓碑。

静态 S 数据只能通过外部数据文件 / 导入数据自行维护。


二十二、导出 /outpredict

在任意输入框输入:

/outpredict

会将当前预测数据库导出到 Rime 用户目录:

predict_export.txt

当前格式为三列:

code<Tab>word<Tab>record_tail

例如:

1;家族    族长  c=3 d=0 t=0

S-Gram 也使用同一格式:

S;我的    名字  c=1 d=0 t=0

二十三、导入 /inpredict

准备:

predict_import.txt

放在 Rime 用户目录,然后输入:

/inpredict

即可导入。

导入时比较 c 的状态强度。

合并规则

假设:

本地:c=3
导入:c=5

则采用:

c=5

如果:

本地:c=5
导入:c=-5

绝对值相同,则负墓碑优先:

c=-5

因此当前合并原则是:

|c| 更大者优先
同 |c| 时删除墓碑优先

这套逻辑不依赖设备本地时钟。


二十四、如何手工添加 S-Gram?

S-Gram 推荐通过导入文件维护。

例如希望:

我的 → 名字

始终作为静态高优先级关联。

在:

predict_import.txt

中写:

S;我的    名字  c=1 d=0 t=0

三个字段之间使用真实 Tab

然后输入:

/inpredict

导入即可。

长句示例

例如:

床前明月光 → 疑是地上霜

写成:

S;床前明月光 疑是地上霜   c=1 d=0 t=0

导入后除了完整上文命中,还可以利用 v12 的 S 前缀桥接功能。

S-Gram 当前统一使用:

S;上文<Tab>续词<Tab>c=... d=0 t=0

二十五、当前真正有效的 YAML 参数

v12 当前读取的主要配置如下:

参数 作用 Lua 回退值 / 当前方案
db_name User Predict 数据库名称 Lua 回退 lua/predict
mobile_predict_style 移动端模式:post / reorder / off 当前 Base:post
enable_fallback_reorder 回头码首二候选兜底交换 Lua 内部默认 true;当前 Base 显式 false
max_candidates Post 联想显示上限及预测结果控制参数 Lua 回退 5;当前 Base 10
max_predictions Post 连续预测链深度上限 3
max_memory_branches 单一动态上下文最多保留的有效分支 15
context_timeout 即时上下文与 Undo 窗口,毫秒 5000
custom_classifiers 数字后量词提权字符集 当前方案自定义列表

二十六、推荐的 Custom Patch

例如:

patch:
  # 移动端:上屏后联想
  "user_predict/mobile_predict_style": "post"

  # 开启回头码兜底
  "user_predict/enable_fallback_reorder": true

  # 联想显示数量
  "user_predict/max_candidates": 5

  # Post 连续联想深度
  "user_predict/max_predictions": 3

  # 单个上下文保留的动态分支数量
  "user_predict/max_memory_branches": 15

  # 即时上下文有效时间 / Undo 时间窗口
  "user_predict/context_timeout": 5000

移动端如果只需要上下文调频:

patch:
  "user_predict/mobile_predict_style": "reorder"

移动端如果暂时不需要 User Predict 的 Post / Reorder:

patch:
  "user_predict/mobile_predict_style": "off"

off 不等于删除数据库

off 只是关闭当前移动端的预测呈现方式。

历史数据库仍然保留,之后重新切回 postreorder 可以继续使用。


二十七、如何固定开启 prediction

当前 Base 的 switches 中:

prediction

位于第 6 项。

列表从 0 开始计数,因此当前 Base 对应:

patch:
  "switches/@5/reset": 1

表示方案初始化时默认:

预测开

不要长期把 @5 当作固定接口

@5 只代表当前 Base 版本中第 6 个 Switch

Pro 或后续版本如果调整了 switches 顺序,索引可能变化。

因此修改其他方案时,应先检查实际 switches 数组,再确定对应索引。


二十八、当前版本可以怎样理解?

当前 User Predict 的核心由以下几部分组成:

即时有效学习
+
c 值累计
+
四级上下文查询
+
上下文隔离
+
分支竞争裁剪
+
负墓碑删除
+
事务回滚

整体流程可以概括为:

用户连续上屏
验证是否属于有效语境
写入 1 / 2 / P 动态关联
正 c 累计学习强度
下次根据 S → 2 → 1 → P 查询
移动端 Post 或输入中 Reorder
误选可 Undo
主动删除写负 c 墓碑

S-Gram 则始终保持独立:

人工导入
→ 静态只读
→ 高优先级查询
→ 支持长上文前缀桥接

User Predict 当前以稳定 c 值记录用户明确形成的上下文关系:动态关联通过真实输入持续累积,静态 S-Gram 负责固定续句,Post 与 Reorder 则分别解决联想展示和输入中调频。