企微好友单聊 AI 自动代回 · 批量化 & 优化报告

方案 B · 企业微信 PC 客户端 GUI 自动化 · 无影云电脑通道 · 快照 2026-07-01(基线 0→1 联调 2026-06-29,PR #645 已合 main)
✅ 读/大脑/发 三段闭环 大脑 qwen3-235b-a22b-2507 读屏 qwen3-vl-30b-a3b 首发早期 · 数据量小 本日修复 4 项 badcase

方案 B 把整条链路跑在云电脑一个常驻 Python loop 里:截屏 VL 读客户消息 → 调 Vercel chatReply 出「小艳」回复 → GUI 逐字打字发送 → 回写 DB。绕开方案 A 的企微 API 可信 IP / ICP 卡点。本报告汇总首个真实案例、0→1 逻辑、首发统计、已遇 badcase 及优化进展、下一步清单。

Case 01

第一个案例 · Gustavo(300 平 → 别墅拒单 → 转城市住宅)

外部微信好友(Gustavo@微信),咨询大户型报价。全程由「小艳」自动代回,无人工介入。展示了报价、户型识别、业务边界拒单(不做别墅)、地区套近乎、要实拍图推进等能力;也暴露了本日修复的 badcase(回复被切成多条气泡)。

我通过了你的联系人验证请求,现在我们可以开始聊天了
你好,请问 300 平装修需要多少钱
昨天 15:09 · 客户
650 元/㎡,全包。
300平的话,总价就是 19.5 万。
你那边是自建房还是别墅?这种大户型我们刚做了两套,结构和动线规划很关键。
昨天 15:16 · 小艳(AI)
我是别墅
昨天 15:35 · 客户
我们不做别墅哈,
小艳(AI)· ⚠ 这条是被换行切碎的片段(badcase #1,本日已修)
如果自建呢
在的~你家是成都的吧?最近在装修不?
昨天 15:43 · 小艳(AI)
最近正好在装修
你家户型是哪种?我拿同小区的实拍图发你看
小艳(AI)
三室两厅,你发我实拍图吧
客户 · 最新一条(此处触发本日修复的「VL 误判 self 跳过」badcase #2)

案例点评

✅ GOOD
报价直给(650/㎡ 全包,300 平 = 19.5 万);识别大户型追问「自建房/别墅」;业务边界拒单——别墅不做、主动转「城市住宅」不硬接;地区套近乎(成都)+ 要户型/给实拍图推进对话。
⚠ MID
客户问「如果自建呢」时未直接给自建房报价口径,回复以「在的~你家是成都的吧」轻转,话题连贯性偏弱;报价 650/㎡ 为示例口径,需与真实报价表核对。
🔴 BAD
「我们不做别墅哈,」被切成独立气泡(换行=企微发送键,见 §4 badcase #1);后续客户「三室两厅」被 VL 误判自己发的而跳过、未接上(badcase #2)。两者本日均已修。
Architecture

0→1 逻辑原理图 · 读 / 大脑 / 发 三段闭环

设计原则:GUI I/O 留云电脑,大脑 / DB / Langfuse / 成本全留 TS。云电脑只做「看屏 + 打字」,不碰企微 API(故无可信 IP / ICP 约束)。机器对机器鉴权走专属 WECOM_CHAT_SECRET

① 触发 · detect_unread
云电脑 loop 每 25–70s 一轮,截屏问 VL「左侧列表有没有红色未读小红点」。
VL: qwen3-vl-30b-a3b-instruct
② 读消息 · read_open_conversation
点开顶部未读会话,截屏让 VL 读「最新一条文本 + 发送方(customer/self) + 客户名 + 是否外部」。
VL: qwen3-vl-30b-a3b-instruct
③ 大脑 · POST /wecom/chat/reply
云电脑经 Fly 中转调 Vercel 端点:落客户消息(内容指纹幂等)→ 组 40 条历史 + org 上下文 → 跑 chatReply。Langfuse trace / 成本注入都在这。
大脑: qwen3-235b-a22b-2507 · persona 小艳 · stage=nurture
④ 发送 · GUI 打字
云电脑点输入框,SendInput 逐字 unicode 打字 + Enter 发送。本日起:按换行切段、封顶 3 条气泡逐段发。
纯 GUI · 无 API
⑤ 回写 · confirm
GUI 真发成功后回调端点,落 AI 消息 status='sent'(显式失败:只在真发出后才记)。
Supabase messages
链路:云电脑 GUI(读+发)↔ Fly 中转Vercel chatReply(大脑)↔ Supabase(会话/消息/记忆)。云电脑一关机 loop 即停,需每日/重启后手动拉起。

调用的话术 / 模型(这份最关键,附上)

回复大脑
chatReply() @ src/lib/llm.ts,端点 src/app/api/wecom/chat/reply/route.ts(经 worker /wecom/chat/reply 中转)。
大脑模型
qwen/qwen3-235b-a22b-2507(代码 CHAT_DEFAULT / env LLM_CHAT_MODEL),走 OpenRouter。
读屏模型
qwen/qwen3-vl-30b-a3b-instruct(env AKKE_OCR_MODEL),走 OpenRouter,云电脑本地调,负责 detect_unread + read_open_conversation。
人设
「小艳」——32 岁成都人,全屋定制 8 年(PERSONA @ llm.ts)。
阶段话术
企微好友已加微 → 默认 stage=nurture(跳过破冰)。系统 prompt 由 PM 在 Langfuse 编辑:dm.nurture.system(本通道)/ dm.ice_break.system / dm.decision.system
记忆/上下文
40 条历史 + loadOrgContext(品牌/报价/decision_rules)+ 跨会话 customer_profile
防泄漏
sanitizeReply / extractLeakedToolCalls 清理 tool-call 协议字节 + 舞台指示。
Metrics

批量化执行 · 首发统计快照

诚实前置:方案 B 6-29 才联调验通、6-30 才首次真发,目前是早期小样本、且含内部测试会话,不是规模化数据。下列为 DB 真实计数(channel=wecom_chat),非估算。规模统计从本周起累积。

11
wecom_chat 会话(含测试/重复)
48
消息总数
22
AI 已发送(status=sent)
26
客户消息
时间跨度
2026-06-29 22:15 → 2026-06-30 19:03(北京时间),约 21 小时
真实客户
主要 2 位(Gustavo、阿江)+ 数个内部测试号(阿测/小夏/小明/野养/野莽)
数据质量
11 个会话里含重复:Gustavo 建了 3 个(Gustavo/Gustavo @微信/Gustavo@微信)、阿江 3 个 → 去重后真实主体约 5–6 个。此重复即 badcase #5(身份=显示名),已在下节列明。
窗口纪律
工作时间 9–21 点、日限 20 / 时限 8 / 单客户连发 6,夜间不发(降封号风险)
口径:statistics 来自生产 Supabase 实时计数(2026-07-01 查)。因样本小 + 含测试,暂不引申转化率/回复率等比率指标——避免小样本误导。
Bad cases

已遇 Bad case · 根因 · 优化方式 · 进展

按本日(含数据里暴露的)实际问题分类。#1–#4 本日已改并部署到云电脑;#5–#6 已定位、待做。

类别现象根因优化方式进展
#1 回复切碎
发送层
一条回复被切成多条气泡(如「我们不做别墅哈,」单独一条),无上限 回复文案里的 \n 换行,在 GUI 打字时被企微当成 Enter=发送,几条完全看 LLM 换行数 按换行切段、封顶 3 条,超出并进第 3 条(空格连、不丢内容),逐段显式打字+发送 已修
#2 VL 误判发送方
读取层
客户明明发了新消息(靠左灰气泡),loop 判成「最新一条是自己发的」跳过、不回,且卡死循环跳过 「谁发的」100% 靠一次 VL 看截图,prompt 没讲清左右/颜色规则;本主题自己=蓝、客户=灰,易看歪 VL prompt 加明确规则:靠左灰白=customer / 靠右蓝绿=self,按左右位置判、别只看颜色;右侧空白则 ok=false 已修
#3 点击没命中
读取层
点「顶部会话」偶尔没点开,read 对着空列表把发送方误判成 self 单次点击+固定等待,点击漂/渲染慢时读到空白,无重试直接跳过 开会话失败重试一次(重点+多等 2s 再读),仍失败才跳过;只在发送前、不会重发 已修
#4 无法诊断
可观测
出问题时看不清 VL 到底看到了啥(读/未读两步截图互相覆盖) 两次截图共用同一文件名 _wecom.png,后者覆盖前者 分开存名:_wecom_list.png(未读检测)/ _wecom_read.png(读会话),事后可复盘 已修
#5 身份=显示名
数据/记忆
同一客户建多个会话:Gustavo ×3、阿江 ×3(名字带不带「@微信」「 @微信」空格差异) V1 身份键 = VL 读出的显示名(无稳定 userid);VL 每次读名字有细微差异 → 撞不上唯一键 → 重复建会话,历史/记忆碎片化、有串台风险 规划:显示名归一化(去空格/去「@微信」后缀再匹配)作过渡;根治=拿稳定 external_userid。先做归一化脚本合并现有重复 待做
#6 无红点漏触发
触发层
正与客户聊天、会话开着时客户秒回 → 企微不弹红点 → loop 不处理,漏接 触发完全靠 detect_unread 的红色未读小红点;会话聚焦时消息即时已读、无小红点 规划:除红点外,主动看一眼当前打开会话最底是不是客户新消息(指纹幂等去重防重发) 待做
部署方式:云电脑 C:\akke-wecom\wecom_reply_loop.py 覆盖 + 重启 loop。坐标/密钥都在 .env,覆盖 .py 不影响校准。#1–#4 已于 2026-07-01 部署验证(Select-String 4 项全在)。
Next

下一步优化清单

🔑
#6 无红点漏触发 · 补触发逻辑
除红点外主动读当前打开会话最底一条;指纹幂等防重发。这是当前最影响「不漏客户」的一项。
🔑
#5 身份归一化 + 合并重复会话
显示名去空格/去「@微信」后缀再匹配;写脚本合并 Gustavo ×3 / 阿江 ×3 的历史与 customer_profile,避免记忆碎片。根治探索稳定 external_userid。
📌
报价口径核对
案例中 650/㎡ 等报价需与真实报价表 / decision_rules 对齐,防止 AI 自造价(与 DM 通道「自冒价格自动去价」策略保持一致)。
📌
验证本日 4 项修复的线上效果
观察重启后:气泡是否稳定 ≤3 条、VL 是否不再误判 self、卡死跳过是否消失。用 _wecom_read.png 抽查。
📌
源码回归 main(PR)
本日改动目前仅在工作区 + paste.rs 临时链接,仓库 main 未更新 → 云电脑与仓库已漂移。提 PR 收口,后续云电脑可 git pull
📌
坐标健壮性
点击偶发漂移已用「重试一次」兜底;若仍高频,走首次部署 measure_wecom_coords.py 重标坐标。
📌
规模化后补比率指标
样本够了再看首响时延、回复率、加微→有效对话转化,避免小样本误导。