企微好友单聊 AI 自动代回 · 全景报告
方案B(GUI 自动化)· 从 0→1 到批量试跑 · 更新于 2026-07-05
Skia 自绘 UI · 只能 VL 读屏
Qwen-VL 读 + GUI 打字发
3 销售号 · 54 条 AI 代回
3 天灰度试跑
主号无隔离 · 灰度从严
客户在企业微信里给销售发消息 → 云电脑自动截屏读出来 → AI(小艳/小夏/小范/小文人设)生成回复 → 自动打字发出去。
全程无人值守。本页讲清:① 已完成的部署 + 0→1 原理图;② 批量跑后的真实数据;③ 踩过的各类 badcase 与对应优化;④ 下一步待做;⑤ 云电脑部署指南。
01 / Deployment & Architecture
已完成的部署操作 & 0→1 逻辑原理图
为什么是「GUI 自动化」这条路(0→1 由来)
2026-06-10
方案A(微信客服 kf API)全链路代码上线(PR #268)——但保存回调时撞上「回调域名 ICP 备案主体校验」硬阻塞:境外域名 vercel.app / fly.dev / upio.ai 全被拒,配置链锁死 → 方案A 冻结待域名。
2026-06-11 → 06-29
转 Windows GUI 自动化(方案B)。探针实测:企微 PC 主界面是 Skia 自绘原生 UI,UIA 控件树恒空、DOM/CDP 也拿不到聊天 → 读消息只能靠截图 + Qwen-VL(判死,别再试 UIA/CDP)。
2026-06-29
V1 上线:迁移 + 大脑端点(PR #645)+ Fly 中转(fc25cdcd)+ 云电脑 loop(含风控)。云电脑 DRY_RUN=1 空跑验证通过后开真发。
2026-06-30 → 07-02
3 销售号批量灰度试跑(小夏/小范/小文)。橘枳等真实会话一天暴露 9 类事故 → loop 迭代 v2026-07-02.1 → .11 + 服务端连修(详见第 3 节 badcase)。
2026-07-05(本周)
三项工程加固 PR:#760 脚本自动同步 · #761 loop 心跳入库 + 存活告警 cron · #762 代回接入 stage 推进 + 客户画像回写。
0→1 逻辑原理图(一条消息的往返)
① 客户发消息
企业微信好友单聊里,业主发来一条消息
▼
② 云电脑 · 读(wecom_reply_loop.py)
双通道扫未读(①轮询当前打开会话 + ②遍历左侧红点)→ 截屏 → Qwen-VL 逐条列出消息 → 代码取最底一条 + 确定性归属判定
▼ 云电脑国内 IP 连不上 vercel.app
③ Fly Tokyo 中转(wecom_relay.py)
纯透传:云电脑 → akke-worker-prod.fly.dev/wecom/chat/reply → Vercel(Fly→Vercel 必通),不持密钥
▼
④ Vercel 大脑(/api/wecom/chat/reply)
chatReply 出小艳人设回复(按 stage:首触/nurture/decision)+ 确定性海报匹配 + 客户画像回写 + Langfuse 追踪 + 成本记账
⑤ Supabase
conversations / messages 落库;指纹幂等去重
▲ 回 reply ┃ ▼ 发送
⑥ 云电脑 · 发
拟人延迟 → 点输入框 → SendInput 逐字打字 → Enter 发送(换行切段封顶 3 条)
⑦ confirm 落库
GUI 真发出后才回调落 AI 消息(显式失败:没发成不记账)
蓝 = 云电脑(只做 GUI I/O) · 紫 = Vercel(大脑/DB/成本全在这) · 黄 = Supabase。
核心分工:Python 端不碰任何企微 API、不做任何判断——读靠 VL、发靠模拟输入,大脑与账本全在服务器。这也是它绕开方案A 那道 ICP 墙的原因(云电脑→Vercel 是普通入站请求,不调企微接口)。
已上线的部署资产
| 组件 | 位置 | 锚点 | 状态 |
channel wecom_chat 迁移 | supabase | PR #645 / 20260629165140 | 已上线 |
大脑端点 /api/wecom/chat/reply | Vercel | PR #645 | 已上线 |
| Fly Tokyo 透传中转 | akke-worker-prod | fc25cdcd | 已上线 |
| 云电脑 loop + 风控(工作时间/日限20/时限8/连发6/STOP自停) | 云电脑 | wecom_reply_loop.py | 已上线 |
| 3 销售身份壳(小夏/小范/小文·福建漳州) | Vercel + loop | PR #669 起 | 已上线 |
| 脚本自动同步(消灭"改了没生效") | GitHub Actions | PR #760 | 评审通过·待合 |
| loop 心跳入库 + 存活告警 cron | Vercel + DB + loop | PR #761 | 评审通过·待合 |
| stage 推进(→decision)+ 客户画像回写 | Vercel | PR #762 | 评审中·待合 |
02 / Batch Metrics
批量化执行后的真实统计
数据源:生产 Supabase channel='wecom_chat' 实时查询(scripts/_wecom-stats.ts,2026-07-05 拉取)。非估算、非样例。
每日 AI 代回量
会话阶段分布(stage)
nurture22
ice_break3
decision0
数据自证缺口 → 25 个会话里 0 个进入 decision(收口)阶段、0 个写下客户画像(customer_profile 全空)。根因:试跑期 wecom 路径把 chatReply 的 toolCalls 直接丢了 → stage 永远停在 nurture、record_customer_info 从不回写。本周 PR #762 已修,合并后新会话会正常推进到 decision 并累积画像。
诚实边界(别当终局 KPI):这是一次 3 天灰度试跑——主号无隔离、日限 20/时限 8 从严、样本 25 会话/54 条。07-02 之后为集中修 badcase 暂停发送。数字用于定性验证「通路跑通、客户有回」,不代表规模化转化率。加微/成交等业务口径需在正式放量后另计。
03 / Badcases & Fixes
踩过的各类 Badcase · 优化方式 · 进展
GUI 自动化把「位置/布局/发送方判断」这类任务交给了 VL 与隐式机制 → 一旦看歪就静默丢单或发错。以下按层归类,均可追溯到具体修复 PR。
A读取层 —— 读错 / 漏读客户消息
| 现象 | 根因 | 优化方式 | 进展 |
| 漏读最底部最新客户消息 | 长会话滚不到底、被上方大气泡带偏;只信 VL 判「哪条最新」 | 打开先滚到底 → read_latest_bottom() 裁底部 58~88% 专读最后一条 → 改「按序列出全部→代码取最后」+ 确定性归属判定,不信 VL 左右 | 根治 #705/707/708/709 |
| VL 把客户消息误判成 self → 卡死跳过 | 发送方归属 100% 靠单次 VL 看图,非确定性 | 不再信 VL 的 customer/self,改:读最底一条,只要非我方近发文案/非会话名/本会话没回过就当客户新消息 | 已修 |
| 名字系统性读空 | 会话列表分隔条被拖宽 → 裁剪切进标题 | 读空改整屏兜底只问标题;裁剪比例提为 env AKKE_WECOM_CROP_RATIO 可调 | 已修 |
| 消息被截半句 → 去重失效重复回 | 裁剪比例 0.15 切进最左气泡 | 裁剪比例降到 0.12;底部窄带二读复核紧贴我方回复下的小气泡 | 已修 |
B防自回 / 防重复 —— 发错对象 / 发重复
| 现象 | 根因 | 优化方式 | 进展 |
| 自问自答(回复自己) | VL 把我方气泡误判成客户消息 | 护栏① 我方近发文案模糊匹配拦截(读串一两字也拦) | 已修 #695/703 |
| 把自家黑窗日志当客户消息回出去 | 截图圈进了 loop 自己的控制台 | 读取侧前台校验 + 黑窗日志误读检测 | 已修 |
| 同一条消息回两遍 | VL 漏读右侧我方气泡时 burst 混入已答旧行 | 护栏③ 每会话回复台账,去重降到逐行 | 已修 #704 |
| 好友验证系统样板被当客户开口回 | 「我通过了你的验证/现在可以聊天了」被 VL 读成左气泡 | 两个变体(有/无"我们")双侧正则过滤,剔空则跳过 | 已修 |
C发送层 —— 发送形态不像真人
| 现象 | 根因 | 优化方式 | 进展 |
| 一条回复被切成多条气泡(无上限) | 回复里的 \n 被企微当 Enter=发送 | 发送前按 \n 切段、封顶 3 条、超出用空格连不丢内容 | 已修 |
| 首触被拆条 / 固定连发 3 条太机械 | 换行发送副作用 + 固定条数 | 首触 single_bubble 钉死单条;代回气泡数随机 1-3 条 | 已修 #692/714 |
| 抢窗口不稳 → 坐标落偏点空 | 无影上 activate/maximize 常没真铺满屏 | focus_wecom 硬置前 + 最大化 + 铺满屏校验,没铺满就跳过本轮 | 已修 #694 |
D身份 / 会话 —— 串台 / 碎记忆
| 现象 | 根因 | 优化方式 | 进展 |
| 同一客户裂成多条会话、称呼念成「橘枳@微信」 | VL 读显示名当身份,后缀/空格差一字撞不上唯一键 | normalizeCustomerName 剥「@微信」后缀 + 空格归一 | 过渡已修 |
| 跨销售号同名客户"通灵"(说出没对本号讲过的城市) | 身份只用裸显示名,跨号共享历史 | 身份键改 persona:客户名,按销售号隔离 | 已修 |
| 同号同名仍会串(无稳定 userid) | GUI 拿不到官方 external_userid,架构级天花板 | 确定性根治只剩官方付费「会话存档 API」 | P2 · 已拍板搁置 |
E话术 / 人设 —— 回复质量
| 现象 | 根因 | 优化方式 | 进展 |
| 给已加微客户发「发个微信号我让设计师加你」 | ice_break 会话代回落了抖音冷开场模板 | route 把 ice_break stage 强转 nurture + wecomOpener 企微专属覆盖(禁要微信号/禁逼单) | 已修 |
把海报记账标记 [图片:config-568] 当文字打给客户 | LLM 学舌历史里的去重记账标记 | 喂历史前 + 回复后 + confirm 落库三道剥离 | 已修 |
| 客服腔重 / 人设密度不足 / 不主动挖需求 | nurture 只说"短"、没钉死节奏与人设 | few-shot 正例 + 4 号人设密度加密 + nurture 注入 SPIN 主动挖需求 + 客服腔自审 | 已修 #754/756/757 |
| 企微号署名冒出抖音「小艳」 | few-shot 署名未按渠道隔离 | 署名 persona 感知;few-shot fallback 按渠道隔离 | 已修 #758 |
F架构 / 运维 —— 静默失效风险
| 现象 | 根因 | 优化方式 | 进展 |
| 会话聚焦时客户秒回不弹红点 → 漏触发 | 触发只靠左侧红点 | 双通道:①轮询当前打开会话 + ②遍历红点会话 | 已修 |
| 改了 loop 但云电脑没生效,排查半天 | wecom-chat 不在自动同步范围,靠人工传文件 | 纳入 sync-wuying-scripts.yml 镜像 + update-wecom.bat token-free 自取 | PR #760 待合 |
| loop 崩了/没起来,几小时无人知 | 心跳只写本地 json、无远端监控 | 心跳入库 + wecom-heartbeat-watch cron 工作时段超时推 Lark 云电脑监控群 | PR #761 待合 |
| engaged 客户永不进 decision、画像永远空 | wecom 路径丢弃 chatReply 的 toolCalls | 接入 stage 启发式推进 + record_customer_info 画像回写 | PR #762 待合 |
04 / Roadmap
下一步待做的优化列表
| 优先级 | 项 | 说明 | 状态 |
| 立即 | 云电脑拉新版 loop | PR #760/#761/#762 合并后,在每台云电脑跑 update-wecom.bat + 重启,心跳/stage/画像才真生效 | 待部署 |
| 立即 | 合并 3 个 PR | #760 先合(loop 才能同步),再 #761/#762;#761 触发建表迁移 | 评审通过 |
| 中 | 正式放量 + 业务口径 | 灰度稳定后逐步放开日限,接入加微/成交口径做真 KPI | 待定 |
| 低 | token / 查询小优化 | HISTORY_LIMIT=40 每次喂 235B 偏贵可裁;matchPosters 全表拉取可加索引 | 待办 |
| 低 | 跨会话记忆 | loadCustomerHistory 硬绑 douyin_user_id;wecom 身份已单会话合并、收益小 | 暂缓 |
| 决策 | 会话存档 API(P2) | 确定性读 + 稳定 userid,一步解决 VL 误读 + 身份漂移;卡点是 ~¥1800/号/年 + 认证主体 + 客户侧存档合规提示 | 已拍板:先不接 |
| 决策 | 转人工 | — | 已拍板:不做 |
两条天花板(非技术阻塞,业务决策):读取靠 VL 单点、身份靠显示名漂移——两者确定性根治都指向同一把钥匙:官方会话存档 API。已决定先不接,读取地基继续用加固后的 VL 方案;转人工明确不做。
05 / Deployment Guide
云电脑部署指南
前置物料(开工前找 PM 要齐)
- 一台无影云电脑(Windows,Python 3.x)+ 一个已加了客户好友的企业微信主号
- OpenRouter key(
sk-or-v1-…,给 VL 读消息用)
WECOM_CHAT_SECRET(大脑端点密钥,所有云电脑同一个值)——真密钥不写在公开页,向团队内部 Lark 私聊取
一次性配置(9 步)
装企业微信 + 登录一次
登录加了客户好友的主号;窗口保持打开、别最小化到托盘(最小化截不到屏)。重启一次确认登录是否保持。
确认 Python 3.x
python --version。没有就 winget install -e --id Python.Python.3.12 --scope machine,新开窗口再验。
拉脚本到 C:\akke-wecom
从公开镜像 raw.githubusercontent.com/upioai/wiki/main/public/akke/wecom-chat/ 下载 wecom_reply_loop.py / start-wecom-reply.bat / update-wecom.bat(合并 #760 后 token-free 可用)。
bootstrap 装依赖
.\bootstrap-wecom.bat 自动装 pyautogui 等 + 从模板生成 .env。
量坐标(最易错·必做)
python measure_wecom_coords.py,按 6 秒倒计时把鼠标停在「输入框 / 发送按钮 / 会话列表第一行」,得到 AKKE_WECOM_C_INPUT/C_SEND/C_SESSION1——每台机分辨率不同,必须量自己的。
填 .env
填 OpenRouter key、WECOM_CHAT_SECRET(内部取)、上一步坐标;WECOM_CHAT_ENDPOINT=https://akke-worker-prod.fly.dev/wecom/chat/reply(固定别改);设 AKKE_WECOM_PERSONA(xiaoxia/xiaofan/xiaowen,本机绑哪个销售号)。
空跑验证(DRY_RUN=1)
默认 AKKE_WECOM_DRY_RUN=1:只读+生成+打印,不真发。看它能否正确读出客户消息、生成合理回复、坐标点得准。
发送测试
python test_send_wecom.py 往一个测试会话真发一条,确认打字/发送键正常(企微设置若是 Ctrl+Enter 就把 AKKE_WECOM_SEND_KEY=ctrl+enter)。
开真发
改 AKKE_WECOM_DRY_RUN=0,双击 start-wecom-reply.bat 启动(崩溃自重启、遇 STOP 锁住)。启动行会打印 VERSION,核对是最新版。
日常运维
- 每天/重启后拉起:走
/cloudpc-wecom-autoreply-restart skill,念黑窗日志 + 看 wecom_reply_status.json 心跳验活。
- 一键停发:在
C:\akke-wecom 建 wecom_reply_STOP 文件即停;删掉才会重启恢复。连续 3 次异常 loop 自己写 STOP 停发。
- 更新版本:双击
update-wecom.bat 从公开镜像自取最新 loop(自动备份 *.bak)→ 重启。
- 登录过期 / 坐标偏:重登企微;换了远程桌面分辨率要重量坐标(loop 检测到分辨率变化会跳过本轮防点偏)。
- 存活告警(#761 合并后):工作时段心跳停 >30min 会自动推 Lark 云电脑监控群,不用盯屏。
安全纪律:本页公开,不含任何密钥值。WECOM_CHAT_SECRET / OpenRouter key 一律走团队内部 Lark 取,别写进任何公开文档或截图。主号无隔离 → 日限/时限从严,别擅自调高。