Claude Code × 无影云电脑 × 企业微信 · 技术讲解

一台没人坐的电脑
在替销售回消息

两台在成都的 Windows 云电脑,登录着真实企业微信账号,自己读屏、自己生成话术、自己打字发送。没有人在它们面前,运维它们的是杭州一台 Mac 终端里的 Claude Code。这篇讲两件事:这条遥控链是怎么搭的,以及这套企微代回七个月里迭代成了今天这样

// 面向工程、运维与产品 · 关键数字标注口径与日期 · 数据截至 2026-07-26

ALIYUN ECD RUNCOMMAND SESSION 0 → SESSION 1 QWEN3-VL 读屏 + SENDINPUT FLY RELAY → VERCEL → SUPABASE LOOP v2026-07-25.ADAPTIVE-SELFUPDATE.2 2 台 · 6C12G · 成都
SCROLL / 向下滚动
01
Why a Desktop, Not an API

三堵墙,把我们逼到了 GUI 上

正常做法当然是调接口。2026 年 6 月我们真的把官方接口那条路走完了——代码全部写完上线,然后卡死在一个和技术无关的地方。下面三堵墙,每一堵都实测撞过,不是推断。

WALL 01 · 备案回调域名要 ICP 主体校验

微信客服 kf API 是官方唯一明确支持「AI 代员工回复外部客户」的通道。全链路代码 6 月 10 日就合并部署了,配置卡在第一步:企微保存回调 URL 时先校验域名 ICP 备案主体必须与企业主体一致vercel.app / fly.dev / upio.ai 全被拒,裸 IP 也拒,报 Domain entity verification failed

方案 A 冻结 · 代码零改动待命

WALL 02 · 无接口员工 1:1 好友单聊没有代答接口

业务动线是加好友后 1:1 单聊,不是客服会话。企微在这条动线上没有任何已验证的合规 AI 代答 API——官方能给的只有会话存档(读,约 ¥1800/号/年)和侧边栏 JS-SDK(要人点一下才发)。全自动 + 1:1 单聊这个组合,官方接口给不了。

评估过、拍板不接会话存档

WALL 03 · 自绘客户端不吐无障碍树

那就退到桌面自动化——也不顺。企微 4.0+ 是 DirectUI 自绘,UIAutomation 深扫 12 层,会话列表 / 消息文字 / 输入框 全部为 0;CDP 只能挂到「文档」那个 webview,聊天窗是原生绘制。四条结构化读法全死,读消息只剩「截图给视觉模型看」。

结论:VL 读屏躲不掉
So

三堵墙叠起来只剩一条路:在一台登录着真账号的 Windows 上,像人一样点鼠标敲键盘。而一台要 7×24 常开的 Windows,不可能靠人天天远程桌面连上去伺候——于是就有了下面这条遥控链。

02
How Claude Code Reaches a Windows in Chengdu

Claude 是怎么够到那台机器的

先说清一件容易误会的事:Claude 不在云电脑里面跑。它跑在杭州一台 Mac 的终端里,手上只有一把阿里云 RAM 凭据和一条异步的远程命令 API。所有能力都是从这条窄管子里长出来的。

CLAUDE
本机终端
写一段 PowerShell → base64 编码 → 组装成 aliyun ecd run-command。命令体上限 16 KB,默认超时 300 秒
ECD OPENAPI
2020-09-30
接口是异步的:立刻返回一个 InvokeId,不等结果。区域必须显式写两处--region + --biz-region-id),否则查不到任何机器
会话 0
SYSTEM
云助手以 nt authority\systemSession 0 执行。它看不见任何 GUI——装依赖、写文件、改 .env、起服务都行,弹窗和点击一律无效
跳板
计划任务
跨过 GUI 边界的办法:注册一个 InteractiveToken 计划任务,它会在已登录的 Session 1 交互桌面里以用户令牌运行,pyautogui 截屏/点击/打字全部可用
读回
Invocations
describe-invocations 取 stdout —— 必须带 --include-output true,否则 Output 字段恒空。输出 >24 KB 会截断,丢弃字符数记在 Dropped
出海
FLY 中转
机器出口是阿里云国内直连,openrouter.ai / ntfy.sh / GitHub raw 全被 按 SNI 阻断。视觉模型和大脑端点都改走东京 Fly worker 透传
验收
业务指标
最后一层永远是业务证据:进程存在性、日志尾部时间戳、数据库里有没有真的多出一条 AI 回复——不看返回码

