资讯动态

Midscene+Puppeteer协同:AI自然语言驱动网页自动化的架构与实践

发布时间:2026/10/18 1:20:47 来源:尧图企业网站定制
先抛一个我这段时间反复被问到的问题我已经用 Puppeteer 把网页自动化写得很顺了为什么还要关心 Midscene反过来还有人问Midscene 是不是要取代 Puppeteer我的结论很直接不是取代是分工。Midscene 可以把自然语言指令“在登录页输入账号密码然后点提交”转成浏览器里真实可执行的动作但真正打开浏览器、渲染页面、点击输入、等待回调的仍然是 Puppeteer。想理解 Midscene 技术架构全景图最关键的一步是先忘掉“框架取代框架”的思维改成看一条流水线AI 负责理解需求Puppeteer 负责执行动作两边靠一份共享的页面上下文协同工作。这篇文章适合两类人。第一类是已经写过几段 Puppeteer 脚本、正在为选择器频繁失效头疼的开发者你想知道 AI 到底能帮你省掉多少维护成本。第二类是正在做浏览器型 Agent 工具、需要搞清楚控制权该放在哪一层的团队。我会先给整体架构图再拆 Midscene 和 Puppeteer 各自的角色最后上一段能跑通的最小示例和避坑清单尽量做到说人话、能落地。1. 全景图先睹Midscene 和 Puppeteer 在一条流水线上各干各的先别急着看代码把角色分清楚比任何 API 都重要。Midscene 在我的理解里是一个“AI 编排层”它负责理解用户用自然语言描述的目标并把目标翻译成浏览器动作。Puppeteer 则是一个“浏览器驱动底座”它负责把浏览器进程拉起来、加载页面、触发点击和输入、读取页面状态。Midscene 不做这些底层事情Puppeteer 也听不懂“帮我查一下今天的天气”这种话。两者放在一起才构成一个完整闭环。这种分层设计不是拍脑袋想出来的。早期做网页自动化时大家习惯把“动作”和“业务意图”混在一个脚本里写比如定位搜索框、输入关键字、点击按钮、等待结果每一步都是硬编码。一旦页面改版选择器失效整个脚本就断掉。Midscene 这种方案最聪明的地方是把“我想要什么”和“浏览器怎么执行”拆开前者交给大模型后者交给 Puppeteer两者之间用一套结构化的动作指令通信。后面我会详细讲这套通信机制先记住这条流水线的大骨架就够了。1.1 Midscene 到底扮演什么角色Midscene 的核心能力可以压缩成两句话看懂页面生成动作。它接收到的输入通常是一句自然语言比如“在搜索框里输入架构全景图然后点击搜索按钮”它需要先理解这句话里的目标元素是什么、动作是什么然后再结合当前页面的实际情况生成一串可供浏览器执行的动作。这个过程不是简单的关键词匹配而是大模型对页面语义的推理。Midscene 本身不启动浏览器它更像个“调度器”。它会从当前页面抓取必要的信息比如可交互元素、页面文本、视觉截图再把这些信息和用户指令一起交给大模型。大模型返回一个结构化的动作序列后Midscene 再把这个序列翻译成具体的浏览器操作最终落到某个自动化执行器上。这里执行器可以有很多种Puppeteer 只是其中之一。所以从架构上讲Midscene 做的是意图理解和动作规划Puppeteer 做的是动作落地。这里有一个非常关键的认知转变过去我们写自动化脚本时最大的精力花在“如何准确找到一个元素”上现在 Midscene 把这个问题变成了“如何准确描述一个目标”。描述得越清楚大模型的定位就越准。如果页面里只有一个输入框你直接说“输入框”就行如果页面里有五个输入框就得说“用户名输入框”或者“页面左上方那个带放大镜图标的输入框”。这种语义化定位方式就是 Midscene 能显著降低选择器维护成本的根本原因。1.2 Puppeteer 在这里面又扮演什么角色Puppeteer 的老本行不用我多介绍它通过一套浏览器调试协议控制浏览器进程能够打开新页面、模拟鼠标键盘操作、拦截网络请求、执行页面脚本、读取 DOM 和截图。在 Midscene 的协同架构里Puppeteer 承担的是执行器和观察器的工作。执行器好理解大模型说要点击某个按钮Puppeteer 就去真实地点那个按钮大模型说要输入一段文本Puppeteer 就去真实地敲键盘。观察器则体现在 Midscene 抓取页面状态时它要依赖 Puppeteer 提供 DOM 内容、可访问性树、截图这些原始数据。没有 PuppeteerMidscene 就像个看不见也摸不着东西的“指挥家”只能凭空想象页面长什么样。有一点很多人容易忽略Puppeteer 的确定性很高。一个按钮的点击 API 要么执行成功要么抛出异常结果是可以追踪的。而大模型生成的回答本身带有随机性同一个指令在不同时间可能给出略有差异的结果。把确定性高的执行层放在 Puppeteer 这一侧把不太稳定的理解层放在 Midscene 这一侧是这套架构能实际用于生产的关键原因。换句话说Puppeteer 兜的是稳定性的底。1.3 为什么不是二选一而是协同有人会问Midscene 不是也能操作页面吗那我是不是可以完全不用 Puppeteer 了理论上 Midscene 可以把“操作”能力也内嵌但它最终还是要借助某种浏览器控制通道。与其重复造轮子不如直接站在 Puppeteer 这类成熟的执行器上面。这样 Midscene 的维护精力可以全部集中在 AI 调度和提示词优化上而 Puppeteer 的进程管理、协议兼容、选择器兜底能力又能原封不动地继承下来。从实际效果来看纯 Puppeteer 方案的最大痛点是页面改版之后选择器失效维护成本高纯 Midscene 方案如果脱离 Puppeteer又缺少可靠的执行兜底AI 一旦看走眼就会点错地方错误还不好追踪。把两者协同起来等于让 AI 负责“找”让 Puppeteer 负责“做”出了问题能清晰地看到是哪一步理解错了还是哪一步执行失败调试体验完全不同。我经常用一个比方来解释这套关系Puppeteer 是汽车底盘Midscene 是自动驾驶算法。自动驾驶算法当然可以自己造底盘但没必要底盘的技术已经足够成熟稳定算法只需要拿到方向盘、油门、刹车的控制接口就能实现更上层的能力。Midscene 和 Puppeteer 之间的关系本质上就是上层智能和底层执行的关系。2. 核心协同原理浏览器进程、调试协议与“大模型当眼睛Puppeteer当手脚”全景图看完下面得往原理层钻一点。Midscene 和 Puppeteer 不是两个各自独立的工具在各自忙活它们之间必须有一套清晰的数据流和控制流。理解这套流程你才能真正掌控它。2.1 同一个浏览器实例是协同的枢纽在整个协同架构里最重要的一句话是Midscene 和 Puppeteer 操作的是同一个浏览器页面对象。Puppeteer 启动浏览器后会返回一个 page 实例这个 page 实例就是浏览器里一个标签页的抽象。Midscene 的 Agent 在初始化时接收这个 page 实例之后它所有对页面的观察和操作都通过这个 page 实例去完成。这样做的好处是显而易见的。你不必再维护两套浏览器状态不用考虑 Midscene 看到的页面和 Puppeteer 操作的页面是不是同一个。两者天然共享同一份 DOM、同一个 Cookie 存储、同一条网络链路。你甚至可以在 Puppeteer 里先完成登录操作再把 page 实例交给 Midscene 做后续的自然语言操作登录状态完全不会丢。底层上Puppeteer 使用现代浏览器调试协议与浏览器进程通信。Midscene 并不直接解析这个协议而是把 page 实例当成一个“标准执行接口”。只要这个接口支持获取内容、截图、点击、输入、等待这些基础能力Midscene 就可以在其上工作。这种设计降低了集成复杂度的同时也让 Midscene 可以支持多种自动化框架不局限于某一个。但这不是重点重点是你得理解协同的关键在于“同一个 page 对象”。2.2 Midscene 的 AI 调度层是怎么看懂页面的大模型不能直接“看见”一个浏览器页面Midscene 要做的第一步是把页面转换成大模型能理解的信息。这个过程我称之为“页面上下文抽取”。Midscene 通常至少会抽取三类信息第一类是 DOM 结构和文本内容让模型知道页面上有哪些元素第二类是可访问性树专门标记按钮、输入框、链接等可交互元素的角色和标签第三类是视觉截图让模型看到页面的真实布局。为什么要同时用三样东西因为单纯靠文本 DOM模型很难理解元素的视觉位置比如“右上角的按钮”到底指的是哪个。单纯靠截图模型又很难精确定位到某个可操作元素容易根据视觉猜测坐标。可访问性树则提供了标准化的可交互语义能有效提升定位准确率。Midscene 把这三样信息作为上下文连同用户的自然语言指令一起发送给大模型模型返回的不再是一段自然语言而是一串结构化的动作序列。这里有一个工程上的取舍页面 DOM 可能非常大全量塞给大模型既费 token 又容易超出上下文限制。所以 Midscene 在抽取上下文时通常会做压缩只保留可见区域、可交互元素和关键文本把无关的脚本标签、隐藏样式过滤掉。这也是为什么大页面或者复杂单页应用里Midscene 的识别效果可能不如简单页面稳定。理解这一点后续排查思路就会清晰很多。2.3 指令下发与结果回传的完整链路把一次 aiAction 的完整调用链路拆开看你就能直观感受到两个工具是怎么咬合的。假设页面已经打开用户调用 agent.aiAction(在搜索框输入某某关键词然后点击搜索按钮)内部大致会发生七步Midscene 从 page 实例获取当前页面的 DOM 上下文、可访问性树和截图。把自然语言指令和页面上下文一起格式化发送给大模型服务。大模型返回结构化动作序列例如先定位输入框执行输入操作再定位搜索按钮执行点击操作。Midscene 校验动作序列把定位结果映射成可执行的浏览器操作。Puppeteer 按动作序列真实执行鼠标键盘事件。执行完成后再抓一轮页面状态验证页面是否如预期发生变化。把成功或失败的信号回传给调用方失败时带上错误信息可供后续重试或修正。这条链路里最有价值的一环是大模型返回的是结构化动作而不是自由散漫的代码。因为动作结构化之后Midscene 和 Puppeteer 的交接面就非常干净。每一步动作都能被记录、被日志输出、被审计。你随时可以知道 AI 决定点击哪个元素、实际点击是否成功。这解决了纯 AI 操作“黑盒不可控”的最大痛点。3. 最小可跑通的协同示例一个搜索场景从前到后的完整过程原理讲完了必须上一段能跑的代码。我先声明一点Midscene 和 Puppeteer 的版本迭代节奏都不慢下面示例里的包名和 API 形态可能随版本变化但核心思路不会变。你实际操作时以官方当前版本的文档为准不需要追求一次跑通更值得关注的是我标注出来的协同点。3.1 先装环境把依赖准备好运行环境建议是 Node 18 以上版本。先用 npm 初始化项目再安装 puppeteer 和 Midscene 相关依赖。Midscene 的重要依赖可能不止一个包请按你使用的版本对应的说明来装。npm init -y npm install puppeteer midscene/web还需要准备一个大模型服务的 API Key。Midscene 通常支持对接兼容大模型接口的服务你只需要在初始化时指定 provider 类型、API Key 和接口地址。如果公司内部有大模型网关也可以直接把网关地址填进去这样既安全又方便统一计量成本。安装完成后强烈建议准备一个低风险的测试页面别一上来就对着生产环境狂点。你可以先起一个本地测试页面或者用一个稳定的演示站点重点是把流程跑通再考虑复杂场景。3.2 用 Puppeteer 拉起浏览器并创建 Midscene Agent协同的起点是先通过 Puppeteer 启动浏览器并创建 page 实例然后用这个 page 实例去初始化 Midscene 的 Agent。注意观察 new Agent 这一步它接收的就是 Puppeteer 返回的 page 对象这是整篇架构里最关键的口子。import puppeteer from puppeteer; import { Agent } from midscene/web; const browser await puppeteer.launch({ headless: true, defaultViewport: { width: 1280, height: 800 }, }); const page await browser.newPage(); const agent new Agent({ page, provider: { type: process.env.MIDSCENE_PROVIDER_TYPE, apiKey: process.env.MIDSCENE_API_KEY, baseURL: process.env.MIDSCENE_BASE_URL, }, }); await page.goto(https://your-target-site.com, { waitUntil: networkidle2, });这里建议把 defaultViewport 固定下来。大模型是通过截图理解页面的如果每次跑的窗口大小都不一样截图比例和布局就会频繁变化AI 的识别稳定性会明显下降。固定视口是花小钱办大事的稳定性手段。3.3 用自然语言驱动一次完整操作页面加载完成后就可以让 Midscene 干活了。aiAction 用来执行操作aiQuery 用来从页面提取信息两者是协同场景里最常用的方法。await agent.aiAction(在搜索框中输入 架构全景图然后点击搜索按钮); const result await agent.aiQuery(搜索结果页里第一条结果卡片的标题是什么); console.log(result);aiAction 执行时Midscene 会完成前面说的“理解指令、抓取页面上下文、调用大模型生成动作、Puppeteer 执行动作”整条链路。aiQuery 则是把“提取结果”这件事也语义化你不需要写选择器直接问页面上的信息它会返回模型理解后的答案。我实际用下来的感受是指令越具体成功率越高。不要只说“点一下搜索”要说“点击页面顶部搜索区域里那个带有放大镜图标的搜索按钮”。如果页面上有多个相似的输入框一定要在指令里补充位置或标签信息。你可以把 aiAction 理解成你在指挥一个眼神不太好但理解力很强的新同事你得把目标描述得足够清楚。3.4 拿回页面状态并做断言AI 操作完成之后并不是万事大吉。页面数据是异步加载的AI 点击完按钮可能数据还没回来。这时候果断切回 Puppeteer用确定性的等待 API 来兜底。比如点击搜索结果后先等某个结果容器出现再让 AI 去提取内容。await agent.aiAction(点击“查询”按钮); // 回到 Puppeteer做确定性等待 await page.waitForSelector(.result-list .item, { timeout: 15000, }); const summary await agent.aiQuery(结果列表里一共有几条数据请返回数字); console.log(summary);这段代码完美体现了“AI 当眼睛Puppeteer 当手脚”的分工AI 负责找到按钮并点击Puppeteer 负责确认执行结果真正落到 DOM 里AI 再负责从最终状态里提取业务信息。每一步都各司其职出问题的时候你也能快速定位是 AI 找错了元素还是页面加载太慢而不是一锅粥地瞎猜。4. 实操避坑协同落地中的常见问题与排查技巧这一部分是我踩过坑之后最想写的内容。任何架构看起来简单一上真实业务就到处是细节。Midscene 和 Puppeteer 协同常见的问题基本集中在定位准确性、异步时序、多页面隔离和运行环境这四类。4.1 元素找不到或者找错元素最常见的问题是截图上明明看得到那个按钮aiAction 却报错说找不到或者点击到了附近的另一个元素。原因通常有三类。第一目标元素在 iframe 里。Midscene 抽取的页面上下文有时只覆盖主框架iframe 内部的元素如果不做额外处理模型很难定位到。第二元素在 DOM 里存在但被隐藏比如下拉菜单收起状态下的选项截图里没有可访问性树里也没有模型自然找不着。第三页面中有多个文本相似的元素模型理解出现歧义选了第一个匹配项结果点错。我建议的排查顺序是先用 aiQuery 让模型描述当前页面有哪些可见按钮确认它到底“看没看见”目标元素再针对 iframe 场景先用 Puppeteer 切换到目标 frame再往下走最后实在不行直接把指令拆碎先滚动到目标区域再执行点击。不要指望一句话解决特别复杂的布局该手动兜底就手动兜底。提示如果发现模型反复定位到一个错误元素最有效的办法是在指令里追加排除条件比如“不要点击页脚区域的链接”或“只看页面主内容区里的按钮”。这种上下文约束比换模型版本更管用。4.2 Puppeteer 的等待机制与 AI 动作的时序冲突AI 动作本身是异步的Puppeteer 的点击或输入 API 返回也不代表页面更新完成。很多人第一次跑通时都遇到同一个现象AI 点击了按钮然后立刻执行 aiQuery结果拿到的还是页面变化前的旧状态。这是因为点击之后页面可能发起了异步请求数据还没回来。解决思路是AI 操作和业务等待要拆开。AI 只负责“把按钮点下去”Puppeteer 负责“等接口返回或元素出现”等待完成后再让 AI 做下一步理解。我习惯把等待做成一个小工具函数封装超时和轮询逻辑。async function waitForAny(page, selectors, timeout 15000) { const start Date.now(); while (Date.now() - start timeout) { for (const sel of selectors) { const el await page.$(sel); if (el) return sel; } await new Promise((r) setTimeout(r, 200)); } throw new Error(等不到目标元素 selectors.join(,)); }另外AI 模型的响应时间波动很大可能几秒也可能几十秒。建议在调用层做整体超时控制不要一个请求挂在那里干等。超时之后做重试重试前重新拉取一次页面上下文让模型看到最新的页面状态成功率会有明显提升。4.3 多标签页、浏览器复用与上下文隔离一旦脚本涉及多个标签页协同的混乱程度会指数上升。Midscene 的 Agent 在初始化时绑定了某个 page它就只认那一个页面。如果你在 Puppeteer 里手动 newPage 打开新标签然后直接让 Agent 继续操作它很可能还在旧页面里找元素结果自然是查无此物。所以我的经验是一个 Agent 绑定一个 page保持一一对应。如果你要并发处理多个页面就创建多个 Agent每个 Agent 绑定各自独立的 page。不要试图让一个 Agent 自动“迁移”到新页面除非你明确重新初始化页面绑定。涉及登录态的协同脚本还要重视浏览器上下文的隔离。用 Puppeteer 的 browserContext 来隔离不同用户的 Cookie 和存储状态比手动清 Cookie 干净得多。并发测试几个账号时尤其好用不用担心串号问题。4.4 截图、网络与运行环境对识别效果的影响Midscene 依赖截图和页面可访问性树所以凡是影响这两种数据的环境因素都会影响识别准确率。典型问题包括窗口太小导致内容被折叠或懒加载没触发页面动画或进度条未稳定截图拍到中间态系统深色模式导致界面颜色和平时不一样第三方弹窗或 Cookie 同意条挡住了按钮。这些都不是 AI 本身的问题而是喂给 AI 的“眼睛”看到的画面不对。建议在启动浏览器时固定视口并在页面加载后禁用部分动画让画面尽快稳定下来。也可以用 Puppeteer 拦截不必要的第三方资源请求既能加快加载速度又能减少弹窗干扰。现象常见原因处理建议元素定位不准多个相似元素、布局遮挡增加位置和排除描述点击后无反应页面异步接口慢用 Puppeteer 等待元素出现截图与预期不符动画或懒加载未稳定固定视口禁用动画iframe 内元素找不到上下文未覆盖子框架先用 Puppeteer 切 frame多标签页操作错乱Agent 绑定了旧 page坚持一个 Agent 一个 page5. 选型与架构权衡什么场景最适合这套组合看完原理和踩坑最后还是得聊聊选型。不是所有网页自动化任务都需要上 Midscene这套组合有它特别擅长的场景也有明显不合适的场景。5.1 这套组合最适合的三类场景第一类是 UI 自动化回归测试里的“语义化定位”。你不想再维护一堆脆弱的选择器希望能用自然语言描述业务操作让测试脚本对页面改版更有抵抗力。第二类是跨页面长流程的冒烟验证比如注册、登录、创建资源、提交订单这一条龙中间每个环节都在变化用 Puppeteer 硬写会很痛苦用 AI 描述步骤则轻便很多。第三类是半自动数据抽取和分析型任务页面结构不固定但操作模式相似让 AI 理解当前页面并提取关键信息能省下大量人工配置时间。如果你的需求是高频、大规模地抓取同一个固定结构页面那我不建议用这套组合。这类任务用普通选择器加循环成本比 AI 输出低得多稳定性也高得多。AI 的价值体现在“结构易变、逻辑复杂、写选择器不划算”的场景而不是所有重复劳动的万能替代。5.2 和纯 Puppeteer、纯视觉模型方案对比有人可能会说我用 Puppeteer 配合一堆截图和定位库也能做语义识别为什么非用 Midscene还有人会问我自己写一套纯视觉模型方案行不行我做了个对比你可以按自己的需求判断。维度纯 Puppeteer纯视觉模型自研Midscene Puppeteer选择器维护成本高低低执行稳定性高中低中高初期开发速度慢快但有风险快可观测性强弱强单次运行成本低高中高对页面改版的抵抗力弱较强较强纯视觉模型自研听起来很酷但它缺少一个可靠的浏览器执行层模型输出坐标之后没有任何机制确认这个坐标对应的元素是否真是你想要的。Midscene 和 Puppeteer 的组合等于在模型和执行器之间加了一道“动作校验”和“状态回传”比纯视觉方案更可控。5.3 我实际用下来的几点体会最后分享几个不写进文档的经验。第一不要指望 AI 全流程替代。我现在最顺手的用法是让 AI 只做两件事语义定位和页面理解固定流程和硬断言全交给 Puppeteer。第二把错误信息喂回模型。第一次执行失败后把失败原因重新作为上下文传给模型让它自己纠正很多问题一轮就能修复。第三一定要留调试快照。每次 aiAction 之后记录当前页面截图、模型返回动作和最终执行结果排查问题的时候这几样东西比任何日志都管用。我还试过把 Midscene 生成的稳定动作固定成普通 Puppeteer 脚本相当于把 AI 当“能工巧匠”让它先把稳定路径摸清楚再固化成低成本的确定性流程。这种用法有点反直觉但对成本敏感的生产环境特别友好。Midscene 和 Puppeteer 的协同关系不是固定的你完全可以根据自己项目的稳定性要求和成本预算灵活调整两边干活的比例。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