
值守链路有个隐蔽的部件AI 自动回复本身在后台跑得好好的但当运营同事在别的窗口干活时平台来了新消息会弹一个通知弹窗点它应该把客服接待台弹到最前面让人能一眼看到客户说了什么。这个点弹窗唤起接待台的动作我们在日志里翻账时发现成功记录九天只有一条——不是没人点运营每天都在点是判据写错了点了也白点。这篇拆这个案例的三层问题每一层都是真机取证才挖出来的日志上看不出任何异常。## 判据要求前台就是接待台而拿前台的是弹窗自己唤醒检测器的判据写的是通知弹窗被点击时拿前台窗口句柄跟接待台窗口句柄比对相等才算一次有效唤起。听起来天经地义——点了弹窗接待台到前台来了前台当然是接待台。实际用真机窗口枚举一看完全不是这么回事点击弹窗后拿到前台的恰恰是通知弹窗自己它的窗口类名是 Qt 的工具窗形态而接待台本体还原封不动地躺在负三万多的坐标上——那是 Windows 里最小化窗口的标准藏身位置。也就是说运营每点一次弹窗系统里真实发生的流程是弹窗接了点击、开始拉起接待台、然后自己退场而我们的检测器只在点击瞬间看了一眼前台看到的是弹窗不是接待台判据不成立记一次失败。用一个瞬时的、必然不相等的比对去做判定九天一条成功记录就是必然结果。修法是把判据从前台必须是接待台本体放宽为厂商通知弹窗拿到了前台窗类后缀匹配工具窗形态再加同进程、同程序名两条兜底。这里有个工程细节值得单独记之所以用后缀匹配是因为完整类名里嵌着界面框架的版本号平台一升级版本号就变整串匹配会静默失效——这正是当年那一条成功记录之后九天零记录的另一种可能成因。所以我们在监控里加了窗口类名采集升级导致匹配失效时会先在日志里看到形态变化而不是又一次静默九天。## 闸门的位置也错了把老板在用电脑当成了让位状态第二个问题更冤检测器外面包着一层是否需要让位的判断原始逻辑是处于让位状态就不跑检测。而让位状态的定义里包含了宿主程序被最小化——这恰好是运营的常态把接待台最小化让 AI 在后台接客自己去干别的。于是出现了一个荒诞的局面越是需要弹窗唤醒的时刻接待台最小化、人在别的窗口干活检测器越是不在运行。修法是把让位判断收窄到真正的模态场景——有模态对话框挡着时确实不该抢焦点其余情况检测器常开。这条的教训是闸门的位置和闸门的条件要分开审。一层套一层的判断外层的正常分支可能把内层的核心路径整个短路掉而且每一层单看都正常工作日志里连一行异常都没有。这种问题靠看代码 review 很难发现靠成功率的分母对不上才能暴露——九天一条分母是运营的实际点击次数这个对不上本身就是最扎眼的异常信号。## 分不清人点的还是弹的人手闸与抢屏护栏判据放宽之后新的问题立刻冒出来唤醒变得太勤了。平台来新消息时自己会弹通知如果把这个系统行为也算成用户点了弹窗接待台每来一条消息就跳出一次直接抢屏——这个形状我们吃过实打实的亏同类产品因为频繁抢屏被用户当成流氓软件强制杀掉过。用户要的是我在前台时别打扰我、消息来了我能看见不是窗口自己跳出来。所以加了一道人手闸读系统的输入年龄——最后一次键鼠输入距今的时间。只有两秒以内才认定这一下是真人点的弹窗读不到输入信息时一律不认宁可漏一次唤醒不可误抢一次屏两个方向的错误我们明确选了保守的那边。这个闸还顺带解决了一个归因问题真弹窗和平台自弹的通知在系统日志里长得一样只有输入年龄能区分。配套的反向护栏还有两条都是修复过程中真机联调抓出来的。一是唤起接待台时如果它正最小化要先恢复窗口并且等恢复动作真正完成再读界面布局——顺序反过来读到的是最小化状态下的残留坐标拿它去计算落点会把接待台挪到屏幕外三万像素的地方窗口唤醒成功了但人在屏幕上看不见。二是如果接待台已经在目标店铺视图上也不能直接跳过——它此刻多半是最小化的跳过就唤而不醒必须重新走一遍进入视图的流程让界面和内部状态一起回来。## 把验收钉死五类用例代替手感修完之后我们把验收固定成五类用例每次动唤醒逻辑都跑一遍厂商弹窗类必中框架版本号变化后仍中接待台本体和普通浏览器窗口拿到前台不中防止无关窗口触发自家程序自己的窗口不中防止自己唤醒自己自锁在循环里人手闸的输入年龄边界跑七组取值。这套用例后来又抓出过一次回归——有人把后缀匹配改回整串匹配版本号一变全套失效用例当场红。## 小结这个案例的三层问题有一个共同的根源判据是照着代码里应该发生什么写的不是照着真机上实际发生什么写的。窗口归属、最小化坐标、系统输入年龄全是真机枚举一跑就现形的事实而日志里的每一层都显得无辜。AI 自动回复可以全年无休地在后台跑但运营能不能及时看到客户的消息靠的是这些不起眼的窗口管理细节——它们坏了不会报错只会让整个值守安静地失效。## 参考文章- 微信客服自动回复怎么设置入口与规则详解- 微信客服常见问题AI自动应答知识库配置方法