
我让 AI 调试自己的工具,调了 15 轮
事情的起点很简单:我想让发公众号这件事变得完全无人值守。
不是"点一下按钮"那种半自动,而是真正的无人值守——文章准备好,Claude 帮我排版、上传图片、存草稿,整个流程跑完我都不用看屏幕。如果中途微信登录过期需要扫码,它也能自动把二维码发到我的 Telegram,我掏出手机扫一下,它继续跑。
这件事我认为应该不复杂。结果,我们调试了将近 15 轮,碰到了四个性质完全不同的坑。
工具是现成的,问题是现实的
我用的工具叫 baoyu-post-to-wechat,一个专门发微信公众号的 Claude Code skill。它有两种发布模式:API 模式(直接调微信接口)和浏览器模式(用 Chrome CDP 模拟人工操作)。
我平时用 API 模式,快,无需打开浏览器。今天要发的文章是《当 Anthropic 亲自下场做金融 AI》,HTML 格式,三张图片,摘要都准备好了。
Claude 跑起来,几十秒后输出一行红字:
Error: Upload failed 48001: api unauthorized第一个坑:以为是自己的问题

48001 是微信 API 的权限错误。我第一反应是 AppID 或 AppSecret 填错了。让 Claude 去验证凭证——access token 拿得到,证明凭证没问题。
那是什么权限问题?
Claude 排查下来发现:我用的是订阅号,微信的 material/add_material(永久素材上传)接口,订阅号根本没有权限调用。不是我的问题,是账号类型的限制。
好,那换临时素材 media/upload,它返回 media_id,也许能用作封面。Claude 改了代码,临时上传成功了。但紧接着——
Error: Publish failed 48001: api unauthorized这次是 draft/add,草稿箱接口本身也不让用。
到这里才彻底清楚:不是某一个接口的问题,是这个账号类型在 API 层面就无法走草稿流程。 唯一的出路是改用浏览器模式,让 Chrome 去帮我模拟人工点击。
这是第一个坑的本质:表象是报错代码,真相是边界条件。
改道浏览器,遇到白屏
浏览器模式用 Chrome CDP(Chrome DevTools Protocol)——Chrome 开着,脚本通过协议操控它,就像远程控制一台电脑。
脚本跑起来,Chrome 窗口打开,检测到需要扫码,于是触发我加的新功能:截图发 Telegram。
Telegram 收到了消息,但图是纯白的。
第一反应:等待时间不够,页面还没渲染完。Claude 加了 4 秒等待,重新跑——还是白的。
加到 8 秒——还是白的。
这时候我们开始往更深处挖。Claude 加了诊断日志,把截图时页面的 URL、DOM 结构都打印出来。结果出来了:
url: "about:blank"imgs: []iframes: []canvas: falseabout:blank。
Chrome 找到了那个 mp.weixin.qq.com 的标签页,但 JavaScript 上下文里,页面还是空白的。微信公众号的页面在 Chrome 里还没真正加载完——或者说,CDP 连上的那个"壳"还在导航途中,JavaScript 世界里还是出发前的空白。
这是第二个坑的本质:CDP 的 Target.getTargets 会提前报告目标 URL(导航意图),但 JavaScript 上下文要等导航真正 commit 之后才会更新。 在这个间隙里截图,永远是白的。
解决方向:不能截图后才等,要先等 JavaScript 上下文的 URL 确认不是 about:blank,再截图。
Claude 改了等待逻辑——用轮询 window.location.href,直到不是空白再继续。
第三个坑:URL 说谎了
等待逻辑加上之后,新的日志出来了:
committedUrl: https://mp.weixin.qq.com/不是 about:blank 了,是真实 URL,进入了截图流程。但 Telegram 收到的图片依然不对。
这里有个我之前没意识到的事:微信公众号是一个 SPA(单页应用)。
登录成功和未登录,URL 都是 https://mp.weixin.qq.com/,根本不跳转。脚本原来的登录判断逻辑是检查 URL 里有没有 /cgi-bin/,SPA 让这个判断完全失效。
更让人困惑的是:即使已经用有效 Cookie 登录了,页面加载完之后 URL 也不会变成 /cgi-bin/home,它就停在根路径。脚本一直以为"没登录",一直在尝试发二维码。
真正的判断方式:不看 URL,看 DOM。登录页面有特定的二维码容器元素,已登录页面有创作菜单。用元素检测代替 URL 检测。
Claude 改了判断逻辑:检查页面里有没有 .login__container(有 = 需要扫码,无 = 已登录)。
这次终于能正确区分了。
第四个坑:图片拿不到
登录检测对了,QR 图片的 URL 也找到了:
https://mp.weixin.qq.com/cgi-bin/scanloginqrcode?action=getqrcode&random=...顺理成章,Claude 写了 fetch(url) 去下载这张图,然后发给 Telegram。
Telegram 返回:
Bad Request: file must be non-empty图片是空的。
原因想想也对:这个 URL 需要微信的 session cookie 才能访问。从 Node.js 进程里直接 fetch,没有 Cookie,服务器返回空内容或错误页。
解决方法是把 fetch 搬到 Chrome 里去执行——Chrome 有 Cookie,在浏览器上下文里下载图片,把 base64 传回 Node.js 进程:
const inBrowserFetch = await cdp.send('Runtime.evaluate', { expression: ` (async () => { const resp = await fetch(url, { credentials: 'include' }); const buf = await resp.arrayBuffer(); const bytes = new Uint8Array(buf); let b = ''; for (let i = 0; i < bytes.length; i++) b += String.fromCharCode(bytes[i]); return btoa(b); })() `, awaitPromise: true,});这是第四个坑的本质:网络请求的 Cookie 上下文。在 Chrome 里跑的代码和在 Node.js 里跑的代码,面对的是两个完全不同的 session 环境。
第 15 轮,成功了
日志出来:
[wechat] QR screenshot sent to Telegram.[wechat] Logged in.[wechat] Clicking "文章" menu......[wechat] All images inserted.[wechat] Done. Browser window left open.Telegram 里收到了清晰的微信登录二维码,扫码,等了十几秒,Chrome 自动跳转到编辑器,标题填好,三张图片都插进去,摘要填好,存草稿,完成。
这中间我做的唯一一件事:掏出手机,扫了个码。
一些真实的感受

