Skip to content
xushengfengPublic

About

大模型驱动的拼音输入法

Topics

Resources

Stars

915 stars

Watchers

6 watching

Forks

Repository files navigation

LIME

llm 驱动的输入法。目前支持拼音。

llm 常用的文本生成方式是自回归,也就是预测下一个词(token)的所有可能,然后通过某种方式采样选择某个可能,追加到模型输入,然后再次预测。这个项目,把词可能的选择采样交给了用户拼音,利用用户拼音来辅助采样。

使用小型大模型 Qwen3-0.6B-IQ4_XS ,兼顾速度和联想能力,打字时速度和普通引擎基本无异。

python 版本的见python 分支,此版本用 ts 重写。

Caution

本项目的结构是运行一个 ai 服务器,输入法前端发送按键数据到服务器计算,然后返回你选择的文字
Rime 输入法前端可以使用 HiAE 对 /candidates 和 /commit 的请求与响应做加密认证
你的按键输入仍然可能包括了你大部分隐私,请不要把服务器暴露在公网或不可信局域网
如果关闭 HiAE 或使用开发 curl 示例,请只在本机可信环境中调试

运行

需要有 Node.js 运行时(建议 22 或更高版本)和 pnpm

下载本项目,建议通过命令git clone https://github.com/xushengfeng/lime,后续可以获取更新,当然也可以下载压缩包

建议切换到某个tag使用,或者在release上下载某个tag,这些tag是验证过的版本而不是中途开发可能存在问题的代码。

安装依赖

pnpm install

下载模型

git clone https://www.modelscope.cn/unsloth/Qwen3-0.6B-GGUF.git

模型文件夹的位置和项目应该是同级的,当然你也可以修改代码

开启服务器

pnpm start

默认端口是 5000,可以显式指定:

pnpm start --port 5001
# 或
PORT=5001 pnpm start

默认读取项目根目录的 user_config.ts。需要同时跑多个配置时,可以用 LIME_CONFIG 指定另一个配置文件:

LIME_CONFIG=user_config.quality.ts PORT=5001 pnpm start

LIME_CONFIG 相对于当前工作目录解析;配置文件里相对 ./src 的 import 需要按它所在的位置调整。

开发时可以使用 pnpm dev,修改服务端代码后会自动重启。

创建密钥,一定程度上防止被滥用或隐私泄露

pnpm key

如果只是先看看这个项目的效果,可以跳转到下面的说明

作为输入法

这里使用rime作为前端。

复制项目 rime 文件夹里面的内容到你的 rime 输入法配置里面。可以修改default.yaml的schema_list,添加- schema: llm,或者创建default.custom.yaml,内容如下:

patch:
    schema_list/+:
        - schema: llm

总而言之,在rime里面启用llm这个schema。

确保系统安装了 curl,大部分系统如Windows(win10 1803+)、Linux、macOS 都自带了。

创建密钥pnpm key,只需要创建一次,把输出的密钥改写在llm_pinyin.lua的key变量里面。

默认开启 HiAE 加密。先运行pnpm build:hiae生成dist/hiae_payload.mjs,再把llm_pinyin.lua中的hiae_payload改成这个文件的绝对路径,例如/home/me/lime/dist/hiae_payload.mjs。加密模式下 Lua 端不会发送明文 bearer key,服务端会用key.txt里保存的 key hash 验证并解密请求。

如果需要临时回退到旧的明文 bearer 请求,可以把llm_pinyin.lua里的enable_hiae改成false。

开启服务器,切换到 llm 拼音输入法即可使用。

注意,并不能与你其他的 rime 输入法结合,只能作为一个新的 rime 输入法。

特性

除了 ai 优化,还有一些输入法特性:

  • 模糊音,可自定义转化表
  • 双拼(自然码、搜狗、微软、小鹤、智能 ABC、拼音加加、紫光,自定义)
  • '号分割拼音

配置

复制user_config.example.ts为user_config.ts,在user_config.ts里面修改配置。

比如可以把shuangpin的值改成false或者改成其他双拼方案。

建议使用现代的代码编辑器修改,比如 vscode、zed、neovim 等,它们提供代码检查,改配置时可以避免错误。

