Rime 设计逻辑与配置思路:从“命名空间”理解方案结构
前言:
刚接触 Rime 配置时,最容易产生的困惑并不是“某一行语法看不懂”,而是不知道这些配置块之间究竟有什么关系。
一个完整方案通常同时包含方案信息、依赖、状态开关、按键处理、分段、翻译、过滤以及大量 Lua 组件。单独看每一段并不复杂,但如果没有整体结构,很容易陷入“知道这个参数是什么意思,却不知道它为什么写在这里”的状态。
理解 Rime 可以先抓住一条主线:
方案声明了哪些组件 → Engine 按顺序调用这些组件 → 每个组件再从自己的配置节点中读取参数。
本文从这个角度出发,用万象方案中的实际结构说明 Rime 的组织方式。
一、方案基础信息与依赖 (Dependencies)
打开一个方案文件,例如 wanxiang.schema.yaml,首先通常会看到 schema: 段落。
这里主要用于声明方案本身的信息,例如方案 ID、名称、版本、作者、描述,以及当前方案依赖的其他方案。
# 方案说明
schema:
schema_id: wanxiang
name: 万象拼音
version: "LTS"
author:
- amzxyz
description: |
请勾选【万象拼音】以启用,万象拼音标准版本,带声调的词库,支持语法模型,全拼、简拼、整句、声调辅助筛选。
【文本框输入:/pinyin全拼,/zrm自然码,/flypy小鹤,/mspy,/sogou,/pyjj等,详见README.md】
dependencies:
- wanxiang_mixedcode # 混合编码
- wanxiang_reverse # 部件拆字、反查及辅码
- wanxiang_english # 英文
dependencies 应该怎么理解?
dependencies 用于声明当前方案依赖的其他 Rime 方案。
当主方案部署时,这些依赖方案也会被纳入部署流程,从而保证主方案中引用到的相关词典、翻译器或反查数据已经准备好。
例如万象主方案可能通过:
table_translator@wanxiang_englishtable_translator@wanxiang_mixedcodereverse_lookup_translator@wanxiang_reverse
去调用附属方案提供的数据。
因此,这些附属方案通常不需要全部作为独立的日常输入方案加入 default.yaml 的 schema_list。它们更多承担的是被主方案调用的依赖角色。
dependencies 与 schema_list 不是一回事
dependencies 解决的是“当前方案依赖哪些其他方案才能完整部署”。
schema_list 解决的是“哪些方案出现在用户可以直接切换的方案列表中”。
一个方案可以作为依赖参与部署,但不一定需要出现在日常方案切换列表中。
二、状态开关与选项组 (Switches)
继续往下,通常会看到 switches:。
它用于声明当前方案中的状态选项。按下 `Ctrl+`` 打开 Rime 方案选单时,其中很多可以切换的项目都来自这里。
1. 普通布尔开关
可以这样理解:
name:这个开关在 Rime 内部使用的 option 名称。states:分别描述关闭和开启状态在界面中显示的文字。reset:可选,用于指定方案初始化时的默认状态。
例如:
表示初始化时设为第一个状态;
表示初始化时设为第二个状态。
如果不设置 reset,实际状态是否延续上一次使用结果,还会受到前端及运行环境的状态管理方式影响。
2. 多选一的 Options 组
除了单个 name 对应的布尔开关,也可以定义一组互斥选项:
这里不再使用单独的 name,而是使用 options 声明多个状态。
这类配置适合:
- 简体 / 通用繁体 / 香港繁体 / 台湾繁体
- 多种字符集模式
- 多种互斥的显示或转换状态
同一组选项通常只会保持其中一个处于当前状态。
3. 可见开关与快捷键切换
switches 负责声明方案中的状态选项,但并不意味着所有 option 都必须通过方案选单手动切换。
在 key_binder 或 Lua Processor 中,也可以直接对 option 执行:
toggleset_optionunset_option
因此可以把它理解为:
switches 定义状态,key_binder 等组件决定用户如何操作这些状态。
通过 Custom Patch 修改某个 Switch
switches 是一个列表,因此可以使用 Patch 的列表索引定位。
例如:
其中:
@0→ 第 1 项@1→ 第 2 项
如果要为第二项 ascii_punct 增加:
可以在 wanxiang.custom.yaml 中写:
固定索引需要注意方案更新
@1 表示的是“当前列表中的第 2 项”,并不是永久绑定 ascii_punct 这个名称。
如果以后上游方案调整了 switches 的排列顺序,这个索引可能会指向其他项目。因此使用数字索引的 Custom,在较大版本更新后应重新检查。
三、核心流水线:Engine 与四类组件
理解 Rime 方案结构,最重要的一段就是:
engine:
Rime 可以看作一条按阶段运行的输入处理流水线。最常见的四组组件是:
processors:接收和处理键盘事件。segmentors:分析当前输入码,将不同片段划分并打上 tag。translators:根据输入码生成候选。filters:对已经生成的候选继续加工。
可以先记住一个大致方向:
按键 → 分段 → 生成候选 → 处理候选
1. Processors:先决定“这个按键怎么处理”
Processor 位于输入流程前端,主要负责键盘事件。
例如:
- 中英文切换
- 快捷键
- 拼写输入
- 标点
- 候选选择
- 光标移动
- 回车、空格、退格
- Lua 自定义按键逻辑
万象中的 Processor 配置示例:
engine:
processors:
- lua_processor@*wanxiang.super_processor
- lua_processor@*wanxiang.user_predict*P
- lua_processor@*wanxiang.partial_commit
- lua_processor@*wanxiang.super_tips
- lua_processor@*wanxiang.super_sequence*P
- ascii_composer
- recognizer
- key_binder
- lua_processor@*wanxiang.key_binder
- speller
- punctuator
- selector
- navigator
- express_editor
这里最重要的是顺序。
同一个按键会依次经过 Processor 列表。前面的组件如果已经消费了这个按键,后面的组件可能就不会再得到相同的处理机会。
因此,Processor 不只是“有没有挂载”,还要关注“挂载在哪里”。
2. Segmentors:判断当前输入属于哪一类
Segmentor 负责把输入内容分成不同片段,并为这些片段添加 tag。
例如:
segmentors:
- ascii_segmentor
- matcher
- abc_segmentor
- affix_segmentor@wanxiang_reverse
- affix_segmentor@add_user_dict
- punct_segmentor
- fallback_segmentor
常见作用包括:
ascii_segmentor:识别 ASCII 输入区域。matcher:与recognizer配合,根据规则识别特殊输入。abc_segmentor:处理常规拼写输入,并赋予abctag。affix_segmentor@...:处理具有前后缀特征的专用输入,例如反查、自造词。punct_segmentor:识别标点输入。fallback_segmentor:处理前面未被识别的剩余片段,因此一般放在靠后位置。
这里可以建立一个重要概念:
tag 是 Segmentor 与 Translator / Filter 之间的重要连接条件。
前面给输入片段打上什么 tag,后面的组件就可以根据 tag 决定自己是否处理。
3. Translators:把编码转换为候选
Translator 的职责是:
输入一段已经识别好的编码,生成候选。
万象中同时使用了多种 Translator:
translators:
- punct_translator
- script_translator
- lua_translator@*wanxiang.user_predict*T
- lua_translator@*wanxiang.version_display
- lua_translator@*wanxiang.set_schema
- lua_translator@*wanxiang.shijian
- lua_translator@*wanxiang.unicode
- lua_translator@*wanxiang.number_translator
- lua_translator@*wanxiang.super_calculator
- lua_translator@*wanxiang.input_statistics
- table_translator@custom_phrase
- table_translator@wanxiang_english
- table_translator@wanxiang_mixedcode
- reverse_lookup_translator@wanxiang_reverse
- script_translator@add_user_dict
- script_translator@user_dict_set
这些 Translator 的数据来源和用途并不相同:
script_translator:用于基于音节表的拼音等输入方案。table_translator:从表格词典中查询候选。reverse_lookup_translator:用于反查。punct_translator:生成标点候选。lua_translator:由 Lua 自定义逻辑生成候选。
同一个输入片段可以同时被多个 Translator 处理,最终形成多个候选来源。
4. Filters:对候选做后处理
候选生成以后,还可以继续经过 Filter。
例如:
filters:
- lua_filter@*wanxiang.auto_phrase
- lua_filter@*wanxiang.super_lookup
- lua_filter@*wanxiang.super_english
- lua_filter@*wanxiang.charset_filter
- lua_filter@*wanxiang.super_comment_preedit
- lua_filter@*wanxiang.super_replacer
- lua_filter@*wanxiang.super_filter
- lua_filter@*wanxiang.super_sequence*F
- lua_filter@*wanxiang.user_predict*F
- uniquifier
Filter 可以用于:
- 修改候选文字
- 修改候选注释
- 字符集过滤
- 简繁转换
- 排序
- 上下文调频
- 去重
Filter 与 Processor 一样,顺序非常重要。
例如:
uniquifier
通常需要放在较后的位置,因为如果过早去重,后面的 Filter 就可能失去本来需要处理的候选。
万象 Engine 完整结构示例
下面保留一份更接近实际方案的 Engine 配置,方便对照理解组件顺序:
# 输入引擎
engine:
processors:
- lua_processor@*wanxiang.super_processor # KP 小键盘、字母选词、符号处理、分词等综合按键逻辑
- lua_processor@*wanxiang.user_predict*P # 用户预测相关 Processor
- lua_processor@*wanxiang.partial_commit # Ctrl+1~0 局部提交
- lua_processor@*wanxiang.super_tips # 提示模块
- lua_processor@*wanxiang.super_sequence*P # 手动候选排序按键处理
- ascii_composer # 英文模式及中英文切换
- recognizer # 与 matcher 配合识别特殊输入模式
- key_binder # Rime 标准快捷键绑定
- lua_processor@*wanxiang.key_binder # Lua 按键绑定扩展
- speller # 接收并编辑拼写输入
- punctuator # 标点处理
- selector # 候选选择与翻页
- navigator # 输入栏光标移动
- express_editor # 空格、回车、退格等编辑操作
segmentors:
- ascii_segmentor
- matcher
- abc_segmentor
- affix_segmentor@wanxiang_reverse
- affix_segmentor@add_user_dict
- punct_segmentor
- fallback_segmentor
translators:
- punct_translator
- script_translator
- lua_translator@*wanxiang.user_predict*T
- lua_translator@*wanxiang.version_display
- lua_translator@*wanxiang.set_schema
- lua_translator@*wanxiang.shijian
- lua_translator@*wanxiang.unicode
- lua_translator@*wanxiang.number_translator
- lua_translator@*wanxiang.super_calculator
- lua_translator@*wanxiang.input_statistics
- table_translator@custom_phrase
- table_translator@wanxiang_english
- table_translator@wanxiang_mixedcode
- reverse_lookup_translator@wanxiang_reverse
- script_translator@add_user_dict
- script_translator@user_dict_set
filters:
- lua_filter@*wanxiang.auto_phrase
- lua_filter@*wanxiang.super_lookup
- lua_filter@*wanxiang.super_english
- lua_filter@*wanxiang.charset_filter
- lua_filter@*wanxiang.super_comment_preedit
- lua_filter@*wanxiang.super_replacer
- lua_filter@*wanxiang.super_filter
- lua_filter@*wanxiang.super_sequence*F
- lua_filter@*wanxiang.user_predict*F
- uniquifier
实际组件、排列顺序和 Lua 模块以当前方案文件为准。阅读 Engine 时,重点不是一次记住所有组件,而是先判断它属于 Processor、Segmentor、Translator 还是 Filter。
四、从“命名空间”理解组件配置
理解 Engine 后,就可以进一步理解 Rime 配置中经常出现的“命名空间”。
这里可以把它理解成:
某个组件实例到哪里读取自己的配置。
1. 内建组件通常有对应的默认配置节点
例如 Engine 中启用了:
那么方案顶层通常会有:
这里的 speller: 就是拼写处理器使用的配置区域。
类似的还有:
recognizer:key_binder:punctuator:translator:menu:
因此在读方案时,可以先在 Engine 中找到组件名称,再去寻找与它相关的顶层配置块。
2. @ 用于给同类组件指定不同实例名称
当同一种组件需要挂载多次时,就不能让所有实例共用同一份参数。
例如:
engine:
translators:
- table_translator@custom_phrase
- table_translator@wanxiang_english
- table_translator@wanxiang_mixedcode
这里三个组件的类型都是:
table_translator
但它们使用了不同的实例名称:
custom_phrasewanxiang_englishwanxiang_mixedcode
于是就可以分别配置:
custom_phrase:
dictionary: ""
user_dict: custom_phrase
db_class: stabledb
wanxiang_english:
dictionary: wanxiang_english
wanxiang_mixedcode:
dictionary: wanxiang_mixedcode
可以把:
table_translator@custom_phrase
拆成两部分理解:
组件类型 table_translator + 实例名称 custom_phrase
@ 后面的名称用于区分同类组件的不同实例,也通常决定该实例对应的配置命名空间。
五、Lua 组件为什么看起来不一样?
万象中大量使用:
lua_processor@*wanxiang.super_processor
lua_translator@*wanxiang.input_statistics
lua_filter@*wanxiang.super_replacer
Lua 组件的写法比普通内建组件多了一层 Lua 模块与导出对象的含义。
例如:
表示 Engine 中挂载的是一个 Lua Filter,而实际执行逻辑来自指定的 Lua 模块 / 导出对象。
此时配置读取方式不一定能简单套用:
“@ 后面是什么,就一定读取同名顶层节点”
这一条。
原因是 Lua 脚本本身可以主动读取配置,例如通过自己的 env、命名空间或明确写死的配置路径访问:
或者:
因此分析 Lua 组件时,通常需要同时看两处:
- Schema 中 Lua 组件是如何挂载的。
- Lua 源码实际从哪个配置路径读取参数。
分析 Lua 配置时不要只看组件名称
普通内建组件通常可以通过组件实例名快速找到对应配置节点。
Lua 组件则可能自行决定读取哪个配置块,因此遇到 lua_processor、lua_translator、lua_filter 时,最好结合 Lua 源码中的 config:get_*()、命名空间初始化等逻辑一起判断。
六、Recognizer、Segmentor、Translator 为什么经常一起出现?
理解命名空间之后,还可以进一步看出 Rime 中另一种常见的连接方式:
Recognizer 定义特殊编码 → Matcher / Affix Segmentor 打 tag → 对应 Translator 根据 tag 工作。
例如反查流程大致可以理解为:
按下反查引导符
↓
recognizer 识别出符合规则的输入
↓
matcher / affix_segmentor 标记为反查 tag
↓
reverse_lookup_translator 处理这个 tag
↓
生成反查候选
因此,配置一个功能时不能只看 Translator。
如果一个特殊 Translator 明明已经挂载,却完全不出候选,还需要继续检查:
recognizer是否识别到了输入segmentor是否生成了正确 tag- Translator 的
tag/prefix/tips等参数是否匹配 - Engine 顺序是否正确
这也是为什么复杂 Rime 方案看起来像很多配置块互相分散:它们实际上是在通过组件、命名空间和 tag 共同组成一条处理链。
七、读一个陌生方案时,可以按什么顺序?
面对一个新的 .schema.yaml,不需要从第一行开始逐项研究。
可以按照下面的顺序快速建立结构:
第一步:看 schema
先确认:
schema_id- 方案名称
dependencies
知道这是什么方案,以及它依赖哪些附属方案。
第二步:看 switches
确认当前方案提供哪些状态:
- 中英文
- 简繁
- 字符集
- Emoji
- 其他功能开关
第三步:看 engine
先不要研究参数,只看挂了哪些组件:
大致判断整个方案有哪些功能。
第四步:找组件对应的配置块
例如看到:
就继续找:
看到:
就找:
第五步:再追 recognizer、tag 和附属方案
如果是反查、特殊指令、自造词等功能,再继续追踪:
第六步:Lua 单独看源码
看到:
如果仅靠 Schema 无法判断配置来源,就打开对应 Lua 文件,查看它实际读取的配置路径。
结语
理解 Rime,不需要一次记住所有节点和参数。
更重要的是建立下面这套关系:
Schema 决定方案结构
→ Engine 决定启用哪些组件以及执行顺序
→ Namespace 决定组件从哪里读取配置
→ Recognizer / Segmentor / tag 决定输入被送到哪里
→ Translator 生成候选
→ Filter 对候选继续处理
掌握这套框架后,再面对复杂方案时,就可以从 Engine 出发逐层追踪,而不是在大量 YAML 节点之间来回猜测。