台账说明、历史版本与统计口径
09-30 历史核对记录(以下包与心跳为当时快照);10-12 台账追加 R1~R5 五项和 W-94 城市串线根因,10-13 追加 W-95 旧会话误触发、W-96 已签客户误回及 W-97 置顶占满首屏时找不到未读,当时总计 96 条(10-19 起 104 条);客户身份和原话均按公开规则脱敏。
电脑端正式包:1.24.4+0e44881(09-29 18:06,https://github.com/Akke-AI/wecom-chat/pull/1781)。
装机心跳:09-30 09:51,运营人员、运营人员、运营人员均在线,回报 1.24.1+4b69cb9;小夏是安卓设备,不计入本电脑台账。虽已有 1.24.4 包,三台当前心跳尚未显示安装该包。
服务端:/api/status 回报 88c1f5d、ok=true;最新 main 是 b3e1382。客户端回报的旧版本和最新正式包分开记录。
验收:合并、打包或心跳达标都不能代替问题场景复测。下方旧现场说明保留问题形态,客户身份及聊天原文已删改;本轮没有逐条真机复现。W-12 的身份键是否完全统一仍待核实。
重点:首触/召回漏发或错认、重复配图、假红点与扫描卡住、人工待核对误报、客户已成交后仍触达、黑名单截图外发风险、昵称手机号身份对不上。
W-17 / W-33 为已移出的安卓问题,空号保留。实时异常列表随日期筛选;台账是人工维护的编号快照。
10-12 新增:R1 → W-89 · R2 → W-90 · R3 → W-91 · R4 → W-92 · R5 → W-93。这五项均保留具体放行原因与验收缺口。另增 W-94:表情差异导致会话分裂,厦门报价串成泉州;10-13 登记 W-95:巡检切旧会话误触发回复、W-96:已签客户收到历史消息的自动回复及 W-97:置顶会话占满首屏,新消息一小时没人回;W-98:40方投影未被识别,正常计算的报价被拦;10-14 登记 W-99:底部灰字让两个读屏模型对不上,召回整天被拦;10-16 登记 W-100:召回逐字打字被输入法打乱,客户收到重复标点、缺字的乱码、W-102:读屏把客户备注名拼进消息,误推「没进代回」漏回卡;10-18 登记 W-103:自动升级重启后程序没自己起来,两台电脑断线到手动启动;10-19 补登 W-101:首屏有置顶折叠栏时,紧挨着它的客户未读认不出(10-16 现场,已修);10-19 登记 W-104:把几个月前的老联系人当成新好友,发了欢迎语、W-105:没送达的首触没撤记录、W-106:有的对话没同步进系统、W-107:置顶折叠栏有时认不出,没去点开及 W-108:只剩「暂不支持该内容」灰字的老客户,召回一直发不出。
W-01升级装不上,只能重启机器已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 每次升级都要重启,销售当天的活被打断;更糟的是装不上就意味着所有客户端修复都下不去——这条是其他所有(原始聊天表述已脱敏)问题的上游。
- 根因
- 占着安装目录的就是
start.bat 自己:老版本写 cd /d "%~dp0",让那个 cmd 的工作目录就是安装根目录 → 窗口开着 = 目录改不了名 = Move-Item 永远失败。设计上靠 run.ps1 交接时 exit 7 让窗口自己关掉来绕开,但只要上一次双击留下的另一个实例还在跑,安装器只等自己 spawn 的那个 pid,就必然卡住。
- 怎么修
- ①
start.bat 不再 cd 进安装目录(改成只 set "HERE=%~dp0",run.ps1 用 $MyInvocation 解析自己路径、从不读 cwd,所以零副作用);② 安装器搬之前先停掉还占着目录的自己人(白名单 cmd/powershell/python/conhost;别人的进程只点名不强杀——销售拿记事本打开 config.txt 也会命中判据,强杀等于把没保存的改动扔了),失败时弹窗带进程名+pid 而不是让人猜;③ 搬不动时兜底逐文件覆盖。
- 查证
cloudpc/windows-weixin/start.bat 与 client/start.bat 现查只有 set "HERE=%~dp0"、没有任何 cd;scripts/build-windows-sidecar.mjs:1771-1791(Get-CimInstance Win32_Process 查谁占着,注释写明(原始聊天表述已脱敏))。
- 进度
- #1479 → 1.18.5
- 真机上
- 要
1.18.5 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
- 09-29 复核
- #1712 / 1.23.3 增加带新版重启前的安装中事件和新版启动后的已安装事件,避免十多分钟无心跳被误认死机。此次未触发任何升级。
W-02升级后机器顶着别人的人设已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 心跳照常、版本照常,服务端看到的是(原始聊天表述已脱敏),没有任何红牌——这类故障最贵的地方是它不报警。排查花了一晚上,中间还因为身份自洽(两边一起被改)绕了一圈。
- 根因
install.ps1 的 Set-Config 里写死了一台机的人设与机器名(#1373,09-17,本意是让那台机免去手动设身份)。它跑在 Merge-Config 之后且无条件执行 → 任何一台机装或升这个包,身份都被改成那一台的,刚带过来的旧身份当场被覆盖。
- 怎么修
Set-Config 只写这次安装重新选过的运营开关(ONLY_ALLOW/ALLOW/DRY_RUN),一个身份键都不碰;预绑定改成打包时注入(--prebind-host=<机器名>,人设 key 由 personaKeyForHost 算出、两边不可能对不上),且只在这份 config.txt 还没有合法人设时才写。
- 查证
- 现查
cloudpc/windows-weixin/ 下搜不到任何写死的 wx_pc_* / salespc-* 赋值;scripts/build-windows-sidecar.mjs:1741 有 Write-ConfigKeys,:2755 有 --prebind-host 只能用于带凭据的已配置包 的守卫。
- 进度
- #1466(09-20 23:24)→ 1.18.3
- 真机上
- 要
1.18.3 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-03话术生成好了却被抢窗口失败耗掉已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 话术已经生成好了,四分钟内被三次(原始聊天表述已脱敏)耗成放弃,客户那条消息就此没有回复——而且日志报的是
retry_exhausted(看起来像大脑答不上),把排查往错方向带。
- 根因
no_foreground 和(原始聊天表述已脱敏)共用同一份 attempts:(原始聊天表述已脱敏)和(原始聊天表述已脱敏)是两种完全不同的失败,却挤在一个预算里。
方向不是(原始聊天表述已脱敏):后台发送 2026-08-24(挪到屏幕外)/ 08-25(隐藏桌面)在真机上试过两种实现都不成立,README 里写死了。所以只能是(原始聊天表述已脱敏)。
- 怎么修
no_foreground / no_foreground_late / row_not_found 走让步语义——只累加 yields、不动 attempts,按 20 秒短退避重排(销售松开鼠标那一刻就能补上);上界从(原始聊天表述已脱敏)改成挂钟(没消耗过 attempts 的给 45 分钟,大脑真失败过一次回到 10 分钟);到点报 retry_expired 而不是 retry_exhausted——两种死法日志上必须分得开。
- 查证
cloudpc/windows-weixin/runner.py:443 RETRY_YIELD_REASONS = frozenset({"no_foreground","no_foreground_late","row_not_found"});:536 有 yields 字段、:2699 task.yields += 1;:439 注释写明 retry_expired 与 retry_exhausted 分开。
- 进度
- #1476 → 1.18.4
- 真机上
- 要
1.18.4 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-04说了发两份协议,图一张没到(召回海报)已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 永久故障伪装成临时故障:任务每轮重领一次、每次占一次销售前台,永远停在 pending,而日志写的是(原始聊天表述已脱敏)——看起来像在等资源,实际上这 5 条永远发不出去。
- 根因
- 不是某台机配错,是默认值与任务契约互相矛盾:全机队默认
AKKE_WEIXIN_MAX_IMAGES=1,而话题一固定要 2 张。端上判据 cfg.max_images < requested → 1 < 2 恒成立。
- 怎么修
- 新常量
RECALL_MAX_POSTERS = 2。召回的图是任务契约,不读 cfg.max_images——那个旋钮管的是代回一轮发几张的前台预算,两件事不该共用一个数。
- 查证
cloudpc/windows-weixin/runner.py:457 RECALL_MAX_POSTERS = 2;:3407 self._fetch_posters(customer, reply, limit=RECALL_MAX_POSTERS)。
- 进度
- #1494(09-21 19:54)→ 1.19.2,打进 1.19.3 包
- 真机上
- 要
1.19.3 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-05矮柜/窗台柜报价少算已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 报低了再往上改,在客户眼里就是涨价,比一开始报准更伤信任——这一条会直接把一个愿意谈的客户谈崩。窗台柜/飘窗柜/地柜/矮柜/电视柜这类本来就不到 1 米高,属于高频场景。
- 根因
- (原始聊天表述已脱敏)这条行业口径没写进计价逻辑,模型拿实际高度算投影面积,把价算穿。
- 怎么修
- 加进报价确定性块(经
{{pricingBlock}} 注入,不是 Langfuse 托管模板,改了直接生效):投影面积 = 长 × 高,高不足 1 米一律按 1 米算,深度不参与计价;只报两个数同样适用;只报一个长度直接按 单价/米 算;单价按客户所在地区那一档取。
写的时候踩到两点:算例不能写死结果(主推档分地区,写死(原始聊天表述已脱敏)只对 568 档成立);不在 prompt 里写错误算式的具体数字——错例会被模型模仿,用例里用 not.toContain("2.784") 钉住。
- 查证
src/lib/llm/pricing.ts:701:(原始聊天表述已脱敏)。
- 进度
- #1489(09-21 10:32)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
- 09-24 复核
- #1656 已合并并包含在线上服务端。本条此前只加文字规则,不能据此认定彻底解决。今天新增尺寸解析,按长 × max(高,1) 计算投影面积,深度不参与计价;发送前核对金额,避免拿房产证面积替代柜子尺寸。代码和测试已随提交加入,真实新会话验收待补。
W-06客户说(原始聊天表述已脱敏)还在推销已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 对已经流失的客户继续推销,是最容易被截图转发出去的那种尴尬;而这道闸命中即永久停掉该客户的全部营销,所以误伤代价远高于漏判——不能为了多拦一点就放宽。
- 根因
COMPLETED_RE 第二支主语只列了(原始聊天表述已脱敏),词表又缺(原始聊天表述已脱敏)→ (原始聊天表述已脱敏)+(原始聊天表述已脱敏)这个最自然的组合落空。(原始聊天表述已脱敏)(原始聊天表述已脱敏)原本是命中的,偏偏最常说的那句漏了。
- 怎么修
- 四种完工说法抽成
DONE_WORDS,同时灌进肯定与否定两条正则(只补一边会让(原始聊天表述已脱敏)被判成已完工、把还没开工的客户永久停掉,所以两条成套改);无主语的(原始聊天表述已脱敏)只认整句((原始聊天表述已脱敏)(原始聊天表述已脱敏)不受影响);(原始聊天表述已脱敏)锚在句尾((原始聊天表述已脱敏)不命中)。每条新规则都配成对反例。
- 查证
src/lib/wecom/churn-signals.ts::27 定义 lost 档((原始聊天表述已脱敏)(原始聊天表述已脱敏)(原始聊天表述已脱敏)),:58 词表,:7 记着案例 3 的原话。
- 进度
- #1485(09-21 07:20)→ 服务端。同一批 24 条真实应答回放:拦截 2/24 → 4/24,新增的正是上面两条,其余判定一条没动。
- 真机上
- 服务端改动,合了就全量生效,三台都在用
- 09-29 复核
- #1694 统一业务终态的停止营销标记并取消召回。业务终态后客户再开口可正常回复,但不恢复首触和召回;删好友/拉黑是另一类停止规则,见 W-65。
W-07认出客户后干等几分钟才拿到任务已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 不是丢任务,是拖慢:本该趁会话开着就发的召回,要等下一拍;等的过程中会话被挤走就得整轮重来。注意:这条不是 09-21 召回损耗的主因——那 33 条任务全都成功派下来了,现场报告里(原始聊天表述已脱敏)这个判断当时下错了。
- 根因
note_verified_title() 核验通过时不清掉 daily mode 的探测退避,列表扫描和任务派发就此脱节。
- 怎么修
- 核验通过即清退避(
_mode_probe_at = 0、next_poll = 0),并打一行 recall_title_verified … poll_now=True 审计。任务通常在同一拍或下一拍(约 8 秒)就到手。
- 查证
cloudpc/windows-weixin/recall_client.py:155 有 recall_title_verified … poll_now=True;回归测试 test_verified_title_bypasses_periodic_mode_probe_backoff 钉住 _mode_probe_at == 0 and next_poll == 0。
- 进度
- #1492(09-21 19:59)→ 1.19.3
- 真机上
- 要
1.19.3 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-08新好友一个都认不出已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 凡是带招呼语加的好友,首触一个都发不出去——而带招呼语加正是主场景。等于首触功能对真实业务完全不生效,装了包也什么都不会发生。
- 根因
- ① 判据要求(原始聊天表述已脱敏),而加好友时随验证请求发的那句招呼语本身就是一条我方气泡;② (原始聊天表述已脱敏)只看文字不看侧别,被判成(原始聊天表述已脱敏)(本期不首触);③ 词表只收了(原始聊天表述已脱敏),而真机三张截图的措辞都是(原始聊天表述已脱敏)。
- 怎么修
- ① 气泡闸改成(原始聊天表述已脱敏)(
customer_spoke()):我方气泡和(原始聊天表述已脱敏)提示都不算客户开口,客户真说了话((原始聊天表述已脱敏))照旧挡死;② 方向判定带侧别:居中系统提示里的第一人称(原始聊天表述已脱敏)= 我方点的通过 → 不首触;对方气泡里的(原始聊天表述已脱敏)= 客户通过了我方请求 → 该首触;③ 词表补上确切写法。
- 查证
cloudpc/windows-weixin/first_touch.py:170 def customer_spoke、:191 def direction_from_view、:167 side == "left" 侧别判定;:61/72/81 三条形态的正则;:219 注释写明(原始聊天表述已脱敏)。
- 进度
- #1491(09-21 18:32)→ 1.19.1,打进 1.19.3 包。后续:#1505(1.20.0)把首触从(原始聊天表述已脱敏)的 P2 干跑升成真发——认出来就问服务端要话术、就在眼前这个会话里发,端上闸是开关
AKKE_WEIXIN_FIRST_TOUCH_SEND(默认开)+ SEND_DAILY_CAP=10(first_touch.py:299 现查)。
- 真机上
- 1.20.0 起首触是真发,所以装上包的两台现在真的会对新好友开口(每天最多 10 位)。
要 1.19.3 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-09召回 70% 卡在发送前回读(23/33)已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这是当天最大的一块损耗。发召回前要从底部往上翻聊天记录、两屏之间还要对得上,翻不动就整条不发 → (原始聊天表述已脱敏)直接变成(原始聊天表述已脱敏)。
但不能简单放宽:2026-09-17 之前只看最底一屏就发,两天里客户说过(原始聊天表述已脱敏)(原始聊天表述已脱敏)的照样收到通用召回。
- 根因
- 回读是安全机制(怕给说过(原始聊天表述已脱敏)的人发通用召回),但它把(原始聊天表述已脱敏)和(原始聊天表述已脱敏)当成了同一件事处理。
- 怎么修
- 兜底的安全边界 = 只对库里从没回过话的客户(
isNeverReplied)——那次事故的客户全是回过话的,这条判据正好把风险那一类整个挡在外面:从没开过口的人,不存在(原始聊天表述已脱敏)。再叠四道闸:只对列表通道 list_topic_one_title_v1 / 先老实重试满 READ_FAILURE_CAP=3 次(本来就要取消的那一次才兜底,重试预算一个字不动)/ 客户资格闸照常过 / 每台机每天封顶 READ_FALLBACK_DAILY_CAP=2(读屏坏掉那天不许变成整天狂发)。话术只用(原始聊天表述已脱敏),不加承接句——没有实读证据就不许说(原始聊天表述已脱敏)这种假装记得的话。
- 查证
src/lib/wecom/windows-recall-service.ts:103 READ_FALLBACK_DAILY_CAP = 2、:104 READ_FALLBACK_MARKER = "read:fallback"、:730 if (!isNeverReplied(...)) return null;单测用源码扫描钉住 isNeverReplied 这道闸不许被删。
- 进度
- #1496(09-22 10:02)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-10同一个人被打两次招呼已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 对刚加上的陌生人连发两条自我介绍,是最直接的(原始聊天表述已脱敏)信号,第一印象就废了。
- 根因
- 服务端唯一的防重复闸是
msgCount > 0 → alreadyGreeted,而 opener 的四条返回路径(门店配置版 / 活动版带价 / 活动版问城市 / 大模型冷开场)一条都不写 messages → 那道闸从来没有生效过:同一位客户每被扫到一次就重新生成并发送一条首触。
- 怎么修
- 四条路径现在都记一条
role=ai / status=sent,与(原始聊天表述已脱敏)同一口径,闸这才有东西可数。落库语义取舍:服务端把正文交给端上时就记。端上万一没打出去,代价是这位客户这轮不打招呼(下轮他开口照样有自动回复接),比重复打招呼轻得多。
- 查证
src/app/api/wecom/chat/reply/route.ts 新增 recordOpenerSent(),向 messages insert role:"ai", status:"sent",并用 fingerprintOf([...,"opener"]) 做 wecom_msgid 幂等键(23505 忽略)。
- 进度
- #1496(09-22 10:02)→ 服务端。这条是首触敢开真发的前提:#1505 明确写(原始聊天表述已脱敏)——端上不记发过谁,靠服务端再被扫到时自己回
alreadyGreeted。
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-11明明知道城市,还问(原始聊天表述已脱敏)已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 多问一句就多一道流失;而且显得没做功课。活动价分 568/598 两档由城市决定,报低了签约要涨价(客户会投诉),报高了直接劝退,所以既不能猜也不该白问。
- 根因
- opener 只看服务端画像里的城市字段,没人去读备注名第一个字——而销售的备注规范第一个字就是地区简称。
- 怎么修
- 新增
campaignFromRemarkProvince:沪苏浙京 → 598 档,闽川津湘等 → 568 档;(原始聊天表述已脱敏)故意判不出(深圳 598、广东其余 568,一个字分不开就不赌),回 null 照旧问城市。价格永远不让模型现编。
- 查证
src/lib/wecom/new-service-group.ts:205 export function campaignFromRemarkProvince;src/app/api/wecom/chat/reply/route.ts:2736 调用处,:2735 注释写明(原始聊天表述已脱敏)回 null 走问城市那条路。单测含(原始聊天表述已脱敏)(原始聊天表述已脱敏)(原始聊天表述已脱敏)。
- 进度
- #1496(09-22 10:02)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-12同一个客户裂成两条会话⚠️ 没验成
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 统计和记忆一起失真:召回效果查出来恒为 0;画像分家(
windows_history 在名字行、客户原话在 hash 行),AI 会反复问已经答过的问题——2026-08-21 同类事故里客户回(原始聊天表述已脱敏)。
- 根因
- 三条腿建会话的身份键形态不同:回复腿上送
conversationIdentity → persona:consult:weixin:<hash>;召回腿只有客户名 → persona:<规范名>。
- 怎么修
- 2026-09-16 已建
src/lib/wecom/weixin-identity-derive.ts(服务端复现客户端哈希)把召回腿和回复腿收敛到同一条会话。
- 查证
- ⚠️ 这条我没验成,结论不敢下死。查 1.19.4 的代码:客户端
weixin_relay.py 的 opener() 确实送了 conversationIdentity,用的是和回复腿同一个 conversation_identity() 哈希函数(#1483 首触上线那天就有);服务端 opener 分支也走 migratePersonalWeixinConversationIdentity + 该 identity 建会话。照代码看首触腿的键已经统一了。但 09-21 现场时间线对不上:首触功能(1.19.0)是当天 17:23 才合的,而裂会话发生在 08:41,那台跑的还是 1.18.5——所以 08:41 那条根本不是首触腿建的,现场报告里(原始聊天表述已脱敏)这个根因判断很可能不准。真正成因要拿那两行 conversations 的 wecom_external_userid 比对才能定。
- 进度
- 收敛工作 2026-09-16 做过一轮;#1502 又堵掉(原始聊天表述已脱敏)这个入口
- 真机上
- 要装 1.19.x/1.20.x 才谈得上验证——三台都没装,真机上无法观察首触腿的行为。⚠️ 优先级提醒:1.20.0 起首触是真发了(不再是干跑),一旦装上包,(原始聊天表述已脱敏)就从统计失真变成真的会对同一个人重复开口——装包前最好先把那两行
conversations 的 wecom_external_userid 比对掉。
⚠️ 两台已经装到 1.21.0 且首触在真发,(原始聊天表述已脱敏)现在是活的风险,不再只是统计失真——建议尽快拿那两行 conversations 的 wecom_external_userid 比对掉。
W-13(原始聊天表述已脱敏)服务端一个字不回已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这 4 位客户从此收不到任何自动回复和召回,而且每轮扫描都要为他们白跑一次、占前台。更要紧的是这条本来被承诺过:2026-09-21 的需求文档白纸黑字写着(原始聊天表述已脱敏),但 #1502 的放宽(
RELAXED_REGION)走的也是 PROVINCES,所以这批人一个都没被救到——09-22 复盘时才逮到。
- 根因
- 两处不合规叠在一起:① (原始聊天表述已脱敏)不是车牌省简称(浙江是(原始聊天表述已脱敏));② 编号只有 5 位,规范是 6 位。
- 怎么修
- 方案已定并提了 PR(#1508):加一张城市简称 → 省简称别名表(端上
CITY_ALIAS / 服务端 TITLE_CITY_ALIAS),在解析之前把开头那个字归一化掉,下游(档位、身份键、召回资格)全部沿用原来那套、不另开分支,原文照样落 CustomerTitle.text。
只收现场真出现过的字(这版就一个(原始聊天表述已脱敏))——凭空补一整张城市表没意义,还会把(原始聊天表述已脱敏)这类既是省又是市的字弄出两套口径;要加新的先去库里数一遍。两个坑都有测试钉住:① 真名不许被改((原始聊天表述已脱敏)不能变成(原始聊天表述已脱敏),后面必须紧跟 ASCII 字母/数字才算地区码——Python 的 str.isalnum() 对汉字也返回 True,第一版就把真名改掉了、单测当场逮住);② 端上和服务端必须同值(新增跨语言守护测试:vitest 直接读 customer_title.py 比对)。
注意这跟(原始聊天表述已脱敏)是两件事——后者(5 位/7 位)仍然刻意继续拒。
- 查证
- 代码里已经解决(09-22 13:17 合入 main):端上
customer_title.py:35 CITY_ALIAS = {"杭": "浙"}、服务端 src/lib/wecom/customer-title.ts:29 TITLE_CITY_ALIAS = { 杭: "浙" },customer_scope() 第一行就调 normalize_city_alias()。真名保护也在(:218 起那段:只动开头一个字,且后面必须紧跟 ASCII 字母数字,注释写明 Python 的 str.isalnum() 对汉字也回 True 的坑)。跨语言守护测试 customer-title.test.ts:103 真的去读 customer_title.py 比对两边同值。
但最后出的那个包 1.20.0 里还没有这段——它是 12:35 构建的,而这条 13:17 才合。
- 进度
- #1508 已合(09-22 13:17,
记录编号已隐藏)→ VERSION 已推到 1.20.2,包正在打、还没记进台账。测试:客户端 154 条、服务端 70 条、Windows 端全量 1431 条通过。
- 真机上
- 服务端那半(
TITLE_CITY_ALIAS)合了就生效;端上 fail-closed 那道闸要装了包才松开,两边都到位这 4 位客户才收得到话。
要 1.20.2 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-14备注只有(原始聊天表述已脱敏)的客户,一个字都收不到回复已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 对照组:安卓同期备注合规率 97.2%,而 Windows 这边 28.7% 的新客户拿不到完整自动化(71.3% 合规 / 23.9% 合规但没城市字母、召回被拦 / 4.8% 完全不合规、一个字不回)。
- 根因
- 身份正则硬要求六位编号,把(原始聊天表述已脱敏)当成(原始聊天表述已脱敏)。
- 怎么修
- 没有六位编号时,只要认得出(原始聊天表述已脱敏)仍作为客户候选;完整身份复核、黑名单状态词、群/公众号闸打开会话后照常生效——放宽的只是(原始聊天表述已脱敏),不是(原始聊天表述已脱敏)。两条红线:
① 编号写错位数(5 位 / 7 位)继续拒——放进来等于拿残缺编号当身份;
② 放宽档解析出的名字不许带编号残片:正则结尾必须用前瞻 (?=$|[^A-Za-z]),消费写法会吃掉紧跟的字符,把 客户备注编码已脱敏张三 解析成名字 2345张三——而名字是会话身份键的来源,名字带残片就又制造一个 W-12。
- 查证
cloudpc/windows-weixin/customer_title.py:35 RELAXED_REGION,结尾确实是前瞻 (?=$|[^A-Za-z]);:212 调用处。测试:test_customer_title.py 154 条、Windows 端全量 1423 条通过。
- 进度
- #1502(09-22 10:26)→ 1.19.4(09-22 10:45 出包)
- 真机上
- 要
1.19.4 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-15说要发效果图/配置清单,图一张没到(代回侧)已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 客户被晾在那里等一张永远不来的图,比不提图更伤——他会认为你在敷衍。同期
POSTER_SENT 4 次而 poster_dropped 15 次,说明掉图是常态不是偶发。
- 根因
- 配图引导语是代码拼在正文里的,图却在后面被个微过滤和执行端张数裁剪掉了——引导语留着、图没了。这类不自然大部分不是模型写出来的,是代码拼出来的。
- 怎么修
- 引导语一律经
attachLeadIn 拼并记下(原始聊天表述已脱敏),发之前按最终真发的图把落空的那几句删掉(dropOrphanMediaLeadIns)。同时 #1476 让服务端按端上(原始聊天表述已脱敏)来配图,从源头少掉图。
- 查证
src/lib/wecom/media-lead-in.ts(dropOrphanMediaLeadIns / MediaLeadIn),在 reply/route.ts 里以 attachLeadIn + mediaLeadIns[] 组装;注释写明(原始聊天表述已脱敏)。
- 进度
- #1497(09-22 01:05)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-16风格选择题答非所问已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 7 次里 0 次踩对,客户会直接判定(原始聊天表述已脱敏)。
- 根因
- 风格选择题这条分支对个微的来话形态普遍判不准。PR 里只记了现象和处置,没有写更深一层的根因,这里不替它补。
- 怎么修
- 个微通道整条不再进风格选择题——不是调阈值,是关掉这条分支。
- 查证
src/app/api/wecom/chat/reply/route.ts 在 quiz 启动条件上加了 privateChannel !== "weixin" &&;quiz 本身(src/lib/llm/style-quiz.ts)没动,企微那条路照常。
- 进度
- #1497(09-22 01:05)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-18回复了,但客户那句没记进库—已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这类记录会让主看板的明细读起来莫名其妙(AI 突然说一句话),也没法核对 AI 回得对不对。目前的处置是把它们从明细、回上率、回复用时、客户消息数里全部剔除,只在本页计数。
- 根因
- 回复服务里有一条补答路径:客户的话被判成和之前说过的重复、又隔了一段时间没回,系统会补答一次,但不把客户这句存进库。
服务端别处(provenance / monitor_events)也不存客户原话,所以缺的那句从库里找不回来;每一条具体是不是走的这条也没留痕迹,分不清。
而且这不只是记录问题:补答时喂给 chatReply 的历史是从库里查的——客户刚追问的那句不落库,模型也看不到,只能对着更早的上下文猜,于是答非所问;而他正是因为上次没答到点上才在重复追问。
- 怎么修
- 已经写好了,在 #1541。
查下来根因是三条补答路被一个布尔挤在一起:freshLines.length === 0 时有三条路会走到补答,都跳过(原始聊天表述已脱敏),但原因完全不同——
· owes_reply(最后一条是客户、我方欠答)→ 那行上一轮已落库,不该再落
· duplicate_insert(插入撞 23505)→ 那行库里已经有了,不该再落
· stale_reask(老话重浮但客户已等 >5 分钟)→ 库里没有这一行,该落
第三条是客户刚刚真的又说了一句,只因为措辞跟历史里某句像被第②道闸滤成(原始聊天表述已脱敏)。而这条路的入口判据本来就写着(原始聊天表述已脱敏)——既然认定他在等,就不该假装他没说过话。
改法:判据抽成纯函数 staleReaskStoredText(path, keptLines)(只有 stale_reask 返回文本);route 里三条路各自记来源,在这条上把剥掉系统提示后的原话落库,幂等两层(落前查 RECENT_DUP_SEC 窗口内同文行 + 23505 不当错误抛)。
- 查证
- 顺带解决了(原始聊天表述已脱敏)这件事:这一行的
wecom_msgid 用独立前缀 wcsr_(普通客户消息是 wc_),以后一条 SQL 就能数出这条路走了多少次(wecom_msgid LIKE 'wcsr_%%')。原来这条路一点痕迹都不留。
测试:纯函数 +6、route 接线 +6(源码扫描);阳性验证——判据改成永远 null 如期红 2 条、落库段整个拿掉如期红 4 条,还原后 76 条复绿;src/lib/wecom + src/app/api/wecom 全量 2658 条通过,tsc / eslint 干净。
- 进度
- #1541 已合(09-23 22:37,
记录编号已隐藏)→ 服务端,已全量生效
- 真机上
- 服务端改动,合了就全量生效,三台都在用
- 09-24 复核
- 补答落库修复已包含在线上服务端;历史缺失原话不能凭空恢复。今日异常徽章为检测结果,需逐条查证,不能仅凭合并或零计数宣称所有孤立回复原因消失。
- 09-29 复核
- #1694 进一步补齐客户端看见、数据库没有的客户历史,使用 wcch_ 指纹去重并记录原因,排除我方回声和系统提示。不是重建从未保存的历史;仍需看异常明细核实。
W-19把微信系统提示当成客户消息,还回了—已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 朋友验证说明客户已经把销售删了,这句回复根本发不出去;被对方拒收也一样。白占一次前台、白烧一次模型调用,还会污染客户消息数和回上率。
- 根因
- 读屏把居中系统提示与客户气泡混在一起取,没有按(原始聊天表述已脱敏)来剔除。
- 怎么修
- 服务端加拦截闸
isWechatSystemNoticeLine(整行等于界面固定长相才算,避免误伤真客户的话);电脑端本机也拦一层。
注意(原始聊天表述已脱敏)不算异常——客户真发了电脑端看不了的东西,AI 请他用手机再发是正常应答。
- 查证
src/lib/wecom/wechat-system-notice.ts:39 isWechatSystemNoticeLine;看板侧 src/lib/weixin-pc-live.ts:21 复用同一判据统计。客户端 cloudpc/windows-weixin/message_kind.py,引入 commit 记录编号已隐藏(#1424)标题写明(原始聊天表述已脱敏)。
- 进度
- 服务端 09-18 17:35 上线;客户端本机拦从 1.14.56 起
- 真机上
- 服务端那道闸 09-18 17:35 就上线了、对三台都生效;下面这行说的是端上本机那层。
要 1.14.56 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-20系统提示被记成了客户消息(没回)—已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 不会发出错误回复,但会让主看板的客户消息数、回上率不准——分母被灌水,所以也挪到异常里不进统计。
- 根因
- 同 W-19。
- 怎么修
- 同 W-19,服务端同一道闸拦掉。
- 查证
- 同 W-19(
isWechatSystemNoticeLine)。
- 进度
- 服务端已拦
- 真机上
- 同
W-19:服务端已拦,下面是端上那层。
要 1.14.56 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-21销售自己点开过的会话,那条消息从此没人回已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这是静默漏回:库里看不出异常、看板上也不体现(它根本没被当成待回消息),只有客户自己知道(原始聊天表述已脱敏)。而销售顺手点开会话是每天都在发生的事。
- 根因
- 未读判定单一依赖红点,红点被人工点掉之后就没有第二条路把这条消息捞回来。
- 怎么修
- 列表轮询本来就逐行点开,顺手判一下(原始聊天表述已脱敏)。
像素判只决定(原始聊天表述已脱敏),不决定发不发——bubble_side 这个模块自己的纪律就是(原始聊天表述已脱敏),放行仍由完整读屏 + 身份闸 + 防自回决定,和红点那条路一模一样;同一条客户消息也不会被回两次(服务端按内容指纹幂等)。
两道成本闸:同一位客户 24h 最多补一次、每台机每天最多 10 条——(原始聊天表述已脱敏)在真实列表里非常常见(客户说完、销售还没回本来就是这形状),不加闸的话 200 行的列表每扫一圈要多花几十次读屏。
- 查证
- #1505 的第二部分,落在
cloudpc/windows-weixin/ 的列表轮询里;测试 2 条(每客户冷却 + 每日上限、台账不无限长)。
- 进度
- #1505(09-22 12:33)→ 1.20.0(09-22 12:41 出包)
- 真机上
- 要
1.20.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-22带来源前缀的备注((原始聊天表述已脱敏))整条进不来已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 跟 W-14 同一类后果(fail-closed 一个字不回),只是触发条件不同——#1502 的放宽没覆盖到它,所以修了 W-14 也救不了这批。
- 根因
- 放宽正则要求地区字前面不是汉字,而来源标记(原始聊天表述已脱敏)本身就是汉字,于是整条被挡在门外。
- 怎么修
- 把
SOURCE_PREFIXES 单列进放宽正则。
- 查证
- 代码里已经解决:
customer_title.py:37 SOURCE_PREFIXES = "抖",放宽正则 :50-51 现在是 (?:^|[{SOURCE_PREFIXES}]|[^一-鿿])——来源前缀已经作为合法开头放进去了。
- 进度
- #1508 已合(09-22 13:17,与 W-13 同一条 PR)→ 随 1.20.2 出包
- 真机上
- 要
1.20.2 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-23双裁剪方向对不上就整轮当读不出,客户消息没人接已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
guards.dual_crop_verdict 对这种形态一律不猜,runner 收到 conflict 只记证据、累 streak,本轮当(原始聊天表述已脱敏)返回——那条客户消息就此没人接。
- 根因
- (原始聊天表述已脱敏)被一视同仁地当成读屏失败,而其中一种形态(
std=right / ext=left)恰恰意味着新消息可能刚出现,两张图拍的是不同时刻。
- 怎么修
- 只对
std=right / ext=left 这一种形态重抓一张图、重跑一次双裁剪,救回来就继续往下走,第二次还冲突才落回原来的 fail-closed。不抢前台(只 capture(hwnd),不激活不点不滚——销售在用这台电脑);同一帧只重读一次;streak 语义不变(一轮仍只 +1,不会提前触发重校准);日志可分辨 dual_crop_reread / _recovered / _conflict,第一次的冲突照旧计数——冲突发生率不该被重读掩盖。
- 查证
- #1512 第 1 节,改
runner 的双裁剪路径,有独立测试。
- 进度
- #1512 已合(09-22 15:52)→ 1.21.0(15:58 出包)
- 真机上
- 要
1.21.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-24备注写得完全规范的新好友,一条招呼都收不到已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 跟
W-08 是一对:W-08 修的是(原始聊天表述已脱敏),这条是备注规范的反而漏掉——两头堵上才算真的覆盖新好友。
- 根因
probe_first_touch 原来只挂在 walk_tick 里(原始聊天表述已脱敏)的拒绝分支。备注规范的新好友走 note_verified_title 进了回复通道,根本不经过那个分支。
- 怎么修
- 在(原始聊天表述已脱敏)分支补一条并行的首触腿。判据全部复用现成零件(
customer_spoke 为空 + 尾巴命中(原始聊天表述已脱敏)词表 + 方向 we_added),没有新增任何文案或正则。老客户由 customer_spoke() 挡住不会被重新首触;探测 24h 去重、PROBE_DAILY_CAP、SEND_DAILY_CAP 新旧两条腿共用同一份台账;前置一道免费的像素预筛(最后一块内容贴右边=我方已开口)省掉那次 VL 调用。
- 查证
- #1512 第 2 节。
- 进度
- #1512 已合 → 1.21.0
- 真机上
- 要
1.21.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-25把(原始聊天表述已脱敏)当客户提问,答非所问已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这是
W-21(红点补回)带出来的新伤口——修一个漏回,制造一个乱回。新好友的第一印象直接毁掉。
- 根因
- 补回判据只看(原始聊天表述已脱敏),没有排除系统通知气泡。
- 怎么修
- 补回之前先问
friend_notice_tail():是通知 → 让给首触通道、不记 backfill 账;读屏没读出来 → 同样不补不记账。三态:成立 / 不成立 / 没验成——没读出来不算(原始聊天表述已脱敏)。
- 查证
- #1512 第 3 节。
- 进度
- #1512 已合 → 1.21.0
- 真机上
- 要
1.21.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-2611 种跳过原因被塌成同一个(原始聊天表述已脱敏),排障看不出真因已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 排障被带偏:日志上看全是(原始聊天表述已脱敏),而真因可能是 11 种里的任何一种。这类(原始聊天表述已脱敏)的 bug 不直接伤客户,但会让后面每一次排查都多花几倍时间。
- 根因
weixin_relay.py 只读 skipReason / reason 两个字段,而 route.ts 有 11 条早退把原因字符串装在 skipped 字段里——读不到就落到默认值 duplicate。
- 怎么修
- 客户端把
skipped 也读上,原因照实记。
- 查证
- #1512 的(原始聊天表述已脱敏)一节。
- 进度
- #1512 已合 → 1.21.0
- 真机上
- 要
1.21.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-27编造展厅、再编门牌号——客户(原始聊天表述已脱敏)已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 编门牌号比编场所名更伤:场所名客户一搜就知道不存在,门牌号客户会真的开车跑一趟。线上占比:个微 275 条里 27 条(9.8%)编门店/展厅。
- 根因
- 根因不是规则没写够(铁律补了两轮都没挡住),而是(原始聊天表述已脱敏)本来是查表任务,却交给了一个概率模型去遵守。
更深一层(#1528 查出来的):确定性层只在(原始聊天表述已脱敏)时说话——厦门那轮 prompt 顶部有整块断言(原始聊天表述已脱敏)+ 地址原文照抄,模型不用判断;杭州那轮探针实测(原始聊天表述已脱敏),结论要模型自己推,而推理正是它会错的地方。加重因素两条:同一个 prompt 里 buildServicePointDirectory 正面列着杭州(59 个网络服务点之一)→(原始聊天表述已脱敏)的语义塌缩;而且它没有(原始聊天表述已脱敏)这个退出口(门店块两头堵死:一边要求干脆答有别打太极,一边禁(原始聊天表述已脱敏))。
- 怎么修
- 三层一起上:① 事后拦截(#1523)铁律补展厅/体验店/样板间 + 投递闸过滤;② 改成查表(#1524)新增
store-address-guard.ts:listedStoreCities() 从清单解析城市白名单(括注别名(原始聊天表述已脱敏)也算),stripUnlistedStoreAddress() 按整句判不按小句判;③ 治因(#1528)城市已知但不在清单时也钉一块确定性断言——把不对称补平,模型不再需要自己推(原始聊天表述已脱敏)。
- 查证
src/lib/wecom/store-address-guard.ts(纯函数 + 8 条单测);buildStoreDirectory 的清单外分支。
- 进度
- #1523 → #1524 → #1528,三条都已合(09-22 17:18 / 17:38 / 18:03)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
- 09-24 复核
- #1631 / #1636 补直辖市备注识别,删除默认引向漳州的指令,拦截与客户城市不符的门店断言。#1621 / #1625 增加问路卡并纠正馆号。均已包含在线上服务端;保留原编号,不把后续复发当成新编号。
- 09-29 复核
- #1703 / #1705 按客户明确城市选择门店或服务点、区分资料未核实和不在清单,并补门店营业时间与指代澄清。当前代码优先客户原话,其次画像,再用可靠备注;前缀与尾部冲突不取。不能继续把旧版仅认直辖市当现状。
- 09-30 复核
- #1703、#1705、#1743 等继续修正客户城市到门店地址的选择。服务端已部署 88c1f5d;本轮未复测全部城市组合。
W-28承诺出方案 → 30 分钟走完(原始聊天表述已脱敏)已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 真实流程是先到店看样柜 → 下定(2000,算进总价)→ 才排设计师量尺出方案,这条通道根本兑现不了。线上占比:个微 275 条里 8 条(2.9%)。
- 根因
- 【承诺纪律】里本来就有(原始聊天表述已脱敏),但整块规则只覆盖发图和时限,没覆盖(原始聊天表述已脱敏)这个词。
- 怎么修
- 承诺纪律新增硬禁令 + 正面替代话术——禁令必配出口,否则模型换个说法继续编;投递闸补 clause 过滤(出方案承诺 /(原始聊天表述已脱敏)一律剥掉)。#1530 又把它提成
stripPlanPromise,企微也过。
- 查证
src/lib/wecom/reply-fact-guards.ts 的 stripPlanPromise。
- 进度
- #1523(17:18)+ #1530(18:32)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-29连着三轮说(原始聊天表述已脱敏),客户收到 0、0、1 张已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 跟
W-15 同族但更难缠:W-15 是(原始聊天表述已脱敏),这条是说了具体数字而实发对不上——客户会数。
- 根因
- 张数根本不由模型决定:服务端最多挑 3 张,Windows 端
AKKE_WEIXIN_MAX_IMAGES 默认 1(每张占 3~5 秒前台 + 8 秒硬预算),多的发前被裁掉。既有两道闸都拦不住:stripFalseSentClaim 只在(原始聊天表述已脱敏)那轮跑;而且它按逗号切句,(原始聊天表述已脱敏)里承诺动词和图片名词正好被逗号劈开,正则两边都匹配不上。
- 怎么修
- 新增
stripDeclaredImageCount:按(原始聊天表述已脱敏)判,而不是按(原始聊天表述已脱敏)判,并且跨相邻小句看;dropOrphanMediaLeadIns 扩成(原始聊天表述已脱敏)(挑 3 张发 1 张 →(原始聊天表述已脱敏)改(原始聊天表述已脱敏))——此前只处理(原始聊天表述已脱敏),少发不算落空所以一路漏过去;个微硬规补一句:讲内容 ≠ 报数量,(原始聊天表述已脱敏)也是在替系统报数。
- 查证
- #1523 第 ③ 条,回归 fixture 用客户真实原话。
- 进度
- #1523 已合(09-22 17:18)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-30模型答对了,却被闸整条换成硬编码短句已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这是病根里最隐蔽的一条:看线上记录会以为模型很笨,实际上模型已经承认错误并纠正了,是闸把这句整条替换掉了。按这个现象去调 prompt,方向从一开始就是错的。
- 根因
- 个微投递闸命中时整条替换成硬编码短句,而不是只删违规的那一小句。
- 怎么修
- 闸改成只删违规的那一小句,剥空由调用方兜底。#1530 把这条写进新模块的文件头作为明确不推广项:(原始聊天表述已脱敏)——防止下一个人顺手(原始聊天表述已脱敏)。
- 查证
src/lib/wecom/reply-fact-guards.ts 文件头的不推广声明;个微闸内联正则改成调用共享判据。
- 进度
- #1523 第 ④ 条 + #1530 → 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
- 09-29 复核
- #1710 / #1711 已上线:节奏类规则改为生成前指令,事实类规则仍拦;只删没有依据的小句,Windows 回复若删空或仍答非所问则不发并转人工,不再硬塞通用兜底句。保留模型第一版与最终回复的差异记录。
W-31事实性护栏只写在个微分支里,企微一直裸着已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 企微数字漂亮,但那是量小 + 运气,不是有防护。⚠️ 而且(原始聊天表述已脱敏)这段没验成:企微 09-21/09-22 两天零 AI 回复,上表口径只到 09-20(已排除假阴性——企微 09-01~09-20 每天 7~107 条 AI 回复,不是没在跑)。
- 根因
- 判据按(原始聊天表述已脱敏)组织,而不是按(原始聊天表述已脱敏)组织。管事实性的规则与通道无关,被放进通道分支就只保护了一半。
- 怎么修
- 新模块
src/lib/wecom/reply-fact-guards.ts,把三条提成通道无关:stripUnlistedStoreAddress(W-27)· stripFabricatedVenue(不许把(原始聊天表述已脱敏)说成一个能去的地方)· stripPlanPromise(W-28)。route 在两条通道上都过,三条都只删违规的那一小句。明确不推广个微那套整条替换(见 W-30)。
- 查证
src/lib/wecom/reply-fact-guards.ts + store-address-guard.ts;个微闸原先两条内联正则改成调用共享判据,单一事实源。
- 进度
- #1530 已合(09-22 18:32)→ 服务端。顺带修掉 #1523 自己造的一个误伤((原始聊天表述已脱敏)同时出现就删,没分清正当用法)。
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-32召回一关,首触 / 补回 / 备注核对全跟着没了已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 一个开关静默带走三件事:首触、红点被点掉的消息补回(
W-21)、备注核对。而且日志里什么都看不到——不是报错,是那段代码根本不存在。
- 根因
- 同一个根、第二次:1.19.0 已经把首触从(原始聊天表述已脱敏)里摘出来过一次(
walk_mode() 的 docstring 写着(原始聊天表述已脱敏)),但只摘了函数内部的判断,对象构造这一层没动。教训:给既有扫描器加新用途时,gate 要一路查到构造点。
- 怎么修
recall_mode 只决定 client 能干什么(live / poll_enabled),不决定它存不存在。off 档给一个哑 RecallClient,三道闸同时关死:poll_enabled=False、live=False、title_only 默认 False。
- 查证
- #1529 已合(
130bebd)。配套还加了两道防回归:test_wiring_contracts.py 用 AST 断言(原始聊天表述已脱敏),以及心跳 telemetry["walk"] 四个键一个不能少(AKKE_WEIXIN_FIRST_TOUCH 默认 0,以前只能去现场翻 config.txt)。
顺带修掉一个会反噬的配套:ALERT list_walk_off 限定个微通道——企微本就不建 RecallClient、history 恒为 None,不限定就成了一条恒真的告警,比没有告警更糟。
- 进度
- #1529 已合(09-22 20:02)→ 1.22.0
- 真机上
- 要
1.22.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-34语音已经自动转成的字,程序不肯读已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 销售机明明开着(原始聊天表述已脱敏),字就明晃晃印在气泡下面,程序却因为(原始聊天表述已脱敏)整段跳过——客户发的语音等于白发。
- 根因
_maybe_translate_voice 把两件性质完全不同的事共用了函数开头那道(原始聊天表述已脱敏):
· 复用微信已经显示出来的转写 —— 不碰 GUI,成本 0
· 点(原始聊天表述已脱敏)按钮 —— 抢前台 + 点击,占销售的屏幕
而 _reread 的契约是(原始聊天表述已脱敏)(那一刻前台在销售手上,这条不能破),于是连读一眼下方已经转好的字都不肯做。
- 怎么修
- 那道闸从函数开头下移到点击段之前:复用走任何路径(重读/召回/历史/核对)都行;点按钮照旧只在回复路径上,退化成没开自动转写时的兜底。配套
_reread 改回返回 (读数, 重抓的那张图)(拿第一次那张没画完的图去找按钮,找的正是当初走到重读的原因);主流程在重读外面补一次点击尝试——补在里面就破了它 0 秒前台的契约;已拿到转写时会被(原始聊天表述已脱敏)挡掉,不会多点一下。
- 查证
- #1529 第 ③ 条。测试
test_voice_transcript.py +3,含阴性对照(没有已显示的转写时仍然不猜不点)。
- 进度
- #1529 已合(09-22 20:02)→ 1.22.0。⚠️ 上线前提:那台机微信要开着(原始聊天表述已脱敏);关了会退回(原始聊天表述已脱敏)那条兜底路径,要抢前台、慢一些。
- 真机上
- 要
1.22.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-35认不出(原始聊天表述已脱敏)这种新好友提示已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 判据形状与(原始聊天表述已脱敏)那版完全一样,只是尾巴换了,而词表只认后者 —— 这一类新好友一条首触都发不出去。跟
W-08 / W-24 是同一串:新好友提示每多一种写法,就可能整批漏掉。
- 根因
- 词表按(原始聊天表述已脱敏)收,而微信的收尾句式不止一种。
- 怎么修
- 照词表上方那条纪律只补这一种确切写法,尾巴不放开:
r'^你已添加了.{1,40}?[,,]?以上是打招呼的消息$'。
为什么不收宽松式(^你已添加了.{1,40}$):(原始聊天表述已脱敏)五个字在客户真话里会出现(单测里就有(原始聊天表述已脱敏))。误判的代价(对已经开过口的人发冷启开场白,客户一眼看出是机器)远大于漏判,所以宁可漏。
- 查证
- #1529 第 ④ 条。测试
test_first_touch.py +4:主场景、标点容错、尾巴不许放开(4 个阴性串)、不绕过(原始聊天表述已脱敏)那道最贵的闸。
- 进度
- #1529 已合 → 1.22.0
- 真机上
- 要
1.22.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-36扫描预算被不花钱的动作烧光,一天只扫到第 11 行已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 预算本来是为了给(原始聊天表述已脱敏)封顶,结果被一堆不花钱的动作提前吃光,列表一天只扫到第 11 行——后面的客户根本轮不到。
- 根因
- 扣预算的地方不分(原始聊天表述已脱敏),一律计数。
- 怎么修
FREE_SCAN_STATUSES 白名单 + mark_scan 退款。
⚠️ 刻意写成退款而不是(原始聊天表述已脱敏):漏登记一个花钱分支时,前者只是多扣一次(保守),后者会让预算失效、真烧 token(危险)。两种写法在正常情况下等价,差别只在写错的时候——要选错了也不出事的那一种。
- 查证
- #1529 第 ⑤ 条。测试
list_walk +4,含阴性对照(不许把花钱状态混进免费白名单)。
- 进度
- #1529 已合 → 1.22.0
- 真机上
- 要
1.22.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-37(原始聊天表述已脱敏)这条保护,在真实预算下永远触发不了已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 列表一旦往下走,当天再也回不到顶部。而红点扫描只看当前这一屏,顶部的新消息全在视野外——微信的新消息恰恰排在最上面。这类 bug 最阴的地方:代码里那条保护看起来是有的,review 也会觉得没问题,只有把它和预算放在一起算才知道它是死的。
- 根因
- 阈值是按(原始聊天表述已脱敏)拍的,没有跟当天的扫描预算对齐。
- 怎么修
MAX_PAGES 60 → 4。并加一条数值不变式测试:MAX_PAGES 必须在预算内够得着——以后谁改预算或改页数,对不上就红。
- 查证
- #1529 第 ⑥ 条。
- 进度
- #1529 已合 → 1.22.0
- 真机上
- 要
1.22.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-38走列表轮询的机器,却拿不到列表轮询的预算已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 跟
W-36 叠在一起:一个把预算烧光,一个一开始就没给够。结果就是列表扫描形同虚设。
- 根因
- 提到 100 的条件原先是(原始聊天表述已脱敏)——拿存储文件名当功能开关。而列表轮询从 1.18.0 起两档都在跑,文件名叫别的那一档就被漏掉了。
- 怎么修
- 判据改成(原始聊天表述已脱敏)这件事本身;不扫就不给;
config.txt 里显式配过的尊重配置。
- 查证
- #1529 第 ⑦ 条。测试含预算判据四档。
- 进度
- #1529 已合 → 1.22.0
- 真机上
- 要
1.22.0 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-39(原始聊天表述已脱敏)被读成客户说的话,三位新好友一条首触都没发已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这是今天真正拦住那三位新好友的那条。跟
W-08 / W-24 / W-35 连成一串——新好友这条路上每多一种屏幕形态,就可能整批人收不到招呼。
- 根因
- 那行居中灰字不是客户气泡,但读屏把它归到了左侧(客户侧),于是(原始聊天表述已脱敏)这道最贵的闸被它误触发。
- 怎么修
- 新增
_GREETING_SEPARATOR_PATTERNS,只喂给 is_friend_notice_bubble。
⚠️ 刻意与方向词表分开放——这句不含方向信息,混进 _WE_ADDED_PATTERNS 会让它把会话判成某个方向,而方向决定(原始聊天表述已脱敏)。
- 查证
- #1537 第 ① 条。拿现场截图的内容直接跑判据做了对照:线上 1.22.0 ❌(原始聊天表述已脱敏)→ 本 PR ✅ 该首触
we_added。
- 进度
- #1537 已合(09-23 00:30,
记录编号已隐藏)→ 1.22.1(历史版本,现有心跳已超过此版本)
- 真机上
- 要
1.22.1 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-40(原始聊天表述已脱敏)合成一条时,方向判不出来已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 与
W-35 同族——W-35(#1529)补的是(原始聊天表述已脱敏)那种形态,本条补的是(原始聊天表述已脱敏)的渲染。同一个语义,微信有多种画法,每种都要单独喂给词表。
- 根因
- 新好友提示的写法按(原始聊天表述已脱敏)收,而微信的渲染不止一种。
- 怎么修
- 照词表上方那条纪律只补这一种确切写法,尾巴不放开——(原始聊天表述已脱敏)五个字在客户真话里会出现,误判代价(对已经开过口的人发冷启开场白)远大于漏判。
- 查证
- #1537 第 ② 条。
- 进度
- #1537 已合(09-23 00:30)→ 1.22.1(历史版本,现有心跳已超过此版本)
- 真机上
- 要
1.22.1 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-41像素预筛把(原始聊天表述已脱敏)的新好友整批否决掉已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 同一个坑第二次:1.19.1 已经在读屏层修过一次同样的推理错误(我方招呼语不算客户开口),1.20.3 又在像素层把它引了回来。加一层优化就可能把上一层修好的语义重新踩坏。
- 根因
- (原始聊天表述已脱敏)被当成(原始聊天表述已脱敏)的充分条件,而它不是。
- 怎么修
- 拿掉那条否决,成本交给本来就有的
should_probe(24h 去重 + 每天 40 位上限)。原来钉住旧行为的测试改成钉新判据,docstring 写清了当初的权衡、什么变了、为什么翻案。
- 查证
- #1537 第 ③ 条。
⚠️ 这条 PR 自己纠正了一个误判,值得单独记一笔:最初把这道预筛写成了(原始聊天表述已脱敏)的根因,拿现场截图实测后推翻——四种裁剪下 lowest_message_side 全判 left(right_pixels=0),这道预筛根本没拦住它们,真正的根因是 W-39/W-40。修复仍然保留,因为它在另一种形态下确实会误拦。
- 进度
- #1537 已合(09-23 00:30)→ 1.22.1(历史版本,现有心跳已超过此版本)
- 真机上
- 要
1.22.1 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
- 09-30 复核
- 1.23.8(#1729)加入头像边界几何判断和点不掉红点按名跳过,补此前假红点导致召回目标被挤走的问题;销售 A、销售 B、销售 C当前心跳均为 1.24.1。真角标样本有限,仍需观察。
W-42首触不发图——活动话术说了发图,客户只收到一句话已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 跟
W-15 / W-29 同族(说了发图却没图),但这条发生在第一次开口——新好友收到的第一条就是个空头支票。
- 根因
- 服务端
opener 返回的 ReplyResult 本来就带 poster_urls / poster_ids,而 1.20.0 的 send_first_touch 把它们写死成空列表——两侧其实都准备好了,中间那一段没接上。
- 怎么修
- 两侧接上,日志加
posters=N(以后一眼能看出发了几张)。
- 查证
- #1537 第 ④ 条。
- 进度
- #1537 已合(09-23 00:30)→ 1.22.1(历史版本,现有心跳已超过此版本)
- 真机上
- 要
1.22.1 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
- 09-24 复核
- 今天仍有图未发全的记录,不能只按 1.22.1 判定已解决。#1642 / #1643 修素材白名单,#1655 修粘贴等待和回执。见 W-59、W-61;后者要 1.22.14,当前三台心跳均未到。
W-43读历史的预算把列表轮询饿死:才 1/100 就报到达上限已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 跟
W-36(预算被烧光)、W-38(预算给少了)连成第三条——预算这一块今天出了三个不同的毛病,共同后果都是列表扫不动、后面的客户轮不到。
- 根因
- 判定式取
max(pages_today, attempts_today),而 pages_today 只在跨天清零:深度读历史把 pages 用掉之后,列表轮询即使一次没跑也被判成到顶。两件性质不同的活共用一个上限。
- 怎么修
- 两条腿各走各的预算——深度读历史用它自己的页数,列表轮询用它自己的次数。排查这条时被可见性坑了一把,单独记在
W-44。
- 查证
- #1537 第 ⑤ 条。
- 进度
- #1537 已合(09-23 00:30)→ 1.22.1(历史版本,现有心跳已超过此版本)
- 真机上
- 要
1.22.1 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-44静默早退 + 看不见的计数器,把排查引向两个错误方向已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这类问题不直接伤客户,但每一次排障都要多花几倍时间,而且容易得出错误结论、去改本来没坏的东西。
pages_today 更甚:它能独自让扫描停摆,却一直不上报——看板上一切正常,机器其实一行都没扫。
- 根因
- 两条早退路径直接 return,没有写下(原始聊天表述已脱敏);而能决定扫描停不停的计数器没有进心跳。
- 怎么修
- 两条静默早退补上状态(
explicit_target_wait / waiting_gui_restore);pages_today 补进心跳。判据很简单:凡是能让功能停摆的量,都必须能在远端看到——否则只能靠人去现场翻日志。
- 查证
- #1537 第 ⑥ 条。
- 进度
- #1537 已合(09-23 00:30)→ 1.22.1(历史版本,现有心跳已超过此版本)
- 真机上
- 要
1.22.1 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-45一位客户昨天收到首触,今天又收到一模一样的一遍已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 这是
W-10 的续集,而且范围不小:服务端的幂等闸是数 messages 条数的,而(原始聊天表述已脱敏)是 2026-09-21(#1496)才做的 —— 在那之前发出去的每一条,库里都没有记录。当时有 1276 条 09-21 之前建的个微会话带着同一风险:它们在服务端眼里都是(原始聊天表述已脱敏)。
- 根因
- memory 里那条坑的原话就是判据:(原始聊天表述已脱敏)——服务端只能数它自己记下来的东西,而(原始聊天表述已脱敏)这件事发生在端上。
- 怎么修
- 两道闸一起上:
· 端上新台账 firstTouchGreeted,没有 TTL(招呼这件事没有有效期),容量 2000 按时间淘汰;真发出去之后、以及服务端回 alreadyGreeted 时两个时机都记——缺一条就会重复。
· 服务端补一道历史判据:会话建于 09-21 之前 + 零消息 = 判定为发过(会话是首触或回复时才建的,老会话零消息只可能是(原始聊天表述已脱敏))。排在 msgCount 那道之后——顺序就是判据;回包带 legacy_conversation_before_ledger 可审计。
- 查证
- #1537 第 ⑦ 条。测试含服务端 16 条。
- 进度
- #1537 已合(09-23 00:30)→ 1.22.1(历史版本,现有心跳已超过此版本)
- 真机上
- 要
1.22.1 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-46拿不到窗口时还去问服务端要话术,这位新好友从此永远收不到已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 跟
W-45 一样是 W-10(首触落库)带出来的副作用——(原始聊天表述已脱敏)修好了重复发送,却给(原始聊天表述已脱敏)开了一个新口子。一条修复解决一个问题、同时制造另一个,这种账要一起看才算清。
- 根因
- 窗口是本地就能判的前提,不花任何调用,本该排在网络调用之前;其余几道闸本来就在前面,只有这一道漏排了。
- 怎么修
- 把(原始聊天表述已脱敏)这道闸提到问服务端之前。
另:opener 调用抛异常改记成暂时性(TRANSIENT_PREFIX + 15 分钟),不再按 24h 压住——那是(原始聊天表述已脱敏),不是(原始聊天表述已脱敏),两者压根不该用同一个冷却时间。
- 查证
- #1537 第 ⑧ 条。
- 进度
- #1537 已合(09-23 00:30)→ 1.22.1(历史版本,现有心跳已超过此版本)
- 真机上
- 要
1.22.1 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-47客户连发的第二句,永远进不了库已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 跟
W-18 是一对:W-18 是(原始聊天表述已脱敏),这条是(原始聊天表述已脱敏)。后果更重——那句往往是最关键的信息(地点、预算、真实顾虑),而 AI 反复问他已经答过的事。
- 根因
- 三段都有证据(Langfuse 真实 prompt + messages 表):
① 生成那一轮客户端确实没上送第二句——它是在大脑生成的那十几秒里发出来的(读屏之后、贴回复之前),于是我方回复被贴在了第二句下面;
② 之后它永远不会再被当成新消息:客户端只把(原始聊天表述已脱敏)的左侧气泡算本轮(guards.py::inbound_turn_bubbles),它已经在我方气泡上面了;
③ 服务端下一轮其实见到了它(mergeWeixinClientHistory 从 recentMessages 临时塞进 prompt),但没落库——再下一轮滚出 6 条可见窗口就彻底没了,模型只能靠我方自己说过的话去倒推地点。
- 怎么修
- 服务端把这句捡回来、落库,并和本轮合成一轮来答。①那半(第一次回复没看到第二句)是客户端时序,服务端改不了,另行处理。
- 查证
- #1600。根因三段全部标注(原始聊天表述已脱敏),证据是 Langfuse 里那几轮的真实 prompt + messages 表逐条比对,不是读代码推的。
- 进度
- #1600 已合(09-23 22:02)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
- 09-29 复核
- #1694 将客户端 recentMessages 中库里缺失的客户原话补落库,每轮最多四行,排除本轮已入库消息和系统提示;补落但本轮未回复的记录另有告警。服务端已包含,不等于所有漏记路径均已验收。
- 09-30 复核
- #1694 补齐客户端看见、库里没有的客户消息;#1736 去重忽略全半角差异,避免 OCR 标点变化被误补成新客户发言。服务端部署 88c1f5d。
W-48同一个客户被拆成多条会话后,城市面积再问一遍已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 是
W-12(身份键不统一)的下游后果,也是它最直接伤客户的那一面:客户答过的问题被重新问,他会觉得(原始聊天表述已脱敏)。
- 根因
- 会话按身份键建,键一变就是一条新会话,画像事实不跟着走。
- 怎么修
- 让新会话继承同一销售号 + 核心名相同的旧会话里的画像事实。口径逐条钉死:
· 同一销售号:会话 key 冒号前那段;archived- 归回原号;同一人的多台机归成一个号
· 核心名:在 normalizeIdentityName 之上再剥车牌前缀、意向后缀(-不确定-沟通中)、尾部手机号;剥完不足 2 字就不剥,单字名不参与
· 只搬白名单事实(city / area_sqm / budget_range / renovation_stage …),不搬聊天原文、召回状态、墓碑字段;当前会话已有的优先;兄弟之间城市不一致 → 不继承(打 sibling_profile_skipped)
· 只对 channel=weixin——企微咨询台同号下允许同名不同人。
- 查证
- #1546。真库全量验证(428 条个微会话):找到同号同名兄弟 27 条,真正补到新事实 6 条,城市冲突不继承 0,内存全扫与 route 同款查询结论不一致 0。口径由运营 09-23 当场选定,不是开发拍的。
- 进度
- #1546 已合(09-23 14:29)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-49(原始聊天表述已脱敏)绕过了出方案承诺闸,08-01 起漏了 18 条已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
W-28 的续集。判据要求(原始聊天表述已脱敏)后紧跟(原始聊天表述已脱敏),中间插两个字就绕过去了。全量回看 08-01 起 5390 条 AI 回复:漏过 18 条真承诺((原始聊天表述已脱敏)),每一条都是兑现不了的承诺。
- 根因
- 正则写成了紧邻匹配,没给修饰词留位置。禁令类判据最容易死在(原始聊天表述已脱敏)上——这跟
W-27(禁了(原始聊天表述已脱敏)就改说(原始聊天表述已脱敏))是同一个形状。
- 怎么修
PLAN_PROMISE 两条:(原始聊天表述已脱敏)与(原始聊天表述已脱敏)之间允许 ≤4 字修饰。前置条件判据(定金/下定/先到店/才排…)不动。
- 查证
- #1565。全量对比:旧判据命中 29 小句 → 新 47,新增 18 条逐条人眼看全是真承诺,0 误伤。
- 进度
- #1565 已合(09-23 16:28)→ 服务端
- 真机上
- 服务端改动,合了就全量生效,三台都在用
W-50抢不到前台不说原因,红点在视野外就再也找不回已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 前者让
W-03 那类问题每次排查都要去现场翻;后者是真漏回:客户来了消息,扫描器在下面扫,永远看不到。
- 根因
- ① 抢前台失败只记了结果、没记现场;② 未读判定只看当前这一屏的行内红点,没有(原始聊天表述已脱敏)这个信号。
- 怎么修
- ① 把(原始聊天表述已脱敏)记进心跳(
foreground_misses / foreground_diag,不读标题文字,避免带出客户信息);② 以左侧(原始聊天表述已脱敏)图标右上角角标为信号(对齐安卓 ChatListUnread.Watch):列表回顶 → 双击量出来的图标跳未读 → 仍无可回则记下该角标不再追。召回/轮询/首话题三个分支都认(原始聊天表述已脱敏)。真机截图验证不误报头像与(原始聊天表述已脱敏)红点。
顺带:列表逐行点开普通档 60 秒 → 15 秒一步(深度读历史仍 60 秒,每天 100 次上限不变);回读失败时留一份那一屏截图在本机(不上传,40 份上限)。
- 查证
- #1549。新增 20 条测试,阳性验证均如期变红;客户端全量 1499 通过。
- 进度
- #1549 已合(09-23 14:29)→ 1.22.3
- 真机上
- 要
1.22.3 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ✅ —— 仅运营人员、运营人员当前在线;运营人员为历史版本
W-51首触仍会重复打、不带海报、没发出去也记成已发已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 前两条直接伤第一印象;第三条跟
W-46 同形——(原始聊天表述已脱敏),而且看起来一切正常。
- 根因
- 判据只看(原始聊天表述已脱敏),没看(原始聊天表述已脱敏);海报字段接了但没真下载;撤账这条路压根没有。
- 怎么修
- · 首触判据多一条拒绝理由
spoke_after_friend_notice(单向收紧,PROBE_RULES_VERSION 故意不抬——旧判决只会偏保守,理由写在常量旁边);
· 台账键改用稳定键(ledger_key):按人认,不按标题原文认;
· 首触海报真的下载并传给发送(上限 2 张),日志报实际发出去的张数;
· 确定没发出去时调 relay.opener_undelivered 撤回服务端先记下的那条,探测台账记成暂时性;
· 补回(backfill)和(原始聊天表述已脱敏)的重试也要过 24h 旧消息闸。
- 查证
- #1602。这条 PR 有个值得学的做法:它和同事的 #1586 改了同一批文件,作者逐条声明(原始聊天表述已脱敏),并删掉了自己重复实现的那部分(连语义冲突的
bottom_verified 都没叠加)。
- 进度
- #1602 已合(09-23 21:29)→ 1.22.6
- 真机上
- 要
1.22.6 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ❌ —— 运营人员 还没到
- 09-29 复核
- #1723 / 1.23.7 再修三处:回车后文字未变、隔秒复看未变且清空验证成立,才判定没发并撤销首触记录;首触前输入框有残留不再粘贴;名字相差一个字且头像完全一致时才兜底防重。运营人员、运营人员已安装,待观察真实首触;不是凭回车就宣称成功。
- 09-30 复核
- #1723 / #1729 继续处理首触发送未落地、头像假红点与重复点行。#1778 / #1780 更正认人顺序以对齐昵称+手机号身份规则。最新正式包 1.24.4,但三台心跳均停在 1.24.1,故本机身份修复未确认安装。
W-52召回回读失败就整条不发(客户端这半)已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- W-09 当天的账是 33 条召回只发出 2 条、23 条卡在回读。服务端兜底合了之后,这一半不修就等于只修了一半。
- 根因
- 端上把(原始聊天表述已脱敏)当成硬性不发条件,没有区分(原始聊天表述已脱敏)这种安全情形。
- 怎么修
- 服务端回
read_status=failed_fallback 时照发,判据是(原始聊天表述已脱敏);真正拦住(原始聊天表述已脱敏)的那道闸改成发前尾巴复核(recall_verified_tail_mismatch)。
配套:标题召回每台每天可配到 50 条;端上拒发(含底部没核上)计入读失败,满 3 次必停;封顶只数当天已发;召回可发时段 9–21 → 8–23 点。
- 查证
- #1586(1.22.5)+ #1599(1.22.7)+ #1601 / #1551(服务端侧)。
- 进度
- 主体在 1.22.5,1.22.7 补(原始聊天表述已脱敏)
- 真机上
- 要
1.22.5 及以上:运营人员 1.23.7 ✅ · 运营人员 1.23.7 ✅ · 运营人员(离线,09-25 最后心跳)1.22.4 ❌ —— 运营人员 还没到
- 09-29 复核
- #1693 补左右误读复核;#1711 连续读失败改长退避、不取消;#1713 空会话无法确定为新好友也不取消。#1718 首轮零消息必须发送前读屏。旧文中 8–23 点已过时,#1694 当前为 9–22 点、同一客户两天一轮。
W-53回复与召回争抢操作时间,扫描长时间不动已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 回复链占满操作时间;程序自己的鼠标动作又被误算为真人操作。
- 怎么修
- 无人等待回复时让出时间;连续三分钟未获执行机会时给召回与扫描一次机会,空闲判断排除程序注入动作。
- 查证
- #1634;2026-09-24 已合并。
- 进度
- 修复版本 1.22.9;发布台账 1.22.13 已包含。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-29 复核
- #1666 为异步领取等待增加让出扫描的机会;#1712 将认人后等任务的窗口扩至九十秒,并修复让路误清另一位候选和写错扫描游标的问题;#1720 再修领到非眼前客户的循环,见 W-75。
- 09-30 复核
- #1666 / #1720 / #1722 修领任务等待、深扫进度与文件夹识别;#1729 使红点点不掉时按客户名跳过以免挤走召回目标。当前机器版本与正式包有差距,未真机回归。
W-54布局没量准,扫描一直等人工重启已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 推算布局复测间隔长,缺少长期卡住后的自动恢复。
- 怎么修
- 两分钟复测;十分钟仍未量准时重新摆窗口并标定,记录卡住事件。
- 查证
- #1634;2026-09-24 已合并。
- 进度
- 修复版本 1.22.9;发布台账 1.22.13 已包含。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-29 复核
- #1693 / #1715 继续修布局:同窗口尺寸下猜测布局不替换量准布局;量准结果落盘,重启窗口尺寸未变时沿用;主窗句柄固定,卡住时取证而不反复挪窗口。最终后续版本 1.23.4,当前两台在线机已覆盖。
W-55新好友空会话被召回占住,首触接不上已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 正文识别忽略灰字;跳过历史读取时也跳过了新好友保护。
- 怎么修
- 单独识别系统提示与气泡数;空会话交给首触,召回延后且不扣尝试次数。
- 查证
- #1627;2026-09-24 已合并。
- 进度
- 修复版本 1.22.8;发布台账 1.22.13 已包含。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-29 复核
- #1718 发现 allowUnreadSend 绕过了首轮新好友检查:首轮且库内零消息时强制读屏,查库失败也要求读;确认为新好友才交首触。无法确定的新旧关系不再直接取消,见 W-67。
W-56已经在底部,仍被判不在底部而拒发已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 底部判断对长海报不稳定,不是单凭升级版本就能解释。
- 怎么修
- 主模型判不在底部时,同图交更强模型只复核该字段;复核失败保留原判。
- 查证
- #1637;2026-09-24 已合并。
- 进度
- 修复版本 1.22.10;发布台账 1.22.13 已包含。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
W-57头像暖色当红点,反复点同一会话已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 扩框红点规则把头像暖色块当角标;点不掉的旧红点没有及时排除。
- 怎么修
- 收紧扩框面积和形状;连续两次读到我方末句后暂存旧红点,画面变化立即重新判断。
- 查证
- #1637;2026-09-24 已合并。
- 进度
- 修复版本 1.22.10;发布台账 1.22.13 已包含。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
W-58在线却被误判退出登录,暂停半小时已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 瞬时画面直接写入风控记录,没有稳定确认。
- 怎么修
- 登录画面持续六十秒才确认;托盘唤回后的保护期不计入,增加取证和撤销误报入口。
- 查证
- #1642;2026-09-24 已合并。
- 进度
- 修复版本 1.22.11;发布台账 1.22.13 已包含。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-29 复核
- #1693 / 1.23.1 补托盘唤回时主窗句柄仍存在的对照,不把登录形小窗直接当被踢。运营人员、运营人员已覆盖,尚未本轮复现场景。
W-59自家海报被下载白名单拦住已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 原白名单只认部分来源,遗漏自家素材桶和 R2 产品海报。
- 怎么修
- 按主机与路径放行指定素材来源,不放开整个域名;大图粘贴前等比缩小。
- 查证
- #1642 / #1643;2026-09-24 已合并。
- 进度
- 修复版本 1.22.12;发布台账 1.22.13 已包含。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
W-60人工确认没发出,重试仍被旧记录卡住已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 本机旧的待确认投递记录与新生成话术冲突。
- 怎么修
- 仅对人工解除标记、同一客户且旧记录未确认的任务作废旧记录;已确认发送的仍禁止重发。
- 查证
- #1645;2026-09-24 已合并。
- 进度
- 修复版本 1.22.13;发布台账 1.22.13 已包含。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-29 复核
- #1667 / 1.23.0 增加带审计的召回人工核对路径,竞争状态冲突明确报错;是否重试必须依据人工核实结果,本轮未改变任何任务状态。
W-61贴图等待太短,文字到了图片没到已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 粘贴后仅等待约零点三秒,繁忙机器来不及显示大图就被判失败。
- 怎么修
- 分段等待累计约三点五秒,未落地重贴一次;确认落地才回车。文字确认发送而缺图时记录缺图事件;发送确认读屏失败先重读。
- 查证
- #1655;2026-09-24 已合并。
- 进度
- 修复版本 1.22.14;正式包发布本次未核实。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-29 复核
- 上次的正式包未核实结论已过期:#1662 已记录 1.22.15 正式发布,本次最新为 1.23.7,运营人员、运营人员已覆盖本条修复;运营人员离线不能验收。
W-62备注是同行或装修公司,仍被当业主触达已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 可能漏触达、错误触达或让任务长时间停住。
- 根因
- 召回与自动回复的非业主排除词未覆盖同行及合作方。
- 怎么修
- 两端排除装修公司、装饰公司、设计公司、同行、材料商、工长;不使用过宽的单独装修一词。
- 查证
- #1659;2026-09-24 已合并。
- 进度
- 修复版本 1.22.15;正式包发布本次未核实。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-29 复核
- 上次的正式包未核实结论已过期:#1662 已记录 1.22.15 正式发布,本次最新为 1.23.7,运营人员、运营人员已覆盖本条修复;运营人员离线不能验收。
W-63客户回答嗯或好,被当成无意义消息已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 回答不完整或与客户需求不符。
- 根因
- 短答判据没有结合上一轮问题,纯媒体占位挡住上一条有效文字;提议词还可能误中否定表达。
- 怎么修
- 结合我方最近有效文字判断;跳过纯媒体占位,区分行不与行不通,给具体回应。
- 查证
- #1619 / #1622 / #1654;2026-09-24 已合并。
- 进度
- 已包含在服务端运行提交 2d39e66。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
W-64客户给面积问价,仍只追问城市不给数已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 回答不完整或与客户需求不符。
- 根因
- 报价规则与重写指令冲突,还残留全国统一价的旧口径。
- 怎么修
- 按轮加入带地区限定的确定性预算;出门核对是否给数,清除旧统一价话术,已知城市按对应档位。
- 查证
- #1624 / #1646;2026-09-24 已合并。
- 进度
- 已包含在服务端运行提交 2d39e66。 真实场景是否不再复现,仍需行为证据。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-29 复核
- #1657 / #1664 / #1710 / #1711 继续调整:隐含问钱也认问价,清理旧统一价口径,合法计量说明保留。价格覆盖只有算好数字才替换,不能再用单纯问城市覆盖原答案。
W-65删好友或拉黑后,仍继续营销已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 只看失败记录,缺少客户端识别与服务端统一停发。
- 怎么修
- 红叹号和对应删除/拒收提示同时成立才上报;服务端停止营销并取消任务。仅红叹号不能推断拉黑,重新加好友也不直接恢复营销。
- 查证
- #1693 / #1694,已核对合并记录及发布/部署链。
- 进度
- 修复版本 1.23.1,已包含在正式包 1.23.7。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-30 复核
- 服务端现行停止营销规则包含删好友/拉黑,并会取消相应待办;重新加好友本身不会恢复营销。服务端运行提交 88c1f5d 已覆盖相关更改。
W-66客户最后一句还没回,就先发首轮召回已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 首轮资格缺少客户最后一句是否已有人接的判断。
- 怎么修
- 第一轮领取前检查库中末句和读屏里的人工接话证据;仍待回复则返回 customer_waiting。
- 查证
- #1694,已核对合并记录及发布/部署链。
- 进度
- 服务端 e433161 已包含。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-30 复核
- #1694 增加首轮召回前确认客户最后一句是否已有人回复,未回复时暂缓领取;服务端部署 88c1f5d。
W-67没读清历史,被当非好友永久取消召回已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 空会话、搜索歧义和多次读失败混成不可再联系。
- 怎么修
- 空会话未核实保留任务并延后;搜索歧义退避,连续读失败长退避。不自动恢复历史已取消任务,需运营核实。
- 查证
- #1711 / #1713,已核对合并记录及发布/部署链。
- 进度
- 服务端 e433161 已包含。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-30 复核
- #1711 / #1713 将没验清的新旧好友状态保留待核实、避免空会话直接取消;人工核实后如何恢复仍按具体任务操作。
W-68销售已接手或任务已停,仍反复报警已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 缺少人工接话证据排除与逐条提醒节流。
- 怎么修
- 有可靠读屏人工接话证据则排除;停止营销不报未回;按类型、机器和记录节流,待核对首次和满一天各提醒一次。
- 查证
- #1694,已核对合并记录及发布/部署链。
- 进度
- 服务端 e433161 已包含。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
W-69广告里的曝光字样,被误判投诉转人工已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 只命中负面词,没有判断是否指向我方及是否是营销内容。
- 怎么修
- 排除明显营销广告和无需求备注;曝光须指向我方才进入对应转人工判据。
- 查证
- #1694,已核对合并记录及发布/部署链。
- 进度
- 服务端 e433161 已包含。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-30 复核
- #1750 减少同一位客户的未回复提醒重复推送;销售后来已接手的记录会排除。
W-70机器重启后程序不自启,现场无人值守已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 旧入口缺失或指向别的安装目录,单凭配置写入不能证明生效。
- 怎么修
- 增加可选远程自启入口和任务路径自检,回读区分开启、未开启、未核实。本轮仅记录能力,不开启任何机器设置。
- 查证
- #1693 / #1708,已核对合并记录及发布/部署链。
- 进度
- 修复版本 1.23.2,已包含在正式包 1.23.7。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-30 复核
- #1755 将确定没发出的召回直接取消,不推待核实卡;真正不确定是否送达的情况仍需人工核实。
W-71锁屏一直重试不提醒,服务号又触发假告警已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 阻塞缺少告警,告警数又直接使用未读行数。
- 怎么修
- 锁屏退避并报告阻塞与恢复;服务号记忆和只读行识别过滤假客户,识别失败仍保守计入。最终后续修复为 1.23.7。
- 查证
- #1708 / #1723,已核对合并记录及发布/部署链。
- 进度
- 修复版本 1.23.7,已包含在正式包 1.23.7。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-30 复核
- #1740 / #1759 调整告警卡去重与需要处理事项的提醒对象;锁屏告警要区分真实客户与服务号。
W-72扫描困在服务号文件夹,下面客户轮不到已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 文件夹变化判据不稳;退出后回顶重扫,缺少深扫进度。
- 怎么修
- 列表变化配合聊天标题区空白识别文件夹;退出接回页行,顶部与深处接力并保存进度。
- 查证
- #1712 / #1722,已核对合并记录及发布/部署链。
- 进度
- 修复版本 1.23.6,已包含在正式包 1.23.7。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
W-73同一人半小时反复点开,巡检额度提前耗尽已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 没有在点开前识别最近已核对且画面未变的整行。
- 怎么修
- 整行像素指纹一致且三十分钟内核过则跳过不扣额;画面变化、待召回或首触未核实继续处理。
- 查证
- #1722,已核对合并记录及发布/部署链。
- 进度
- 修复版本 1.23.6,已包含在正式包 1.23.7。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
W-74首触额度被老客户占满,新好友没有招呼已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 探测前未有效排除已知客户、服务号和已判定非新好友。
- 怎么修
- 下发已知客户名单与额度,已知对象先跳过;非新好友另存判定账本;召回识别的新好友交首触,红叹号出现当天暂停首触。
- 查证
- #1693 / #1719 / #1720,已核对合并记录及发布/部署链。
- 进度
- 修复版本 1.23.5,已包含在正式包 1.23.7。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
W-75召回总领到非眼前客户,任务就绪仍发不出已合并 · 待场景验收
- 现场
- 记录中曾出现该编号对应的故障现象。客户姓名、备注、联系方式和聊天原文已从公开摘要中移除。
- 代价
- 造成漏回、重复动作、错误触达或无效人工排查。
- 根因
- 模式不一致回包缺 dailyTopics 被误算成关闭,又从本地任意挑候选。
- 怎么修
- 回包缺字段保留原值;标题模式只询问眼前已经认准的客户,没有则本拍不领。
- 查证
- #1719 / #1720,已核对合并记录及发布/部署链。
- 进度
- 修复版本 1.23.5,已包含在正式包 1.23.7。 本轮未逐条真机复测。
- 真机上
- 09-30 心跳:三台电脑均为 1.24.1;正式包 1.24.4 尚未在心跳中确认安装。场景仍待验收。
- 09-30 复核
- #1720 修复标题模式领到非当前客户后循环让路;眼前未认准客户时不从本地随意挑另一位。
W-76召回图重复发给同一客户已合并 · 待场景验收
- 现场
- 同一客户后续召回重复投递之前已经发送的配图。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 首触图片发送记录没有参与召回组图去重。
- 怎么修
- 复用已发送图片记录,后续召回不再重复发送客户已收到的图。
- 查证
- #1730
- 进度
- 服务端代码已包含于线上 88c1f5d。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-77召回回车已核实没发,却被列入待核对已合并 · 待场景验收
- 现场
- 发送前已开始记账,实际文字仍留在输入框、消息区未变化,却被记为可能已送达。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 发送记录进入待确认后,没有把客户端三步验证的确定未发送标记传到服务端。
- 怎么修
- 三步核实成立才按未发送回队列重试;没有验证标记则继续按不确定处理。
- 查证
- #1729 / #1733
- 进度
- 修复包 1.23.8 已发布。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-78视觉模型限流导致首触漏判,并登记了召回已合并 · 待场景验收
- 现场
- 主模型读屏遇上限流,首触没判成;同一拍又登记了召回任务。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 限流失败、身份未确认和召回登记顺序没有区分。
- 怎么修
- 主模型限流后同模型短暂重试一次;首触未判清的这一拍不首触、不登记召回。
- 查证
- #1747
- 进度
- 修复包 1.23.9 已发布。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-79首触发出后无确认,运营难以对账已合并 · 待场景验收
- 现场
- 首触可能已经发出,但缺少清楚的逐条回执。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 发送结果和通知链路未连接,或把首触回执按自动回复规则提醒。
- 怎么修
- 确认发出后推单条不 @ 回执;通知失败只记日志,不改变发送结果。
- 查证
- #1746 / #1747
- 进度
- 服务端代码已包含于线上 88c1f5d。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-80没有必要处理的召回待核对反复报警已合并 · 待场景验收
- 现场
- 确定没发出的召回也进入待核对并通知运营。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 将确定失败与真正不确定送达混为一类。
- 怎么修
- 确定没发则取消本轮且不推卡;可能已发或电脑配置问题仍保留待核对。
- 查证
- #1740 / #1755
- 进度
- 服务端代码已包含于线上 88c1f5d。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-81备注已成交或勿扰,仍被首触或召回已合并 · 待场景验收
- 现场
- 备注明确表示已成交、做完或勿扰,仍被当成待触达客户。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 服务端、客户端首触与召回的状态词判据没有同步。
- 怎么修
- 两端共用状态词,首触在读屏和扣额度前拦截;自动回复仍按原规则处理。
- 查证
- #1756
- 进度
- 修复包 1.24.1 已发布。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-82拒绝联系的客户截图仍传给正文视觉模型已合并 · 待场景验收
- 现场
- 已经在黑白名单拒绝的联系人,其会话截图仍被上传到正文读屏模型。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 拒绝名单检查发生在截图读取之后。
- 怎么修
- 先读取标题并过白名单,再决定是否读取正文;双层检查保留。回归验证拒绝联系人时视觉调用为零。
- 查证
- #1771
- 进度
- 修复包 1.24.2 已发布。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-83昵称加手机号被识别成错误客户,任务身份对不上已合并 · 待场景验收
- 现场
- 备注没有客户编号、但含昵称与手机号时,客户端和服务端算出不同身份键。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 客户端先用了宽松地区简称,服务端先按手机号认人,导致派单身份不一致。
- 怎么修
- 身份判定顺序与服务端一致:规范备注、昵称加手机号、最后才是无编号放宽。正式包 1.24.4 已出,三台心跳仍报 1.24.1。
- 查证
- #1778 / #1780
- 进度
- 修复包 1.24.4 已发布。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-84客户说已在别处成交,仍收到召回或被误判已在我方成交已合并 · 待场景验收
- 现场
- 客户说明已在其他地方成交后仍收到召回,回复也可能把外部成交误当成本方订单。
- 代价
- 可能漏发、重复触达、身份核对错误或让不该读取的会话进入读屏链路。
- 根因
- 召回资格、流失判据和历史成交分类覆盖不一致,OCR 错字也会漏判。
- 怎么修
- 共同识别已在别处下单、定做完成等表达,排除问句、条件句、历史项目和本次仍有需求的情况;服务端已部署 88c1f5d。
- 查证
- #1783
- 进度
- 服务端代码已包含于线上 88c1f5d。 尚待在实际场景验证行为。
- 真机上
- 三台电脑 09-30 心跳均显示 1.24.1;正式发布包为 1.24.4,三台当前均未确认安装后者。
W-85客户说会自己找时间来店,AI 还连着邀约,开头半句原样复读已上线 · 待场景验收
- 现场
- 10-09 晚,一位召回后开口的客户先说可以到店,我方问时间;客户回“没事,我有时间会过去”“再过几天”,AI 两轮都继续推到店,两条回复开头同一句“样柜、色卡、工艺细节都能亲手摸到”,后一条只在后面续了个新问句。
- 代价
- 客户已经表态,还被连着催,读起来像机器人在复读、没听他说话。
- 根因
- ① 「会自己来、时间他定」这类话既不是拒绝到店(
detectStoreVisitRefusal 判否,store-visit.ts 里「我…过去」命中到店意向,到店邀约一直开着),也不是到店承诺(没给时间),没有任何规则管,模型就接着邀。② 防复读闸(llm.ts 约 4117 行)只认整句相似度 ≥0.62 或末尾问句逐字相同;「前半句照搬 + 新问句」整句相似度 0.478、问句也换了,两条判据都没触发。复读的那半句是模型自己写的(调用记录里的模型输出就有);实发比模型输出少了开头一小句(“您到店来一趟最直观”),删句是哪一步做的还没定位,但没有任何闸把回复改成复读。
- 怎么修
- 改由 #2059 分两层修(原修复 #2057 按运营要求暂停,未合并):① 提示词:
wecom.nurture.rules 新增【客户已应允到店·推迟承接】——客户已答应到店、只是把时间往后放时,先接住他的安排,不再重复邀约、不复述到店理由,最多留一个轻钩子;之后几轮除非客户主动问门店、地址或时间,不主动重提到店。代码兜底常量同步加了同一条。② 防复读:新增「连续片段复读」判据——回复里有一段至少 12 字的原文与最近 6 条 AI 回复逐字相同,就算复读、触发改写;片段里带地址、门店、楼号类用字(如「路」「号」「楼」)或指定品牌词的放行,这张放行词表比较宽。
- 查证
- Langfuse
wecom.nurture.rules v34 于 10-10 03:50Z 切为 production,回读确认含新规则;#2059 于 10-10 03:57Z 合并(40321fb),之后 /api/status 回报 40321fb;CI 的 tsc + lint + test(含「线上 vs 代码兜底」同步检查这一步)通过。
- 进度
- 提示词和代码两层均已上线。尚未在真实对话里复现「客户推迟到店」场景,待场景验收。
W-86报价说「全包」,客户以为灯带、玻璃也包含部分已修 · 仍有出口
- 现场
- 10-09 上午,客户问“你不是写的一平方要868?”,AI 回“868是原价,现在样板房补贴300,实际支付568一平,全包板材五金柜门安装”;客户后来追问 568 是不是也包了柜门五金玻璃灯条。同一条里还把漳州客户说成「您在厦门」。
- 代价
- 客户按「什么都包」理解,到报增项时就成了「说好的全包怎么又加钱」。
- 根因
- 这句是模型照提示词写的(调用记录
sanitizeChanged=false,实发与模型输出一字不差)。当时的提示词(wecom.nurture.rules v31)里多处示范句就是「板材五金柜门安装全包」,没有任何一句要求同时说明「灯带、玻璃门等增项另算」;当轮没有召回增项价目文档。知识库里另有一篇写着「全包含造型灯带」的资料。「您在厦门」来自客户备注车牌字母 闽D 被当成「已识别城市」(windows-customer-name.ts 车牌兜底 → llm.ts 约 2340 行)。
- 怎么修
- 已上线:10-09 晚 #2043 提示词禁用「全包」并在模型回复收尾替换;10-10 #2055 代码自动报价句去掉「全包」、替换改为整串。未修:提示词里仍有多处「全包」示范句;三个提前返回的出口(公司身份直答、硬闸、审定原话直发)不经过收尾替换;报价不带「增项另算」;知识库那篇资料;车牌字母当城市。
- 查证
- #2055 于 10-10 02:11Z 部署(部署任务成功、/api/status 回报 9da58be);之后到 10-10 03:07Z,AI 消息 11 条,0 条含「全包」。样本太小,只能说没再看到。
- 进度
- 部分上线;其余按运营要求暂不修。
W-87加好友招呼语被认成「销售在手动回复」,会话转人工,客户这一句和之后两句 AI 都不回已上线 · 待场景验收
- 现场
- 10-09 晚,一位重新加上的客户问完价后连着问材料、问能不能上门量、问店在哪,三句都没有 AI 回复;截图里是销售 19:31、19:38 手打补的(这两条库里没有记录)。
- 代价
- 客户问到点子上时 AI 不接,只能靠销售自己看到再补。
- 根因
- 客户问「材料怎么样」那一轮,读屏把加好友时随验证请求发出的招呼语排到了 AI 报价之后;它是右侧我方气泡、库里没有、也对不上任何 AI 已发内容,
salesTypedScreenLines(sales-typed-handoff.ts)判成销售手打 → 落库成 wcst_ 行、会话转人工(sales_typed)、本轮不回(route.ts 约 4967 行)。当时转人工冷静期是 24 小时(#2045 10-09 22:25 才改成 10 分钟),所以后面两句也没接,三句都没有任何模型调用记录。
- 怎么修
- 读屏判销售手打时排除招呼语:屏上出现在「好友通过」提示之前的我方气泡,以及带「加微申领」的验证语(09-01 起 AI 回复里 0 次,误判的
wcst_ 行里就这一条)。
- 查证
- #2056;未查明:读屏为什么把招呼语排到了报价后面(当时的读屏内容没有留存)。
- 进度
- 10-10 03:07Z 部署(部署任务成功、/api/status 回报 6569dd2)。服务端判定,不需要电脑端升级;真机场景尚未复测。
W-88会话标题区域空白,客户新消息没有进入自动回复链路待修复 · 已定位
- 现场
- 10-10 北京时间 16:56,电脑B 的 Winds 端在客户新消息到达前后没有继续自动回复。该客户的入站消息未产生模型调用,也没有 AI 发送记录;客户身份、备注及聊天原文均未在本公开页展示。
- 具体证据
- 客户端在 16:56:20 记录
identity_unverified:reason=title_frame_blank、stage=identity_pre、active_chat=false、title_empty=true、confidence=0。客户消息于 16:56:51 到达;之后没有该会话的生成调用或自动发送证据。
- 根因
- Winds 读到的企微当前窗口没有可验证的会话标题,且没有确认已激活某个聊天。身份闸无法把屏幕上的新消息安全绑定到具体会话,按防误发策略停止在身份校验阶段,消息没有被交给服务端或模型。不是客户内容、话术、模型拒答或服务端故障。
- 影响
- 客户有新消息但系统不回复;因为没有进入服务端,常规模型日志和回复记录都看不到这次漏回。
- 待修方向
- 客户端应在标题帧为空时先恢复并复核当前会话,再继续读取;连续出现
title_frame_blank 且存在未处理入站消息时,需把会话标记为待处理并告警,不能只静默跳过。尚未实施修复。
- 查证边界
- 已确认故障点在 Windows 客户端的会话识别/交接层;无法仅凭现有遥测再证明标题为何为空(例如窗口未前台、界面渲染空白或视觉读取失败),因此未把这些推测写成根因。
- 进度
- 2026-10-10 已定位并登记;尚未提交代码修复或完成真机场景验收。
W-89R1 · 已检测到复读,纠错失败却仍放行旧回复修复已合并 · 待线上验收
- 来源
- 2026-10-11 私有逐句复盘 xiaoyu-chat-review.html,根因 R1;2026-10-12 登记。原截图、客户身份及完整原文不公开。
- 现场
- 私有复盘显示,客户已拒绝更换产品、并投诉重复后,回复仍连续重复配置和选择题。生成记录中存在 antiRepeatFired=true、antiRepeatFixed=false,部分相似度达到 1.0。此处是匿名事故摘要,不是聊天原文。
- 根因
- 防复读仅执行一次重写;重写文本未优于原文时,回退保留原回复。antiRepeatFixed=false 当时只是观测信号,没有形成必须停止发送的返回结果,也没有被路由作为发送拒绝条件消费。
- 为何放行
- 检测命中 → 重写失败 → 保留旧文本 → 后续仍获得可发送 replyText。发送出口没有再次核对重复及客户拒绝约束,因而“发现问题”和“禁止发送”脱节。W-85 修的是未命中检测,本条修的是命中后失败仍继续。
- 怎么修
- 重复、拒绝约束和报价冲突共用一次纠错预算;纠错后对最终文本复检,仍失败只停止当前轮,不回退旧稿。客户主动要求重发、再次询问品牌等必要事实允许回答;下一条新消息重新正常进入回复,不永久锁会话。
- 验收
- 同时覆盖:重复失败不发;纠错成功只发新稿;客户要求重发可发;拒绝更换后仍能回答其他问题;本轮停止后下一轮能发。并发画像更新误判回复过期的补修复仍未完成,不能据此宣称所有漏回风险已解决。
- 进度
- wecom-chat #2072 已合并(4c4d3c1c,2026-10-11)。代码检查通过;本条登记时未独立确认生产部署及真实场景复测,保持待验收,不计为已解决。修复约束按最终确认方案记录。
W-90R2 · 客户问498的板材品牌,快答却返回公司品牌修复已合并 · 待线上验收
- 来源
- 2026-10-11 私有逐句复盘 xiaoyu-chat-review.html,根因 R2;2026-10-12 登记。原截图、客户身份及完整原文不公开。
- 现场
- 私有复盘中的材料品牌追问命中公司身份快答,客户必须再次解释自己问的是板材。原问句的离线最小复现与事故配置一致。
- 根因
- brandAsked 只检查“什么牌子”前面有限长度内是否有板材/五金等词;没有把价位数字识别为产品对象,也漏看疑问之后的材料限定词。快答入口未传完整对话,无法利用前文已讨论的产品。
- 为何放行
- 对象识别误判为公司 → 直接构造公司介绍返回 → 未对照本轮问题的产品对象验证回答。此路径绕开正常模型的上下文理解,单改生成提示词不能修复快答误路由。
- 怎么修
- 区分公司品牌、材料品牌与价位指代,优先明确产品对象;厦门498元按业务确认回答千年花牌板材,不用公司名代答,不输出模糊的档位称呼。客户问材料品牌即回答品牌,不把询问当成接受更换产品。
- 验收
- 材料词在疑问前后、仅价位加品牌追问、以及真正问公司名称均有对照用例;检查最终快答出口。业务口径只适用已确认的城市与产品,不能泛化到全国。
- 进度
- wecom-chat #2072 已合并(4c4d3c1c,2026-10-11)。代码检查通过;本条登记时未独立确认生产部署及真实场景复测,保持待验收,不计为已解决。修复约束按最终确认方案记录。
W-91R3 · 指定配色出图请求被降为发送任意风格图库修复已合并 · 待线上验收
- 来源
- 2026-10-11 私有逐句复盘 xiaoyu-chat-review.html,根因 R3;2026-10-12 登记。原截图、客户身份及完整原文不公开。
- 现场
- 私有复盘出现生成稿只含价格说明,最终发送稿却额外拼接不同风格图库引导语及图片标记;客户要求的指定配色没有被实际满足。
- 根因
- 后置 pickGalleryForMessage 可选择多风格 sampler,并在生成后追加 leadIn;指定品牌、色号和本户型要求没有成为素材匹配与交付的硬条件。文本生成阶段还可能先假设图片会发送,承诺与实际选图结果脱节。
- 为何放行
- 模型文本通过不等于最终图文合格:后处理又改变发送内容,既没有验证素材匹配,也没有设计任务或发送动作回执。“已发任意图”因而被当作“满足出图请求”。具体事故选图内部命中分支未完整取证,不能断言每张均出自 sampler。
- 怎么修
- 指定配色/本户型没有核验匹配素材时禁止回退通用图库;区分参考图和定制图。设计师已接单、图片已发等状态必须有真实回执,不让口头重写虚构动作。
- 验收
- 最终发送稿及媒体均需匹配约束;无匹配图不附错图、不承诺已交付。PR 只补防误发与虚假承诺拦截,尚未建设精确素材审核或设计派单平台,实际指定配色交付仍待闭环。
- 进度
- wecom-chat #2072 已合并(4c4d3c1c,2026-10-11)。代码检查通过;本条登记时未独立确认生产部署及真实场景复测,保持待验收,不计为已解决。修复约束按最终确认方案记录。
W-92R4 · 缺少共同订单明细,基础小计被承诺为最终总价修复已合并 · 待线上验收
- 来源
- 2026-10-11 私有逐句复盘 xiaoyu-chat-review.html,根因 R4;2026-10-12 登记。原截图、客户身份及完整原文不公开。
- 现场
- 私有复盘中,销售报价含颜色差价和服务费,AI 却依据基础单价乘面积承诺总价,并对安装费、见光板是否计费给出相互冲突的说明。手写单及部分销售消息未进入数据库上下文。
- 根因
- 销售与 AI 没有共享带版本的本单计价明细;通用知识直接覆盖本单收费事实。原数字护栏给可计算总价留有来源豁免,但该豁免并不验证加项是否齐全、算式是否一致。
- 为何放行
- 模型自行推算基础小计 → 命中数字来源豁免 → 未检查费用完备性和订单版本 → 作为最终价输出。检查“这个数字可由乘法得到”不足以证明“它是本单全额”。
- 怎么修
- 系统用有来源的数量、单价、加项与优惠自动计算到分,状态由计算器判为 complete/incomplete;费用齐全直接报总价,不设人工核总价或审批前置。未知费用不能当零,缺项先报已知小计并点明缺项;发送前核对输入版本,模型草稿不能充当自己的数字来源。
- 验收
- 验证面积与金额计算、颜色费用、服务费、安装/见光板边界、缺项小计和过期版本拒发。当前已合并计算器及约束;完整费用来源接入与真实订单场景仍需验证,不能把基础小计包装成完整自动报价闭环。
- 进度
- wecom-chat #2072 已合并(4c4d3c1c,2026-10-11)。代码检查通过;本条登记时未独立确认生产部署及真实场景复测,保持待验收,不计为已解决。修复约束按最终确认方案记录。
W-93R5 · 内部禁报品牌规则外露,降价推荐优先于客户拒绝修复已合并 · 待线上验收
- 来源
- 2026-10-11 私有逐句复盘 xiaoyu-chat-review.html,根因 R5;2026-10-12 登记。原截图、客户身份及完整原文不公开。
- 现场
- 事故版本中启用的两条知识规则分别要求不报材料品牌、预算受限主动推荐低价替代;该轮检索同时命中,生成稿直接出现内部禁报品牌措辞。客户拒绝替换后仍不断被推荐。
- 根因
- 内部行为指令与对客产品事实混在可检索知识中;主动推荐规则缺少客户明确拒绝后的退出条件。已选品牌、配色及拒绝项没有作为跨轮动作约束稳定支配生成与路由。
- 为何放行
- 相互冲突的规则进入模型 → 输出内部指令并继续销售推进 → 最终出口既不检查内部规则泄漏,也不检查动作是否被客户拒绝。复读重写只换措辞不能消除这种语义违规。已停用且未命中的其他旧规则不归为本事故原因。
- 怎么修
- 将产品事实与内部指令分开,最终文本检查内部口径与拒绝动作;客户锁定品牌、配色后停止主动替换推荐。厦门498元的板材品牌直接回答千年花;拒绝换产品、拒绝电话只限制该动作,不阻止正常答疑;明确要求停止联系仍按停止规则处理。
- 验收
- 测试拒绝后不再推荐、主动追问品牌仍可答、询问不等于接受更换、拒绝电话不等于拒绝文字答疑,以及明确断联仍停止。线上知识源是否同步消除冲突需另核,代码约束已合并不代表所有外部知识已经更新。
- 进度
- wecom-chat #2072 已合并(4c4d3c1c,2026-10-11)。代码检查通过;本条登记时未独立确认生产部署及真实场景复测,保持待验收,不计为已解决。修复约束按最终确认方案记录。
W-94备注表情差异导致会话分裂,已确认厦门的客户收到泉州报价话术部分修复 · 待验收
- 现场
- 客户已明确房屋在厦门,我方也已发送厦门门店地址;后续客户询问兔宝宝每平方米价格,我方报价却带入泉州,客户再次纠正所在城市。截图只能证明城市表述错误,不能据此认定 568 元单价错误。
- 具体证据
- 2026-10-12 数据库核对:同一个 Windows 微信账号、同一台电脑下,前后消息分别落在两条 conversation。旧会话保存厦门及装修阶段,后续报价所在的新会话只有价格与品牌询问,没有前面的厦门消息。两条客户显示名仅表情不同;按当前默认 epoch 哈希算法分别重算,两次结果均精确匹配各自数据库身份键。报价生成日志与实际发送正文一致,未被改写。新会话直到客户再次纠正城市后才提取到厦门;当前画像正确不能反推事发时画像正确。
- 具体根因
- ① 身份键把易变表情纳入哈希:
weixin_identity.py / canonical_identity_name 保留表情,weixin_relay.py / conversation_identity 因表情不同生成不同身份键。
② 服务端找不到旧会话就建新会话:identity.ts / conversationIdentityLookupKeys 只查当前哈希与当前显示名对应的键;旧哈希、旧显示名均不命中。chat/reply/route.ts 因而新建 conversation,历史查询又只按该 conversation_id 取数,旧会话的厦门信息没有进入本次上下文。
③ 城市兜底使用了备注地域:windows-customer-name.ts / remarkCityOf 将备注中的闽C映射为泉州;store-visit-cards.ts / knownCustomerCityOf 在新会话既无城市消息、又无城市画像时回退到该值。客户消息优先的规则本身存在,但厦门消息已被身份分裂隔离,无法参与优先级判断。
- 为什么放行
- 没有在身份未接续时阻止地域兜底进入回复,也没有覆盖这种城市表述的发送校验。
reply-outbound-guards.ts 调用的 store-address-guard.ts / storeConsistencyViolation 主要检查无门店声明、远程费用和异地具体街道地址;报价中的泉州地域描述不属于其拦截形态。对实际函数做离线探针:即使显式传入 customerCity=厦门,该泉州报价仍返回 null(无违规)。因此仅把正确城市写回画像,也不能保证此类错误被发送闸拦住。
- 复现结果
- 使用合成客户信息调用实际源码,无付费模型调用、无客户触达:表情变化→哈希变化;当前查找键不含旧哈希;仅价格询问+备注兜底→泉州;补回厦门历史→厦门;厦门上下文下校验泉州报价→未拦截。数据库证据证明现场分裂,源码探针验证分裂后的城市回退与放行机制。
- 修复需求
- ① 同一微信账号范围内建立稳定身份与显示名别名映射,表情变化优先接回已确认会话;不能只删表情全库合并,也不能仅凭短编号或跨账号自动合并。冲突候选需保留证据并停止猜测身份。② 接回会话时恢复有效历史与城市来源;明确客户城市优先于备注推断,来源冲突不得静默降级。③ 发送前增加回复地域与已确认项目城市的一致性校验,区分报价地域、门店地址和客户讨论其他城市,避免只看到外地名称就误拦。④ 错误草稿被拦后基于正确上下文有限重生成,重新校验;只取消错误草稿,不把客户消息标成已成功回复,不堵住后续新消息。⑤ 对符合证据的历史分裂会话补建别名或安全归并,保留原消息与审计关系。
- 验收
- 同账号同客户仅表情变化时仍命中原会话;厦门→装修阶段→兔宝宝询价的完整链路不再输出泉州适用地域;无城市时不把备注推断写成客户确认事实;错误城市草稿拦截后能生成并成功发送正确答复;客户主动比较泉州门店不会被误拦;跨账号、同名、短编号冲突不串会话;新消息不被旧草稿阻断。
- 关联与边界
- 关联 W-12 同客户会话分裂,本项补充具体表情哈希断点及其城市报价后果。表情差异来自读屏误识别还是实际修改备注,目前证据不足,不能确定。未取得本次完整模型输入,模型内部具体选择过程不作已证实结论;截图中的等待时长也不能直接归因于模型生成慢。
- 登记时进度
- 2026-10-12 已完成数据库核对、身份哈希精确重算及实际源码离线探针。本次仅登记根因和修复需求,尚未实施修复、未做线上修复验收。公开页面已去除客户身份、联系方式、原话及数据库标识。
W-95巡检切入旧会话后,把历史提问误当新消息生成回复部分修复 · 发送闸待补
- 现场
- 电脑 C 巡检打开一段旧聊天后,系统针对客户此前关于联系方式来源的提问生成了回复。运营反馈出话后约 27 分钟仍未发出去,期间重试 2 次。生成和重试不等于送达,本项记录的是误触发回复链路,不记为已发送事故。客户身份、设备标识和聊天原话均不公开。
- 问题链
- 程序主动打开旧会话后,活动聊天画面变化被当作新入站候选;读屏把历史客户气泡判为待回复;底部居中的系统提示使发送方像素复核无法纠正;活动窗口路径绕过 24 小时旧消息闸;非好友提示没有成为发送阻断。四处局部防线各有原本要解决的问题,叠加时却漏掉了这种旧会话。
- 根因①
- 像素复核只在明确判到我方气泡时否决模型:
runner.py / _side_pixel_gate 检查聊天区最下面一块内容。模型报客户、像素报右侧时才改判我方;底部为居中系统灰字时会保留模型原判。截图证实底部有灰字,代码证实居中不否决;模型当时为何排错仍待证。
- 根因②
- 活动窗口变化不足以证明客户刚发消息:
ROW_SOURCE_ACTIVE 为避免新消息挂旧时间分隔条而豁免 24 小时闸;巡检主动切到旧会话也会改变画面,豁免缺少对变化来源的区分。
- 根因③
- 巡检切会话未清上次窗口基线:读历史路径在点开旧会话前清
_active_chat_fp,避免把选中旧会话当作新消息;列表巡检是独立点击路径,没有沿用这条状态边界。后来补过已选中行免重复点击,仍未补基线重置。
- 根因④
- 非好友提示未进入发送闸:
_tail_unreachable 设计为只上报、不改发送判断;它先要求我方气泡出现红叹号,命中后才读灰字。本例截图中的旧我方气泡没有红叹号,系统灰字又被正文读屏过滤,当前需验证好友关系的信号没有传给发送闸。灰字本身不能证明客户主动删除好友。
- 待证推断
- 底部连续灰字可能干扰了读屏模型对消息顺序的判断;事发原帧和模型原始输出未留存,无法重放坐实。两次重试各自未发出的原因,也不能仅凭聊天截图确定。
- 修复方向
- 程序主动切换会话的入口统一重置或重新建立活动窗口基线;只有证明同一会话内出现新客户消息,才沿用活动窗口的旧时间条豁免。发送方或好友关系无法确认时停止本轮自动发送并留下待核对证据;真正新消息和符合条件的历史补回应保留各自路径。
- 验收
- 旧会话底部有居中系统提示时不误回;真正新消息即使挂旧时间条仍能回复;主动切换和失败重试不把历史消息变为新消息;好友关系无法确认时不自动触达。以同形回放及最终发送回执验证,不能只看模型出话日志。
- 登记时进度
- 2026-10-13 根据运营逐项复盘、截图和现有客户端代码登记。本次只补 bug 台账,尚未修改客户端、部署修复或完成事发原帧重放。
W-96已签客户收到针对已人工处理历史消息的自动回复代码已有修复 · 待验收
- 现场
- 2026-10-12 北京时间 21:49,电脑 C 的个人微信客户端
1.25.0+4c4d3c1 对一位备注已标明到店已签的客户发出自动回复。它回应的是数周前的历史提问,销售之后已人工说明并处理。运营提供的日志确认 21:49:38 发送成功;这与 W-95 的生成后未确认送达不同。客户姓名、编码、手机号、设备标识及聊天原话不在公开页展示。
- 影响
- 本应停止自动触达的已签客户又收到旧问题的回复,且回复没有考虑销售已经处理过的后续消息,造成重复、脱离当前语境的沟通。
- 根因①
- 已签状态规则缺口:自动回复关闭词覆盖
已定,没有覆盖 已签、到店已签;召回规则的 已到店 也不能靠字面匹配识别 到店已签。这类客户未被全通道统一排除。
- 根因②
- 主动切会话误触发活动窗口:21:48:55 列表巡检点开该客户后,没有同步重建当前聊天画面基线;后续轮询把画面变化交给自动回复通道。与 W-95 共享切会话状态边界。
- 根因③
- 活动窗口路径绕开历史时效:这条路径豁免超过 24 小时的旧消息检查,程序切到旧会话时,历史消息因此仍可进入即时回复流程。
- 根因④
- 人工处理未进入生成上下文:本次请求只含更早的简短确认、媒体和通话记录及客户历史提问,没有其后销售已作出的人工说明;模型无法知道该提问已经处理过。
- 根因⑤
- 底部复核只合并了部分结论:主读屏与复核读屏对历史提问是否位于底部的判断相反,代码只更新底部状态,仍保留主读屏的消息内容和左右判断;底部状态通过不等于原消息顺序、内容和最后发送方都正确。
- 根因⑥
- 居中提示遮住真实末条气泡:像素复核只看最下面的内容块;遇到撤回、时间或不支持内容等居中提示时返回
center,没有继续向上定位最后一条真实气泡。运营提供的截图已复现这类形态。
- 尚未确认
- 后续人工回复最初为何没有被读屏带入请求:事发原始截图与模型原始返回未保存,不能确定是模型漏读,还是当时画面未包含该消息。事发前裁剪标定正常,目前没有证据支持裁剪错位。
- 修复需求
- ① 客户备注出现已签状态时,客户端与服务端一致排除自动回复、首触、召回及漏回告警;排队任务执行前重查。② 程序主动切会话后按会话重建基线,确认新增且未处理的客户消息才进入自动回复。③ 所有自动回复入口统一检查时效和处理状态,历史消息只走合资格的召回路径。④ 识别客户消息之后的人工回复,避免重复处理,同时不阻断之后真实新消息。⑤ 底部复核跳过居中系统提示,并同时核对顺序、内容与最后发送方。⑥ 在权限和保留期限内留存触发来源、会话身份、读屏帧、识别结果及放行理由。
- 验收
- 到店已签客户收到文本、图片、通话记录或系统提示均不触发自动动作;巡检打开旧会话不代回;历史客户消息之后已有人工回复、底部又有居中提示时仍不重复回复;非排除客户后续发来真实新消息仍能正常回复;主读屏与复核结论冲突时不得仅改底部状态就发送。用本次截图形态和事件顺序建立回归,并核对最终发送回执。
- 登记时进度
- 2026-10-13 根据运营复盘和发送日志登记。本次仅补 bug 台账,尚未实施客户端或服务端修复,未完成回归验收。
W-97置顶会话占满首屏时,新消息的未读找不到,客户约一小时才收到回复代码已修 · 1.25.40 起 · 待装机 · 未验收
- 现场
- 2026-10-12 北京时间 19:11,一位客户连发两条询价消息,另一台销售电脑(客户端
1.24.26+3b4f047)直到 20:23 才找到这条未读,20:24 发出回复。这 71 分钟里程序一直看得到左侧聊天图标上的未读角标,共追找 115 次,其中 114 次判为眼前没有可回的会话后放弃。该机 10-12 全天追找 400 次、只找回 2 次;10-11 追找 204 次、找回 6 次。另一台电脑同期基本能找回。客户姓名、编码、手机号、设备标识及聊天原话不在公开页展示。
- 前提
- 该机微信置顶了至少 12 个会话,正好占满列表第一屏(约 13 行)。新消息的非置顶会话排在整个置顶块下面,列表回到最顶时看不到;置顶块里的会话来消息时,也可能落在第 9 行以后。
- 根因①
- 未读红点只扫列表前 8 行:客户端
runner.py 的 max_unread_rows=8。列表在最顶时前 8 行全是置顶会话,置顶块之后、以及置顶块第 9 行以后的未读都不在扫描范围内。
- 根因②
- 屏外未读找回默认新消息在最上面:第一步回到列表最顶,在有置顶时必然落空;第二步双击聊天图标跳到未读,115 次只成功 1 次。跳转后为何仍未认出该行没有原始画面,未证实:可能是跳转后目标行落在第 9 行以后,也可能是先跳到了群或服务号的未读。
- 根因③
- 巡检与找未读互相打断形成循环:角标一亮,列表巡检就让路;找未读双击后把巡检位置记为不明,巡检下一拍又回到最顶,从第 0 行重新开始。71 分钟里巡检回顶 57 次、只往下翻页 5 次,本可兜底补回的巡检也走不到那一行(同日 18:36 巡检曾逐行补回过同一客户)。
- 根因④
- 追找后静默没有生效:日志写明角标不变就不再追,实际每 20~40 秒重追一次,每次又把巡检打回最顶。指纹为何一直变化尚未查清。
- 登记时修复方案
- ① 红点扫描按屏上实际行数扫满整屏。② 屏外未读先回顶整屏扫,再逐页下翻(最多 5 页),最后双击跳未读兜底(最多 3 次),遇到群、服务号跳过继续找。③ 记住置顶块高度,下次直接翻过置顶块。④ 找未读后还原巡检位置,静默改为角标变化才重追、否则最多每 2 分钟一次。⑤ 角标持续亮起超过 5 分钟仍找不到可回客户时告警并附截图。⑥ 每次追找记录找到的页码、行号、是否置顶、耗时与跳过原因,落空时留存画面。
- 验收
- 置顶 0 / 5 / 12 / 20 个;置顶客户在第 10 行来消息;非置顶客户在 12 个置顶下方来消息;置顶块里有群未读、下方同时有客户未读;巡检翻到第 4 页时来消息;按 10-12 那 71 分钟的事件顺序重放。以上各种情况,机器空闲时从角标亮起到点开客户在 1 分钟内完成,过程中不出现巡检反复回顶,告警在测试中真实触发一次。
- 临时缓解
- 取消不需要的置顶,让置顶块少于一屏。
- 登记时进度
- 2026-10-13 根据 10-11、10-12 两天全量客户端日志定位,运营已确认方案并要求修复;客户端修复进行中,尚未出包、未装机、未完成回归验收。
W-98客户已报40方投影,面积单位漏识别导致正常计算的报价被护栏拦下面积单位与失败原因已修 · 运营已验面积识别 · 补修后真实场景待抽查
- 现场
- 2026-10-13 北京时间 19:07:53,个人微信客户发来“我们家做40方投影”。19:08:06 首稿生成:“40方投影按568算,基础柜体约2.27万。您这边要做哪几组柜子?我让设计师按您户型理个方案,得空去店里当面看更清楚。”该稿被服务端拦下,未发送给客户。客户身份、联系方式、销售账号与设备标识已省略。
- 重写现场
- 19:08:06 至 19:08:19,模型正常返回重写稿:“40方投影按568算,基础柜体约2.27万。您家主要做哪几组柜子呀?我让设计师把灯带、见光板这些增项也一并理出来。”仍被同一护栏拦下。以上两段均为后台草稿,不是微信已发送气泡;本轮未产生新的 AI 发送消息。
- 告警
fabricated-customer-area "2.27万"(重写失败 repair-error)。这是服务端主动停止发送,非电脑端卡住,也非本轮模型接口或网络失败。
- 根因①
src/lib/llm/fabricated-area.ts 的 knownSizesFrom 面积正则识别平、平方米等单位,遗漏口语单位“方”。客户画像也没有面积字段可兜底,因此“40方投影”得到 sizeGiven=false,虽然数字列表已提取到40。
- 根因②
- 该护栏同时检查虚构面积和无尺寸依据的总价,并非把金额当成面积。错误的“无尺寸”状态让回复中的“2.27万”被误拦。算术上
40 × 568 = 22,720元 ≈ 2.27万元;此结论仅说明该小计计算正确,不代表已核验完整报价及增项。
- 根因③
- 重写稿保留相同报价,再次命中误判。微信一次纠错额度已使用;
src/lib/llm.ts 最终检查因已有未修复护栏或额度用完抛出 correction-budget-exhausted,src/lib/wecom/reply-contract.ts 的统一捕获将其标成 repair-error,掩盖了实际原因。
- 确定性复现
- 同一条回复配同样画像:输入“我们家做40方投影”得到
sizeGiven=false,命中“2.27万”;改为“我们家做40平投影”或“我们家做40平方米投影”得到 sizeGiven=true,该护栏不再命中。复现只运行纯函数,没有调用模型或触达客户。
- 修复需求
- ① 支持40方、40方投影、投影面积40方等有效面积表达,兼容空格、小数、中文数字。② 已有有效尺寸时不得因单位表达差异触发“零尺寸编总价”护栏,保留其他报价检查。③ 区分重写请求异常、重写后仍命中护栏、纠错额度耗尽,并记录次数和最终原因。④ 不自动补发历史被拦消息,避免重复触达。
- 验收
- 覆盖40方、40.5方、四十方、40 方投影、投影面积40方,与40平识别一致;方案、方便等词不误识别;客户确未给任何尺寸线索时,模型凭空报面积或总价仍拦截;模拟重写正常返回但仍违规与接口异常,告警应明确区分。本案例须通过原消息与原草稿回放,确认不再被此护栏误拦。
- 登记进度
- 2026-10-13 已核对数据库、模型调用记录和源码并完成纯函数复现。仅登记问题与需求,尚未修改回复逻辑、未补发、未完成修复验收。
- 怎么修
- #2124(2026-10-13 北京时间 19:21 合并,比本条登记早 8 分钟,登记时漏看):同族 8 处解析客户面积的正则统一补上「X方」,并排除方案、方便、方面等词(
fabricated-area.ts、signals.ts、customer-claims.ts、budget-quote.ts、material-delivery.ts、style-quiz.ts、wecom-recall/worth.ts)。服务端改动,合并部署即对所有机器生效,不用装机;生产部署同日成功(部署任务 19:21 启动)。
- 10-14 复核
- 用本案原话和原首稿分别跑修复前后的代码:修复前
sizeGiven=false,命中“2.27万”;当前 main sizeGiven=true,不再命中。只跑纯函数,没调用模型、没触达客户。上线后真实客户再说“X方”的场景尚未专门抽查。
- 运营验证
- 2026-10-14 运营反馈已自行验证,面积单位识别通过。
- 10-14 补修
- 独立检验首轮结论 FAIL:#2124 漏了中文数字(四十方),重写失败也没分清原因。#2127 补上:护栏与报价触发都认
四十方;重写失败分成「纠错额度已用完」与「重写请求异常」两种记录。第二轮检验发现新分支把三方监理、两方、一方也当成报了面积(护栏偏松),#2134 改为「方」前的中文数字须带十或百(三十五方、一百二十个方有单测)。服务端改动,部署即对所有机器生效。检验记录见 10-14 独立检验,结论 CODE_ONLY。
- 还没做
- 上线后真实客户说「X方」、以及额度用完时记录新原因,尚未抽查。两种失败原因目前只区分记录,后续处理相同。物料、预算、客户说法、风格测验、召回价值判断等几处面积识别仍只认阿拉伯数字加「方」。本案被拦的那条消息按需求 ④ 不补发。
W-99聊天底部两行「暂不支持该内容」灰字,两个读屏模型说法对不上,一台电脑的召回整天被拦复发后再修 · 1.25.42 起 · 待装机 · 未验收
- 现场
- 2026-10-13,一台销售电脑(客户端
1.25.4+9abb756)发召回前读屏核对,北京时间 13:36 至 17:14 共报 22 次「当前窗口状态未核实」(recall_live_view_unverified);装新版前当天只发出 1 条召回。同期另两台(1.25.2、1.24.26)照常发。该机客户聊天的最底下,几乎都是两行微信灰字 暂不支持该内容,请在手机上查看,不是消息气泡。客户身份、设备标识与聊天原文不在公开页展示。
- 用的模型
- 读屏主模型
qwen/qwen3.8-flash(日常读聊天、发前核对聊天记录都用它);复核模型 qwen/qwen3-vl-235b-a22b-instruct,只在主模型判「不在底部」时再看同一张图。
- 重放结果
- 拿该机当天 11 张截图逐张重放:flash 11 张里 10 张判「不在底部」——真实消息读对了,但最底下是灰字不是气泡,它就认为没滚到最新。235b 这 10 张全判「在底部」,这一点是对的;但它没遵守「灰字不算消息」:2 张把灰字当成「最新一条」标成系统消息,2 张把灰字当成客户发的消息,6 张把灰字塞进「最近几条」、挤掉真实消息,顺序和 flash 对不上。
- 根因
1.25.4 新加的规矩(cloudpc/windows-weixin/vision.py 的 _bottom_recheck_mismatch):两个模型对「最新一条 / 最近几条」说法不一致,就当不在底部、不发。于是这 10 张全被拦。1.25.2 和 1.24.26 没有这道闸,235b 一说在底部就放行,所以另两台能发。
- 更正
- 此前说的「发前核对聊天记录读不准」不是卡点:这一步读不准时,程序会退一步用服务端的通用话术继续发;当天 9 次最后都倒在上面的「不在底部」。
- 怎么修
- #2119(2026-10-13 合并,
1.25.6):只对召回,强模型说在底部就放行,两边不一致只记 bottom_recheck_mismatch_waived;召回内容仍有最后一句比对、像素不变核对和服务端闸。普通代回读屏不变,保留 1.25.4 防老消息误回的规矩。
- 真机上
- 该机 10-13 17:23 开始装
1.25.6+55aabbd,17:49 装好启动;之后到 21:28 发出 9 条召回,10-14 09:04 又发 1 条。
- 还没解决
- 灰字本身仍会让 flash 判「不在底部」、让 235b 把灰字当消息。普通代回仍走 1.25.4 的不一致即拦,同样的底部灰字可能让这台电脑的自动回复也被拦,本次未查、未修。
- 10-20 补修
- wecom-chat #2257(1.25.26):比对两个模型的读数之前,先剔掉居中灰字(不可显示占位、朋友验证、拒收、撤回);最新一条是灰字时,退回最后一条真实气泡再比。普通代回和召回走同一判据,灰字之外的真分歧照样拦。独立检验结论「代码层通过、未验收」:检验人把新测试放到旧代码上实测,3 条失败。还没装机,真实场景未验收。
要等:1.25.26 跟随自动升级先装到试点电脑,跑稳后再铺到其余电脑;装上后看普通代回日志里是否不再出现 bottom_recheck_mismatch。
W-100召回正文逐字模拟打字,被电脑的中文输入法打乱:标点重复、紧跟的字被吃掉,客户收到乱码已修并装机 · 待独立检验
- 现场
- 一台销售电脑的召回。库里存的召回话术完整,乱在电脑往微信输入框打字这一步:每个标点打成两个(
,,、【【、;;),标点后面紧跟的那个字被吃掉,✅ 全部丢失。普通代回走复制粘贴,同一台电脑上一直完整。客户身份、设备标识与聊天原文不在公开页展示。
- 影响
- 按电脑端「发后读屏」日志统计(登记时口径):10-13 全天召回完整;10-14 13:23 出现第一条乱码,当天 2 条召回都乱;10-15 11:51 之后几乎每条都乱;截至 10-15 14:26 共 29 条乱码召回,发给 29 位客户。客户端当时把它记成「已发送」,只留了一行
WARN recall_receipt_visual_fallback,没有告警。
- 根因
- 召回正文走
Runner._recall_type_text → human_type_text 逐字模拟按键(cloudpc/windows-weixin/runner.py),按键仍会经过这台电脑的中文输入法;普通代回走 _paste_text 粘贴,不经过输入法,所以同一台电脑上只有召回乱。触发时机:10-14 上午 09:28–09:33 这台电脑被连续重启 3 次,当天 13:23 出现第一条乱码,时间先后对得上;输入法具体哪项状态变了没有查明(推断,非观测)。
- 怎么修
- #2154(
1.25.9+a46be34):召回正文改用 human_paste_text,和普通代回一样粘贴,不再经过输入法;新增 guards.recall_text_garbled,发后读屏看到原文里没有的「同一标点连着两个」,就打 ALERT recall_text_garbled,不再静默记成「已发送」。
- 真机上
- 该机 10-15 15:52 装好
1.25.9+a46be34。抽查装机后 10-15 晚 3 条、10-16 09:04–09:25 4 条召回的发后读屏,均未出现重复标点,也没有 recall_text_garbled 告警。
- 还没解决
- 已收到乱码的 29 位客户是否人工补发完整话术,待运营决定。输入法为何在重启后改变行为没有查明。修复尚未经独立检验 agent 复核。
W-101首屏有「N个置顶聊天」折叠栏时,紧挨着它的那位客户的未读认不出、点不准,最后由销售人工回复已修并装机 · 待独立检验
- 现场
- 10-16 08:34–08:42,电脑 B(客户端 1.25.9)。微信会话列表首屏有一条「N个置顶聊天」折叠栏。客户 08:40 发来两条消息,左侧聊天图标亮了角标;程序去追未读(回到顶部、往下翻 5 页、跳到未读),每一步都没找到红点,08:41 记下
unread_unreachable,这位客户最后由销售人工回复。客户身份与聊天原文不在公开页展示。
- 影响
- 客户的新消息没有自动回复,要等销售自己看到。已确认在首屏有折叠置顶栏时出现。
- 根因
- 一句话:折叠栏比普通会话行矮,程序却按每行一样高去找红点,紧挨着折叠栏的那一行位置对不上。
细节:折叠栏约 47 像素高,普通会话行约 65 像素。找红点时按等距格子开窗(runner.find_unread_rows → calibrate.row_center_y),每次量出来的第一行位置都不一样,最上面那一行的红点只有在一小段位置里才看得到。
- 怎么修
- wecom-chat #2167(客户端 1.25.10,10-16 合并):找红点前先认出这一帧里每个头像的真实位置,按头像定行,点击也用这个位置;回顶、翻页、跳未读之后等画面停稳再找;回到顶部看到箭头朝下的折叠栏就先点开,没展开 10 分钟内不再点;留证改存回到顶部的那一屏。测试用当时的截图打码回放,旧代码在 4 种真实位置里有 3 种认不出最上面那一行。
- 真机上
- 10-19 12:16 心跳:电脑 A、B 是 1.25.14,电脑 C 是 1.25.15,都高于 1.25.10,包含这次修复。
- 还没解决
- 展开后的置顶列表没有真机截图,修复时只按箭头方向判断要不要点。修复尚未经独立检验 agent 复核,同形态现场未抽查。本条 10-19 补登:修复 PR 已用了 W-101 编号,台账当时漏登。
W-102读屏把客户备注名拼进消息,本轮刚处理的那句被当成「端上读到了、当时没进代回」:补存一条假的客户消息,并误推漏回卡服务端已修上线 · 独立检验 CODE_ONLY
- 现场
- 10-16 16:20,一台销售电脑。客户回了一句「不用了,我定好了」,系统正常收到了,却又把同一句当成「之前漏掉没回的消息」:多存了一条重复的客户消息,还给负责人发了一张错误的提醒「⏸️ 企微本轮未发 · 请留意」,原因一栏写着「客户这 1 句端上读到了、当时没进代回」。
原因出在电脑读屏:交给服务端的「最近几条消息」里,这一句前面多了客户的备注名(形如 备注名: 不用了,我定好了),而本轮最新消息是干净的原话,两者没被认成同一句。几秒内(16:20:31)会话因「客户连续 2 次拒绝」转了人工,AI 之后没再发消息。客户身份、备注名与聊天原文不在公开页展示。
- 影响
- 误报,不是漏回:运营被叫去看一条已经处理过的消息,卡上写的「下一轮 AI 会照常自动回」对这位已转人工的客户也不成立。库里同一句客户原话多出一条带备注名的重复记录。按库里「客户端补存」的记录统计(登记时口径):09-26 起共补存 80 条,其中 2 条以所在会话客户的备注名加冒号开头(10-10、10-16 各一条,分属两个会话),两条都在不到 1 秒内另有一条同句的正常存库,都属这一类。
- 根因
src/lib/wecom/client-seen-lines.ts 的 unpersistedClientCustomerLines 把客户端看到的每一行和库里最近的消息、本轮消息逐行比(折全角、去空白)。它已经能认出「老消息的半截」(#1822)和广告行(#1830),但认不出「前面多了备注名和冒号」的同一句,于是走进 src/app/api/wecom/chat/reply/route.ts 的补存与漏回告警。读屏为什么会把备注名拼进这一项没有查明(推断是把气泡上方的名字当成了文字,非观测)。
- 怎么修
- #2175:新增
isSpeakerPrefixedKnown:一行如果是不超过 40 字的前缀加冒号、后面是一句已知的客户原话(库里有的或本轮的),就按已知处理,不补存、不报。冒号后面是没说过的话照常补存,真漏回仍会报。新增 4 条测试(全角/半角冒号、库里老话、冒号后是新话照落、前缀超长不认),不改源码时其中 2 条失败,改后全过。
- 上线
- 服务端改动,合并后全量生效。10-16 合并(
e91572f);合并后的首次部署和 main 上的检查都因为 GitHub 机器卡住没跑起来,重跑后都已成功,线上 /api/status 已是 e91572f。上线后还没遇到同类读屏,真实场景未抽查。
- 还没解决
- 客户端读屏仍可能把备注名拼进最近消息,这次只在服务端兜住,客户端未改。10-10、10-16 两条带备注名的重复客户消息仍在库里,没有删。独立检验结论
CODE_ONLY(检验记录):代码、测试、部署和库里计数都已核对,等下一次同形态读屏确认线上真的不再误报。
W-103白天自动升级发出重启装新版的指令后,电脑 A、B 上程序没有自己起来,一直断线到有人手动双击拉起链可观测 + 受控重试 · 根因未查明
- 现场
- 10-18,两台销售电脑。服务端自动升级在白天给客户端下发重启以安装新版的指令(
restart_client),两台都回了送达。电脑 A:08:33:28 日志记下 15 秒后重新拉起,之后一行日志都没有,09:33:01 运营手动双击启动,这时才装好 1.25.13;11:03:32 又收到一次重启(1.25.14),同样空白到 17:03:30 手动启动才装好。电脑 B:08:31 送达重启,日志停在 08:30:33、没有报错,17:03:17 手动启动后装好 1.25.13。
- 影响
- 断线期间这台电脑不自动回复、不发召回:电脑 A 约 1 小时 + 6 小时,电脑 B 约 8.5 小时。到期没发的召回在这两台上越积越多。安装本身没坏:每次手动启动都当场装好新版并正常工作。
- 根因
cloudpc/windows-weixin/main.py 的 restart_client 起一个脱离的 PowerShell,等 15 秒再执行 Start-Process start.bat,本进程 8 秒后退出。电脑 A、B 上这个延后的拉起没有让程序跑起来(之后没有任何启动日志),具体失败在哪一步没有查明:拉起这一步本身不写日志。电脑 A 的安装目录在另一个应用的虚拟化目录里(…\Packages\OpenAI.Codex_…\LocalCache\Local\AkkeWinds),电脑 B 不在,所以不能只归因于它。试点电脑 C 走同一流程能自己起来。
- 止血
- (10-20 起已失效:排除名单已不在生产配置里,改为下面「10-20 进展」的按安装目录拦截。)10-18 起把电脑 A、B 加入服务端自动升级排除名单(
WECOM_AUTO_UPDATE_EXCLUDE_HOSTS),不再给它们发白天重启;自动升级接口试运行回读两台均为 excluded。这两台的新版改为每天早上手动双击时顺带装上。
- 还没解决
- 拉起失败的原因。下一步给延后拉起加一份落盘日志(PowerShell 是否起来、
start.bat 退出码),在一台电脑上复现定位;修好后撤掉排除名单。(10-20:落盘日志已在 1.25.16 加上(#2216),但两台机器一直没装上新版,所以还没拿到;排除名单已改为按安装目录拦截,见下「10-20 进展」。)
- 10-20 进展
- 10-20 查生产环境变量,排除名单已经不在了,两台机器重新会被白天自动重启(上面「止血」一栏的做法已失效)。自启体检上报的安装目录:一台在升级暂存目录(
AkkeWinds.update\<编号>,装完就清理),一台在 Codex 虚拟化目录;试点电脑 C 在普通目录,每次都能起来。服务端 #2258 对这两类机器只下载、不自动重启,等下次手动打开时顺带装上(部署后生产试运行回读两台都是「不自动重启」,输出贴在 #2258 评论)。端上 #2257 在这两类目录上拒绝重启、不退出(要等这两台装上 1.25.26 才生效)。两台什么时候装新版:服务端照常让它们下载,下次有人手动打开程序时,装上当时最新的正式版。拉起失败的确切一步仍然没有留痕,所以这是止血,不是修复。
W-104把几个月前加的老联系人当成刚通过的新好友,发了新好友欢迎语已修出包 · 待装机
- 现场
- 10-19 运营在电脑 C 上截图核对:10 月被选为首触对象的 6 位客户(5 位发出、1 位被自动撤回),加好友的时间其实都在几个月前。其中带地区编号的一位,聊天里的提示是 5 月 12 日 16:56 的添加提示,却在 10-12 13:04 收到首触;另 4 位备注无编号,分别是 8-7、8-15、8-15、8-15 加的好友,首触分别在 10-13 15:05、10-13 16:15、10-14 18:19 至 18:20 之间、10-16 19:28 发出(10-14 这一位日志时间为 18:19/20,具体到分钟以日志为准)。第 6 位带另一个地区编号,4-14 加的好友,10-14 18:09 被选为首触对象,因认不出身份被自动撤回,没有发出。这些客户后来都删了我们,或开了朋友验证,发出的 5 条首触全部带红色感叹号,微信提示对方拒收或需要先通过朋友验证。客户身份、备注与聊天原文不在公开页展示。
- 影响
- 给老联系人发新好友欢迎语。已删我们的人全部拒收,什么也没送到;若对方没删,会在加好友几个月后突然收到一句欢迎语,同时消耗当天的首触额度。
- 根因
cloudpc/windows-weixin/first_touch.py 的 first_touch_verdict(第 323 行)只看四件事:是不是群、客户是否开过口、加好友之后我方是否说过话、direction_from_view 能否从聊天区认出一句加好友提示。它不读提示上方的日期分隔条。这些老好友加上之后没有聊过,窗口里最新一行仍是当年那条添加提示,于是被判成新好友。idle_history.py 第 2019 行调用它之前,runner.py 的 _note_friend_accepted(第 2602 行)确实读了提示的时间标签,并在第 2614 行用 0 <= time.time() - at <= 86400 判断是否在 24 小时内;但这个判断只决定要不要把客户记成刚通过,不参与要不要首触。以上按 wecom-chat main ffb0ada 的源码核对。
- 怎么修
- wecom-chat #2226,10-19 已合并(
a9e8ff7)。三条都已在 main 上核对:first_touch.py 的 NOTICE_MAX_AGE_SEC = 3 * 86400,添加提示所在的日期分隔条距今超过 3 天就不首触(notice_stale);读不到日期也不首触(notice_date_unverified);同一屏出现「开启了朋友验证」「被对方拒收」等提示直接不发(unreachable_notice)。
- 进度
- 10-19 已随电脑端
1.25.19+176dd00 出包(客户端版本台账已登记),三台电脑都还没装上(10-19 12:16 心跳:电脑 A、B 是 1.25.14,电脑 C 是 1.25.15)。装机安排在销售在场时进行。
- 怎么验收
- 用这 6 位客户的窗口形态回放,均不发首触;1.25.19 装到电脑 C 后,首触前日志里的添加日期字段应在 3 天以内。
W-105没送达的首触没有撤销记录:对方拒收的首触仍被记为已发,看板首触被多算已修出包 · 待装机 · 历史 5 行待撤
- 现场
- 电脑 C 上 5 位客户:C·e54fd2(10-12 13:04)、C·c19786(10-13 15:05)、C·d40f0a(10-13 16:15)、C·3d14d5(10-14 18:19)、C·4a49e7(10-16 19:28)。日志在同一拍依次出现
ALERT send_uncertain … failed_marker=True、ALERT customer_unreachable kind=blocked_by_customer 或 kind=friend_removed、first_touch_not_sent result=uncertain tag=send_uncertain、first_touch_sent ok=False。运营截图 5 位都是红色感叹号。可是库里 messages 中这条首触话术(role=ai,status=sent)没有被撤,看板首触多算一条,这位客户也被记成已打过招呼。
- 影响
- 看板的首触数偏高;这些客户在系统里是已打招呼,之后不会再被安排首触;生成回复时的历史里也多了一句客户没收到的话。
- 根因
- 两处没接上。客户端
idle_history.py 第 2153 行 elif unknown or getattr(outcome, "maybe_delivered", False): 把所有 UNCERTAIN 一律当成可能已送达,只记已打过招呼,不撤账(runner.py 第 6934 行 maybe_delivered=result == TickResult.UNCERTAIN)。服务端 src/app/api/wecom/chat/reply/route.ts 的 recordOpenerSent 在交出话术时就先写一行 role=ai/sent,只有收到端上回报的 opener_undelivered(同文件第 3497 行)才撤。同一拍里 customer_unreachable 已经判出对方拒收或已删好友,等于确定没送达,首触这条路径却没有用它。以上按 wecom-chat main ffb0ada 的源码核对。
- 怎么修
- wecom-chat #2226:同拍已经读到
customer_unreachable(来源是首触)时,改走 first_touch_undelivered,按确定没送达撤账;单纯的 UNCERTAIN 保持原样。看板口径另由 #2221 在 10-18 合并,改为以端上回执计首触,不再被幽灵行抬高。
- 还没解决
- #2226 已于 10-19 合并(
a9e8ff7),idle_history.py 新增 first_touch_undelivered 撤账路径;随 1.25.19+176dd00 出包,三台电脑尚未装上(10-19 12:16 心跳:电脑 A、B 是 1.25.14,电脑 C 是 1.25.15)。上面 5 行历史记录仍未撤,删不删待运营决定。验收:下一次对方拒收或需要验证的情形在同一拍出现时,库里首触那一行被撤,看板首触不计。
W-106有的对话没同步进系统:客户说过话,库里两个会话的消息都是 0 条已修出包 · 待装机
- 现场
- 电脑 B 上一位湘地区编号的客户,在系统里对应两个会话 B·6eb55a 与 B·bdc080,备注只差一个表情,实为同一个人。销售电脑上的微信截图显示:10-10 16:20 我方发出加好友附言;10-10 20:07 客户通过了验证,出现系统提示;之后客户发了一句约定过几天联系的话,附带一个握手表情;10-11 10:12 我方回了一句好的。系统里这两个会话的 messages 都是 0 条,好友记录表里却有 10-10 20:07 的通过时间。
- 影响
- 系统以为客户从没开过口:回复率被拉低,召回按客户没开口处理,同一个人被拆成两个会话。
- 根因
- 一句话:备注里的客户微信号被程序当成了打错的手机号,这位客户被判成「不是客户」,聊天就没导入。
细节:这位客户的备注末尾带着客户的微信号(字母开头、后面一串数字)。认人规则把 5 位以上的连续数字都当成手机号的一部分(customer_title.py 的 BROKEN_DIGITS),这串数字又不是完整手机号,于是判成残缺;电脑 B 的日志在 10-11 10:01:30 与 10:13:04 都记了这条拒绝(list_walk_not_customer)。两个会话都是 0 条消息,召回找不到我方最后一句做锚点,也一直没排上。两个会话是备注里的表情被读成不同字符拆出来的。
- 怎么修
- wecom-chat #2232,10-19 合并(
176dd00):电脑端、服务端、安卓三处的同一条规则一起改,跟在两个以上字母(或下划线加字母)后面的数字段算微信号,不算残缺;单个字母后的数字照旧算残缺(车牌头、把 0 读成 O 的手机号)。共享用例表加 4 条,三处测试同读。
- 同类
- 按修复 PR 的统计,生产 698 条 wx_pc 会话里,旧规则因此拒掉的只有这一位(2 个会话)。
- 进度
- 已随电脑端
1.25.19+176dd00 出包,三台电脑尚未装上(10-19 12:16 心跳:电脑 A、B 是 1.25.14,电脑 C 是 1.25.15)。安卓端 0.26.4 同样含这条规则,安卓心跳仍是 0.26.3,也待升级。
- 怎么验收
- 电脑 B 装上 1.25.19 后,日志不再出现这一位的
list_walk_not_customer,这位客户的会话在库里有消息。
W-107置顶聊天折叠栏有时认不出是不是折叠着,程序就不去点开,之后 10 分钟也不再试待修复 · 缺原帧
- 现场
- 10-19,电脑 B(10-19 12:16 心跳为客户端 1.25.14)。微信会话列表顶部有一条「N个置顶聊天」折叠栏。W-101 修复后,程序回到顶部时会先看这一栏右端的箭头:朝下(∨)就点开。当天 00:00–15:44 的日志里,程序 11 次找到了这一栏:7 次认出箭头朝下并点开了(
pinned_fold_expanded,10:32、11:57、12:42、13:43、14:09、14:44、14:55),4 次认不出箭头朝向,没去点(pinned_fold_skip … chevron=None,09:41、12:03、12:16、13:16)。一天里要点开 7 次,推断每次点开后过一阵又被折回去了;日志里没有直接记下折回的那一刻,是谁折的也不知道。
- 影响
- 折叠着的置顶会话不在列表里,那里的客户来消息,红点程序看不到。认不出的那一次之后,10 分钟内程序都不再试着点开。当天还没有证据表明因此晚回了客户:同一天有两段时间左侧聊天图标亮着角标、程序却找不到要回的人(09:33–09:51 共 7 次、11:57–12:19 共 8 次),每次都是回顶、翻页、跳未读后一个红点都没看到。其中只有 11:57:26 那一次是先把折叠栏点开了再找的,仍然 0 个红点,这一次可以排除折叠区;11:58–12:19 的另外 7 次,折叠栏是开是关不清楚,而且中间 12:03、12:16 正好是认不出、没去点。角标到底来自哪一行,要看留证截图,截图只存在销售电脑本地,没有上传。
- 根因
- 一句话:判断箭头朝向的规则太窄,看不出来时一律当作「不能点」。
细节:row_anchor.fold_chevron 在折叠栏右端截一小块,逐行量深色笔画的宽度,最上一行比最下一行宽两倍以上算 ∨,反过来算 ∧,其余(包括深色笔画少于 3 行)都回 None。unread_chase.expand_pinned_fold 拿到 None 不点,并把下次重试推到 600 秒后(FOLD_RETRY_SEC)。认不出的 4 次,程序都已经找到了这一栏(位置在窗口高度的 9.3%–9.7%),卡在下一步认箭头。为什么这 4 帧里箭头认不出(鼠标悬停高亮、箭头被裁到一半、还是这一刻其实已经展开),没有原帧,不下结论。
- 怎么修
- 还没修。先拿到一张认不出时的画面:下次再出
pinned_fold_skip chevron=None,就远程取一张诊断截图,按截图调整判断规则,并加打码后的回放测试。规则改好之前不能改成「认不出也点」:点错一次,就会把销售展开着的置顶又收起来。
- 相关
- W-101(加上「先点开折叠栏」这一步的那次修复)、W-97(置顶会话占满首屏)。
- 怎么验收
- 修复装到电脑 B 后,日志里不再出现
pinned_fold_skip … chevron=None;有折叠栏的那几次全是 pinned_fold_expanded。
W-108聊天里只剩「暂不支持该内容」灰字的老客户,召回每次都被判「画面没核实」退回,一直发不出,也没人收到提醒代码已修 · 1.25.43 起 · 待装机 · 未验收
- 现场
- 10-19,电脑 A(小鱼)两位老客户。运营截图里,两人的聊天框整屏都是带日期的「暂不支持该内容」灰字(后半句是请在手机上查看),没有一句能读出来的话,也没有 3 天内的「你已添加了对方」提示。两人的召回到期后一直没发出去,监控卡上只报了一句「仍未处理 N 项」。这两位不一定在下面说的 4 条卡住的任务里。运营确认两人都不是新好友,定下规则:这类会话当老联系人,照常发通用召回。
- 影响
- 这类老客户永远收不到召回,任务却一直挂着、反复重试,没有人知道原因。10-19 17:10 查库,电脑 A 上还有 4 条召回停在同一个原因(
live_view_unverified):最早一条 9-23 到期,最近一条 10-18 到期,重试最多的一条已试 36 次。这 4 位是不是都是灰字会话,还没逐个看过画面。另外 9-27 和 10-19 按「不是新好友」手动取消过一批召回(分别 10 条、9 条),其中有没有这类灰字老客户,没有核对过,不下结论。
- 根因
- 一句话:召回打开客户后,只认得出「有消息气泡」的画面。整屏只剩灰字的会话被当成「画面没核实」退回,而服务端对这个原因只会无限重试。
细节:现在大多数召回是标题召回,服务端不要求发前往上读(10-19 电脑 B 当天,不往上读 49 次,往上读 7 次)。这条路上,程序读最底下一屏时,正文读法按规定忽略居中灰字,结果是一个气泡都没读到(NO_BUBBLES)。_empty_chat_probe 看到「暂不支持该内容」,判定不是空会话,回 None(日志 unavailable_history_not_empty,这是 10-07 那次修复加的,为了不把这类会话当成新好友)。之后程序落到「输入框空、在底部」的核对,而 NO_BUBBLES 不带这两项观察,于是交还 live_view_unverified。10-07 的修复只覆盖了「发前往上读」那条路:读失败时服务端兜底,照发通用召回;不往上读的这条路没有覆盖到。服务端对 live_view_unverified 走通用重试:前三次间隔 15、45、120 分钟,之后每 30 分钟再派一次,没有上限,也不进待核对。监控只看「挂单没人领」和「空会话等首触」,机器在线时看不见这类任务。
- 怎么修
- wecom-chat #2240(电脑端 1.25.20)。① 读屏结果是
NO_BUBBLES 时,同样带上「输入框空、在底部」两项观察。② 新增一个判断:只有下面几条全部满足才照发通用召回,任何一条没验成都按原来的「画面没核实」退回。条件是:正文读法 0 个气泡、输入框空、在底部;单独读灰字时 0 个气泡,至少一行「暂不支持该内容」,其余每行都是日期时间;首触判据不认为是新好友。③ 发送前最后核对一次:画面仍是同样形状,而且和打开时相比像素没有变化(防止中间进了新消息),才发。④ 服务端监控新增一张黄牌:同一条召回反复因「画面没核实」退回,试满 5 次就按电脑和人设合并报出,写明任务号、销售和已试次数。发出后如果出现红色感叹号、「开启了朋友验证」或「被对方拒收」,由现有逻辑取消召回、停止营销,这次没有改动。
- 验证
- 新增回放测试,用 10-07 小鱼那一屏的灰字结构。正例:整屏灰字、灰字加日期、只有一行灰字,都会走到发送。反例:混入别的灰字、只有日期、新好友提示、输入框有字、不在底部、读屏把握不足、没在打开时验过灰字就想靠像素放行,都不发。拿掉修复后 4 个用例失败,修复后电脑端全量 2195 个测试通过。另派一个没参与实现的 agent 独立复核了代码,结论是没有阻断问题。还没有真机证据。
- 还没解决
- 灰字里可能藏着客户发过的语音或表情,里面也许说过「已经定了」,程序读不到。这和 9-23 运营定的「读不出来也照发通用召回」是同一类风险。另外,如果日期分隔条上没有具体时间(比如只写「昨天」),这一屏仍会按「画面没核实」退回,偏保守。装上之后要看电脑 A 这两位的实际日期条长什么样。
- 相关
- W-99(底部「暂不支持该内容」灰字让两个读屏模型对不上)、W-104(把老联系人当成新好友)。
- 怎么验收
- 1.25.20 装到电脑 A 后,给这两位重新建召回任务,日志里出现
recall_title_only_unavailable_history_tail_verified,接着是发送成功;之后不再出现这两位的 recall_live_view_unverified。上面 4 条卡住的任务里,已试 5 次以上的 3 条要么发出,要么被监控黄牌点名;只试了 3 次的那条另外看。
W-109召回前读聊天时读屏模型被限流,程序没读到客户刚说过不需要,照样盲发了一条通用召回已合并 · 已出包 1.25.21 · 待装机 · 未验收
- 现场
- 10-19,电脑 C。据运营截图:一位当天刚加上的客户,通过后先问门店在哪,我方回了门店定位,客户回复大意是暂时不需要、人不在那边,之后我方又发过消息。16:42 程序把他排进标题召回,16:46 决定发,16:50 发出一条通用召回(讲「和传统定制的区别」那一条,意译)。运营截图发现此事;运营补充说明,这位客户之后又咨询了别的内容,仍有意向,召回照常跟,不停。
- 影响
- 客户刚表达过不需要,紧接着收到一条和当时聊天无关的推销,体验差,也像机器人。近 30 天召回前读不到聊天、按规则兜底发出的一共 194 条(库里任务原因带
read:fallback)。其中多少条是因为读屏模型临时限流才读不成的,库里没有记原因,数不出来。另外,这 194 条里有 93 条发给的会话,系统里的会话记录建立不到一天,库里能用来拦的聊天很少。
- 根因
- 一句话:召回前要往上读聊天,读不成时服务端按 9-23 定的规则直接兜底发;它分不清「这个聊天真读不出来」和「读屏模型这几分钟被限流」。
细节:16:30 起读屏主模型多次返回 429(服务商限流,日志 vision_attempt_failed … status=429),发前往上读判失败(recall_presend_read status=failed … reason=vl_http)。电脑端上送的读取包只有 status=failed,不带原因。服务端 recallContext 对列表召回第一次读不成就走 readFailureFallback,换一个话题、加一个提问照发(任务原因 read:fallback#1)。本来能拦的「24 小时内聊过不召回」(10-19 #2225 加的)只看库里的消息,而库里这个会话只有我方这一条召回,当天客户说的几句都不在库里。日志只看到:16:01 程序点开这个会话时读到最后一条是我方的,按规矩没有再回头去读(retry_probe_dropped);客户的话为什么没进库,疑似和 W-106 同一类,没有逐帧证据。
- 怎么修
- wecom-chat #2242(电脑端 1.25.21,服务端在同一个 PR)。① 电脑端读失败时把原因码一起上送。② 服务端看到原因属于「读屏模型临时不可用」(限流 / 超时 / 连不上 / 主模型没答上),不再兜底发,10 分钟后重新读,读到真实聊天再判;照样计入连续读失败,满 3 次按原规则隔 24 小时再来。③ 其余真读不出来的情况(比如只剩灰字的会话)照旧兜底发,9-23 的规则不变。老版本电脑端不带原因码,行为不变,所以要装上 1.25.21 才生效。
- 验证
- 新增服务端测试:读失败带「限流」原因时,不发、任务 10 分钟后重领、计 1 次连续读失败;带「没读到气泡」原因时照旧兜底发。拿掉服务端修复后,第一个用例失败。新增电脑端测试:读失败的读取包带原因码,读成功的不带。电脑端全量 2196 个测试通过,服务端相关 173 个测试通过。另派一个没参与实现的 agent 独立复核:日志、任务行、消息表三处证据链成立;电脑端读取包从发前读取原样送到服务端;没有阻断问题。还没有真机证据。
- 还没解决
- ① 当天客户的话没入库(疑似 W-106 同族),程序不知道当天聊过。读成功时,服务端能从现场读到的聊天里看到;读不成时仍然看不到。② 兜底召回本身的风险没变:真读不出来的会话照样盲发。是否把「系统对他一无所知」(前一天内才第一次见到、库里没有聊天)的会话也改成必须读到才发,已请运营定,默认不改。③ 限流本身没有处理:读屏主模型用的是服务商共享额度,高峰时会 429。④ 连续 3 次限流后隔 24 小时再来,期间这位客户不会兜底发,比原来的「尽量都发」慢。
- 相关
- W-106(对话没同步进库)、W-108(灰字老会话召回发不出)。
- 怎么验收
- 1.25.21 装上后,下一次召回发前读取遇到限流时,电脑端日志里不再接着出现
recall_context_fallback_send … reason=vl_http;库里该任务原因变成 read_transient#1,约 10 分钟后被重新领取,读成功后再决定发不发。
W-110安卓手机核对聊天底部读到「被拒收 / 朋友验证」(多半是对方已删我们或拒收)却不报服务端,同一批人被反复重开,召回日发从 32 掉到 4已合并 · 已出包 APK 0.26.5 · 待装机 · 未验收
- 现场
- 定制小夏(安卓手机执行器)。据运行记录:核对聊天底部时读到「被拒收」或「朋友验证」提示,说明对方已经删了我们。执行器只在本机跳过,不告诉服务端,所以服务端仍把这些人排在召回名单里,下一轮又重开。漏斗里
bottom.fail.blocked 从 131 升到 306,日发从 32 掉到 4(数字据运行记录,本次未重新取数)。
- 影响
- 召回名额被已经删了我们的人占满,能发的客户发不出去,小夏的召回几乎停摆。
- 根因
- 一句话:执行器知道对方已删我们,服务端不知道。
细节:RecallPolicy.recallBlocker 读到这两类系统提示时只在本机判失败,没有上报;服务端没有标记这个人联系不上、该停营销的出口,名单里的人一直有效。
- 怎么修
- wecom-chat #2244(APK 0.26.5,服务端同一个 PR)。读到这两类提示就上报新 action
android_recall_unreachable,服务端给该客户打上停营销标记,之后召回见到标记直接跳过;同一人 6 小时内不重复上报。10-20 又补了一处:新 action 漏登记进 ANDROID_RECALL_ACTIONS 白名单,不补的话新包一上线调用就被拒。
- 验证
- 新增服务端测试 3 个:认出唯一客户后写标记;重复上报不重写;重名、种类无效、没带客户名都不写。独立复核在隔离目录跑相关测试 21 个全部通过;白名单与按 action 路由、鉴权的位置(
route.ts、reply-key-scope.ts)都引用同一个数组。没有真机证据,Kotlin 端测试当时依赖 CI 的 gradle 任务,复核时还没出结果。
- 还没解决
- ① 上报只接在一处核底检查,另两处「被拒收」分支是否都经过这一处没有逐条核。② 上报在后台线程,失败只写日志不重试。③ 线上还没有任何一次「被停客户」的真实记录。④ 合并后的服务端部署、APK 正式包、装机都没有证据。
- 相关
- W-105(拒收后一律当可能已送达)。
- 怎么验收
- APK 0.26.5 装到小夏手机后,日志里出现上报
android_recall_unreachable 且服务端返回成功;之后 bottom.fail.blocked 不再上升,日发回升。
W-111销售手动发过消息、程序判「销售在打字」让位后,客户新来的消息一直没人回,也没有到点把客户接回来的机制已合并 · 随 1.25.23 出包 · 待装机 · 未验收
- 现场
- 10-19,电脑 C,一位客户。据运行记录:15:34 销售手动给他发过消息;19:38:57 程序判「销售在打字」让位(
stage=handed_off reason=sales_typed);19:39:30 客户回复,约周四、周五上门测量;到 22:49 仍没有人回。
- 影响
- 让位本意是销售在聊,程序不抢话。但销售后面没接上,客户的约时间消息就一直晾着。库里电脑端因
sales_typed 让位的会话共 6 个,其中 3 个最后一条是客户说的(据运行记录,本次未重新取数)。
- 根因
- 让位之后程序只会等销售来接,没有到点还没人回就由 AI 接回这一步。
- 怎么修
- wecom-chat #2246,已合并(0dfba42e,10-19 15:04Z),服务端 10-19 已上线。① 销售让位后到点没人回,就由 AI 接回:服务端算出接回时间点和「客户是否欠一句回复」,端上
takeback.py 到点点开该会话,当天核对过的也照样点开,补回放行一次。② 提醒卡里电脑端因销售手打让位的文案,改成:销售没接,AI 稍后接回。电脑端 1.25.22 没有单独出包,随 1.25.23 一起出包。
- 验证
- 独立复核跑了 4 组测试:
test_takeback.py 8 个、服务端 handoff 相关 146 个、相邻电脑端用例 275 个、发版脚本测试 11 个,全部通过。反例也有用例:销售在客户之后回过话不接回;刚认出销售手打的冷静期内不抢话;客户要真人或发火的转人工不排接回;超过 1 小时作废。没有真机证据,没看到生产日志里的 takeback_scheduled 一行。
- 还没解决
- ① 这位客户已经过了 1 小时,不会自动接回,已请销售手动回。② 端上要在 1 小时内巡检到该会话,轮不到就作废;时钟容差只有 30 秒。③ 只做了电脑端,安卓和 macOS 没有同类接回。④ 电脑端 1.25.22 没出包也没装机。
- 相关
- 暂无直接同类编号。
- 怎么验收
- 1.25.23(含 1.25.22 内容)装到电脑 C 后,下一次销售手打让位且客户之后来消息、销售没回时,日志出现
takeback_scheduled,到点后 AI 回复发出。
W-112新好友「已添加」提示被聊天顶出屏外,程序只看当前屏,没记下「通过」,当天新加好友漏统计修复中 · 未合并 · 未验收
- 现场
- 10-19,电脑 C,一位当天新加的客户。据运行记录:16:42 列表巡检第一次点开这个会话时,「你已添加了…」灰字已经被后来的聊天顶出了屏幕。C 当天
friend_accepted 日志 0 条,weixin_friend_lifecycle 表里 C 10-19 0 行(据运行记录,本次未重新取数)。
- 影响
- 当天新加的好友没被统计;运营看到的「通过数」比实际少。客户自己先开口说话的,同样没记。
- 根因
_note_friend_accepted(cloudpc/windows-weixin/idle_history.py 两处调用)疑似只看当前这一屏(未做代码复核):通过提示一旦被顶出屏,就找不到了。
- 怎么修
- 运营要求:往上翻找通过提示,找到就按提示带的当天日期入库;客户先开口说话的也要统计,只计数,不补发首触。修复在 wecom-chat 分支
fix-friend-accept-scrollup 里实现,尚未开 PR。
- 验证
- 还没有 PR,没有可检验的代码,没有独立检验结论(UNVERIFIED)。
- 还没解决
- 整条修复未合并、未出包、未装机。往上翻会多读几屏,会不会拖慢巡检、翻多远停下,等 PR 出来再看。
- 相关
- W-109(同一位客户)、W-104(把老联系人当新好友)。
- 怎么验收
- 修复装机后,当天通过的好友在
friend_accepted 日志和 weixin_friend_lifecycle 里有对应记录;客户先开口说话的也算进去,且没有多发首触。
W-113会话列表第 6 行已经是绿底选中、右边却没打开它:程序读不出标题就跳过,下一轮又选它,在原地打转一整夜已合并 · 生产已见信号 · 未验收
- 现场
- 运营要求登记。10-19 晚到 10-20 早,电脑 A。客户端日志(北京时间):20:24:25 到 07:24:22,
selection_current_reused owner=reply row=6 click=False 共 1007 条,意思是程序判定第 6 行已经是选中状态,没有去点。同一段时间 SKIPPED … reason=identity_unverified 共 3038 行(20:14:47 到 07:26:15,其中第 6 行占多少没有分开数)。07:25:47 起日志里才换成第 10 行。抽一个时间点看:22:09:49 复用第 6 行不点,随后标题条连续 4 次读出空白,22:10:10 ALERT identity_unverified,跳过。整机这段时间没有回复、没有召回(据运行记录,日志侧没有逐条核)。电脑 B 10-19 20:16 到 20:29 同形态:ALERT identity_unverified 25 次(B 只核了这个次数,没核是否也是「复用不点」)。
- 影响
- 第 6 行的客户若有未读消息,红点一直在,没人回;程序全程忙在这一行,别的客户和召回疑似也轮不到(日志只能看到整机反复处理同一行,没有逐个核别的客户)。
- 根因
- 一句话:程序以为「绿底就是已经打开」,其实右边没打开,又没有别的出口。
细节:cloudpc/windows-weixin/runner.py 的 _handle_row(修复前约 6041 行)里,这一行已经是绿底选中就不点(selection_current_reused / reuse_current);但右边没打开这个会话,标题条读不出,走 ALERT identity_unverified 跳过;红点还在,下一轮又选中同一行,仍然不点。
- 怎么修
- wecom-chat #2248(电脑端 1.25.23),10-20 07:55 已合并(35cc9dd9)。上一轮绿底没点、标题又读不出的那一行,下一轮强制真点一下(日志
INFO selection_reuse_overridden row=N),认人成功就清掉记录。逻辑在 conversation_selection.py 的 reuse_blocked_by_blank / next_blank_row。
- 验证
- 新增测试
test_selected_row_with_unreadable_title_is_clicked_next_round。独立复核把代码换回修复前,这条用例的两个参数都失败(下一轮仍不点);既有反例(绿底且标题读得出时照旧复用不点)通过;该文件 7 个用例通过。没有真机证据。
- 还没解决
- ① 强制点击后标题仍然读不出,会不会反复点同一行,测试没覆盖。② 只记一行,几行交替出问题时的表现没测。③ 测试只模拟「标题读不出」,没模拟「右边确实没打开」。④ 1.25.23 已出包,还没装机。⑤ 第 6 行是哪位客户没有查(日志里没有标题)。
- 相关
- W-108(被退回后无上限重派)、W-107(认不出就不点开)。
- 怎么验收
- 装 1.25.23 后,日志出现
selection_reuse_overridden,且不再有同一行长时间反复 identity_unverified。
W-114召回的图片贴进输入框后回车被拒,图没发出去,输入框留下「[草稿] [图片]」,第 2 张没发,任务却仍记成已发代码已改 · 待合并出包 · 待装机 · 未验收
- 现场
- 10-19 12:08,电脑 B。一条召回是文字加 2 张图,文字发出去了。日志:第 1 张图回车被拒(
ALERT poster_enter_rejected index=1),清空输入框没清掉,输入框里留成「[草稿] [图片]」;12:08:23 ALERT recall_images_missing posters=0/2,写着 文字已送达、图没发全,按已发回执。第 2 张没发。任务仍记成已发,也没有提醒。
- 影响
- 客户只收到文字,没收到图;输入框里的草稿残留,下一条消息可能带上它。运营看任务状态是「已发」,发现不了。近 7 天带图召回 208 条,图没发全 11 条(电脑 B 5、电脑 C 5、电脑 A 1;口径:
wecom_client_events 里 code=recall_images_missing,occurred_at 不早于 10-12T16:00Z;数字据运行记录,本次未重新取数)。
- 根因
- 发图失败的几条路径里,清空输入框之后没有再看一眼是否真清空了;服务端收到的回执也只有「已发」,不带图发了几张。
- 怎么修
- wecom-chat #2250(电脑端 1.25.25)。① 失败路径清空输入框后抓图核对,没清成就报
ALERT poster_draft_left 并发提醒。② 回执带 imagesSent / imagesRequested,服务端把任务原因追加为 images_incomplete:S/R。没有做自动补发(需要新的任务类型)。
- 验证
- 独立复核:电脑端新增测试 5 个通过,服务端相关测试 188 个通过,电脑端 poster / recall / image 相关 226 个通过;图全发成功时不改 reason。PR 当前必选检查
tsc + lint + test 是红的,原因是 handleWindowsRecall 函数行数超过棘轮上限(1015 行,上限 1013),不是逻辑错误;没合并。没有真机证据。
- 还没解决
- ① 粘贴后抓不到图(
poster_unverifiable)那条路径仍是直接清空,没有核对,也不报 poster_draft_left,同类草稿在那里仍然静默。② 重贴第二次的清空同样没核对。③ 缺图只能从任务原因后缀看出,没有结构化的列。④ 没有自动补发。⑤ 合并、服务端部署、出包、装机都还没有。
- 相关
- W-100(召回正文发送)、W-105(发送回执)。
- 怎么验收
- 1.25.25 装上后,再遇到贴图失败,日志出现
poster_draft_left 并有提醒,库里该任务原因带 images_incomplete:S/R;图全发成功的任务不受影响。
W-115等召回发送名额时,程序把找新好友、首触一起停了,一位半夜通过的客户完全没被看到已合并 · 生产已见信号 · 未验收
- 现场
- 电脑 B,10-19 晚。日志:22:06:12 最后一次列表巡检;22:06:20
topic_one_assessed reason=waiting_for_send_slot;此后到次日 07 点多,只剩每分钟一条 history_gate status=waiting_send_slot。一位客户 23:39 通过了好友,程序完全没看到,首触没有发出(后两句据运行记录,本次只核了 22:05 到 22:08 这一段日志)。
- 影响
- 只要有召回在等发送名额,整台机器就不再巡检会话列表,找新好友和首触都停下,这次一停就是一整夜。
- 根因
cloudpc/windows-weixin/idle_history.py 约 2265 行:if getattr(self.client, "send_slot_waiting", False) is True: 之后直接返回,在列表巡检之前就提前退出了。
- 怎么修
- wecom-chat #2252(电脑端 1.25.24),待合并。等召回名额时列表巡检照走(只扫顶部页、整行没变的行跳过,步长放慢到 30 秒,不烧当天点开额度),新好友发现与首触不再被冻结;召回在等名额期间仍暂停,名额到了恢复原样。首触关着时仍整拍冻结。
- 验证
- 修复 PR #2252 已开、未合并,必选检查当时为红(版本号闸,已改正重跑),尚未做代码复核(UNVERIFIED)。
- 还没解决
- 修复未合并、未出包、未装机。那位 23:39 通过的客户,要等名额放开后程序重新翻列表才会看到;如果迟迟没被看到,需要人工补。等名额期间巡检可能把正在等的召回对象点走,任务回来时走现有的让位路径,这块没有专门测试。
- 相关
- W-104(首触判断)、W-112(新好友漏统计)。
- 怎么验收
- 修复装机后,召回在等名额期间,日志里仍有列表巡检,新通过的好友当晚就被看到并发首触。
W-116通过好友没自动统计:每日汇总只认销售报的数,程序记通过又比首触窄,10-19 通过的 4 位当天一位都没准时记上已合并 · 待出包 · 待装机 · 未验收
- 现场
- 10-19 销售报通过 4 位(电脑 A 2 位、B 1 位、C 1 位)。看板「每日汇总」里的通过数是 10-20 09:13 按销售报数手录的。程序自己的通过记录里,这 4 位当天一位都没准时记上(下面的时间来自通过记录;各台卡住的原因据运行记录,没有逐条重核):A 一位 22:18 通过,10-20 10:02 才记上,那晚 A 一直在列表同一行上来回打转(W-113);B 一位 23:39 通过,10-20 09:12 才记上,那晚 B 在等召回发送名额,把翻会话列表也停了(W-115);C 一位点开时通过提示已被聊天顶出屏(W-112),10-20 按截图手补;A 另一位 17:44 首触已发出(日志已核),但当天日志里没有「记通过」那一行,10-20 按截图手补日期。
- 影响
- 看板每日汇总的「通过」只能靠人报;从这 4 位看,程序记的通过偏少、偏晚。另查 10-20 当时的通过记录:9-30 以来有首触回执的 31 位里,11 位没有通过日期(其中有一部分可能是 W-104 那批被误当新好友的老联系人,没有逐条拆分,不全是漏记)。
- 根因
- ① 看板每日汇总只显示销售报的数,没去读程序自己记的通过(
src/lib/weixin-friend-daily.ts)。② 程序记通过只认 24 小时内的「已添加」提示(10-01 定),首触却认 3 天内的(10-19 合入,1.25.19 是第一个带它的正式包):1–3 天前通过的好友,首触照发、通过不记(runner.py _note_friend_accepted 与 first_touch.py)。A 那位当时装的是旧版(据运行记录 ≤1.25.15),旧版首触读不到提示日期也照发,记通过却要读到日期,于是首触发了、通过没记。③ 程序要点开那个会话、看到「已添加」那行灰字才记通过,W-112 / W-113 / W-115 任何一个卡住都会让通过晚记或漏记。
- 怎么修
- wecom-chat #2261(10-20 已合并,电脑端 1.25.28):每日汇总接口另外返回程序记的通过数(按电脑、按通过那天);看板(wiki #177,10-20 已合并)在报数旁显示「系统记 N」,两边对不上标黄。端上记通过也放宽到 3 天,和首触一样,记的仍是提示上的通过时间,不是扫到的时间。
- 验证
- 本地自动测试与检查全部通过。独立检验结论见顶部状态。
- 还没解决
- 服务端部署、1.25.28 出包和装机还没完成。读不到提示日期的新好友,新版既不首触也不记通过(为避免把老联系人当新好友,故意这样,见 W-104);会话没被点开之前程序看不到通过(W-112 往上翻找在修);1.25.28 装机前,1–3 天前通过的照旧漏记。10-19 漏记的两位(C 那位、A 17:44 那位)已按截图手补。
- 相关
- W-112(通过提示顶出屏)、W-113(一行打转)、W-115(等名额停巡检)、W-104(老联系人当新好友)。
- 怎么验收
- 上线后看板每日汇总选 10-19,A/B/C 三台的系统记分别是 2、1、1,与报数一致、不标黄;装 1.25.28 后出现一位隔天才被点开的新好友时,日志里出现
friend_accepted reported=True(出现这行就算记上了),看板按提示上的日期计数。
W-118销售用过电脑后,列表巡检按旧行号接着点,被滚过的一段客户整段跳过(新好友漏首触)代码已修 · 1.25.40 起 · 待装机 · 未验收
- 现场
- 电脑 C(1.25.26)。10-20 一位新好友(贵A61**88)11:36 通过。C 10:11 远程升级重启,11:51 才起来(和 W-103 同源:远程升级重启后迟迟没起来)。11:52 回到列表顶部后先核了展开的置顶区第 0–8 行;11:54–11:57 销售在用这台电脑,巡检让路(日志
recall_topic_one_yield_to_reply / waiting_desktop_idle,idle_ms 显示约 11:55:27、11:56:12 有真人输入)。11:57:18 恢复时程序按记下的第 9 行去点,但列表已被销售滚过,屏幕上第 9 行的位置换成了另一位客户(置顶区往下第 6 位);折叠栏下面的 5 位(含这位新好友)被整段跳过,11:57:25 就翻到下一页。运营 12:1x 人工补发了首触。
- 影响
- 电脑一旦被销售用过又让回给程序,被滚过的那一段客户这一轮不会被看到;其中的新好友漏首触,要等下一轮或人工补。
- 根因
- 列表巡检只按上一拍落盘的(页, 行)点下一行,两拍之间不核对列表有没有被人滚过(
cloudpc/windows-weixin/idle_history.py 的 walk_tick)。程序自己没动列表,但销售动过。
- 怎么修
- wecom-chat #2270(电脑端 1.25.30,已合并 09b041a,已出包 1.25.30+09b041a,台账已记):新增
list_walk.resync_after_human。上一拍之后只要有真人键鼠输入(win32.idle_ms 只算真人),就先记下断点、回到列表顶部重新定位,再按页翻回断点处接着巡检。#2272(1.25.31,自动合并中)补上独立检验指出的缺口:刚回顶、还没点第一行时被滚过,也重新回顶。
- 验证
- 独立检验(未参与实现):
CODE_ONLY 通过。新增测试在旧代码上失败、在新代码上通过;检验指出一处缺口,由 #2272 补上。
- 还没解决
- 1.25.31 未合并,两个版本都没装机。没有真人输入的列表重排(新消息把会话顶上来)不在覆盖范围内;兜底档下,深页的断点会从第 0 页重走(既有行为);销售频繁打断时,回顶次数有上限,会多花一些。
- 相关
- W-103(远程拉起不可靠)、W-112(新好友漏统计)、W-115(巡检被停掉)。
- 怎么验收
- 电脑 C 装上后,日志出现
list_walk_resync_after_human,且之后回顶接上、没有跳行。
W-119「每天新话题」模式下,到期的召回任务只等巡检碰巧翻到这位客户,翻不到就一直挂着,没有作废,也没有按逾期天数的告警根因已查明 · 逾期搜索出口 1.25.43 起 · 待装机
- 现场
- 电脑 B,10-20 晚 21:4x 直接查库(表
windows_recall_tasks):状态是待领、到期时间已过的召回任务共 40 条(精确计数)。任务上记的原因:list_target_not_open 35 条、preempted_by_unread 2 条、read_failed 2 条、recall_tail_unverified 1 条。最早一条 9-22 到期,多数在 10-04 和 10-14 到期。当晚运营取消了 1 条(一位 10-20 新加的好友,首触已由销售手发),剩 39 条。这 39 位是不是改了备注、删了好友、还需不需要召回,运营正在逐个核对,结论还没有。
- 影响
- 这些客户的召回过了到期日也没发,有的已经压了近一个月;任务不会过期。服务端日报里有“逾期待发 N”的计数,但设备在线且客户端开关是 live 时,不会出文字告警,也不按逾期天数或原因提示;这 40 条是 10-20 晚有人直接查库才看到的。
- 根因
- 「每天新话题」只发列表里刚认准、会话就开在眼前的客户。服务端不替它挑人:
src/lib/wecom/windows-title-recall.ts 的 pollTitleRecall 在 serverNext && !customerName && daily 时只回 waiting_for_title_scan,注释写明“不扫表、不派单”。任务要等列表巡检翻到并点开这位客户、客户端来要任务才会被领走。点开时目标不在眼前,客户端按 cloudpc/windows-weixin/runner.py 的 _recall_yield(定义约 4666 行;list_target_not_open 交还处约 4888 行)交还任务,注释写明“不去搜索框找人”(代码注释称搜索找人是第二次召回 0/42 的卡点),任务回到待领。所以巡检永远翻不到的客户(备注改了、藏得太深、被前面几类问题挡住),任务就一直待领。我在 src/lib/wecom 和 cloudpc 里没找到给到期任务设过期、按逾期天数告警(安卓侧有类似的逾期超 1 天告警,Windows 侧没有),或在巡检够不着时换一条路联系这位客户的代码;已有的只有日报“逾期待发 N”计数和心跳日志“到期未领 N 条”,没有做全仓穷尽搜索。
- 怎么修
- 方案还没定,运营在核对 39 条备注。候选(待运营定):① 不再需要召回的取消;② 还需要召回的,先看是不是翻得太深,必要时请销售打开该会话;③ 长期:到期未发要有提醒。③ 里已经答应的默认做法是:当天翻到底仍没见到这位客户就告警;另外每天上午发一张卡,列出到期 2 天以上还没发的任务。以上都未开发。
- 验证
- 表里的计数和原因分布来自 10-20 晚的直接查库;机制来自
Akke-AI/wecom-chat 主干(origin/main)上的上述两处调用链,只读了这两处,未做全仓搜索。没有修复,没有独立检验。
- 还没解决
- 整条都没解决。这 40 条逐条为什么翻不到没有查;只有
list_target_not_open 的 35 条直接对应上面的机制,其余 5 条(被未读抢占 2、读屏失败 2、尾巴未核实 1)触发条件不同,但同样经 _recall_yield 交还,是否也由这条机制拖着没核。只查了电脑 B,电脑 A、C 有没有同样的积压没查。
- 相关
- 翻不到这一行的各类原因:W-120(翻页接缝)、W-118(被销售滚过)、W-107、W-72、W-73、W-43、W-37;同样是“卡住了没人提醒”的 W-108。
- 怎么验收
- 运营核完 39 条并取消不需要的之后,电脑 B 上待领且到期的召回任务只剩确实要发的;新增告警上线后,在现有日报计数之外,第二天上午的卡里能列出到期 2 天以上未发的任务,当天翻到底仍没见到客户时有告警一条。
W-120列表翻页的接缝处的行,整天都点不到(新好友漏首触)第三轮代码复核通过 · 1.25.47 · 待装机 · 未验收
- 现场
- 电脑 C(10-20)。一位新好友(贵A61**88)11:36 通过,销售 12:04 手发了首触;这位客户的名字在电脑 C 当天的日志里出现 0 次,列表巡检整天没点开过他。额度用完后,巡检每圈只走顶部几页:第 0 页点完 0–9 行 → 翻页 → 新一页第 0 行已经是再往下一位,这位客户在页缝处被滚出了画面。电脑 B 同日另有两位好友(湘A61**19、闽G61**06,16:49、16:41 通过)日志里同样没有被点开的记录,也是销售手发的首触;这两位是否同属接缝跳行未逐条核,B 机日志是否逐条查过 0 次也未复核。
- 影响
- 列表顺序稳定时,页缝处被滚出画面的若干行每一圈都被跳过,永远轮不到;这次这位新好友是销售手发的首触。
- 根因
cloudpc/windows-weixin/idle_history.py 的 _walk_page_down 按“滚 4 格 × (行数 − 1)”翻页,设计上应当留一行重叠。电脑 C 当天的日志里,翻页后新画面第 0 行出现在上一页的只有 1 次(页数按日志统计,约三百多次,不同口径有出入)。据日志与代码推断:滚轮每格不止 1/4 行,多滚出去的行被顶到表头下面,_refresh_row_phase 又把表头下面第一条完整可见的行当作第 0 行,被切掉的行就不在任何一页的行序列里(未在真机上量过滚轮像素)。
- 怎么修
- wecom-chat #2279(电脑端 1.25.33,已合并 47305829,已出包 1.25.33+809f689,台账 #2280 已合并):新增
walk_seam.py。翻页前记下最后 3 行的整行指纹,翻完在新画面里找;找不到就每次往回滚 2 格、最多 8 次,找到后从“上一页最后一行的下一行”接着点;用完次数仍找不到,留日志 list_walk_seam_lost 并按新画面第 0 行继续。巡检(idle_history)和未读红点追踪(unread_chase)的翻页都接入。接缝重叠后“折叠置顶聊天”栏会第一次进入行序列,点它会把展开的置顶收回去,按像素认出来跳过。
- 验证
- 独立检验(未参与实现):
CODE_ONLY。新增 9 条测试在 1.25.33 上全过;把同一份测试放到修复前的代码上,多数用例失败(页缝丢行、重叠行被重复点;其余因缺少新模块报错,旧代码上的结果为检验人本机运行)。windows-weixin 全量 2246 条通过。
- 还没解决
- 1.25.33 已装机:电脑 B 21:31、电脑 C 22:38(心跳版本 1.25.33+809f689);电脑 C 装机后,巡检 23:09 点开了此前从未被点开的一位新好友并建出会话,是第一条真机证据。但日志里的
list_walk_seam ok / list_walk_seam_lost 还没核,页缝两侧的行是否每圈都被巡检到也没核,按“待日志验收”处理。最多回滚 8 次(共 16 格)仍找不到时退回旧行为并留 WARN;折叠置顶栏的像素判断只用模拟画面测过,没有拿真机原图验。
- 相关
- W-118(同一位客户 11:57 那一圈被跳过,W-118 记为销售滚过列表所致;本条是每一圈都会发生的页缝跳行,两者可能叠加)、W-103(远程拉起不可靠)、W-115(巡检被停掉)。
- 怎么验收
- 电脑 C 已装 1.25.33,接下来看日志出现
list_walk_seam ok(翻页接缝验证通过),且额度用完后的每一圈里,页缝两侧的行都被巡检到(点开,或按规则跳过);出现 list_walk_seam_lost 要回头查。
W-121销售在电脑上手发的新好友首触,程序补记不进库(只补「客户最后一句之后」的我方消息)根因已查明 · 未修
- 现场
- 10-20 三位新好友的首触是销售在电脑上手发的:电脑 C 的魏*发(贵A61**88),电脑 B 的淡*女、华*装饰。三条首触都没有被程序记进库,是 10-21 运营发截图后人工补记的。
- 影响
- 看板「添加好友记录」和首触统计里少了这几条;后续召回、回复率都按“没发过首触”算。
- 根因
- 销售手发消息的补记(
Akke-AI/wecom-chat #2254,1.25.26;PR 标题误写成 W-106)只从“屏上最后一条客户气泡之后的我方气泡”里挑要入库的(src/lib/wecom/sales-typed-own-tail.ts 文件头注释,PR 说明的已知局限②)。新好友的首触发在客户回话之前,排在第一条客户气泡前面,永远不在这个范围里。
- 怎么修
- 默认做法:新好友会话在库里还没有我方消息时,把屏上第一条客户气泡之前的我方气泡也补进来(排除加好友验证语和招呼语),记成首触。未开发。
- 验证
- 机制来自主干(
origin/main)上的上述文件;三位客户的漏记来自 10-21 运营截图与库里核对。没有修复,没有独立检验。
- 还没解决
- 整条都没解决。
- 相关
- W-106、W-116、W-120。
- 怎么验收
- 修复装机后,销售在电脑上手发一条新好友首触,当天库里出现这条首触,看板「添加好友记录」能看到,不需要人工补。
W-122名字没变、搜得到的老客户,到期召回仍一直发不出去:列表巡检翻不到,逾期搜索出口又一条都没跑过搜索出口已装机并在跑(电脑 B 已发 11 条)· 积压未清 · 未验收
- 现场
- 接 W-119:电脑 B 上到期未发的 39 条召回,运营 10-21 在微信里逐个人工核对:37 位名字没变,在微信搜索框都能搜出来;1 位只改了名字里的表情(川A60**91 高*洪,🏠 改成 🏡);1 位已不是好友(闽C61**08 小*,会话里有“开启了朋友验证,你还不是他(她)朋友”灰字,10-13 那条我方消息带红叹号)。所以 W-119 原来猜的“备注改了 / 删了好友”只占 2 条,绝大多数是程序自己没去找。
- 影响
- 37 位能联系上的客户,召回压了 1 周到 1 个月没发(最早 9-22 到期)。
- 原因
- ① 这些任务几乎从没被领过:10-21 08:1x 查库,电脑 B 这 40 条待领任务里 39 条从未被领取(
claimed_at 为空),32 条一次都没尝试过(attempts=0),说明列表巡检始终没有在这些客户的会话开着的时候来要任务。
② W-119 加的“逾期 ≥2 天用搜索框找人”出口(1.25.43 起)全公司一次都没跑过:库里带搜索领取标记(dedupe_key 以 overdue-search: 开头)的任务为 0 条。原因是三台销售电脑都还没装上:10-21 08:12 心跳电脑 A 是 1.25.32、电脑 B 和 C 是 1.25.33;电脑 B,1.25.47 已下载好(stage=ready)等销售双击安装。
③ 装上后每台每天默认只用搜索框领 3 条(src/lib/wecom/windows-title-recall.ts 的 OVERDUE_SEARCH_DEFAULT_PER_DAY),39 条要 13 天左右才能轮完。
改表情那位不受影响:搜索按六位编号搜,打开后认人只比地区 + 编号(cloudpc/windows-weixin/guards.py 的 identity_gate)。已不是好友那位:打开会话后程序能认出「朋友验证」灰字并上报停发(runner.py 的 customer_unreachable_detected),但会话从没被打开,所以一直挂着。
- 怎么修
- 已做:已不是好友那位的召回任务已取消(10-21,原因记
not_friend_verified_by_ops_20261021)。
待做:① 电脑 B 装 1.25.47(需要电脑 B 的销售双击安装包);② 装后第一天看搜索出口是否真的领到并发出(日志 recall_overdue_search_opened、库里出现 overdue-search: 标记);③ 第一天发出正常就把电脑 B 的每日搜索额度调到 10,约 4 天清完积压;④ 巡检为什么一直没碰到这 37 位(深处轮巡是否覆盖到、是否被每日点开额度截住)还没查。
- 验证
- 计数来自 10-21 08:1x 直接查库(表
windows_recall_tasks、wecom_loop_heartbeats);机制来自主干上述文件。没有修复,没有独立检验。
- 还没解决
- 10-21 22:0x 更新:电脑 B 已装 1.25.53、电脑 C 已装 1.25.51,搜索出口已在跑,库里带
overdue-search: 标记的任务 15 条(电脑 B 已发 11、待发 3,电脑 C 待发 1);电脑 B 建于 10-20 之前、仍待发的召回任务还剩 14 条(上午 09:5x 是 35 条),积压还没清完,没有独立检验。以下为 10-21 上午的记录:37 条仍待发;搜索出口还没在任何一台机器上跑过;巡检翻不到的原因没查(电脑 B 近几天的客户端日志这次没拿到)。电脑 A 另有 15 条到期未发,没核。
- 相关
- W-119(到期召回挂着不过期)、W-120(翻页接缝)、W-108。
- 怎么验收
- 电脑 B 装上 1.25.47 后,库里出现
overdue-search: 标记的任务并且状态变成已发;37 条积压逐日减少到 0;运营在微信里能看到这些客户收到召回。
W-127销售电脑开着 Clash 全局代理,程序连中转全部 SSLError:电脑 A 失联约 29.5 小时,启动时也下载不了新版,关掉 VPN 才升级成功1.26.3 已加自动直连 · 待真机触发验证
- 现场
- 电脑 A(张*鱼)。10-21 12 点那一小时日志上传正常;13:30:51 起读屏模型调用
ProxyError,随后读屏、心跳(heartbeat_failed)、召回拉任务(recall_poll_failed)、日志上传(log_upload_failed)全部 SSLError。程序一直开着,只是连不出去。10-22 18:53 销售双击程序,运营当时的截图显示启动时的更新检查失败、异常是 SSLError,新版没下载下来。运营人工排查,发现销售在 Clash 里开着全局模式的 VPN;关掉 VPN 后,18:59 服务端重新收到这台电脑的上报,新版才下载成功。
- 影响
- 10-21 13:30 到 10-22 18:59 约 29.5 小时,电脑 A 不读新消息、不自动回复、不发召回,看板显示离线,到期召回越积越多。关 VPN 后装新版又碰上 W-128(跑进暂存目录、配置回到出厂),召回到 10-23 07:57 才恢复真发。
- 根因
- 客户端连中转用的网络库(Python
requests)在 Windows 上会自动读系统代理和 HTTP(S)_PROXY 环境变量。Clash 开着系统代理时,程序连自家中转的每一条请求(读屏、心跳、召回、日志、自动更新)都被送进 VPN 节点;节点对中转握手失败,就全部 SSLError。程序只连自家中转,本不需要翻墙;10-15 三台电脑同一分钟失联 2 小时也是这个机理(当时是 Clash 节点挂了,同机直连中转正常)。10-21 这次 12 点那一小时还通、13:30 起才断,是节点挂了还是那时才切到全局模式,没查明。1.26.3 之前客户端没有任何绕开代理的处理。
- 怎么修
- 已做:
Akke-AI/wecom-chat #2377(10-22 合并,1.26.3)给网络层加「代理不通自动改直连」(cloudpc/windows-weixin/proxy_fallback.py):走了代理、且失败在请求到达对端之前(ProxyError / SSLError / 连接超时)时,直连重发一次;直连成功后 10 分钟内先走直连,直连也失败就退回代理。主程序和双击启动时的更新检查(startup_update.py)都装了这一层,后台下载更新包走的是同一个网络层。请求已发出后的读超时不重发,避免同一条回复发两次。配置里写 AKKE_WEIXIN_PROXY_FALLBACK=0 可关掉。
- 验证
- 日志与上报时间来自电脑 A 10-21 的客户端日志和生产库客户端事件表(10-23 查);18:53 更新检查失败来自运营当时的截图(启动检查的输出不进客户端日志);关 VPN 的经过来自运营 10-23 说明。电脑 A 10-23 07:49 升到 1.26.3 后,启动日志有
proxy_fallback=on;到 10-23 09:16 日志里没出现过代理失败,也就是修复还没在真机上被触发过。没有独立检验。
- 还没解决
- ① 「开着 VPN 也能连」还没在真机上验证。② Clash 如果开的是 TUN(虚拟网卡)模式,程序看不到代理,流量在网卡层被接管,自动直连不起作用:这种情况仍要关 VPN,或在 Clash 规则里把中转地址设为直连。③ 电脑 B 10-23 09:48 起心跳已是 1.26.3,三台都已带这层保护。
- 相关
- W-103(升级后程序没自己起来)、W-128。
- 怎么验收
- 销售开着 Clash 时,日志出现
proxy_fallback proxy_failed ... -> direct ok,同一时段心跳不断、召回照常发;看板上不再因为 VPN 出现长时间离线。
W-128程序从「更新暂存目录」里启动:电脑 A 关 VPN 后装新版,跑起来的是暂存目录里那份 1.26.0,配置回到出厂代码已修 · 1.26.18 起 · 待装机 · 未验收
- 现场
- 接 W-127:10-22 关掉 VPN 后,电脑 A 下载到新版,但安装那一步 PowerShell 报
0xc0000142 没装成。19:07 起,启动脚本是从升级用的暂存目录(AkkeWinds.update\…\akke-sidecar.update\09fca21075f6\akke-sidecar)启动的 1.26.0,20:18 起这份在跑。它的配置是出厂的:先提示还没设置身份、拒绝启动,设好身份后是演习模式(不真发),召回只预览。
- 影响
- 销售按运营指引分几次手改配置:设身份、切真发、改召回设置。召回到 10-23 07:57 才恢复真发;期间 29 条已核过的召回只预览不发,07:58 人工改成立即到期补发。
- 原因
- ① 电脑 A 的安装位置本来就不正常:开机自启体检记录的安装目录,9-28 在 Codex 的虚拟化目录里,10-13 起变成 9-28 那次升级的暂存目录(
AkkeWinds.update\a5e426f80fba\akke-sidecar),10-22 又变成上面那个暂存目录里的暂存目录。② 启动脚本不认「我在暂存目录里」,不会去找原来的安装、把原配置和本地记录带过来;新解出来的那份就是出厂配置。
没查明:是谁、从哪里双击了暂存目录里的启动文件;9-28 的暂存目录为什么没被升级程序清掉;0xc0000142 的成因。
- 怎么修
- 已做:
Akke-AI/wecom-chat #2448(1.26.17,不发布)+ #2449(1.26.18,10-28 19:41 合并,当晚出包)。启动脚本先把自己所在目录剥掉 .update\<编号>\akke-sidecar、.bak-<时间>、.new-<时间> 这几层,算出正式安装目录;和当前目录不一致时先判在用的是哪一份:
① 正式目录里有 start.bat、已设置身份,而且这里没设置身份(刚解出来、还没装过的暂存包),或正式目录的运行记录比这里新(双击了普通升级留下的旧备份)——改去打开正式目录的 start.bat,不搬,也不停那边在跑的程序;
② 否则这里就是在用的那份(电脑 A、C 的情形):交给安装程序把这份连同配置、本地记录搬回正式目录(A、C 各是哪个文件夹见下面怎么验收),旧目录留一份说明写着搬去了哪里。只有包里缺安装程序、或正式目录的上一级文件夹不在时,才留在原地照旧跑;搬到一半失败不会自动退回,会弹窗写明原因。
10-28 20:00 已给电脑 A、C 推送 1.26.18。第一次升级会先被旧版装进原来的错目录,新版起来后再搬回正式目录,可能会多弹一次升级完成窗口。
- 验证
- 安装目录时间线来自生产库客户端事件表里电脑 A 的开机自启体检和启动链记录(10-23 查);出厂配置的几项来自 10-22 晚程序提示与心跳。1.26.17 做完后第一轮独立检查只看了代码、没在真机上验证(CODE_ONLY),提了两条必须改的(另有几条次要意见):双击旧备份或没设置身份的暂存包时,会拿旧配置或出厂配置盖掉在用的正式目录;搬家会停掉正式目录上正在跑的程序。1.26.18 已按这两条改,146 个测试通过;1.26.18 本身还没做独立检验,也还没装上机器。
- 还没解决
- 还没装机:10-28 20:00 查车队状态,电脑 A、C 上报的版本都还是 1.26.7(之前推的 1.26.14、1.26.16 也一直待装),1.26.18 已推送待装,要有人在机器上双击 start.bat 才会装。电脑 C 10-25 15:24 起也跑在备份目录里(
akke-sidecar.bak-20261022-221534.bak-20261023-095932),同样等这次升级搬回。谁双击了暂存、备份目录里的 start.bat,10-22 那次安装报错(0xc0000142)的成因,仍没查明。
- 相关
- W-127、W-103、W-01。
- 怎么验收
- 电脑 A、C 装上 1.26.18 后,下一条开机自启体检里的安装目录是正式目录(A 是
AppData\Local\AkkeWinds,C 是 销售机代回客户端-v1.25.0-已配置-请勿转发\akke-sidecar),不再带 .update\ 或 .bak-,启动记录里有一行搬回记录;之后每次升级,身份、真发、召回设置都保持原样,不需要人工改配置。
W-129销售连发几句后读屏看不到 AI 那句,认不出销售在手打,AI 照常插话;客户报了钟点问我方行不行,也没转人工服务端已上线(10-23)· 还没在真实流量里触发 · 未验收
- 现场
- 电脑 C,10-23 上午一位客户。11:00 客户发「我大概要10点半到11点左右,你看可以吗?」,11:01 AI 回「好的,那咱们10点半见,我这边先和师傅对接好,您到了直接碰面就行。」——AI 替人答应了一个见面时间。11:09 销售在电脑上手打一句,告诉客户刚才是 AI 回的;11:17 又手打问客户在哪一层。两句之间和之后,客户发了图片和三句短话(上不来 / 在车库 / M)。11:18 AI 又插进来回「您发的图我这边没加载出来,能说下是想看哪部分吗?」,跟销售抢着回。
- 影响
- 销售已经在亲自接待,AI 还插进来回了一句不在点上的话,客户同时收到两边的消息;约见面时间这种 AI 不能替人定的事,被 AI 直接答应。运营 10-23 11:31 把这位客户设成人工接管,交还前不再自动回复。
- 根因
- ① 认销售手打要先在屏上找到 AI 刚发的那句当起点,起点之后、库里没有的我方气泡才算销售手打。读屏只带最后 6 条;11:18 那一屏是销售第一句、图片、上不来、在车库、销售第二句、M,AI 那句已被挤出屏外,起点找不到,规则一句都不认,于是不让位。② 到店转人工的判据只认到店、量尺、出发、到了这些字眼;客户这句只有钟点加「可以吗」,原判据全漏。
- 怎么修
- 已做:
Akke-AI/wecom-chat #2398(10-23 11:56 合并,服务端改动,当天部署上线,不用装新版)。① 屏上没有 AI 那句时,改用客户本轮发的话当起点(这几句各取它在屏上最后出现的位置,再从中取最靠前的一个),其后对不上客户原话、库里也没有、不是纯图片且够长的我方气泡算销售手打。只用于电脑端,安卓的口径不变。② 客户报了钟点、同一句问可以吗、行吗、方便吗这类,转人工,交接卡原因写「客户报了时间问我方行不行」;汉字钟点要带上午下午、半、钟、左右这类字,带小数、条目序号、方位的不算,砍价一类的问句不算。上线前用近 60 天 1212 条真实客户消息回放,新规则只命中这位客户那一句。
- 验证
- 现场来自库里这段会话的消息记录与电脑 C 当天的客户端日志(销售手打的两句没进库,是日志里的底部复核记录)。第二轮独立检查只看了代码、没看真实消息(CODE_ONLY):还有少量会误转的说法,比如「5 点多公斤」「价格 3 点半」后面问行不行。10-28 查库:上线后到 10-28 下午共 74 条客户消息,报钟点问行不行的一条都没有,新判据还没真实触发过;同期全部入口(含安卓)认出销售手打并记库 16 行(10 位客户),但日志不区分入口,也不区分走的是哪种起点,新加的那一支有没有起作用,无法确认。
- 还没解决
- ① 两处改动都还没有真实触发的记录。② 起点改用客户原话后,如果屏上这几句客户话其实是以前的旧消息又被读了一遍,它后面早先的销售手打也会被认成刚打的,这位客户 10 分钟内 AI 不回;比两边抢话轻,没另做处理。③ 读屏只带最后 6 条这个限制没动。
- 相关
- W-111(销售手打让位后没人接回)、W-121(销售手发的首触补不进库)。
- 怎么验收
- 出现一次客户报钟点问行不行,交接卡原因是「客户报了时间问我方行不行」、AI 没替人答应;销售连发几句、AI 那句已被挤出屏外时,AI 没有插话(技术同事可查服务端日志
sales_typed handoff ... source=screen 出现在这位客户身上;日志不区分起点,只能结合屏上内容判断走的是不是客户原话这一支)。
W-137停在「< 有大有小」这类文件夹子列表里,程序不点左侧「聊天」图标回主列表,在夹子里一圈圈空转修复已合并(1.26.14)· 出包中 · 三台待装机 · 未验收
- 现场
- 10-28 运营截图:微信会话列表停在「< 有大有小」企业微信折叠夹里,列表只剩「可能的同事」一行,左侧「聊天」图标是选中的;程序没有点「聊天」图标回主列表(人手点一下这个图标就能退出来)。运营说电脑 A、B、C 三台都出现过。
10-27 客户端日志(每圈一行 list_walk_cycle)里有成段的一页都翻不动、每圈只有几行的情况,和停在夹子里的样子一致(日志本身证明不了画面停在哪,没有那一刻的截图):
· 电脑 B 11:40–13:44 约每 85 秒一圈,几乎每圈都是 opened=0 skipped=1 not_selected=13 pages=0 skip_recent=1(中间偶有一圈 opened=1 skipped=0),13:45 程序重启后才恢复;14:32–15:18、15:55–18:10 约每 3.5 分钟一圈,大多是 opened=2 skipped=1 not_selected=30 pages=0 skip_unread=1;18:21–19:29 每圈 opened=3 not_selected=30 pages=0。
· 电脑 C 10:25–10:31、15:56–16:07、19:05–19:10 每圈 opened=2 not_selected=10 pages=0;14:05–14:17 约每 75 秒一圈 opened=2 skipped=1 not_selected=9 pages=0 skip_unread=1;15:13–15:36 每圈 opened=4 not_selected=17 pages=0;16:26–18:11 大多每圈 opened=5 not_selected=16 pages=0。
· 电脑 A 11:57–12:04、12:26–12:45、18:55–19:03、19:23–19:30 几段,每圈只有 1–2 行、pages=0(多为 opened=1 skipped=1 skip_unread=1,个别圈带 skip_folder=1),各段之间夹着正常翻页的圈。
- 影响
- 这些时段巡检只在那几行上转、一页都翻不动,主列表里的客户没被核对。这期间有没有客户因此漏回、漏召回,没有逐条核。
- 根因
- 退出文件夹的动作本身是对的(单击「聊天」图标,
cloudpc/windows-weixin/idle_history.py 的 _walk_back_to_main_list),问题在程序没认出自己停在夹子里:
① 10-27 三台跑的都是 1.26.10 之前的版本,只在巡检自己刚点进文件夹的那一下才认(点完整个列表换了、或标题是「服务号 / 订阅号」等固定名)。人点进去的、回复流程点进去的、名字不固定的企业微信夹子(如「有大有小」)都认不出,这就是 10-27 各段一直不退的原因。
② 1.26.10 起加了一条判据:一圈一页没翻、没碰到一位客户,就算停在夹子里(walk_coverage.py 的 stuck_in_folder,今天要装的 1.26.13 里有),但它只要这一圈有客户、或有 30 分钟内 / 今天核对过被跳过的行,就当成在主列表。按代码推演:电脑 B 11:40 那种每圈 skip_recent=1 的形态,装上 1.26.13 也不会退。
- 怎么修
- 1.26.14(
Akke-AI/wecom-chat #2437,10-28 08:49 合并):一圈下来真行(点开 + 跳过)不超过 5 行、下面点到的是空白、一页没翻,就当成停在夹子里,单击「聊天」图标回主列表,不再看里面有没有客户。上面 A、B、C 各段的形态都落在这条里(C 偶尔一圈 14 行的不算,下一圈 5 行又会算)。销售电脑主列表都是几百位,不会一屏不满;万一真不满,多点一下也无妨(下一拍本来就回顶)。
- 验证
- 两条新测试(夹子里一位半小时内核对过的客户跑 3 圈,每圈都退;夹子里两位客户第一圈就退)在旧代码上失败、新代码通过;Windows 端全量测试 2559 条通过。按运营截图的画面(只有「可能的同事」一行)临时在模拟器里跑过 4 圈(没有留在仓里),1.26.13 已经每圈都退:这一行不是客户,不会被当成核对过跳过。没有独立检验,三台都还没装。
- 还没解决
- 三台装上 1.26.14 之前都没解决。10-28 早上三台版本据看板转述是 1.26.7(本卡未直接核)。
- 相关
- W-107(置顶折叠栏)、W-120(列表翻页)。
- 怎么验收
- 装上 1.26.14 后,停进夹子时日志出现
WARN list_walk_stuck_in_folder,紧接着 list_walk_back_to_main,下一圈翻页数大于 0;不再出现连续多圈 pages=0 且每圈只有 5 行以内的情况。
W-138对方通过好友后接着发了个表情,程序把表情当成客户开口,不首触、交给自动回复当普通对话接修复已合并(1.26.19,10-29 出包)· 三台待装机 · 未验收
- 现场
- 电脑 A,10-23 11:07 通过的一位新好友。11:49(日志时间)巡检第一次点开:左边是微信替对方带出的一句「我通过了你的朋友验证请求,现在我们可以开始聊天了」,下面是客户发的一个动画表情。程序日志判客户已经说过 1 条、不是新好友,不发首触,交给自动回复;11:50、12:43 AI 当普通对话回了两句(问是不是泉州、介绍泉州三家店),没有活动介绍和海报。之后客户没再说话,也没有召回。另一位客户 10-24 的同一句通过提示被读错了:开头的我读成了你,还漏了一个了字。
- 影响
- 新好友收不到首触(活动介绍 + 海报),收到的是普通对话回复。同一分钟通过的客户35 程序判「已经说过 2 条」,那一屏没有留画面,是不是同一个原因没有证实(他的备注另有繁体「閩」被判不是客户的问题)。
- 根因
- 判新好友的规则是「客户一句话都没说过」。那句通过提示本身早就不算客户说的(
cloudpc/windows-weixin/first_touch.py 的 is_friend_notice_bubble),但读屏把表情写成「[动画表情]」,customer_spoke 把它算成了客户开口。另外,带红点进回复通道时,只要最后一条在左边就直接交大脑回复;通过套话会被服务端拒答再转首触,表情不会,于是直接当普通对话回。
- 怎么修
Akke-AI/wecom-chat #2452(1.26.19):① 通过提示之后、整条只有表情的气泡不算客户开口;客户打了字、或表情在通过提示之前,照旧算。② 通过提示被读错成上面那种写法、后半句完整时,也认成通过提示;后半句不全的不认。③ 回复通道带红点读到「通过提示 + 表情」时先交首触,首触没接再照常回复。后面「通过提示是 3 天内」那道日期判断不变。
- 验证
- 拿这一屏的读屏结果写成测试(5 条,含红点通道 2 条),其中 4 条在旧代码上失败,另 1 条是对照;Windows 端全量测试通过(一条无关的网络测试偶发失败,已一并修稳)。独立检查只看了代码(CODE_ONLY):逐个试过的开过口的客户仍不首触;通过提示后连发几个表情也会首触;屏上是几个月前的旧通过提示 + 一个表情的老客户,补回那条路这一轮不回(首触因日期太旧也不发),触发面窄,没另做处理。三台都还没装。
- 还没解决
- 三台装上 1.26.19 之前都没解决。表情被读成「[动画表情]」「[表情]」以外的写法(比如直接是 emoji 字符)时仍按开口算,只会漏首触、不会误发。
- 相关
- W-126(一行高的通过灰字被判太矮)、W-132(日期条没读出压 24 小时)。
- 怎么验收
- 装上 1.26.19 后,通过后只发了表情的新好友有首触记录、第一条我方消息是活动介绍 + 海报;日志里这种会话不再出现「客户已经说过 1 条」随后自动回复。
W-139新好友还没首触,召回发前读聊天失败,服务端按兜底话术把召回发成了第一句服务端已上线(10-29,不用装新版)· 还没在真实流量里触发 · 未验收
- 现场
- 电脑 A,10-26 11:33 通过的一位新好友。11:35 程序把他交给首触,但识图没读出「你已添加了…」上方的日期条,判「读不到日期,不首触」,并 24 小时不再试(W-132);11:41 召回任务来了,那次程序认出是新好友、拦下没发。10-27 11:43 巡检再点开,日期条还是没读出;召回发前读聊天失败,这一次没认出新好友,服务端按兜底话术把召回介绍发了出去——这是他收到的第一句。
- 影响
- 新好友没收到活动介绍和海报,第一条就是召回。两周汇报里客户26、38 都是读聊天失败后兜底照发这条路。
- 根因
- 召回发不发,只靠端上读屏判一次「是不是新好友」;那道判断跟首触用同一套读屏判据,首触认不出时它也认不出。服务端手里明明有记下了通过、首触时间是空的、我方一句没发过这些库里的事实,却没用来兜一道,读失败时直接走兜底话术(
src/lib/wecom/windows-recall-service.ts 的 readFailureFallback)。
- 怎么修
Akke-AI/wecom-chat #2452(服务端,合并后 Vercel 部署即生效):7 天内记下了好友通过、首触时间是空的、库里我方一句没发过的客户,召回一律不发、先交首触(读成读不成都一样;不读聊天直接发的机器也要先读)。交首触后 24 小时再看,连续 3 次还没首触,这一轮召回不发。没有通过记录的老客户、存量召回不受影响;通过超过 7 天的按老客户召回。
- 验证
- 拿这位客户的形态写成测试(读失败、读成功屏上只有我方申请话术、不读直接发三种都不发;首触已记、我方说过话、通过超过 7 天、没有通过记录四种照常发),旧代码上 3 条失败、新代码通过,另 4 条是对照;服务端全量测试通过。独立检查只看了代码(CODE_ONLY):销售手发了但没进库的,会被当成「我方一句没发过」而拦下召回,方向偏保守、不会多发。
- 还没解决
- ① 还没在真实流量里触发过。② 首触一直发不出的人(比如 W-132 那种日期条一直读不出),召回也不会发,连续 3 次交首触都没发出首触(约 2 天),这一轮召回取消;真正的修法是首触那边(W-126、W-132 已出包,三台还没装)。
- 相关
- W-126、W-132(新好友认不出)、W-138(表情被当开口)。
- 怎么验收
- 出现一位记下了通过、还没首触的新好友时,他的召回任务原因是
awaiting_first_touch、没有发出;客户收到的第一句是首触而不是召回(技术同事按好友通过记录与召回任务表对着查)。