RuneSight:分辨文本与代码的神经引擎
1.0.6 版淘汰了那份手写的正则表达式列表——过去正是它来判断提取出的每一行究竟是文本,还是路径、标识符和控制码。取而代之的是 RuneSight,一个 400 KB 的神经网络分类器,训练数据来自九十万行真实游戏文本、十六种语言的真实句子,以及每一种有文档记录的引擎控制码。现在,这个判断只需在你的 CPU 上花几微秒就能完成,每一行都会附上判断理由,而且绝不会碰到任何带有真实文本的行。
RuneTranslate 打开的每一款游戏,大部分是玩家会读到的文本,此外还有一条长长的尾巴——那些看起来像文本、其实不是的东西:资源路径、变量名、控制码、插件键、调试标签,还有谁忘在某个数据文件里的一段 base64 数据。翻译了其中任何一行,最好的情况是浪费一次请求;最坏的情况是场景再也无法加载,因为引擎现在要查找一个已经不存在的名字。
自最初的版本以来,这两者之间的界线一直是手工划出的:数百条正则表达式,每遇到一种新形态的垃圾就添一条规则,一个引擎接一个引擎地累积。1.0.6 版用RuneSight取代了这份规则列表——一个在真实数据上训练出来的小型神经网络。它为每一条提取出的行做出判断:这一行是文本,还是内部管道内容,而且只需几微秒,就在你的 CPU 上、在应用内部完成。
为什么规则列表不再够用
一条正则表达式,只能捕捉到它当初写出来要匹配的那种形态。bg_office_01 是一个标识符,所以一条针对 snake_case 的规则能捕捉到它。接着,一款 Wolf RPG 游戏带来了 <<GET_FILE_EXIST>>Data/\s[1],一款 Bakin 游戏带来了 Lv.\partystatus[0],一款 Unity 游戏带来了 "CubismTaskQueue.OnTask" already set.,还有一个用日语写的文件夹名,击破了那条假定全是 ASCII 字符的路径规则。每一次都变成一条新规则,而且都要小心划定范围,免得把真正的对话也吞掉,然后每来一个新引擎,这个循环就重新开始一次。
规则也没法权衡证据。一行内容大部分是控制码,结尾却带着三个词的对话,那它是文本,必须保持可翻译。一行是在讲某个 .png 文件的完整句子,那它是文本。一行只有 \f[\cself[1]]\space[4],那它不是。要区分这些情况,需要对整行有个整体判断,而不是只匹配其中一部分。
RuneSight 是什么
RuneSight 是一个大约 400 KB 的分类器。它把一行文本读成一袋字符片段——每个片段一到四个字符长,哈希进一张固定的表里——再把这些片段和几十项对这一行测得的属性配对:字母、数字、标点或空格各占多少,用了哪些文字系统,看起来是不是一个路径,括号是否成对,结尾是不是一句话该有的结尾方式。一个小小的隐藏层把这些信息变成两个答案:这一行不是文本的概率,以及如果不是,它属于哪一种非文本。
第二个答案就是你在编辑器里看到的东西。一行被排除后,仍然带着一个你能读懂的理由:代码模式、资源引用、系统标签、数字、注释、插件键、仅含控制码、二进制或插件指令。这一行还会带上一个小小的 RuneSight 标记和判断背后的置信度,所以这个决定从来不是一个黑箱,点一下这一行就能把它重新打开。
整个过程每行大约只需四微秒。一款十万行的 JRPG,在任何一台 Windows 10 或 11 电脑上,不用 GPU、不向任何地方发送任何数据,就能在远低于一秒的时间内完成分类。
它是从什么数据里学来的
这类任务本来没有现成的标注数据,所以它是被专门建出来的。训练集略多于九十万行,来源如下:
- 真实游戏。十三款游戏、横跨十种引擎的完整提取结果,从 RPG Maker 和 Ren’Py 到 Wolf RPG、Kirikiri、Artemis、Unity、Godot、Bakin 和 Unreal,全部按 RuneSight 要取代的那套规则标注。这是底线:它必须捕捉到那套规则捕捉到的一切。
- 真实句子。二十五万句,来自十六种语言,其中包括日语、中文、韩语、泰语、阿拉伯语、俄语,以及使用拉丁字母的那些语言,这样任何源语言里的普通文本都不会被误判成代码。
- 真实的管道内容。十六万条路径、标识符、点号连接的名称和注释行,采集自源代码和各种软件包,是各个引擎共有的那些形态。
- 故意补上的盲点。近三十万条生成出来的行,取材自 Wolf RPG、RPG Maker 和 Bakin 官方的控制码参考文档,每一条都配上一句它不能吞掉的近似对话:一句提到某个文件的话、一个后面跟着数字的名字、一行中间夹着代码的文本。
- 人工核对过的黄金测试集。旧规则曾经钉死的每一行,加上从实际游玩的游戏中报告上来的真实行,逐条裁定。
上线前必须先过的关
一个平均水平更好、但仍会犯下新错误的模型,对撞上那些新错误的人来说仍然是一次倒退,所以 RuneSight 必须通过一组固定的门槛,而且每次重新训练都要重新跑一遍:
- 在数据集里的每一款真实游戏上,它至少要捕捉到手写规则所捕捉到内容的 99.5%。
- 在那份必须保持可翻译、旧规则一直拿来测试的冻结行集合上,它做出的排除是零。
- 在真实的散文文本上,被排除的行永远不到 0.2%。
- 它给出的理由,至少有 97% 的时候是对的。
- 给一行打分的时间不到 5 微秒。
其中最重要的一条,是关于混合行的规则。一行里如果真实文本和代码并存,那它就是文本,依然保持可翻译。只有当整行都是管道内容时,RuneSight 才会把它排除。
没有变的地方
RuneSight 判断的是一行的形态。需要靠上下文而非形态来判断的决定,仍然来自各个引擎适配器:一个 Godot 节点名被排除,是因为它在场景树里所处的位置;一条 RPG Maker 事件注释被排除,是因为携带它的那条指令;一个插件参数键被排除,是因为它所在的文件。这些规则都还在,理由列里能看到的那些策略性判断也还在,比如一行完全不含源语言字符。
你自己的正则过滤器仍然在 RuneSight 之后运行,行为和以前完全一样,而且不管排除是谁做出的,依然是一行你可以随时重新打开的红色可选启用行。RuneSight 做出的判断,没有一个是最终的。
在哪里能看到它
RuneSight 徽章位于应用右下角,版本号的上方。模型加载完成后,它上面的圆点会变绿。点开它,会告诉你模型大小、出厂自带的置信度阈值,以及上一次提取做了什么:打了多少行分、排除了多少行、和旧规则有多少分歧。每次提取结束后,一条简短的提示也会重复这些数字。
万一模型某次无法加载,徽章会如实说明,提取则会直接退回旧规则。应用绝不会因为这个而拒绝工作。
模型和旧规则每一次意见不合,都会被写进一份小小的本地日志,绝不会上传。如果你认为是真实对话的某一行被排除了,在编辑器里把它重新打开,再到 Discord 上告诉我们。这些反馈正是下一次重新训练要学习的东西。