项目结构

  • src/engine/core:共享引擎,模型会话、上下文、token 索引、用户词
  • src/engine/algorithms:候选算法,内置 lime-v1 和 dict-llm-v1
  • src/engine/key_map:按键到拼音/双拼索引
  • src/server:Hono 服务、鉴权、启动入口
  • src/crypto:HiAE 加解密
  • src/cli:密钥生成、用户词预加载等命令行工具
  • src/config:配置类型和默认配置
  • interface:Vite 前端
  • tests/unit:不需要模型的单元测试
  • bench:需要 Qwen 模型的防退化评测、对比/消融实验和速度基准
  • bench/articles:评测文章(冰灯、孔乙己、论语等),可自行添加
  • bench/dicts:从模型导出的 Qwen token 表
  • tests/fixtures:测试数据
  • assets/pinyin:rime-ice 字表和词表

现状

长句的输入可能并不智能。

输入太快可能会漏字母。

没有保存数据的功能,也没有生词记录,所以服务器重启后会丢失记忆。

理解与展望

模型有的地方让人惊喜,有些候选又不合适地排在后面。总的来说,联想能力不输以前传统大厂的输入法,利好开源输入法,但 AI 时代竞争会更激烈,大厂的或者新加入的输入法会更智能。

从拼音引导文字生成来看,人对语音的理解不是顺序的,是大体上顺序,小范围逆序,一些音的识别在后面才会有明确的结果或纠正。现在这个项目只能对部分置信度高的候选生成长词组,对于更长的长句,没有一定的把握是不会生成的。我了解到 fim 补全模式,这可以是一个方向,用它来表示没有把握的候选,但并不能提供拼音信息。有几种方向(AI 也告诉我了一些,我不是专业的,仅抛砖引玉),可以修改 mask,让拼音候选匹配的文字权重加大,占用位置编码,但不具体下来;类似翻译模型,前后关系在模型内部处理。为了更好补全,可以微调模型,减少其在指令遵循、编程相关的能力,提高其文学能力。另外发现不同的 token 粒度对置信度影响较大,在“ta de”中,“他的”是一个 token,但“他”和“的”各是一个 token,“他的”排名靠后,但“他”*“的”的置信度还会更低,所以现在输入法采取长词优先,尽管这个不符合置信度排序。

在应用方面来说,不同焦点的切换应该发生给模型以提示,否则容易串。删除或者光标改变也应该考虑。这些输入法框架应该具有相关功能,我研究一下。

开发

Rime 前端默认使用 HiAE 加密。下面的 curl 示例仍保留为本机开发调试用的明文接口,需要传入 bearer key。

可以发送按键让引擎分析

curl --request POST \
  --url http://127.0.0.1:5000/candidates \
  --header 'content-type: application/json' \
  --header 'Authorization: Bearer your key' \
  --data '{
  "keys": "nihaoshijie"
}'

返回

{
    "candidates": [
        {
            "pinyin": ["ni", "hao", "shi", "jie"],
            "score": 1.1879427571978856e-13,
            "word": "你好世界"
        }
    ]
}

选好词后,发送,将作为上下文记录

curl --request POST \
  --url http://127.0.0.1:5000/commit \
  --header 'content-type: application/json' \
  --header 'Authorization: Bearer your key' \
  --data '{
  "text": "你好世界"
}'

其他输入方案

添加类似key_to_pinyin的函数,用于把输入按键转换为文字索引,放在src/engine/key_map文件夹下。添加类似load_pinyin的函数,提供把文字转为索引的方法。

添加候选算法

共享引擎在 src/engine/core,候选算法在 src/engine/algorithms。新算法实现 CandidateAlgorithm,可以复用模型会话、上下文和 token 索引,但自己实现筛选、生成和排序。

import type { CandidateAlgorithm } from "./src/engine/algorithms/index.ts";

const myAlgorithm: CandidateAlgorithm = {
	id: "my-algorithm",
	label: "我的算法",
	async candidates(input, { session, index }) {
		// 使用 session.lastResult / session.evaluateNext / index.pinyinOf 等
		return { candidates: [] };
	},
};

在 src/engine/algorithms/index.ts 注册,或者在 initLIME 的 algorithm 参数里直接传实例/工厂。

内置算法:

  • lime-v1:从模型下一步概率中按拼音筛选,并做长句补全。
  • dict-llm-v1:用词库生成单词和长句草稿,再由 LLM 排序。支持单词前缀、多词长句切分、词库多音/模糊音、* 通配;词库唯一路径直接固定,候选 token 用前缀树共享评估,可以按 beam 剪枝,并额外保留一批单字/单词安全候选。默认是快速模式:单次最多处理 7 个音节、每键固定评估预算,并降低词格搜索宽度,长句按键不会越来越慢;每个剩余音节默认扣 selectionPenalty(0.8),偏向一次提交更多内容。质量模式默认最多保留 1 个多词长句候选,其余长句移到词语候选之后,避免错误长句占满 Top5;完整覆盖候选与第一名差距在 coverageFirstWithin(默认 0.5)内时会优先排第一;完整长句评估可以用 createQualityDictLlm(),或选择已注册的 dict-llm-v1-quality。它需要 assets/pinyin/words.dict.yaml,先运行 pnpm words 从 rime-ice 下载生成;没有词表会直接报错,不会静默退回只有字表。
