在Linux下,我们也能愉快地畅骂AI了......

语音输入法,真香
原本我对语音输入的态度一直都是拒绝的,即便手机上的讯飞在我小学时就已经支持了这个功能, 我也从来没用过。
但是,自从用上了vibe coding以后,我发现,面对时不时整出逆天操作的ai时,还是用语音输入畅快地骂出来比较有利于身心健康。

但众所周知呢,我又是一名常年的桌面Linux用户,至少一半的开发工作都是在Linux下进行的。在Windows下语音输入法确实很多也很完善,像用得最多的微信输入法,还有一些新兴的千问输入法等。但在Linux下,几乎就是一片空白了。
Linux下目前的方案主要聚焦于本地模型或云端api调用。
本地模型派的代表是VoCoType-linux,它使用优化后的FunASR,可以在CPU上离线运行,但本地模型的最大问题就是:缺乏商业级输入法的实时AI润色能力。
还有一些对接云端ASR api的方案,比如LexiSharp。这些实现的最大问题是:调用api要钱。我本来用AI都花钱了,骂AI再花钱那不是成冤大头了。
以及部分商业输入法在UOS/Kylin下提供语音输入,但显然,打死我都不会用那些信创发行版的。
那还有什么解决方案呢?
喜报:您的输入法为您引入了一个新的chromium浏览器!
打开各种AI网页对话助手,比如豆包,我们可以惊喜地看到,在输入框的右侧,有一个语音识别的按钮。而且这种语音识别能力是跨平台的,即便在Linux下也能使用。

诶,那为什么不能靠这个功能来做个语音输入法呢?实现方案也很明晰,要么逆向对应的网页的语音识别接口链路,要么直接内置一个浏览器去从前端控制转发。
你现在看到的这个项目,TamaASR,便是第二种方案下的产物。
选二不选一的主要原因一是逆向的接口不稳定,时不时得重新适配,而前端元素改动的可能性却是非常低。而我是一个懒人,没多少精力去维护这么个业余项目。二是现在的大模型网安道德水平都高得要死,逆向请求这类操作轻易不会让你做的,想做还得搞破甲或者手搓。三是现在的设备带个electron应用基本上问题都不大,我个人是没什么electron洁癖的,多装个chromium就多装个。
至于为什么不用Tauri,我在项目仓库里也写明了,其实主要就是我的一台主力设备不兼容,使用webkit2gtk打开这些AI助手的网页是根本没法进行语音输入的。
事实上,后来我发现已经有了类似思路但是走逆向请求的项目,如Doubao Murmur,如果不喜欢内嵌一个chromium并且常驻500-700mb的内存的话,也可以看看这个项目哦。
X11? Wayland? 没事,我们有fcitx5
我和AI讨论这个方案时,AI就提醒我,需要注意X11和Wayland的兼容性问题。
尤其是Wayland,不得不说,可是让人又恨又爱啊。它是比X11现代化了,But at what cost?为了避免堆成屎山,Wayland选择尽可能少的协议约束,但是做桌面又不能没有那些功能,于是各家桌面环境就推出了各种碎片化的第三方协议,对于输入法这样的涉及原生事件的软件来说简直是一场灾难。
但是!输入法毕竟是桌面使用的刚需,总得有人去做的,于是我们就等来了一位“白马王子”,CJK输入法使用最广泛的框架,fcitx5。诶,那我们直接去适配fcitx5,不就能解决Wayland下的兼容问题了吗?
于是,最难啃的部分也解决了,现在,让我们为Linux桌面带来接近微信输入法体验的高精度语音输入法吧!
选择什么后端?豆包?元宝?千问?ChatGPT?
嗯,遇到这个问题,大部分人应该还是首选豆包吧,毕竟是国内大模型多模态做的最好的。
但是!豆包的网页版存在一个严重问题:它是直接识别完后把识别内容作为对话请求发出去,这就意味着,单靠纯前端控制元素很难达成目标了,因为前端不会显示输入的中间态,在发送请求前也不会存在可抓取的前端DOM。如果坚持使用豆包就会导致实现很不优雅,首先得拦截最终的发送请求(你也不想让对话记录里一堆乱七八糟的识别内容被发给豆包吧?),其次中间态也得想办法拦截,不然最后只是整段地呈现的话,说的话一多就很难受。
于是只好转向国内的其它几家啦。国内的AI助手网页端总共也就那么几家,智谱和deepseek是没有网页端语音识别的,最后敲定使用千问和元宝。千问是最先适配的,但实测元宝会更好用点,元宝会有一个最终的AI精修的动作,而千问基本上是边说边修,结束输入就不再改动了。
至于ChatGPT,openai的whisper在业内一直都很出名,但我毕竟要做的是一个日用而且中文友好的,一个需要时刻使用魔法,中文识别也谈不上顶尖的选择就暂时不考虑了。
后来发现讯飞星火和百度文心也有符合要求的网页版语音识别,但因为元宝和千问够用了,所以一直没适配,前面说了,我懒。
适配时遇到的一些问题
- Wayland: 没错,还是万恶的Wayland。因为我们需要用悬浮球显示输入状态,而在Wayland下,悬浮球一出现就会打断输入焦点,导致输入被中断。最后强制指定了x11,在Wayland下靠XWayland运行。
- 状态不统一: 因为是靠的前端DOM进行判断和操作,所以总会因为各种莫名其妙的边界条件导致状态不统一,比如取消并再次输入太快了就会导致多次点击网页后端的输入按钮,造成网页后端维持在输入态而前台已经退出输入。
- electron43的托盘图标: 很难想象这么大个项目还能整出这种篓子,electron43早期的一些版本(但已经是正式版而非测试版了)的托盘图标会不显示,而我们很依赖托盘图标进入后台,最后全部指定为42。
- 对Tauri的迷信: 毫无疑问,这种要常驻后台的Web应用,人们的第一反应肯定是Tauri。我其实也尝试过至少两版Tauri移植。但实际上,对于这个应用的很多DOM操作,Tauri其实是并没有提供对应的api的,如果想移植到Tauri,必须还得自行实现一套DOM操作和DOM状态判断的API,甚至于和移植到QtWebEngine都无异了。继续移植下去肯定也能做,但我之前也说了,我有台设备根本用不了Tauri版,而也能使用Tauri的其它设备性能要强得多,后台多挂个electron应用根本没啥影响,对我而言开发Tauri版的收益就很低了,也许以后学习Rust时会再次尝试吧。所以说,选择Tauri还是Electron,有时也不能光看刻板印象。
解放双手以后
历经波折明明是烧了很多token吧,也是在Linux下用上语音输入了。不得不说还是语音输入开骂舒服啊。

项目也已经以MPL2许可证开源,欢迎各位来提出宝贵的建议,或者开发更好的方案,比如使用逆向请求或者移植到Tauri。