这一层的完整版(官方接口口径全表、读回通道可信度实测、把 88 KB 脚本塞进 16 KB 管子的分块校验法、十四条坑位速查)在 /learn/claude-code-cloud-pc,本文只取和企微直接相关的部分。

会话 0 能做什么

  • 装 Python、装依赖(挂阿里 PyPI 镜像,否则易超 400s)
  • 分块写文件:gzip + base64 切 9 KB 一块,写盘后解压 md5 校验,匹配才替换
  • .env、查进程、拉起 / 结束计划任务
  • 读日志尾部、读 status.json、算文件 md5

会话 0 做不到 / 会骗你的

  • 任何 GUI:截屏、点击、弹窗——必须经 InteractiveToken 跳到 Session 1
  • py.exe 启动器在 SYSTEM 下起不来,要用完整路径或纯 PowerShell
  • schtasks /run 返回 rc=0 ≠ 真起来了(见第 06 节最阴的那个坑)
  • 中文日志经 PowerShell 管道会 GBK/UTF-8 撞车,要显式转码
一条纪律

验证探针自己要先有存在性证据。用某条日志、某个字段当回执前,先拿一个「必然产生该信号」的动作跑一遍、确认看得见。否则「收不到信号」和「动作没发生」分不开——我们靠这条纪律把三条已判死的通道又救活了两条。

03
What Actually Runs on the Machine

机器上那个循环在干什么

云电脑上常驻的就是一个 Python 文件wecom_reply_loop.py,10 万字节出头)。它不做任何判断以外的智能工作——话术由服务端大脑生成,它只负责眼睛和手

扫红点
像素扫描
截会话列表,像素扫描找未读红点(早期用 VL 判,误报多且有 y<60 死区,已换成确定性扫描)。非外部会话进 300 秒抑制缓存防空转,带「@微信」的外部客户永不进缓存 = 零漏接
身份门
读标题
点开会话前后强制现读标题——身份键是 persona:显示名,读歪就会新建会话、历史清零,甚至发错人。发送路径上这一步是硬门
读消息
QWEN3-VL
裁剪聊天区截图喂视觉模型。左右裁剪比例每台必须实测:左裁会话列表(CROP_RATIO)、右裁新版客户端的 AI+ 侧栏(CROP_RIGHT_RATIO),裁错就会把自己的话或侧栏示例卡片当成客户消息
大脑
generate
POST 到 /api/wecom/chat/reply(经 Fly 中转)。服务端落客户消息 → 拼历史 + 滚动摘要 + 客户画像 → 生成回复 → 按触发词匹配官方海报 → 返回文本 + 图片 URL
发送
SENDINPUT
清空输入框草稿 → ctypes SendInput 逐字 Unicode 打字 → 回车。粘贴模式试过,回滚了:文字进得去但不触发发送,卡成草稿 = 日志报「已发送」而客户收不到
回执
confirm
GUI 真发出去之后才 POST confirm 落 AI 消息(wecom_msgid 幂等指纹,3 次重试——单次失败会导致下一轮重生成重发)
分工

这条链的设计原则是「模型只做模糊判断,代码做一切确定性的事」:视觉模型负责「读出这句话」,气泡归属 / 会话路由 / 计数 / 限流 / 幂等 全部走确定性代码。早期让 VL 判「这条是客户还是我自己发的」,翻车了,改成确定性归属才收敛。

04
Seven Months of Iteration

迭代时间线

