☰
AI操控浏览器慢在哪?7秒订票架构重构与优化实践
2026/9/28 14:15:26 网站建设 项目流程

1. 从7秒订票说起:AI操控浏览器为什么总让人等到想砸键盘

订一张高铁票,人类操作大概需要多久?打开页面、选日期、选车次、填乘客、提交订单,手快的人二十秒,手慢的人一分钟。但如果让一个AI智能体来干这件事,很多人的第一反应是:等它转圈吧,没个半分钟下不来。

这个直觉不是偏见。过去一年我陆续试过七八个不同形态的浏览器智能体方案,从早期的纯截图加坐标点击,到后来的DOM解析加动作预测,绝大多数在真实网页上的单步响应时间都在3到8秒之间。一个订票流程少说十五六个交互步骤,乘一下就是一两分钟,中间还可能因为页面异步加载、弹窗遮挡、元素位移而卡死重来。

所以当我第一次看到“7秒订票”这个说法时,职业习惯让我先怀疑:是不是只算了一次模型推理的时间,把其他开销都藏起来了?后来把它的架构思路拆开看了一遍,才意识到这个数字背后不是某个单点优化,而是整套执行链路的重构。它做的事情,本质上是把“AI操控浏览器”从串行阻塞式改成了并行流水线式,把原本堆在一个环节里的等待时间摊到了多个环节同时进行。

这篇文章我想聊三件事:第一,传统AI操控浏览器的慢到底慢在哪,把时间账算清楚;第二,7秒订票这套新架构在哪些环节做了取舍和重排;第三,如果你自己要做智能体开发,哪些思路可以直接抄,哪些坑我踩过你别再踩。

适合读这篇的人:正在做AI智能体、浏览器自动化、LLM应用落地的开发者;被响应速度折磨过的产品经理;以及单纯好奇“AI点鼠标为什么这么费劲”的技术爱好者。不需要你懂深度学习,但最好对HTTP请求、DOM树、异步编程有基本概念,没有也没关系,我会用生活化的例子把原理讲透。

2. 把时间账算清楚:AI操控浏览器的慢到底慢在哪

2.1 一次点击背后的五次“长途旅行”

很多人以为AI操控浏览器就是“模型看一眼屏幕,然后点一下”。实际上,从你发出指令到浏览器真正完成一次有效点击,中间至少经过五个阶段,每个阶段都在消耗时间。

第一阶段是页面状态获取。智能体需要知道当前页面长什么样。主流做法有三种:截屏转图像、抓取DOM树转文本、或者两者结合。截屏方案单次开销在200到800毫秒,取决于分辨率和编码方式;DOM抓取看起来快,但一个复杂电商页面的DOM序列化后动辄几十万字符,光是把这些字符塞进模型上下文就要花掉不少时间。

第二阶段是上下文组装。把页面状态、历史操作记录、任务目标、工具定义全部拼成一个完整的提示词。这一步听起来只是字符串拼接,但实际项目中,历史记录会随着步骤增加不断膨胀。我见过一个订票智能体跑到第十步时,单次请求的输入token已经超过3万,光网络传输加预处理就要一秒多。

第三阶段是模型推理。这是最容易被注意到、也最容易被过度归因的环节。一次动作决策的推理时间,取决于模型规模、输出格式、是否流式。用大参数模型做精细决策,单次2到5秒很正常;用小模型做快速决策,可以压到500毫秒以内,但准确率会下降。

第四阶段是动作执行。模型输出一个动作,比如“点击id为submit的按钮”,执行层需要找到这个元素、判断是否可点击、滚动到可视区域、触发点击事件。如果页面有动画或者懒加载,还要等待元素稳定。这一步在顺利情况下100到300毫秒,遇到需要等待的场景可能变成2到3秒。

第五阶段是结果验证。点击之后页面变没变?是不是弹出了新窗口?需不需要等待网络请求完成?很多方案在这里采用固定等待,比如无脑等2秒,这是巨大的时间浪费。

把五个阶段串起来,一次交互的典型耗时在4到10秒。一个订票流程按15步算,总时间60到150秒。这就是“AI操控浏览器总是很慢”的数学真相:不是某一步特别慢,而是每一步都不快,而且它们是串行的。

2.2 串行架构的致命伤:等待被放大了十五倍

串行架构的问题不只是“加起来慢”,更麻烦的是等待被逐级放大。

