SYSTEM DESIGN · 2026-05-26
Claude Code 怎么"指挥"云电脑发抖音私信
整套系统其实没有"指挥"。云电脑藏在很深的网络里、外面找不到,所以反过来——它自己每分钟来公告板取活儿。理解这一点,整张图就通了。
触发节奏 · 每 5 分钟
单账号节奏 · ≥ 45 分钟一条
单账号日上限 · 18 条
横向扩展 · 1 台云电脑 = 1 个抖音号
一句话画面
把它当成一家 "远程帮你发抖音私信的代发公司"。
公司里有 8 个角色:闹钟、派单员、共享公告板、N 间远程办公室,每间办公室里坐着小助理、动手的人、还有一双 AI 视力,桌上摆着一台开着抖音电脑版的电脑。
全公司没有任何一根电话线直接打进远程办公室——所有信息都通过公告板中转。这是整套系统能在 NAT、IP 漂移、风控之下稳定运转的根因。
全景图 — 一张图看清整个公司
绿色 = 云电脑主动来取 / 回写 · 紫色 = 派单员往公告板贴 · 公司里没有任何一根线从外面直接打进云电脑
8 个角色 — 谁是谁的"真名"
① 闹钟Vercel Cron · */5 * * * *
没有任何业务判断,唯一职责是"每 5 分钟敲一下派单员"。Vercel 平台内部触发,不消耗任何对外网络。
② 派单员/api/cron/cloud-pc-dispatch · runDispatch()
跑在 Vercel 的一段代码。看每个抖音号"上次发是几点"、"今天发了多少条",节奏不到就跳过。最后把通过的人挑出来,往公告板贴一张便签。
③ 共享公告板Supabase Postgres · dispatch_queue 表
整套系统的交汇点。一张表,存所有派单。云端写入 + 云电脑读取走的是同一张表的两面,谁都不需要"找到"对方。"撕单子 15 分钟不回自动还回"这个机制由数据库里一个 RPC 函数保证。
④ 小助理wuying_poll_agent.py · 常驻 60s 循环
跑在每台云电脑里的 Python 进程,开机自启。一直待在办公室里,每分钟去公告板看一眼。它只认识带本办公室 ID 的便签(一张抖音号 = 一台云电脑 = 一个 account_id)。
⑤ 动手的人douyin_dm.py · pyautogui + pyperclip
真正模拟真人发消息的脚本。9 步固定动作:点搜索框 → 粘抖音号 → 等结果 → 点"用户" tab → 双因子匹配 → 点进主页 → 找私信按钮(双击兜底) → 粘贴消息 → 回车。每步之间留 1~3 秒像真人。
⑥ AI 视力Claude Vision · screenshot → API
进入对方主页后强制停一下,把屏幕截图发给 Claude,让 AI 看:"这个昵称和我手上工作单写的,是同一个人吗?"相似度 ≥ 95% 才继续。抖音搜索结果同名号一抓一大把,这是最后一道防认错人的关卡。
⑦ 桌上的抖音电脑版抖音 PC v7.8.0 客户端
就是普通用户装的抖音电脑版,登录着"小张号"。在它眼里没有自动化——它收到的是一连串"用户按了鼠标 / 用户敲了键盘"的系统事件,和真人操作完全一样。
⑧ 抖音服务器抖音 IM API(黑盒)
客户端按 ⌨️ 把消息送过来,抖音服务器收下,推给对方账号。这一步完全是抖音内部,我们看不见、不可控——也正因此,没有办法绕过客户端用 API 发,必须有一台真电脑装真客户端。
信息通道详解 — 每一跳到底是谁找谁
把每一跳的"谁主动 / 用什么方式 / 带什么东西"列出来:
| 跳 |
从谁到谁 |
方向 |
用什么方式 |
带什么 |
| 1 |
闹钟 → 派单员 Vercel Cron → Vercel Function |
推 平台内部 |
云端内部触发 HTTPS + Bearer CRON_SECRET |
只是个"叫醒"信号 无业务 payload |
| 2 |
派单员 → 公告板 Vercel Function → Supabase |
推 写入 |
数据库写入 HTTPS · PostgREST |
插入一行 pending 便签 (account_id, target, content) |
| 3 |
云电脑 → 公告板 poll_agent → claim_dispatch RPC |
拉 主动取 |
云电脑主动来抢锁 HTTPS · supabase-py RPC |
"撕下来" ≤ 3 张属于本办公室的便签 原子操作 + 15min 死锁回收 |
| 4 |
小助理 → 动手的人 poll_agent → douyin_dm.py |
本机 |
子进程 + CSV 文件 subprocess.run |
一份 contacts.csv (_dispatch_id, sec_uid, content) |
| 5 |
动手的人 → 抖音客户端 douyin_dm.py → 抖音 PC |
本机 不走网络 |
OS 层模拟键鼠 SendInput · 剪贴板 |
9 步动作序列 点击坐标 + Ctrl+V + Enter |
| 6 |
动手的人 ⇄ AI 视力 screenshot → Anthropic API |
双向 |
调用 Claude HTTPS · Vision API |
主页截图 + 期望昵称 返回 0~1 匹配分 |
| 7 |
云电脑 → 公告板 poll_agent → complete_dispatch RPC |
拉 回写 |
数据库 RPC HTTPS · supabase-py |
"这张便签干完了 / 失败了" + mark_lead_contacted 永久去重 |
| 8 |
抖音客户端 → 抖音服务器 |
推 |
抖音自家协议 HTTPS · 我们看不见 |
消息内容 由抖音 IM 路由 |
看这张表的关键:跳 3 和跳 7 都是「云电脑主动来」。Vercel 那边永远不知道哪台云电脑在线、IP 是什么、有没有重启。它只管往公告板贴。云电脑那边就算重启 5 次、IP 换了 8 次,也照样能继续工作——因为锁回收和便签都还在公告板上。
应急通道 — 公告板卡住时手动补一条
平常这家公司是全自动的。但有时候我(Claude Code,在 Mac 上)需要亲自跳进去看看——比如新版部署后第一条 DM 现场验证、或者公告板卡住了想手动塞一条。这条小后门是这样的:
红色 = 推送方向 · 这条路是反过来的:云电脑主动"伸线"建反向隧道,外部请求顺着隧道倒灌回来
这条通道的边界:临时网址每次云电脑重启都换;只在联调 / 队列卡住时人工用。生产 DM 还是走公告板那条主链路——后门没有节奏闸、也不写公告板,绕过去 = 同一个人有可能被两台云电脑同时发,请谨慎。
为什么这么设计 — 几个"看上去多此一举"的解释
为什么派单员不直接告诉云电脑"该干活了"?非要走公告板?
云电脑藏在很深的网络里(在阿里 IDC 的 NAT 后面),外面根本找不到它。要么云电脑出公网门牌号(贵 + 风控会盯)、要么反过来"让云电脑自己来取"。我们选了第二种。代价是有 60 秒延迟,收益是云电脑可以随便重启 / 漂移 / 加台数,外部完全无感。
为什么一张便签只能撕一次,还要 15 分钟自动贴回去?
同一个抖音用户不能被两个号同时联系——对方一眼就能看出"为什么两个无关账号都来骚扰我",立刻举报。15 分钟自动贴回是兜底:万一一台云电脑撕了便签后突然断电 / 重启,便签会自己回到公告板,让另一台继续。两种情况都用数据库的"原子操作"保证不会出错。
为什么派单员要看"上次几点发的"再决定派不派?
真人不可能每 3 分钟连发 20 条私信。节奏太密 = 抖音风控当场判定机器人,账号封禁。45 分钟 + 随机抖动模仿真人节奏;每天 18 条上限对应"运营白班 8 小时手动发"的速率。这两个数字不是拍脑袋——是 5/21 PoC 实测下来的安全区。
动手的人为什么不直接调抖音 API,非要点鼠标?
抖音不开放发私信的 API(开放过的早就被滥用了)。能用的"通道"只有真人客户端。所以让一台云电脑装真客户端、登真号、由脚本模拟键鼠"像真人一样"操作。这也是为什么必须用云电脑——本地 Mac 装抖音电脑版也行,但风控会看 IP,多账号同 IP = 必封。云电脑给每个号配独立 IP。
为什么进对方主页后还要让 AI 视力再看一眼?
抖音搜索结果按抖音号搜,看似精确,但同昵称号一抓一大把——搜"王女士"出来 25 个。第一道我们用"昵称 + 抖音号"两个字段一起对,但偶尔 UI 上抖音号渲染慢、被截断、识别错位。截图给 Claude 看一眼"屏幕上这个人和工作单写的是同一个吗"是最后一道保险,相似度 < 95% 整单立即跳过,不发。
为什么把"小助理"和"动手的人"拆成两个进程?
这两件事关注点完全不同。小助理是个稳定的轮询循环,需要长时间稳定运行 + 跟数据库通信。动手的人是高风险的 UI 操作脚本,可能因为抖音弹窗 / 网络抖动 / 元素加载慢而卡死。拆开之后,动手的人卡死或崩溃,小助理可以单独干掉它、重新调起;小助理本身也能被外部脚本(比如运营手动跑 dispatch-leads.ts)复用。
相关文档
15 · CLOUD PC 上手指南 — 从零到第一条 DM 的 30 分钟流程 ·
07 · DM 投递与引导加微 — 整条 DM 链路的产品视角 ·
03 · 双链路防风控 — 节奏闸 / 视力校验在防风控体系里的位置 ·
02 · 系统架构详解 — Akke 三段式架构全景