这是我在做本地化部署的客服自动回复工具时踩到的坑。整套链路跑在内网一台机器上OCR 读微信聊天窗口顶部的标题栏拿会话对象的名字做归属校验名字对得上机器人才允许代客服回消息。日均处理几百场会话上线半个多月一直平稳直到有客服反馈李文宇这个会话开始漏回消息机器人像没看见一样。现象一个字的偏差整场会话被拒日志很快给出线索OCR 读出的标题栏文本是李文字而联系人真名是李文宇。归属校验按字符串精确匹配一字之差整场会话被判为归属不明自动回复直接跳过。这是当初定的保守策略——宁可漏回不能回错漏回可以报警补回回错没法撤回。先排除了名字库写错和客服改昵称两种可能名字库里李文宇三个字一字不差联系人资料页显示的昵称也没变过。再让那位客服把窗口拉大、切换几次会话再切回来误读依旧。问题收敛到 OCR 环节标题栏里的宇被认成了字。有意思的是这不是整条链路第一次出小字问题。之前手机端一些字号更小的界面元素也被读错过当时靠正则白名单兜住了没往深处查。这次不一样——李文字这个串在名字库里恰好也存在说明字符串层面完全合法白名单兜不住这种错必须把根因挖出来。对照实验合成 36 个样本锁定崩法要回答的问题是这是偶发抖动还是系统性误读我写了个脚本复现标题栏的截图条件合成 36 个样本做对照——不同背景色、不同抗锯齿渲染相位、成对出现宇和字的联系人名还混了几个形近组做陪跑比如峰/锋喂给同一套 OCR 流程。之所以用合成样本而不是直接截图是因为截图条件不受控窗口大小一变、DPI 一变字渲染出来就不一样变量太多。合成可以一次只动一个变量。结果很干净带字的样本识别恒对一次没错过原生分辨率档里带宇的样本识别分落在 79 一档错例集中崩向字陪跑的峰/锋组在同样条件下表现正常说明不是所有小字都糊问题特定在宇/字这组笔画差异上同一样本连续截多张图只改截图的取像相位误读跟着相位漂移——说明问题不在内容而在像素级采样。顺带把 det 检测框和 rec 裁剪两路的日志对了一遍det 框定位标题栏文字没有偏裁剪区域也踩得准两路单看都正常。这排除了框漂移把嫌疑集中到识别环节本身。三层根因小字、偏置、没放大先说字本身墨芯只有 12px。标题栏字号 12px宇和字的差别只在宝盖头底下那几笔——宇是于字是子而于和子在 12px 下本来就长得像。低分辨率下这几笔的墨芯加起来不到三个像素宽抗锯齿一糊两个字形在像素层面几乎收敛成同一个图案。相位差实验正好从侧面印证换一个采样相位糊的位置就变。可以说这组字天生就踩在小字号识别的坑底。再看模型不对称的崩向。识别日志里有个很典型的不对称把宇认成字的错例置信度是 0.84而真字样本的置信度恒为 1.000。原因不难猜——rec 模型的训练语料里字的出现频率远高于宇笔画证据不足时模型塌向先验更强的那个字。好认的字置信度拉满难认的字反而给出一个看着还不错的分数。0.84 搭配 1.000想靠置信度阈值拦截两头都拦不住阈值设高0.84 的错例漏过去设低一堆本该信的正确结果被反复打回人工。这个不对称后来成了我们审核 OCR 结果时的一条经验分数高不等于可信高频字的满分尤其值得多看一眼。后看链路裁剪框恒取原始分辨率。这是真正可以在工程上补的洞rec 识别阶段直接按 det 框在原始截图上抠图整条链路没有一处对小字区域做过放大或插值12px 的字就以 12px 的原始状态进了识别网络。前两层是先天条件——字形本身的像素就不够模型先验又偏向错的那一边这一层是流程缺陷属于本可以做但没做的那类。三层叠在一起误读就成了必然事件只是等一个高频字的会话来触发。修复窄带 2× 放大坐标 ÷2 映射回去修复思路很克制不换引擎、不重训模型在 det 之后、rec 之前对高度低于阈值的窄带区域做 2× 放大识别完把坐标除回去。插值选 Triangle双线性对小字够用开销也小。为什么不是 4×试过收益递减还会放大噪点推理耗时也上去了2× 已经能把宇和字的墨芯拉开可分的距离。# 伪代码窄带区域 2x 放大 坐标回映 UPSCALE 2 MIN_LINE_H 20 # 像素低于该高度按小字窄带处理 def recognize_regions(frame, det_boxes): results [] for x, y, w, h in det_boxes: crop frame[y:yh, x:xw] if h MIN_LINE_H: big resize(crop, (w*UPSCALE, h*UPSCALE), interpTRIANGLE) # Triangle 双线性插值 text, conf, rel rec(big) # 在放大图上识别 rel [(px/UPSCALE, py/UPSCALE) # 坐标 ÷2 映射回原图 for px, py in rel] else: text, conf, rel rec(crop) results.append(((x, y, w, h), text, conf, rel)) return results改完重跑那 36 个样本宇组识别全对字组保持恒对没有引入新的回归。同时加了兜底放大识别链路一旦异常裁剪越界、rec 超时之类降级为跳过该会话的自动回复并记日志告警不吞异常、不装无事发生——漏回可以补装作正常才是大问题。复盘三条经验警惕满分置信度。不对称崩向意味着低置信度过滤的盲区恰好落在高频字上——难字给 0.84高频字给 1.000真正该怀疑的反而分数漂亮。校验环节后来补了形近字表宇/字、已/己、土/士一类匹配时把表内字视为等价候选再结合备注、昵称等上下文字段二次确认。det 和 rec 不是一条链路。定位准不代表识别拿到的图是对的中间的裁剪与缩放策略值得单独审计一遍。小字问题优先在图像侧解决。与其在文本后处理里猜错字不如在进识别网络之前把像素喂够。这类会话归属问题在客服自动化里很典型名字错一个字校验链路就整段失效。之前整理过两篇相关文章一篇讲微信自动回复机器人如何像真人一样接住会话一篇拆微信 AI 客服的成本构成一并附在文末。参考文章微信自动回复机器人像真人一样接住会话的落地方案微信 AI 客服到底要花多少钱一张报价拆解表