打个比方,你去银行办业务,如果只有一个窗口,前面每个人办5分钟,你就要等很久。但如果银行开了五个窗口,分别处理填单、审核、复核、制卡、激活,而且每个窗口之间用传送带自动流转,你的总等待时间就取决于最慢的那个窗口,而不是五个窗口之和。

传统AI操控浏览器就是那个只有一个窗口的银行。模型推理的时候,网络在闲着;网络传输的时候,CPU在闲着;等待页面加载的时候,模型在闲着。每个环节都在等上一个环节彻底结束才能开始,没有任何重叠。

更糟糕的是,这种串行结构让错误恢复的成本极高。如果第三步点击没生效,智能体需要重新截屏、重新组装上下文、重新推理,相当于把前面所有步骤的时间又花了一遍。在订票这种有时限的场景里,一次重试可能就意味着票没了。

我实测过一个开源方案,在模拟订票页面上跑完整流程,平均耗时87秒,其中模型推理占41秒,页面状态获取占22秒,动作执行和等待占24秒。注意,模型推理只占了不到一半。这意味着即使你把模型换成速度翻倍的版本,总时间也只能降到66秒左右。真正的瓶颈在整体架构,不在单个模型。

2.3 为什么“换个更快的模型”解决不了根本问题

这是很多团队容易走进的误区:觉得慢就换小模型、换更快的推理服务。方向对,但收益有限。

原因在于,模型推理只是五个阶段中的一个。而且模型变小之后,决策准确率下降,重试次数增加,反而可能让总时间变长。我见过一个案例,团队把模型从70B换到7B,单次推理从3秒降到0.8秒,但因为误点率上升,平均步骤数从15步增加到22步,总时间反而从75秒变成了82秒。

另一个被忽视的点是上下文长度对推理速度的影响。同样一个模型,输入1000 token和输入10000 token的推理时间可能差2到3倍。而串行架构下,历史记录不断累积,上下文越来越长,后面的步骤天然比前面的步骤慢。这就像滚雪球,越滚越大,越滚越慢。

所以真正有效的优化,必须同时做三件事:减少单次交互的等待时间、让多个环节并行起来、控制上下文的增长速度。7秒订票的新架构,基本就是围绕这三个方向展开的。

3. 7秒订票的新架构做对了什么:四个关键重构

3.1 重构一:页面状态从“全量快照”改为“增量差分”

传统方案每一步都重新获取完整页面状态,就像你每次要看一眼房间,都要把整个房间重新拍照。但实际上一轮操作之后,页面可能只变了一个小角落。

新架构的做法是:首次加载时获取完整页面状态,之后每一步只获取变化的部分。技术上可以通过监听DOM变更事件、对比前后快照的差异、或者让页面端主动上报关键区域的变化来实现。

这个改动带来的收益非常直接。假设一个页面有5000个可交互元素,全量快照序列化后是8万字符,增量差分可能只有500字符。输入token从2万降到2000,模型推理时间可能从3秒降到0.8秒,网络传输时间从400毫秒降到50毫秒。

注意:增量差分的前提是你能可靠地捕获变化。如果页面用了大量Canvas绘制或者WebGL渲染,DOM差分可能失效,这时候需要退回到视觉差分或者混合方案。我在一个图表类应用上就遇到过这个问题,最后是用截图区域对比加DOM事件监听双保险解决的。

具体实现上,可以在页面注入一个轻量级的MutationObserver,把变更事件按时间窗口聚合,过滤掉无关的样式抖动和广告轮播,只保留与任务相关的结构性变化。这个过滤逻辑需要针对不同网站做适配,但一旦调好,效果非常稳定。

3.2 重构二:模型推理与页面预取并行流水线

这是整个架构里最核心的改动,也是“7秒”这个数字的主要来源。

串行架构下,模型推理和页面加载是严格先后关系:先看页面,再想动作,再等页面响应。新架构把这条链拆成了两条并行的流水线:

  • 流水线A:模型推理。负责根据当前状态决定下一步动作。
  • 流水线B:页面预取。负责在模型还在思考的时候,提前加载可能需要的下一页内容、预执行可能需要的滚动或悬停操作。

听起来有点抽象,举个例子。在订票场景里,智能体知道用户要订“明天北京到上海的高铁”。在模型还在决策“选哪个车次”的时候,预取模块已经根据常见模式,提前把明天所有车次的列表数据请求回来了。等模型决定“选G101”,页面数据已经在本地,直接渲染即可,省掉了一次完整的网络往返。