下面每一格都对应真实的合并记录或线上验证。看得出一个规律:前两个月在找路,中间两个月在治「回得对不对」,最近一个月全在治「这东西自己活不活得下去」

06-10
方案 A
kf 客服 API 全链路上线:自建回调 + AES 验签 + 固定出口代理(Fly static egress)。当晚撞上 ICP 备案主体校验,冻结
06-11
转向
改走方案 B:Windows 企微客户端 GUI 自动化。写探针、上云电脑
06-29
V1 上线
UIA 探针判死结构化读取 → 拍板纯 VL 读 + GUI 发。端点 + loop 脚本上线;云电脑连不上 Vercel(大陆不可达),加 Fly 透传中转
07-01
发图
代回补齐内联发官方海报:按客户消息对海报触发词做确定性子串匹配,封顶 2 张 + 会话内去重
07-05
两条拍板
不接会话存档 API(花钱 + 认证 + 客户侧合规提示三件业务决策);企微代回一律不转人工,模型就算 emit 转人工也忽略
07-10~12
读屏加固
不信 VL 判发送方,改确定性归属;底部裁剪专读消除大气泡干扰;红点检测改像素扫描;三连发根因(回读假阴性 + VL 偶发误读)→ 重验优先于重发
07-15
换模型
5 模型 × 28 探针 × 3 采样 × 双裁判复评,切 kimi-k2.6(更短、无城市编造)
07-16
召回
7 天召回上线:沉默客户按画像选 7 个话题,前 3 天每天 1 条、之后退避隔天,客户回复 / 被删 / 满 7 次出库
07-21
回滚
红蓝对抗风洞起底:kimi 是推理模型,生产只传了 effort:"low" 压不住 → 话术漂移 + 延迟。回滚 qwen3-235b-2507
07-22
六修
拿「引导到店 / 撑 50 轮」当尺子重量后的一批修复:转人工闸误伤(9 句常见问句有 5 句会被永久闭麦)、认人裂会话、决策期永久催单、非真人闭麦扩表、去重窗口、到店指标漏斗(此前全仓零度量)
07-22
记忆
50+ 轮对话记忆:确定性画像抽取 + 滚动摘要 context_summary,按水位增量消化、不受历史窗口限制
07-22~24
拆仓
企微从主仓拆到独立仓 + 独立 Vercel 项目;隔离审计出 5 个跨仓缺陷全部修复;第二台云电脑(夏伟)上线真发
07-25
提速 + 自愈
自适应节奏(活跃窗口内快轮询)+ loop 自愈(每 20 轮比对远端 md5,有新版就干净退出让 launcher 拉新版)+ 看门狗。整套升级从 Mac 端远程命令做完,没人连过无影
07-26
配置版本化
把决定回复长相的五层配置压成一个版本号,每 15 分钟快照落库,可回溯可回滚
05
Current Production Snapshot

现在线上跑的是什么

下面这张表的每一行都能从线上现查——脚本版本查 /wecom/loop-script/meta,配置版本查 /api/wecom/config-version写这页时实拉过一次

0
台云电脑真发
成都 · 6 核 12G · 2940×1684
0s
单聊回复时延 p50
客户消息落库 → AI 回复落库
0
条服务端定时任务
召回 / 漏斗 / 通过通知 / 配置快照
0
个销售人设壳
同一张表按 persona 前缀硬隔离

时延口径:不含客户实际发出到云电脑读屏那一段(轮询等待 + VL 读屏,测不到)。近 7 天单聊 51 个活跃会话 / 162 条客户消息 / 141 条 AI 回复,p90 88s。

