我给跑在本机的 AI 助手下了一个再普通不过的指令:「打开公众号后台,准备发一篇新文章。」
对我来说这是十秒钟的事:扫码进后台、点左侧「内容管理」、点「新的创作」、点「文章」,编辑器就开了。
但我马上意识到有场好戏要看——屏幕上这些按钮,在 AI 眼里长着完全另一副模样。
第一幕:一切顺利得可疑
AI 用的浏览器是 ego Lite——一个专为 Agent 设计的浏览器,我在之前的文章里分析过它的架构判断:浏览器正在从 Tool 变成 Runtime。这次正好现场验证。
它有个很妙的设定叫「任务空间」:AI 在一个隔离的房间里干活,但这间房继承了我的登录态——像酒店给了它一间挂着我名字的客房,刷房卡就能进公众号后台,还不打扰我正在用的窗口。
于是第一步顺得离谱:
- 打开 mp.weixin.qq.com,上次留下的登录态还在,连扫码都省了;
- 首页数据读得干干净净:总用户 360,昨天 177 人阅读、15 人分享、3 个新关注;
- 最近发的文章列表也顺手抓了下来。
我说:「准备发表内容,先点开左侧边栏的内容管理。」
第二幕:点不动的按钮
侧边栏的「内容管理」是个折叠菜单。AI 用一行 JavaScript 把它点开了,很顺。
有意思的是它顺手干的事:它嫌「再点一层草稿箱」麻烦,直接去翻了这个菜单的 DOM,发现答案早写在里面——整个子菜单(草稿箱、素材库、发表记录、原创、合集)连各自的网址都躺在 HTML 里,只是盖着一块 display: none 的布。它掀开布把网址抄了下来,直接在地址栏输入,进了草稿箱。
这里埋了第一个伏笔:网页上一切看得见的东西,底下都藏着一条看不见的路。
草稿箱页面有个绿色按钮:「新的创作」。AI 点开它,下拉菜单弹出六个选项:文章、选择已有内容、贴图、视频、播客、转载。
然后,喜剧开始了。它要点的只是「文章」两个字,结果连碰了五次壁:
- 用 JavaScript 的
el.click()点——返回「clicked」,页面纹丝不动; - 想,也许这个按钮讲究仪式感?把 mouseover、mousedown、mouseup 全套补齐再点——还是不动;
- 换自动化工具的正规点击函数,用 CSS 选择器精确定位——还是不动;
- 决定截图看看。截图成功了,返回来一个文件路径——这时它想起一个尴尬的事实:它这个模型看不了图。举着望远镜的盲人;
- 回过头再去找那个下拉菜单——「元素不存在」。菜单,自己收起来了。
第三幕:破案
五连败之后,它把两件事想通了。
第一件事:菜单是有寿命的。 下拉菜单不是一张静态图片,是个活物——鼠标移开、时间一到就自己收起。AI 前几轮折腾(截图、找元素、换方法)消耗的每一秒,都是菜单的余寿。等它端着新方法回来,桌上的菜都撤了。
第二件事:点击和点击,是不平等的。 JavaScript 造出来的点击事件,在浏览器眼里是「假」的——每个事件对象上都盖着一个章:isTrusted: false。像一封代笔的信,字迹再像也不是本人。这套界面里,有的门卫不查证件(比如「内容管理」,代笔就放行了),而「文章」这个门卫偏要验真人。
破局只用了一秒:重新点「新的创作」让菜单复活,立刻用 getBoundingClientRect() 算出「文章」按钮此刻的屏幕坐标(1744, 160),再用浏览器协议(CDP)发一次货真价实的鼠标点击——这一下不是代笔,是本人到场,事件上的章是 trusted。
新标签页应声弹出:图文编辑器,标题框、正文、工具栏,一应俱全。
第四幕:彩蛋
故事到这本该结束了,但 AI 干了一件优秀员工都会干的事:复盘。
它盯着新标签页的地址栏看:
https://mp.weixin.qq.com/cgi-bin/appmsg?t=media/appmsg_edit_v2
&action=edit&isNew=1&type=10&token=1561097741&lang=zh_CN
# generated by hugo AI
地址栏里写着整场戏的剧本。刚才那整套「侧边栏 → 内容管理 → 草稿箱 → 新的创作 → 文章」的舞蹈,跳到最后,原来只是跳到了一个网址上。而网址里那个 token,就是这场舞的门票——它就印在会话里每一个已打开页面的地址栏里,随手可抄。
于是它主动提议:关掉所有标签页,重来一遍给我看?
它关掉编辑器和草稿箱,只留首页;从首页地址栏抄下 token,拼出编辑器的网址,直接在地址栏输入——
4.3 秒后,新文章编辑器就绪。 所有的菜单、下拉、坐标计算,全部跳过。
第一次,含试错,七八轮脚本、好几分钟;第二次,4.3 秒。
第五幕:把学费写进 skill
这个故事本来可以到此结束,但还有一个容易被忽略的问题:这条 4.3 秒的路径,只存在于这次对话里。会话一结束,它就跟着上下文一起消失——下次接到同样的任务,AI 还得从第一次碰壁重来。学费交一次是学习,每个会话都重新交一遍,叫不长记性。
于是它多干了一件事:把直达路径和沿途的坑,写进了一个叫 wechat-mp-browser-ops 的 skill——
- 直达路径:编辑器的 URL 模板、token 从哪抄;
- 坑的清单:
el.click()点不动、菜单有寿命、坐标要现拿现用; - 适用边界:哪些门卫不查证件、哪些要验真人。
从此,任何会话接到「打开公众号后台」的任务,都会先加载这个 skill,直接走 4.3 秒那条路。第一次是学费,第二次是复利,从第三次开始,不再有学费。
四条原理,值得抄进你的 Agent 手册
故事讲完,把干货提炼出来。前三条适用于任何让 AI 操作网页的场景,第四条适用于一切 Agent 任务:
原理一:点击有「保真度阶梯」。
| 层级 | 方式 | 浏览器怎么看 |
|---|---|---|
| 低 | el.click() | 代笔签名,isTrusted: false,讲规矩的组件不认 |
| 中 | dispatchEvent 补全套鼠标事件 | 模仿更逼真,依然是代笔 |
| 高 | CDP 坐标点击 | 本人到场按手印,isTrusted: true,几乎所有界面都认 |
调试的正确姿势是沿阶梯往上爬:先试便宜的,不行再升级。本文的「文章」按钮就住在阶梯最高一级上。这和AI 操作你的电脑,最大的对手不是防火墙里讲的是同一堵墙的两面——那篇说检测方在看行为特征,这篇说执行方要补行为特征。isTrusted 就是浏览器内置的最基础行为检测:它不查你像不像人,只查你是不是真的来了。
原理二:UI 是舞蹈,URL 是目的地。
现代网页(SPA)的一切交互——点菜单、切标签、翻页——终点几乎都是一次页面跳转。人会被舞蹈吸引,Agent 应该盯住目的地:把你到过的每个页面 URL 记下来,下次直接瞬移。 token 这类会话参数就藏在已打开页面的地址栏里,不用重新跳一遍舞去取。
这也是为什么在Agent 操作浏览器的九条路线里,API 永远优先于点击——URL 直达就是最便宜的 API。
原理三:状态会过期,坐标要「现拿现用」。
下拉菜单、弹窗、popover 都是活物,有自己的生死。对它们的操作要压缩成一个原子动作:打开 → 立刻量坐标 → 立刻点。「拿坐标」和「点击」之间别插入任何别的事——你磨刀的时候,菜凉了。
原理四:不固化的经验是一次性的。
4.3 秒的路径只存在于一次会话里,会话结束就跟着上下文消失。调通之后的最后一步,是把两样东西写进 skill:验证过的直达路径,让下个会话跳过整支舞蹈;交过学费的坑,让下个会话不再掉进同一个。这正是Jeff Dean 把最值钱的东西公开了:Skill 是组织知识的新载体里的判断——知识应该被编译成 Agent 可直接加载的可执行形态,而不是留在故事里讲一遍就丢。
还有一条备选经验:诚实面对工具的边界。 截图返回的是路径不是答案;看不了图就大方承认,回 DOM 里继续找路。盲目重试不如换探针。这一条反过来也解释了工程 AI 化六条标准里为什么把 vision 列为硬门槛——这次的 AI 恰好看不了图,第 4 次碰壁就耗在了「举起望远镜才发现自己是盲人」上。有 vision 的 Agent 在第 1 次失败后就能看到菜单还活着,试错轮次会少得多。
结尾
回到那个 4.3 秒。
它不是模型变聪明了——模型一步都没升级。变的是 Agent 把一次试错的经历,编译成了一条直达路径,又把这条路径连同沿途的坑写进了 skill。第一次的几分钟是学费,第二次的 4.3 秒是复利,skill 是让复利跨会话存活的存折。
人类看到的是按钮,AI 看到的是 DOM,但最终大家都去同一个 URL。
会点按钮的是好员工,会抄 URL 的才是老司机,会把路写进地图的才不再迷路。
你的 Agent 上一次在同一个按钮上碰壁,是多久以前的事?欢迎留言聊聊。