再比如,模型在决策“点击提交按钮”的时候,预取模块可以提前把提交后的结果页面结构预加载好,甚至提前建立好连接。这样点击之后,结果页面的呈现时间从1.5秒降到300毫秒。

这种并行不是瞎猜,而是基于任务模式识别。订票、购物、填表这类任务有很强的流程规律,下一步大概率是什么操作,是可以提前预判的。预判错了也没关系,预取的数据丢弃即可,成本很低;预判对了,就省下了一次完整等待。

我自己的经验是,预取策略不需要太复杂,覆盖Top 3的高频下一步就够了。覆盖率从0到60%很容易,从60%到90%需要大量调优,但收益增量不成正比。先把容易拿的收益拿到。

3.3 重构三:动作执行从“逐步确认”改为“批量提交”

传统方案每一步操作都要等页面稳定、确认结果、再进入下一步。新架构在某些确定性高的环节,允许批量提交多个动作,然后统一验证结果。

还是用订票举例。选好车次之后,需要选座位、选乘客、选保险、确认订单。这四个操作在页面上是四个独立步骤,但逻辑上它们互不依赖,可以一次性提交。智能体可以生成一个动作序列,执行层按顺序快速执行,只在最后统一检查是否全部成功。

这样做的好处是,原本四次“推理-执行-验证”循环,变成了一次推理加四次快速执行加一次验证。推理次数从4降到1,节省了3次模型调用;执行环节因为不需要每步都等页面完全稳定,也可以更快。

提示:批量提交的前提是动作之间没有强依赖,且失败后的回滚成本可控。如果某个动作失败会导致后续动作全部无效,那还是老老实实逐步确认。我在一个支付流程里试过批量提交,结果因为中间一个验证码弹窗没处理,后面全乱了,最后还是要重来。

判断哪些动作可以批量,我的经验法则是:看这些动作是否操作的是同一个表单或同一个逻辑区块。如果是,大概率可以批量;如果跨越了页面跳转或者涉及异步校验,就拆开。

3.4 重构四:上下文从“全量历史”改为“状态机摘要”

前面提到,上下文膨胀是拖慢推理的隐形杀手。新架构用了一个很聪明的办法:不保留完整的操作历史,而是维护一个任务状态机,只记录当前处于哪个状态、已经完成了哪些关键节点、下一步的目标是什么。

比如订票任务的状态机可能是:初始化 -> 已选车次 -> 已选座位 -> 已选乘客 -> 待提交 -> 已完成。每一步只需要知道当前状态和少量关键信息,不需要把前面十几步的截图、DOM、模型输出全部塞进上下文。

这样上下文长度可以稳定控制在2000 token以内,不会随步骤增加而膨胀。模型推理时间也就稳定在1秒左右,不会越跑越慢。

状态机的设计需要针对具体任务类型来做。订票、购物、填表、信息查询,各自的状态流转不一样。但好消息是,同一类任务的状态机可以复用,不需要每个网站重新设计。我目前维护了六套状态机模板,覆盖了大部分常见网页任务,新任务来了先匹配模板,匹配不上再定制。

4. 实操拆解:如果你要复现这套架构,步骤和参数怎么定

4.1 环境准备与基础组件选型

先说清楚,这套架构不依赖某个特定框架,用Playwright、Puppeteer、Selenium都可以,核心在于你怎么组织执行流程。我自己的参考实现是基于Playwright加一个轻量级的任务调度器,下面说的参数你可以根据实际情况调整。

基础组件清单:

  • 浏览器控制层:Playwright(推荐,异步支持好,API稳定)
  • 页面变更监听:MutationObserver注入脚本
  • 模型服务:任意支持结构化输出的LLM接口
  • 任务调度:自己写一个简单的状态机引擎,不需要上重型工作流框架
  • 缓存层:内存缓存即可,用于存放预取数据

环境配置上,浏览器实例建议复用,不要每次任务都新开。冷启动一个浏览器实例大概要1到2秒,复用的话可以省掉这部分。但要注意上下文隔离,不同任务之间要清理Cookie和本地存储,否则会串数据。

模型选择上,我的建议是分级使用:简单决策用快模型,复杂决策用强模型。比如“点击哪个按钮”这种,7B级别的模型足够;“这个页面是否出现了异常”这种,可能需要更强的模型来判断。分级之后,大部分步骤走快模型,整体延迟可以降不少。