这次调试让我最印象深刻的,不是找到了答案,而是找的过程。
每一个坑暴露的时候,我们都以为"这次应该好了"。API 换临时素材 → 还是 48001。加了等待时间 → 还是白屏。URL 对了 → 图还是空。每次离终点看起来只差一步,但那一步后面还有一步。
Claude 在这个过程里做的事,是系统性地缩小问题范围。每次遇到新的失败,它的下一步不是猜测,而是加一行日志、问一个更具体的问题。about:blank 暴露了 CDP 时序问题,https://mp.weixin.qq.com/ 暴露了 SPA 问题,空图片暴露了 Cookie 上下文问题。每一轮诊断都让下一轮更有方向。
但有一件事 Claude 做不到:感受到那种"又绕回来了"的挫败感。这件事由我来承受。
换句话说——AI 能保持注意力不散,能把每一个错误转化成下一步的线索,但"到底要跑多少轮"这个问题带来的疲惫感,是真实的,是我的。
这不是说 AI 辅助调试不值得。恰恰相反——如果完全靠我自己,我大概在第三个坑就放弃了,觉得"太复杂了以后再说"。有 Claude 在,每一个坑都有人陪着挖,就算挖出来是另一个坑,也还是继续。
这就是 AI 辅助调试对我来说最真实的价值:不是一次性给出答案,而是让我在本来会放弃的地方,多坚持了几轮。
最后那篇文章,顺利存进草稿箱了。
如果你也想让发公众号真正无人值守——至少现在知道了,Chrome调试端口 + Cookie上下文 + SPA判断,这三个坑过了,后面的路就顺了。
本文写于 2026 年 5 月 10 日。涉及工具:Claude Code、baoyu-post-to-wechat skill、Chrome CDP。
夜雨聆风