摘要

游戏的**战斗日志(Logs)**是一页按时间排列的文字,每行形如“[01:02:03] 玩家:单位(31) 受到了 90 伤害。死亡单位:30。”。项目的日志读取器对每一行做三遍文字识别(OCR),三遍结果互相印证后才输出。

之前有一个私有候选规则(下称“三遍候选”):当两遍读数能完整解析且一致、第三遍解析不了时,也接受这一行。后来又给它加了一个“窄检查”:只有第三遍仅仅缺少时钟右括号 ]、其余原文一致时才合格。

本实验发现这个窄检查有个漏洞:它在比较前删除全部空白。这样“A B”和“AB”、“90”和“9 0”、甚至带换行的两行合并文本,都会被当成一致。而项目现有的日志解析器明确规定:名字里的空格要保留,被拆开的数字要拒绝。

新检查在保留旧检查所有拒绝条件的基础上,改用现有日志语法逐字段比较。结果:

  • 52 个合成案例:16 个合格对照全部保留;36 个不合格要求全部拒绝,其中 18 个是旧检查仍会接受的。
  • 已保存的 13 条私有新增输出全部仍合格,没有任何一条因为新检查被取消。

这是对软件资格条件的收紧,不是测得的识别精度提升。

要回答的问题

“删掉空白再比较”会不会把含义不同的读数误判为一致?换一种尊重语法的比较后,已经接受的输出还能保住吗?

数据与来源

  • 合成案例:52 个固定输入(16 个合格对照、36 个不合格要求,其中 24 个来自前一轮检查、28 个本轮新增)。标签由资格规则决定,是规范要求,不是像素真值。
  • 已保存输出:之前日志困难页实验的 6 张开发截图上,三遍候选比生产多输出的 13 条日志行(短页 3 条、长滚动页 10 条;洞穴页和 3 张无日志负样本为 0 条)。
  • 本次新增原图读取、解码、OCR、GPU、候选生成、准确率重评分、评分参考读取均为 0。

方法

旧窄检查要求:三遍分数 ≥0.90、两遍完整读数的 claim(结构化字段)相同、两遍完整原文经 NFKC 归一并去空白后相同,第三遍只差时钟右括号。漏洞就在“去空白”。

新检查依次:

  1. 先跑旧窄检查;旧检查拒绝的,保持拒绝。
  2. 三遍原文中只要出现任何换行类字符(CR/LF、垂直制表、换页、记录分隔、NEL、Unicode 行/段分隔符),拒绝。不能用去空白把两行合成一行。
  3. 用 Perception 现有日志语法的正则模式解析:两遍完整原文用正常时钟模式;第三遍用只删去时钟右括号的比较模式。绝不往第三遍里补括号、修字或拼接。
  4. 比较三遍解析出的字段:时、分、秒、玩家、事件种类,以及主体/行动者/目标/标签/数值等。每个字段做 NFKC 归一和首尾去空白,但保留名字内部的空格和数字的连续写法。三者必须解析成同一种唯一事件、字段完全相同,才合格。

语法本身允许的分隔空格(例如冒号后、数字前后)、全角字符归一、制表符分隔仍然合格;名字内部空格增删、数字/时钟/谓词被拆开、括号内多空格则不合格。

检查只返回“是否合格 + 原因”,不生成新文字或新 claim。它是 Perception 内部实验对现有私有语法的适配,不是新接口;将来生产接入需要另行评审。

设置

协议、合成输入、实现和测试先提交冻结,然后对 13 条已保存输出只检查一次,之后不再调整。

结果

软件验证

67 项新增测试 + 227 项既有实验回归 + 94 项日志字面解析器测试,共 388 项通过(0.51 秒)。其中包括全部 10 种换行类字符、畸形输入,以及“输入不被修改”的检查。

合成案例

固定合成输入 旧窄检查合格 新检查合格
16 个合格对照 16 16
18 个既有不合格要求 0 0
18 个新增空白 / 分行不合格要求 18 0

新增被拒的 18 类:主体、玩家、法术名的空格被删或被插入;行动者、目标、能力名、效果名内部空格被删;伤害、死亡数、括号内数量、持续时间、时钟被拆开;谓词被拆开;括号内部多空格;多行合并。

几个例子:

两遍完整原文中的片段 第三遍(缺右括号)中的片段 旧检查 新检查
主体 A B 主体 AB 合格 拒绝
玩家 p q 玩家 pq 合格 拒绝
伤害 90 伤害 9 0 合格 拒绝

已保存输出

截图 检查数 合格 不合格
短页 3 3 0
长滚动页 10 10 0
洞穴页与 3 张无日志负样本 0 0 0

13 条的检查 CPU 时间约 2.085 ms(不含导入和输入预检),不是识别延迟。

沿用此前已保存的(未重新评分)成绩:可读整行 36/39 正确,已输出且可核验的整行 36/36,另有 1 条参考不确定。本次检查不会把不确定行变成已证正确,也没有解决剩下 3 条被拒识的行。

失败与负结果

旧窄检查存在被复现的漏洞(18 类输入被错误接受)。新检查没有让已保存的任何一条输出失去资格,也没有提高任何识别成绩。

局限

  • 合成案例是规范要求,不能据此估计真实的误接受率。
  • 三遍读数来自同一个识别引擎;如果三遍共享同一个错误,新检查仍会放行。多遍一致不等于像素真值,分数 ≥0.90 也不是校准概率。
  • 全部是已暴露开发数据;像素真值、独立留出覆盖和独立验收都还需要各自的证据。生产默认和公共接口未改。

结论与下一步

“去空白后比较”过于宽松,会隐藏名字和数字内部的差异;用现有语法逐字段比较能挡住这些情况,同时不影响已保存的合格输出。新检查待独立审阅后再决定是否接入;生产接入需要单独评审,不能让外部模块直接依赖这些私有语法实现。