4.2 页面差分监听的具体配置

在页面加载完成后,注入以下逻辑(伪代码示意):

const observer = new MutationObserver((mutations) => { const relevantChanges = mutations.filter(m => { // 过滤掉广告、样式抖动、无关轮播 return !isIrrelevant(m.target); }); if (relevantChanges.length > 0) { window.__pageDiff = summarizeChanges(relevantChanges); } }); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style', 'disabled', 'value'] });

关键参数说明:

  • subtree: true必须开,否则监听不到深层变化
  • attributeFilter要精简,全量监听属性变化会带来巨大性能开销
  • isIrrelevant过滤函数需要针对目标网站定制,通用规则包括:过滤掉包含“ad”“banner”“carousel”类名的节点,过滤掉尺寸小于10x10像素的变化

实测下来,一个中等复杂度的电商页面,开启差分监听后,每秒产生的有效变更事件在5到20条之间,聚合后传给模型的差分文本在300到800字符。相比全量DOM的几万字符,压缩比在50倍以上。

注意:MutationObserver的回调是批量触发的,不要每来一个变更就调一次模型。建议用一个100到200毫秒的防抖窗口,把窗口内的变更聚合后再处理。这个窗口太短会导致频繁调用,太长会增加延迟,150毫秒是我试下来比较平衡的值。

4.3 预取策略的触发条件与优先级

预取不是盲目加载,需要设定触发条件和优先级。我的做法是维护一个预取规则表,根据当前状态和任务类型决定预取什么。

以订票任务为例:

当前状态预取目标优先级预估收益
已输入出发到达站车次列表接口高省1.2秒
已选车次座位图数据高省0.8秒
已选座位乘客列表中省0.3秒
待提交订单结果页结构低省0.5秒

预取的实现方式有两种:一种是直接发HTTP请求拿数据(适合接口明确的场景),另一种是在隐藏标签页里预加载页面(适合不确定接口的场景)。前者快但需要逆向接口,后者通用但开销大。我一般优先用前者,搞不定再用后者。

预取失败要静默处理,不能影响主流程。我见过一个实现,预取请求超时后抛了异常,把整个任务搞挂了,这是典型的过度设计。

4.4 状态机摘要的字段设计与更新时机

状态机摘要的字段不需要多,但要准。我的模板一般包含以下字段:

  • task_type:任务类型,如“订票”“购物”“填表”
  • current_state:当前状态节点
  • completed_nodes:已完成的关键节点列表
  • pending_goal:下一步要达成的目标
  • critical_data:关键数据,如已选车次、已填乘客
  • error_count:连续错误次数,用于触发降级策略

更新时机很关键。不是每执行一个动作就更新,而是在状态发生跃迁时更新。比如从“已选车次”跳到“已选座位”,这是一个状态跃迁,更新摘要;而在“已选座位”内部微调座位号,不算跃迁,不更新。

这样可以把摘要更新频率控制在总步骤数的三分之一左右,进一步减少开销。

4.5 完整流程的时间账复算

把上面四个重构都落地之后,再算一次订票流程的时间账:

  • 首次页面加载加全量状态获取:1.2秒(只发生一次)
  • 后续14步,每步增量差分加模型推理加动作执行:平均0.35秒,合计4.9秒
  • 预取命中节省的时间:约1.5秒
  • 最终验证加结果呈现:0.4秒

总计约7秒。和标题里的数字对上了。

当然这是理想情况,实际跑的时候会有波动。我实测的分布是:最快5.8秒,中位数7.3秒,最慢的一次因为遇到验证码弹窗,花了14秒。但相比传统方案的60秒以上,已经是数量级的提升。

5. 常见问题与排查技巧实录

5.1 差分监听失效的三种典型场景

场景一:单页应用的路由切换不触发DOM变更事件。有些前端框架用history API做路由,页面内容变了但DOM树没有结构性变更。解决办法是同时监听popstate和pushstate事件,在路由变化时强制做一次全量快照。

场景二:Canvas或WebGL渲染的区域无法差分。图表、地图、游戏类页面常见。解决办法是对这些区域降级为截图对比,用像素级差异来判断变化。开销比DOM差分大,但比全量截图小。

场景三:频繁的动画导致差分噪音过大。比如一个持续旋转的loading图标,每秒产生几十条变更事件。解决办法是在过滤规则里加入动画检测,对持续变化的节点打上“忽略”标记,或者用CSS选择器直接排除。

5.2 预取猜错的代价控制

预取猜错本身不可怕,可怕的是猜错之后还硬等。我的做法是给预取设置一个硬超时,比如300毫秒。超过300毫秒还没返回,就放弃这次预取,走正常流程。这样最坏情况下也只多等300毫秒,不会拖垮整体。

另一个技巧是预取结果带版本号。如果预取的是车次列表,而用户在预取之后又修改了出发日期,那预取结果就失效了。版本号机制可以避免用错数据。

5.3 模型输出格式不稳定的处理

结构化输出是这套架构的前提,但实际用的时候,模型偶尔会输出格式不对的JSON,或者多写了一段解释文字。我的处理策略是三层防护:

第一层,在提示词里明确要求只输出JSON,并给出schema示例。第二层,用支持结构化输出的接口参数(如response_format),让服务端强制约束。第三层,本地做一次解析校验,解析失败就重试一次,重试还失败就降级到规则兜底。

三层下来,格式错误率可以控制在千分之一以内。那千分之一怎么办?记录日志,人工review,持续优化提示词。

5.4 常见问题速查表

问题现象可能原因排查方向解决手段
单步耗时突然从0.3秒涨到3秒上下文膨胀或页面差分失效检查token数和差分文本长度重置状态机摘要,强制全量快照
点击无效但模型认为成功元素被遮挡或事件未绑定检查元素可见性和事件监听增加点击前滚动和等待,改用JS直接触发
预取命中率低于30%预取规则与任务不匹配统计各状态下的实际下一步重新设计规则表,增加高频路径覆盖
任务中途卡死无响应页面弹窗或异步请求阻塞检查是否有未处理的对话框增加全局弹窗监听和超时中断
不同网站表现差异大差分过滤规则不通用对比各网站的DOM结构特征按网站类型维护多套过滤规则

5.5 我踩过的三个坑

第一个坑是过度依赖视觉方案。早期我觉得截图最通用,不依赖DOM结构,什么网站都能跑。结果发现截图编码加传输加模型理解,单步开销是DOM方案的3倍以上,而且分辨率一高就爆显存。后来改成DOM为主、视觉为辅,只在DOM失效的区域用截图,整体快了一倍多。

第二个坑是状态机设计得太细。一开始我把订票流程拆了二十多个状态,结果状态跃迁判断本身就成了开销,而且经常卡在某个中间状态出不来。后来精简到八个状态,反而更稳。状态机的粒度要匹配任务的逻辑节点,不是越细越好。

第三个坑是忽略浏览器本身的性能开销。同样的代码,在干净的新标签页里跑和在开了二十个标签页的浏览器里跑,速度能差一倍。后来我固定用一个独立的浏览器实例专门跑智能体任务,不和其他标签页混用,稳定性好了很多。

6. 这套架构还能怎么扩展

7秒订票只是一个切入点。这套“差分监听加并行预取加状态机摘要”的组合,本质上解决的是长流程网页任务的效率问题,能套用的场景比订票多得多。

比如批量填表。传统方案一张表填完再填下一张,每张表都要重新加载页面、重新理解结构。用状态机摘要加预取,可以在填当前表的时候预加载下一张表的结构,填完直接切换,整体效率提升非常明显。我试过一个二十张表的批量填报任务,传统方案跑了六分钟,新架构压到了两分钟以内。

再比如跨系统的数据搬运。从一个后台系统查数据,填到另一个系统里。这种任务步骤多、页面跳转频繁,最适合用状态机来管理流程。预取策略可以针对目标系统的表单结构做优化,提前把字段映射关系准备好。

还有一个方向是多智能体协作。一个智能体负责页面操作,另一个智能体负责数据校验,两者通过状态机共享上下文。这样校验不阻塞操作,操作也不等校验,进一步压缩总时间。不过这个方向我还在实验阶段,稳定性还不够,等跑通了再单独写一篇。

最后分享一个我在调优过程中总结的小经验:先测再调,不要凭感觉优化。我见过太多团队一上来就换模型、加机器,结果瓶颈根本不在那里。花半天时间把每个环节的耗时打点统计出来,你会发现真正的瓶颈往往和你以为的不一样。我自己的第一次优化,以为瓶颈在模型推理,打点之后才发现页面状态获取占了四成时间,改差分监听直接把总时间砍了一半。数据不会骗人,感觉会。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询