摘要
游戏里有一个**战斗日志(Logs)**页面,按时间列出每一条战斗事件,例如“[01:02:03] 玩家:某单位(31) 受到了 90 伤害。死亡单位:30。”。项目的日志读取器从截图里找出每一行,做文字识别(OCR),再把文字解析成结构化字段(时钟、玩家、主体、伤害、死亡数、魔力、法术名……),这组字段称为 claim。
要知道读取器好不好,需要一个评分器:把输出和人工审核过的参考逐行比较。本实验不改读取器,而是检查评分器本身是否公正。用合成数据复现出旧评分器的几个漏洞,写了一个更严格的私有评分器,并在冻结规则后,用它对两组已保存的输出各评一次。
结果:修正版在 33 个合成案例上全部符合预期;旧评分器有 12 个字段结果、19 个整页状态与新要求不一致,并在 1 个不完整 claim 上直接报错。对已保存输出重评后,基线整行正确 24/39、候选 36/39 都不变,所有已知字段的计数也不变;唯一变化是一张长日志页的整页状态在两组中都从“未知”改为“错误”。这是评分口径的修正,不是新的识别准确率提升。
要回答的问题
旧评分器会不会给本不该得分的输出算分,或把“参考里本来就未知”的字段算成错?修正后,之前报告的日志读取成绩还成立吗?
数据与来源
- 已保存输出:之前一轮日志困难页实验(内部编号 A31)用到 6 张已经暴露过的开发截图:3 张有日志(短页、长滚动页、洞穴背景页),3 张是不含日志的负样本(战场、英雄面板、说明文字)。
- 基线:当时生产读取器的 6 份输出。
- 候选:一个私有候选规则(A31R)的 6 份输出。读取器对每行读三遍;该候选在“两遍完整解析一致、第三遍解析不了”时也接受这一行。
- 参考:A31 已审核的逐行参考,以及原有的评分结果。
- 本次只读取这些 JSON。原图路径只是标签,从未打开;新的原图读取、解码、OCR、GPU 调用、候选生成均为 0。
方法
用合成数据复现的旧评分器问题
- 输出文字为空(读取器拒绝输出)时,仍会读取它附带的 claim,于是拒绝的行也可能拿到字段分。
- 参考字段本身为空(人工也看不清)时,仍计入“应答数”,于是输出会被判错或判缺。
- 行匹配只看纵向中心:同一高度但横向错位、过高或横跨整帧的框也能得分。
- 整数比较可能把布尔值或浮点数当整数;主要字段不检查 claim 的事件种类。
- 整页状态先看“参考里是否有不确定行”,所以即使已知行有明确遗漏,也会返回“未知”;文字和结构化字段没有分开的整页状态。
这些都是用合成数据复现的,并没有断言历史真实输出犯过同样的错误。
修正版规则
- 只评日志行;回合标题不产生事件。
- 行框必须有限、有面积、在 2560×1440 原生画面内;与唯一最近的参考行纵向中心相差 ≤16 像素,并且水平覆盖 / 预测宽度 ≥0.85、垂直覆盖 / 较大高度 ≥0.60、预测高 / 参考高 ≤1.50。多框争同一行则全部不算;不拼接、不修字。
- 只有输出了非空文字的行才评字段;文字为空却带 claim 记为“claim 泄漏”,不得分。格式不对的 claim 当作缺字段,不能让评分器崩溃。
- 整行文字用历史的 NFKC 归一与去空白比较,保留标点;整行正确率与字段正确率分开报告。
- 参考字段为空记为
unknown_reference,不计入分母,也不当作 0;已知的 0 是有效整数。数值必须是真整数(排除布尔与浮点),显示名必须是字符串且完全相等。 - 主要字段还要求 claim 种类正确(伤害/死亡 → 伤害事件,魔力损失、魔力恢复、施法各对应),并要求玩家、时钟、主体或行动者也已知且正确;参考里明确有数量时也要对。上下文未知时记
unknown_context,除非已有已知错误。未知不等于已证正确。 - 每个字段分列:应答、正确、错误、缺失、参考未知、上下文未知,以及“可核验分母”(正确 + 错误 + 缺失)。
- 文字整页、字段整页、组合整页三个状态分开;“错误”优先于“未知”,两者都没有才“正确”。这些只是私有评估标签,不改生产的状态字段。
冻结与一次评估
协议、33 个合成案例、实现和测试先提交冻结,然后对 12 份保存输出各评一次;看到结果后不再调规则。
设置
- 合成案例在纯 CPU 上运行;它们是软件要求,不是测得的 OCR 错误。
- 几何阈值是保守的开发选择,只适用于这一种原生分辨率和布局。
结果
软件验证
- 52 项新增测试 + 44 项原有测试 = 96 项通过,0.18 秒。
- 33 个合成案例全部符合修正版预期。旧评分器:12 个首行伤害字段结果、19 个整页状态与新要求不一致,另有 1 个不完整 claim 触发报错。这些计数有重叠,不能相加,也不能解释为真实 OCR 错误数。
已保存输出重评
| 指标 | 基线 | 候选 |
|---|---|---|
| 可读参考整行正确 | 24/39 | 36/39 |
| 已输出且可核验整行正确 | 24/24 | 36/36 |
| 全部输出行数 | 24 | 37 |
| 输出文字参考不确定 | 0 | 1 |
| 拒绝输出行数 | 16 | 3 |
| 伤害数字正确 | 3/13 | 11/13 |
| 死亡数字正确 | 3/13 | 11/13 |
| 魔力数字正确(损失 + 恢复) | 8/11 | 11/11 |
| 法术名正确 | 5/5 | 5/5 |
| 有日志页整页正确 | 0/3 | 1/3 |
| 无日志负样本整页正确 | 3/3 | 3/3 |
与旧评分器相比,两组输出的所有总数和已知字段计数变化都是 0;40 条日志框全部保持匹配,没有发现 claim 泄漏。
唯一变化:长滚动日志页的整页状态在两组中都从旧的“未知”改为“错误”。基线在这一页有多条已知行被拒绝;候选仍拒绝第 03、20 行。候选第 28 行的参考文字不确定,这一点保留:它不计入整行精度分母,所以不能说候选 37/37 正确。
保存输出的评分 CPU 时间约 0.623 ms(基线)和 0.831 ms(候选),这不是 OCR 延迟。
失败与负结果
- 旧评分器有上述结构性漏洞,并在 1 个合成案例上崩溃。
- 修正后没有任何成绩变好;候选仍拒绝 3 行,长滚动页和洞穴页仍不是整页正确。
局限
- 全部是已暴露开发样本上的软件复核,不是盲测。
- 评分只验证标注过的字面字段,不能证明游戏身份、归属、规则或图像真值;三遍读取来自同一引擎,共同误读仍可能被当成一致。
- 修正版评分规则尚未经过独立验收,历史分数仍保留;生产和公开接口未改。
结论与下一步
旧评分器确有会虚增或误标的漏洞,但在这批真实保存输出上恰好没有造成分数偏差:基线 24/39、候选 36/39 的结论在更严格的口径下仍成立,只是长滚动页的状态从“未知”更正为“错误”。下一步是由独立审阅者核对协议、合成要求和另存结果,再决定是否采用新评分语义。
Comments