现状版本 / 取值
云电脑脚本常驻主循环,自带自更新v2026-07-25.adaptive-selfupdate.2
md5 04a937bc · 103,487 B
启动器预启动同步 + 崩溃 10s 重拉start-wecom-reply.bat(机上 CRLF)
看门狗SYSTEM 计划任务,每 5 分钟AkkeWecomLoopWatchdog
话术模型企微通道独立选型,可 env 灰度qwen/qwen3-235b-a22b-2507
视觉模型读屏,经 Fly 中转出海qwen3-vl(OpenRouter)
轮询节奏活跃档与空闲档都压在 5–12sMIN/MAX = ACTIVE_MIN/MAX = 5/12
发送限流生产值(测试期曾放宽,已收口)日 200 · 时 50 · 单客户 100
召回独立日限与被动回复分开计5
机器 A人设 xiaogu(小顾)ecd-5qfbmvz3yvpvbiog0
机器 B人设 xiawei(夏伟)ecd-eq68qf5rxfcpvxrl2
坐标每台必须实测,同分辨率也不能抄C_INPUT / C_SEND / C_SESSION1
CROP_RATIO / CROP_RIGHT_RATIO
最反直觉的一条

两台机器分辨率完全相同(2940×1684),坐标却完全不能互抄——企微客户端版本不同,左侧会话列表右缘从屏宽 21% 挪到 34.7%。同理,新版客户端多出来的「AI+」侧栏里有一张虚构客户的示例卡片,不裁掉右边界,视觉模型会把它当成真实客户消息读进来。

06
Self-Update, Watchdog, and Three Bugs They Exposed

让它自己活下去

7 月之前,改一行 loop 代码要人肉传两台机器——而 GitHub raw、jsDelivr、paste.rs 在这台机器上全部被墙或扛不住体积。7 月 25 日这条链改成自动的,代价是逼出了三个原本看不见的 bug

MECHANISM 01脚本自愈

后端把 loop 脚本本身做成发布产物GET /wecom/loop-script 给文件,/meta 给 md5 + 版本 + 字节数。loop 每 20 轮比对一次远端 md5,不一致就干净退出,让 launcher 在重启前先同步再拉起。全程无人工。

跨版本端到端实测通过:两台自主升级

MECHANISM 02看门狗

整条链死掉时(黑窗被关、cmd host 崩)此前唯一兜底是重启机器。现在一个 SYSTEM 计划任务每 5 分钟查一次,三重守卫缺一不可:有 loop 进程 / 有停机锁 / 有 launcher host 进程——任一命中就不动。少一个守卫就会起出第二个 loop,两个 loop 抢同一个窗口 = 重复回复客户。

破坏性验证:307 秒自动恢复
BUG 01 · 最致命