await initLIME({ ziInd: load_pinyin(), algorithm: "dict-llm-v1" });

测试

pnpm test               # 单元测试,不需要模型
pnpm bench              # 模型相关的防退化评测
pnpm bench:golden       # lime-v1 行为快照
pnpm bench:compare      # LIME 与 baseline 对比
pnpm bench:ablation     # LIME + 字频/调频/上下文/雾凇词库/Qwen token 一起跑
pnpm bench:speed        # 长句输入速度评测
pnpm bench:latency      # 按键延迟,标记超过 109ms 的场景
pnpm bench:word         # 用字准确率评测
pnpm words              # 下载生成 dict-llm-v1 需要的词表
pnpm typecheck

bench/compare.ts 默认用 bench/articles/冰灯.txt,两边都输入自然码双拼,统计首选字符覆盖、Top5、缺失和平均排名。可以换文章或截断:

pnpm bench:compare --article bench/articles/孔乙己.txt
pnpm bench:compare --limit 300 --json bench/results/冰灯-300.json
pnpm bench:compare --algorithms lime-v1,dict-llm-v1
LIME_ALGORITHM=dict-llm-v1 pnpm bench:speed
pnpm bench:words        # 重新生成雾凇词表
pnpm bench:tokens       # 重新导出 Qwen token 表

词表和 token 表都是本地生成的,不提交到仓库;首次跑词库/Token baseline 前需要先执行上面两条命令。没有 words.dict.yaml 时会退回只使用字表。

四篇不同领域文章上的首选字符覆盖(“字典”使用 assets/pinyin 字表,“雾凇词库”和“Qwen token”使用词表 + 长词优先):

文章 汉字数 LIME 字典字频 字典+调频 字典+上下文 雾凇词库 Qwen token
冰灯 936 69.66% 38.68% 51.82% 53.85% 59.40% 59.94%
孔乙己 2180 66.70% 51.42% 58.90% 63.58% 66.38% 63.07%
一件小事 865 61.97% 59.54% 62.20% 63.58% 67.28% 62.66%
论语 1410 73.33% 37.02% 51.84% 54.11% 62.27% 62.41%

首选字符覆盖 = 正确候选排在第 1 位时覆盖的汉字数 / 全文汉字数,是首个候选的字符覆盖率,不是自由选择首个候选后最终文本的 CER;测试过程用正确文本作为 oracle 选择候选,只测排序。LIME 和词库 baseline 一次可能提交多个字,所以按候选覆盖的字数加权。

LIME 在冰灯和论语上领先约 10 到 11 个百分点,孔乙己基本持平,一件小事落后雾凇词库 5.31 个百分点;Top5 覆盖 LIME 在所有文章上都最高。baseline 的消融和详细方法见 bench/results.md 与 bench/README.md。

bench/ 里的结果不是严格的单元测试,而是用来观察排序和输入体验是否退化的基准;失败时通常需要人工判断模型或排序策略是否真的变差。bench:speed 会按长句拼音模拟输入,统计索引、按键数、提交数,并可按 kpm 估算 cpm。

统计

服务器会尝试统计按键的速度(按照相邻请求来计算)、实际输入文字时间、查找候选的时间等,通过/inputlog可以获取,可以使用中位数等或者平均数计算你自己相关的打字数据。

高级配置

使用ollama的模型

添加import { getOllamaModel } from "./src/engine/ollama.ts";

initLIME的参数中设置modelPath,添加getOllamaModel('模型名称'),名称为ollama list命令列出的模型名称。

前端

执行pnpm build:interface构建前端,开发时可以使用pnpm dev:interface(默认端口 5179,API 会代理到 5000)。

重启服务器

访问 http://127.0.0.1:5000/demo.html?passwd=你的密码 将有个模拟平时输入法界面的页面

需要连接其他服务器时,加 server 参数:

http://127.0.0.1:5000/demo.html?server=http://127.0.0.1:5001&passwd=目标服务器的密码

页面会直接请求该服务器,不影响当前 5000 服务器。

其他界面在 http://127.0.0.1:5000 可以导航,如上下文获取、输入统计计算等

About

大模型驱动的拼音输入法

Topics

Resources

Stars

915 stars

Watchers

6 watching

Forks

Releases

Contributors

Languages