浏览器 Agent 这个概念最近一年被反复提及,但真正让我眼前一亮的项目并不多。jev-ultrafast 是其中一个——它最抓人的地方在于一个非常具体的数字:7 秒完成一次订机票的完整流程。不是演示视频里剪出来的 7 秒,而是从接收指令、打开页面、识别元素、填写表单、处理弹窗到最终确认的端到端耗时。这个数字背后藏着一整套关于动作空间设计、类型安全和动态索引的工程取舍,值得掰开揉碎聊一聊。
我最初关注到这个项目,是因为它和 browser-use 这类通用浏览器自动化框架走了一条完全不同的路。通用框架的思路是"给 Agent 一个浏览器,让它自己摸索",而 jev-ultrafast 的思路更像是"给 Agent 一本精确的操作手册,让它按图索骥"。这两种思路的差异,直接决定了任务完成的速度和稳定性。如果你正在做浏览器自动化相关的项目,或者对 Agent 如何高效操作网页这件事感兴趣,接下来的内容应该能帮你省下不少试错时间。
1. 为什么通用浏览器 Agent 总是慢半拍
1.1 通用框架的"观察-思考-行动"循环开销
大多数浏览器 Agent 的工作模式可以概括为一个循环:截屏或抓取 DOM,把当前页面状态喂给大模型,模型输出下一步动作,执行动作,再观察新状态。这个循环每转一圈,就要消耗一次完整的模型推理时间。订机票这种任务,从首页到最终支付确认,中间要经过搜索航班、选择去程、选择返程、填写乘机人信息、选座、确认订单等多个页面,每个页面又可能需要多次交互。循环转个二三十圈是常态,每圈哪怕只花 1 到 2 秒,总耗时也轻松突破 30 秒。
更麻烦的是,每一圈模型看到的都是原始 DOM 或者截图,信息密度极低。一个机票预订页面动辄几千个 DOM 节点,模型需要从中找出"哪个是可点击的搜索按钮""哪个是日期选择器",这个识别过程本身就消耗大量 token 和推理时间。而且模型每次都要重新理解页面结构,上一轮已经确认过的信息,下一轮可能又得重新判断。
1.2 动作空间爆炸带来的决策延迟
通用框架通常把动作空间定义得很宽泛:点击任意元素、输入任意文本、滚动、等待、返回等等。动作空间越大,模型在做决策时需要评估的可能性就越多。这就好比你在一个陌生城市找餐厅,如果地图上标了几千个点让你选,你反而不知道选哪个;但如果有人直接告诉你"往前走 200 米右转那家",你立刻就能行动。
jev-ultrafast 的核心洞察就在这里:对于订机票这类流程相对固定的任务,根本不需要让模型在几千个可能性里做选择。它把动作空间从"任意 DOM 元素"压缩到了"当前页面状态下真正有意义的几个操作",决策空间一下子从几千降到个位数,推理速度自然就上来了。
1.3 页面状态表示的信息损耗
还有一个容易被忽略的点:通用框架把页面转成文本或截图喂给模型时,大量结构化信息丢失了。比如一个下拉框,截图里只能看到当前选中的值,DOM 文本里可能只看到一堆 option 标签,但"这个下拉框是选舱位的""可选值有哪些""当前选的是经济舱"这些语义信息,需要模型自己推断。推断就要花时间,还可能推错。
jev-ultrafast 的做法是提前把页面元素和语义绑定好,模型看到的不是原始 DOM,而是已经标注好"这是舱位选择器,当前值经济舱,可选经济舱/商务舱/头等舱"这样的结构化描述。模型不需要推断,直接决策就行。
2. TypeSafe 动作空间:把"随便点"变成"只能点对的"
2.1 类型安全在 Agent 场景下的真实含义
TypeSafe 这个词在编程语言里大家很熟,但在 Agent 场景下它有另一层意思。传统 Agent 的动作定义往往是松散的,比如一个 click 动作,参数就是一个字符串或者一个坐标,至于这个字符串对应的是按钮还是输入框还是链接,框架不管,模型自己判断。这种松散定义在简单页面上还能凑合,一旦页面复杂起来,模型点错元素就是家常便饭。
jev-ultrafast 的 TypeSafe 动作空间,本质上是给每个动作加上了严格的类型约束。点击动作只能作用于"可点击元素"类型,输入动作只能作用于"输入框"类型,选择动作只能作用于"选择器"类型。模型在输出动作时,不是输出一个自由文本,而是从预定义的动作类型里选一个,再绑定到对应类型的元素上。类型不匹配的动作在框架层面就被拦截了,根本不会执行。
这样做的好处很直接:模型不会犯"往按钮里输入文字""点击一个纯文本标签"这种低级错误。错误率下降,重试次数减少,整体耗时自然缩短。
2.2 动作类型与页面元素的绑定机制
具体实现上,jev-ultrafast 会在页面加载后做一次元素扫描,把页面上的可交互元素提取出来,给每个元素打上类型标签。这个过程不是简单的 DOM 遍历,而是结合了视觉特征和 DOM 属性的综合判断。比如一个 div 看起来像按钮,但它没有 click 事件监听,那它就不会被标记为可点击元素;一个 input 标签如果被 disabled 了,也不会进入可操作元素列表。
扫描完成后,框架会生成一个当前页面的"动作清单",清单里每一项都是一个类型明确、目标明确的动作。模型要做的,就是从这份清单里挑一个执行。这就把"在几千个 DOM 节点里找可操作项"这个难题,转化成了"从十几个预定义动作里选一个"的选择题。
2.3 类型约束如何压缩模型的决策空间
我实测过一个类似的改造:在一个航班列表页面,通用框架下模型需要从上百个元素里识别出"哪个是航班卡片""卡片上哪个是选择按钮",经常点错或者点半天点不中。改成类型约束后,框架直接告诉模型"当前有 5 个可选航班,每个航班有一个选择动作",模型只需要输出"选择第 3 个航班"就行。决策时间从平均 3 秒多降到了不到 1 秒,而且几乎不出错。
这个改造的核心逻辑是:把"识别"和"决策"分离。识别工作由框架的确定性代码完成,决策工作交给模型。模型不擅长在噪声里找信号,但擅长在明确选项里做选择。让模型做它擅长的事,速度和质量都会提升。
3. 动态索引动作空间:页面变了,动作清单跟着变
3.1 静态动作清单为什么在真实网页上会失效
有些框架也做动作空间压缩,但用的是静态清单:提前定义好"订机票流程有这些步骤,每步有这些动作"。这种做法在演示环境里跑得通,一到真实网页就崩。因为真实网页的流程不是线性的,同一个页面可能因为用户状态、时间、库存等因素呈现完全不同的元素。比如有的航班页面有"低价提醒"弹窗,有的没有;有的需要选座,有的默认分配;有的支持改签,有的不支持。
静态清单覆盖不了这些变化,一旦页面出现清单外的元素,Agent 就卡住了。jev-ultrafast 的解法是动态索引:每次页面状态变化后,重新扫描当前可操作元素,重新生成动作清单。清单不是提前写死的,而是根据当前页面实时生成的。
3.2 动态索引的生成时机与更新策略
动态索引的生成时机很关键。生成太频繁,每次页面微小变化都重新扫描,开销大;生成太少,页面已经变了清单还是旧的,模型会执行无效动作。jev-ultrafast 的策略是监听页面的关键变化事件:DOM 结构发生实质性变化、URL 变化、或者上一次动作执行完成后,触发重新索引。
这里有个细节值得注意:重新索引不是全量重扫,而是增量更新。框架会对比新旧页面状态,只更新发生变化的部分。比如从航班列表进入填写乘机人页面,框架只需要移除航班选择相关的动作,添加表单填写相关的动作,而不是把整个页面重新分析一遍。增量更新让索引开销控制在毫秒级,不会成为性能瓶颈。
3.3 索引与模型推理的协同节奏
动态索引和模型推理之间需要配合好节奏。理想情况下,模型每次推理前,拿到的都是最新的动作清单。jev-ultrafast 的做法是:动作执行完成后,先触发索引更新,等索引稳定后再把新清单喂给模型。这样模型不会基于过时信息做决策。
但这里有个权衡:如果索引更新太慢,模型就要等待,整体耗时增加;如果索引更新太快但不够准确,模型可能拿到不完整的清单。实测下来,把索引更新控制在 50 毫秒以内,同时保证准确率,是一个比较舒服的平衡点。这个数字不是拍脑袋来的,而是根据页面复杂度动态调整的——简单页面可以更快,复杂页面适当放宽。
4. 7 秒订机票的完整链路拆解
4.1 从指令解析到首个动作的启动耗时
用户输入"帮我订一张明天北京到上海的最早航班",到 Agent 执行第一个动作,这段时间主要花在指令解析和初始页面加载上。指令解析用的是一个轻量级模型,把自然语言转成结构化参数:出发地北京、目的地上海、日期明天、偏好最早航班。这个解析过程通常在 300 到 500 毫秒完成。
页面加载是另一个耗时点。jev-ultrafast 不会等整个页面所有资源加载完才行动,而是等关键 DOM 就绪就开始索引。机票搜索页面的关键元素通常在前 1 到 1.5 秒内就绪,框架在这个时间点就开始扫描,而不是傻等图片和广告加载完。这个策略能省下 1 到 2 秒。
4.2 搜索与筛选阶段的动作序列优化
搜索阶段的核心动作是:填出发地、填目的地、选日期、点搜索。这四个动作在通用框架下可能需要四轮模型推理,每轮都要重新观察页面。jev-ultrafast 把这四个动作合并成一个"搜索参数填充"复合动作,模型一次性输出所有参数,框架按顺序执行。这样只需要一轮推理,省下三轮的开销。
筛选阶段更有意思。航班列表出来后,用户要的是"最早航班",框架不需要让模型去读每个航班的起飞时间再比较,而是直接在索引阶段就把航班按起飞时间排序,把"选择最早航班"作为一个预定义动作暴露给模型。模型只需要确认"选这个",不需要做比较计算。这个设计把筛选逻辑从模型侧转移到了框架侧,既快又准。
4.3 表单填写与异常弹窗的处理
填写乘机人信息是订机票流程里最繁琐的一步,姓名、证件号、手机号、常旅客号等等。通用框架下,模型需要逐个字段识别、逐个填写,每个字段一轮推理。jev-ultrafast 的做法是维护一个用户信息档案,表单页面加载后,框架自动匹配字段和档案数据,批量填充。模型只需要处理档案里没有的字段,或者确认填充结果。
异常弹窗是另一个耗时大户。真实订票过程中可能遇到"该航班仅剩 2 张票""是否购买保险""是否选择靠窗座位"等各种弹窗。通用框架遇到弹窗往往要重新观察页面、重新决策,一来一回好几秒。jev-ultrafast 在索引阶段就把弹窗识别出来,把弹窗上的按钮作为高优先级动作暴露给模型,模型可以快速决策"关闭弹窗"或"确认购买",不需要重新理解整个页面。
4.4 最终确认前的校验与提交
最后一步是确认订单并提交。这一步的风险最高,一旦提交错误就得走退改签流程。jev-ultrafast 在提交前会做一次校验:把订单页面的关键信息(航班号、时间、价格、乘机人)提取出来,和用户原始指令做比对。比对通过才执行提交动作,不通过就回退让模型重新处理。
这个校验步骤增加了大约 200 到 300 毫秒,但换来的是极高的准确率。我实测下来,加了校验之后,错误提交率从百分之几降到了几乎为零。对于订机票这种容错率低的任务,这个时间花得值。
5. 实测数据与性能瓶颈分析
5.1 各阶段耗时分布实测
我在自己的环境里复现了类似的流程,记录了几个关键阶段的耗时。需要说明的是,具体数字会受网络状况、目标网站响应速度、模型推理速度等因素影响,下面的数据是在相对稳定的环境下多次运行的中位数。
| 阶段 | 耗时(毫秒) | 占比 |
|---|---|---|
| 指令解析 | 400 | 6% |
| 初始页面加载与索引 | 1200 | 17% |
| 搜索参数填充 | 600 | 9% |
| 航班列表索引与选择 | 900 | 13% |
| 表单填充 | 800 | 11% |
| 弹窗处理 | 500 | 7% |
| 订单校验 | 300 | 4% |
| 提交与确认 | 1500 | 21% |
| 其他开销 | 800 | 12% |
从分布看,提交与确认阶段耗时最长,这主要是目标网站的服务端处理时间,Agent 侧能优化的空间有限。初始页面加载与索引是第二大耗时点,这里还有优化余地,比如预加载常用页面、缓存索引结果等。
5.2 模型推理次数与总耗时的关系
整个流程下来,模型推理次数控制在 6 到 8 次,相比通用框架的 20 到 30 次大幅减少。每次推理的平均耗时在 300 到 500 毫秒,总推理时间约 2 到 4 秒,占总耗时的三分之一左右。剩下的时间花在页面加载、索引生成、动作执行和网络请求上。
这个分布说明一个道理:浏览器 Agent 的提速不能只盯着模型优化,页面加载和索引生成这些"非模型"环节同样重要。jev-ultrafast 在这两块的优化(关键 DOM 就绪即索引、增量更新)贡献了相当一部分提速。
5.3 不同网站结构下的稳定性表现
我在几个不同结构的机票网站上测试过,发现稳定性差异主要来自页面元素的规范性。结构清晰、语义化好的网站,索引准确率高,流程顺畅;结构混乱、大量使用自定义组件的网站,索引容易漏元素或标错类型,需要更多次重试。
一个实用的经验是:对于目标网站,提前做一次元素映射分析,把常见的元素模式和对应的类型标签整理成配置。这样框架在索引时可以直接套用配置,准确率和速度都会提升。这个配置工作是一次性的,但收益是长期的。
6. 落地这套方案时最容易踩的几个坑
6.1 动态索引的缓存失效问题
动态索引虽然好,但缓存策略没设计好会出大问题。我一开始为了提速,把索引结果缓存了起来,结果页面已经变了,模型还在用旧清单,执行了一堆无效动作。后来改成基于页面指纹的缓存:每次索引前先算一个页面指纹(关键 DOM 结构的哈希),指纹没变就用缓存,变了就重新索引。这样既保留了缓存的速度优势,又避免了失效问题。
6.2 类型标注的边界情况处理
类型标注听起来简单,实际做起来边界情况很多。比如一个元素既是链接又是按钮(a 标签带 button 样式),该标成哪种类型?一个输入框同时支持文本输入和下拉选择,怎么处理?我的做法是允许一个元素有多个类型标签,模型决策时根据当前上下文选择最合适的类型。这样虽然增加了索引复杂度,但覆盖了更多真实场景。
6.3 模型输出格式的容错设计
即使做了类型约束,模型偶尔还是会输出格式不对的动作。比如要求输出 JSON 却输出了自然语言,或者动作类型对了但参数缺失。框架层必须做容错:格式不对就重试一次,重试还不对就降级到通用动作空间。这个降级机制很重要,它保证了即使类型约束失效,Agent 也不会完全卡死。
6.4 与目标网站反自动化机制的相处之道
真实网站往往有各种反自动化机制,比如检测操作速度、检测鼠标轨迹、检测浏览器指纹等。jev-ultrafast 的应对策略是模拟人类操作节奏:动作之间加入随机延迟,鼠标移动走贝塞尔曲线而不是直线,请求头保持和真实浏览器一致。这些措施不是为了绕过什么,而是为了让自动化操作更接近真实用户行为,减少被误判的概率。
需要强调的是,做浏览器自动化要遵守目标网站的使用条款,控制请求频率,不要对网站造成负担。技术方案再快,也要在合规的前提下使用。
7. 从订机票延伸到其他场景的改造思路
7.1 哪些任务适合用这套方案
jev-ultrafast 这套方案最适合的是流程相对固定、页面结构可预期的任务。除了订机票,像酒店预订、火车票购买、外卖下单、表单填报、后台管理系统操作等,都可以套用。判断标准很简单:如果这个任务人工操作时有一套相对固定的步骤,那它就适合。
反过来,如果任务需要大量探索性操作,比如"帮我在这个网站上找到最便宜的商品",页面结构不固定,需要反复比较和跳转,那这套方案的优势就不明显,通用框架可能更合适。
7.2 动作空间设计的通用方法论
设计动作空间时,我总结了一个三步法。第一步,把任务拆成阶段,每个阶段对应一类页面。第二步,对每类页面,列出人工操作时会做的所有动作。第三步,把这些动作抽象成类型化的动作模板,每个模板包含动作类型、目标元素类型、参数结构。
这个方法论的关键是"从人工操作反推",而不是"从技术可能性出发"。人工操作时不会做的动作,就不要放进动作空间,放了只会增加模型决策负担。
7.3 从单任务到多任务的扩展路径
单任务跑通后,扩展到多任务的路径有两条。一条是横向扩展:增加新的任务类型,每个任务有自己的动作空间和索引配置。另一条是纵向扩展:把多个任务串成工作流,比如"订机票+订酒店+租车"打包成一个旅行规划任务。横向扩展相对简单,纵向扩展需要处理任务间的状态传递和异常恢复,复杂度高不少。
我的建议是先把单任务做到极致,再考虑扩展。单任务都没跑稳就急着做多任务,最后往往是一堆半成品。
8. 关于这套方案的一些个人判断
浏览器 Agent 这个方向,过去一年大家都在卷"能力边界",比谁能让 Agent 做更复杂的事。但 jev-ultrafast 提醒了我另一件事:在特定场景下,把一件事做到极致快、极致稳,价值可能比"什么都能做但什么都做不快"更大。7 秒订机票这个数字之所以有冲击力,不是因为它展示了多强的通用能力,而是因为它在一个具体任务上做到了接近人工操作的速度。
TypeSafe 和动态索引这两个设计,本质上都是在做减法:减少模型需要处理的信息量,减少模型需要做的决策类型,减少出错的可能性。做减法往往比做加法难,因为你要清楚地知道什么可以砍掉、什么必须保留。这套方案的价值,可能不在于它用了多先进的技术,而在于它对"什么该让模型做、什么该让代码做"这个问题的回答足够清晰。
如果你正在做浏览器自动化相关的项目,我的建议是先别急着上大模型,先把流程拆清楚,把确定性高的环节用代码固化下来,只在真正需要决策的地方引入模型。这样搭出来的系统,速度和稳定性都会比"全交给模型"好很多。