发布端根本不会重新部署。部署 workflow 的 paths-ignore 里有 cloudpc/**——而被发布的脚本正是那个目录下的文件,只改 loop 的提交不触发部署,端点发出去的永远是旧字节。症状极隐蔽:CI 绿、PR 合了、meta 端点 200,就是 md5 一直不变。
教训:改了一个目录的「性质」(从人工拷贝物 → 构建发布产物),要回头查所有按路径过滤的 workflow。

BUG 02

launcher 的同步一次超时就丢掉整轮更新:103 KB 穿墙下载有约 1/3 概率超 30 秒,而挂掉那次恰好是要装新版那次 → 「检测到新版 → 退出 → 同步失败 → 旧版重启 → 20 轮后再来一遍」的空转,每圈赔 40 秒停机且永远装不上。改成 meta 3×25s / 下载 3×120s,每次都 md5 校验。

BUG 03 · 最阴

schtasks /run 返回 rc=0,任务动作却瞬间死掉、零输出。真因:任务动作是 cmd /c "... >> _loop.log 2>&1",而刚被杀掉的上一个实例通常还要 4–8 秒才释放日志文件句柄 → 追加重定向建不起来 → cmd 立刻 exit 1;而错误信息本身要走那个失败的重定向,所以哪儿都看不到
修法是拉起前用同一个操作探测日志可写(最多等 60 秒)。推广:凡是「重启一个把 stdout 追加重定向到固定日志的常驻进程」都有这个坑,验收一律用进程存在性,别信返回码。

07
Config Versioning

「那天到底跑的是哪一版」

代回天天在改,但改动散在五层,各层版本号互不联合。后果是:「prompt v3」在上周和今天不是同一个行为——回滚 prompt ≠ 回滚行为,事后复盘也说不清那天到底什么配置。

配置层改了之后原本有版本吗
Langfuse 话术后台一改立刻生效有 version,但不发版、不过任何 CI
代码常量随部署生效只有 commit
模型 env改完 redeploy 生效连部署记录都没有
云电脑 loop 脚本自愈拉取后生效手写 VERSION 字符串
数据库语料改行即生效直接改,无历史

做法

  • 五层压成一个指纹 + 人读版本号 wc-YYYY.MM.DD-xxxxxx
  • 每 15 分钟快照,变了才落一行(含各层原文,约 88 KB/行)
  • 变更推 diff 卡片到提示词群
  • 每条回复带一个免费层指纹(commit + 模型 + prompt 版本),热路径零 IO

两条写进代码注释的铁律

  • 取不到 ≠ 变了:任一层抓失败就整条降级、跳过落库。缺层落库 = 往历史里塞一个从未存在的配置,之后所有回滚锚点都错
  • commit 是兜底 catch-all:逐常量 hash 用来指出「哪块变了」,其余代码改动由 commit 兜住。代价是纯文档提交也落一行——宁可多记不漏记

能回答什么

  • 现在线上跑的是哪一版(/api/wecom/config-version
  • 这条回复当时是什么配置生成的(回溯到条)
  • 某个时刻的配置全文长什么样(--at / --diff
  • 这份配置有没有被记下来——没记下来说明「历史正在丢」,那才是要修的
08
The Brain Side

大脑侧这半年改了什么

GUI 那半边解决「发得出去」,服务端这半边解决「说得对不对」。这里的每一项都是被真实翻车逼出来的,不是设计时想到的。

MEMORY50+ 轮记忆

三条病根:历史压缩超 16 条只留最近 10 条且只扫我方消息(客户说的城市/面积/预算全丢);画像靠模型自愿调工具(59 条会话只写了 4 条);路由历史窗口 40 条封顶。
修法:确定性抽取 7 字段 + 滚动摘要按水位增量消化,异步落库不占 GUI 等待。

RECALL7 天召回

对「拿了资料就不回」的客户:按画像从甲方话术库选 7 个话题冻结成计划,前 3 天每天 1 条、之后退避隔天,客户回复 / 被删 / 满 7 次出库。
护栏对齐平台风控:官方建议 ≤1 条/天、消息相似度 >70% 会被限流,所以有专门的防复读指令。

FUNNEL到店漏斗

此前全仓零度量,日报报的是加好友数。现在有确定性「约成到店」判据(必须有明确时间锚点或应约,「哪天过去方便」不算)+ 每天推五档漏斗。
造出来才发现真正的漏点在最前面:46 条像真客户的会话里,33 条客户开口 ≤2 句就没下文

MODEL选型与回滚

企微通道独立于抖音选型,可各自灰度。7-15 复评切 kimi-k2.6(更短、无编造门店),7-21 回滚——它是推理模型,生产只传了低档 effort 压不住,话术漂移 + 延迟。
回滚成本为零:改一个 env 就行,代码默认不动。

GUARDRAIL六修里最贵的那个

转人工闸的判据是裸子串,全屋定制最常见的 9 句问句里有 5 句会触发永久闭麦(「人工费」「真人上门量尺」「找个人」…),而当时闭麦是终态、无路可回。
修成带上下文正则 + 否定放行 + 24 小时冷静期自动复活

TENANCY多操作员隔离

同一个组织下多个销售的客户混在一张表,只靠会话身份键的 persona 前缀区分。加一个操作员 = 建一行账号 + 云电脑 .env 填三个值,零代码改动
踩过的坑:另一个看起来更自然的字段在这条通道上恒为 NULL,拿它筛候选会得到零结果。

09
Known Gaps

现在还没解决的

写对外页最容易的失真是只讲修好的部分。下面这几条是今天仍然存在的缺口,都有实测数据。

群聊分不清谁在说话

  • 外部群聊是重点业务、不是误触发——但 loop 的归属规则是单聊二元的(左气泡=客户 / 右气泡=我方),群里发言方 ≥3,两分法失效
  • 实测 12 轮:只有 2 轮是对真实客户话的正确应答,2 轮把我方自己在群里发的话当客户提问回了一遍
  • 修的方向是认气泡上方的发言人昵称行,不是「检测到群就跳过」——那等于关掉重点业务

群聊的裁剪参数是按单聊标的

  • 群聊窗口右侧多出群成员/群公告区,同一组裁剪比例会切进消息正文
  • 症状很好认:读出来全是被切掉头尾的半截词,然后模型从半截词里脑补出不存在的门店和话题
  • 判据:正文出现大量半截词 → 去量裁剪比例,别调 VL 提示词

「没误发」不等于「没问题」

  • 一次实测:86 分钟内 60+ 次读到同一批碎片并判为待回,全部被去重护栏拦下,数据库零新增 AI 回复
  • 护栏是有效的,但 loop 每轮照样烧一次截图 + 视觉模型 = 纯空转
  • 所以查健康度要同时看两个数:数据库的 AI 消息数(有没有乱发)和日志的待回频次(有没有空转)

业务侧的真瓶颈不在技术

  • 决策期话术和 50 轮记忆至今 0 次真实触发——能力造好了没派上用场
  • 真实对话最长 11 个来回(不是 50),平均活不过 3 轮
  • 瓶颈不在「记不住」,在「聊不下去」和「根本没在跟真人聊」。下一步是攒真实案例实测,不是继续静态排查
10
What Transfers

可以搬去别处的几条

这套东西很特殊,但里面有几条判据是通用的——尤其是「什么时候该让模型进决策环、什么时候一定别」

维度值得让模型驱动一定别
操作错的代价读数、整理文件,错了重来发错 = 骚扰真客户 / 烧线索 / 限号
频率一次性、低频、人工触发高频、批量、无人值守
谁来点击模型只做核身 / 分类 / 话术模型驱动每一步点击
目标元素大而独立(整窗、大按钮)小而密集(输入框、发送键)
确定性要求模糊判断(是不是目标 / 什么意图)需要可复现的路由 / 状态 / 计数

任一行落右列,就别做「模型遥控点击」那一版。混合情况就拆开:确定性部分走代码,模糊部分才交给模型——这正是这套系统最终的形态。

RULE 01返回码不是验收

这条链上至少三个返回码骗过我们schtasks /run 的 rc=0、远程命令的 InvocationStatus(网络故障期连必失败的脚本都报 Success)、以及看门狗 v1 的空转。验收一律用进程存在性或业务指标——数据库里有没有真的多出一行。

RULE 02否定性证据要先校准

「grep 得 0」「没收到回执」这类结论,必须先证明该通道在正例下会产生信号。我们靠一个未校准的日志通道把「远程命令根本不执行」写进过文档,次日复测全是正常的——假失败和假成功一样贵

RULE 03量裁剪,别调模型

视觉读屏出错时,第一反应往往是改提示词或换模型。实际上这套系统里绝大多数读屏事故的根因都是几何的——裁剪比例把标题、客户气泡或侧栏切进/切出了。先量像素,再谈模型。

RULE 04能力上线不等于能力被用上

50 轮记忆、决策期话术、转人工——三个精心做的能力,真实触发次数都是 0。造之前先问「现在的对话能走到那一步吗」,比造完再找场景便宜得多。先做度量,再做能力。

如果只记一句:这套系统的可靠性不来自任何一个聪明部件,来自「每一层都能被独立证伪」。脚本有 md5,配置有版本号,发送有回执,进程有心跳,业务有漏斗——出事时可以逐层砍半,而不是对着一个黑盒猜。