多模态网页智能体实战:从WebArena评测到OpAgent工程落地
1. WebArena不是普通排行榜而是多模态网页智能体的试金石OpAgent最近在WebArena这个多模态Web Agent评测基准上冲到了前列这个结果值得聊的远不只是它把成功率抬高了几个点而是它再次验证了一条已经越来越清晰的路线在真实网页环境里纯文本依赖DOM解析的技术方案正在触及天花板而视觉信号能带来的增量比很多人想象中大得多。这篇文章不打算复述论文式的实验表格我主要想从复现和工程落地的角度把这类多模态Web Agent的设计思路、数据合成、训练细节和评测跑通流程拆开来讲。不管你是想在WebArena上刷榜还是想给自己做一个能操作网页的自动化助手下面这些经验应该都用得上。先说背景。WebArena是什么简单理解它是一个专门用来评测网页智能体的模拟互联网环境包含自建的购物网站、内容管理系统、论坛、日历、协作工具等站点上面挂了八百多个真实的长尾任务。每个任务都要求智能体像人一样打开浏览器、理解页面、执行一连串操作最后用站点数据库里的状态变化来判断任务是否真正完成。它跟那些静态问答基准完全不同不会给你一个固定答案让你选而是让你在一个活生生的浏览器里从头到尾把事情干完。1.1 一个任务到底长什么样拿典型的购物类任务举例它会这样描述“请在购物站找到那件顾客评价最高的防风夹克如果它还有黑色的库存就把它加到购物车然后使用优惠码AUTUMN15结算并在订单备注里填写今天的日期。”听起来不难但对智能体来说这中间每一步都藏着坑你得先理解“评价最高”意味着要去商品详情页看评论汇总而不是只看列表页排序要判断库存状态、颜色选择器、加购按钮的位置要找到结算页里的优惠码输入框并且知道备注栏和结算按钮不是同一栏里的东西。更有挑战的是评分机制。WebArena不是看你最后停在哪个页面而是去检查购物车里的商品、订单记录里的优惠码、日历上是否真的多了一个事件。任何一个中间环节因为误操作被跳过最终状态对不上整个任务就算失败没有部分得分。这种“全有或全无”的评判方式对智能体的多步推理、记忆保持和错误恢复能力提出了很高的要求也让排行榜上的每一点成功率都变得很扎实。1.2 多模态到底解决了什么问题早期做Web Agent的主流思路是纯文本路线把页面HTML序列化连同URL和可访问性树一起拼成大段文本喂给模型让模型从文本里判断下一步动作。这个思路在一段时间内效果不错但越往后越吃力。最核心的问题是信息密度失控一个普通电商详情页的DOM树拆成文本轻松达到几千甚至上万token模型在长上下文里记住之前某个按钮的文字描述还要准确找回它的位置这本身就非常容易出错。更重要的是纯文本方案丢掉了渲染后的空间信息。一个按钮在页面的哪个位置、和旁边的输入框是什么相对关系、它是否被弹窗遮挡这些在DOM树里都不直观。而多模态Web Agent正好补上这块短板它直接把浏览器截图喂给视觉语言模型让模型“看到”用户实际看到的界面。布局、对齐、遮挡、视觉层级这些人类直觉里非常自然的信息在截图上是一眼可见的根本不需要从HTML标签里反推。我整理了一个对比表可以更直观地看出两种路线的差异对比维度纯文本DOM方案多模态截图方案输入信息HTML、AOM序列化文本截图轻量AOM索引Token消耗长页面经常过万图片token相对可控空间定位需要通过DOM推理无视觉参照截图直接呈现空间关系动态渲染依赖DOM解析易漏掉CSS伪元素截图还原真实渲染结果长页面性能序列化越长注意力越分散可滚动裁剪按需聚焦坐标操作输出元素ID依赖脚本查找输出标记ID或坐标框直观可靠当然多模态方案也不是没有缺点。截图分辨率、缩放比例、弹窗遮挡、滚动区域外元素的处理这些都是新增的麻烦。OpAgent的做法不是抛弃文本只靠视觉而是把视觉、可访问性树和轻量HTML索引组合起来用互相弥补短板。这也是我写这篇文章最想展开的部分——多模态不是简单加一个视觉分支而是要解决“看得到”和“选得准”之间的系统性配合问题。2. 整体设计拆解让Agent“看得见”比“读得懂”更重要OpAgent的总体架构概括起来是“三条输入流加双循环控制”。三条输入流分别是浏览器截图、可访问性树和轻量HTML索引双循环控制则是一个负责宏观规划的慢循环和一个负责立刻执行动作的快循环。这个架构并不是一开始就拍脑袋定下来的而是被几个真实问题一步步逼出来的。第一个问题是纯文本方案的信息过载。早期实验里我们试着把整个DOM树修剪后喂给模型结果长任务的早期步骤还好越往后模型越容易在冗长的上下文里“忘记”自己正在填哪个表单或者把两个页面的元素混在一起。第二个问题是纯视觉方案的坐标漂移。模型看一眼截图如果直接让它输出点击位置的像素坐标往往差几个像素就点到了错误元素上尤其是遇到密集排列的链接列表时错误率会高得离谱。2.1 三条输入流的配合方式先说截图。截图承担的是“全局理解”职责模型看到截图后能确认当前处在什么页面、页面结构大概什么样、哪些区域是输入框、哪些区域是按钮。它解决的是“该做什么”的方向性问题比如“现在应该去找搜索栏输入商品名”这种高层判断。可访问性树Accessibility Object Model简称AOM承担的是“稳定索引”职责。每个UI元素在AOM里都会有自己的身份标识、角色、名称、状态和边界框。它比原始DOM干净很多因为浏览器已经帮你过滤掉了大量不可见的、用于布局的容器节点剩下的是真正对用户有意义的可交互元素。OpAgent把AOM里的元素按坐标渲染到截图上得到一张带编号标记的图片模型只需要在标记里选择目标元素ID而不是凭空生成一个坐标。轻量HTML索引则处理那些AOM里不够清晰的边界情况。比如某些链接的跳转地址、表单元素的name属性、隐藏在折叠区域里的文本内容光靠AOM信息是不够的。所以OpAgent会保留一小段经过裁剪的HTML片段只包含当前视口内元素的属性和文本用于精准提取。三条输入流的分工很明确截图解决“看到什么”AOM解决“选哪个元素”HTML索引解决“这个元素的深层属性是什么”。任何一个单独拿出来都不够用组合起来才能既看得清又点得准。2.2 用标记机制压住空间幻觉多模态模型的一个常见问题是空间幻觉。你给它一张截图让它点某个坐标它经常点到模糊的位置你让它输出DOM元素的ID它又可能编造一个根本不存在的ID。OpAgent的思路是把这两条路都堵住转成“看图选编号”的简单分类任务。具体做法是把截图和AOM对齐之后为每一个可交互元素画一个半透明的彩色边框并在边框角落标上数字。文本框、按钮、链接、下拉框、单选项都可以成为标记对象。模型看到的是一张布满编号标记的图片然后只需要输出“点击13号元素”“在7号输入框中填入内容”这样的指令。13号是谁答案藏在同时喂给模型的那份AOM索引里。这一步看似简单却解决了一个很深层的问题多模态模型并不擅长像素级的坐标回归但非常擅长在视觉提示下做离散选择。你给它一堆编号元素让它挑一个相当于把连续预测变成了分类问题难度直接下降一个量级。实测下来使用标记机制后元素选择的准确率比直接输出坐标高出大概两成而且任务越长这个优势越明显因为不会再出现坐标累积漂移。标记机制的另一个优点是允许模型输出元素ID后再做一次校验。模型说点击13号脚本就拿着这个ID去AOM里查对应元素的边界框再调用浏览器自动化接口去点击。即使模型因为视觉遮挡看错了标记只要ID在有效范围内操作本身仍然是安全的不太可能点到页面外或者空白区域。2.3 双循环控制慢规划与快操作的组合多步网页操作最怕的不是单步出错而是做着做着忘了整体计划。为了让模型既保留全局目标、又不至于每一步都被长程上下文拖累OpAgent把决策拆成了两层。慢循环负责规划。每执行三到五步操作后模型会结合任务描述、当前页面截图和已经写下的过程记忆重新生成一个“下一步子目标”。比如原始任务是“买一件黑色夹克并用优惠码结算”规划层拆出来的子目标可以是“搜索夹克”“筛选黑色”“进入第一个商品详情”“选择尺码加购”“去结算页填写优惠码”“确认订单”。快循环负责执行。当前子目标明确之后模型进入一个尽量少思考的状态截图、看标记、点击、再截图反复循环直到子目标完成。这种“快慢结合”的思路有点类似工程实践里的分层架构慢层负责方向快层负责细节防止每一小步都重新思考而导致决策漂移。还有一个容易被忽略的组件是工作记忆。OpAgent会把已经完成的关键操作、看到的商品信息、填过的表单内容用简短的文本记录到一个记忆槽里。后续规划器在生成子目标时会把这段记忆一并输入从而避免“重复搜索”“重新加购”之类的无用操作。这不是什么高深技术但实际效果非常明显尤其在超长任务中记忆槽的累计收益几乎可以和规划模块本身相比。3. 核心细节与实操要点数据、训练、评测三位一体架构搭好了真正决定效果的往往是训练数据和训练方式的细节。OpAgent这类模型并不是用现成的视觉语言模型直接做推理就能达到榜单水平的它必须经过一个和目标环境高度对齐的微调阶段。这一节我讲几个实操中绕不开的要点。3.1 训练数据从哪来网页智能体训练数据的获取和普通图文问答数据不太一样。你很难直接从公开数据集里找到“任务描述完整操作轨迹最终状态”这种结构化的三元组所以绝大多数团队都会走合成数据路线OpAgent也不例外。合成数据的目标很简单用一批已知的网页环境让一个行为良好的模型或规则脚本自动执行任务把任务描述、每一步的截图、AOM快照、动作序列和最终状态记录下来形成监督数据。这里有个容易踩的坑直接记录真实任务的轨迹是不够的因为模型会过拟合特定站点的布局换个风格就崩。合成阶段需要主动制造多样性比如随机更换商品的图片和名称、随机调整CSS颜色和按钮位置、随机插入一些干扰弹窗让模型学会在页面结构变化下依然找到正确操作路径。实测下来多样性比数据量更重要几万条高质量、带干扰的轨迹效果可以好过几十万条重复模板。除了完整轨迹还需要一种更轻量的训练数据截图定位问答。给定一张带标记的截图和一句自然语言指令比如“点击那个蓝色的登录按钮”模型要输出对应的标记编号。这类数据成本很低但能把模型的空间定位能力先练出来属于性价比很高的补充数据。3.2 微调阶段的几个关键设置模型底座方面OpAgent遵循的是主流的开源多模态大模型路线。训练时并不会把整个模型全部解冻重训那样既贵又不稳定更常见的做法是冻结视觉编码器只对语言模型部分做LoRA微调同时在视觉特征和语言特征融合的投影层上做更多的参数更新。因为对于网页操作任务来说视觉编码器对自然图像的理解能力已经够用真正需要学的是怎样把“截图上的标记区域”和“操作意图”关联起来。训练时采用的是一种结构化动作表示。模型输出的不是自由文本而是“动作类型目标标记ID填充值”的组合。动作类型限定为点击、输入、悬停、滚动静置、选择下拉、返回、等待这有限的几种。以输入动作为例训练样本就是“在7号输入框中填入用户名”模型需要同时正确识别动作类型、目标编号和填入内容。把动作分解成多个预测头后训练更加稳定解码时也不容易出现“模型想表达点击某处却写成了凌乱的自然语言”这种情况。训练时还需要注意Loss权重问题。很多团队会在动作类型、目标编号和填入值三者之间分配相同的权重但实测下来目标编号的预测难度最高应该给它更大的权重。填入值其实是一个条件生成问题依赖于目标编号正确如果编号错了值填得再好也没用。适当加大编号分类的损失权重能有效提升最终的端到端成功率。3.3 坐标映射与评测对齐训练完成后部署推理时最先遇到的坑往往是坐标映射。一张由浏览器截图得到的图片尺寸和页面实际的视口宽高通常存在一个缩放系数尤其在设备像素比不是1的时候截图尺寸是视口尺寸的两倍甚至三倍。模型在截图坐标系上生成标记框脚本在点击时却必须把坐标换算回浏览器视口坐标系否则点出去的位置全都会偏到右下角。正确的换算流程是先确定截图图片的宽度和浏览器视口宽度之间的比例scale然后把模型输出的目标框中心像素坐标除以scale得到视口坐标如果页面需要滚动还要进一步加上当前滚动偏移量才能得到页面对应的绝对坐标。这一套流程必须写成统一的坐标转换工具函数任何新加入的输入源都走同一个转换入口防止各处自己换算导致不一致。评测对齐同样重要。WebArena官方任务列表和验证脚本是固定的但很多人复现时会忽略一点每个任务的初始状态可能依赖数据库的默认内容。如果你跑任务之前没有重置站点的数据库快照上一次任务留下的订单、评论、日历事件就会干扰当前任务。所以评测之前必须清理站点数据按官方要求的方式把测试站点恢复到干净状态否则成功率数字会莫名其妙地忽高忽低。4. 最小复现指南从环境到WebArena首跑我的建议是不管你是初学者还是老手先不要追求复杂架构先跑通一个最小版本再逐步加模块。最小版本就是截图加AOM标记加模型推理目标是在几个固定任务上达到可接受的完成率之后再去叠加记忆、规划器这些模块。下面这套流程是我个人实践中比较顺手的顺序。4.1 环境搭建与组件选型基础环境需要这几样Python 3.10以上的运行环境、一个浏览器自动化框架、一个能加载开源多模态大模型的推理后端以及WebArena官方的Docker环境。浏览器自动化框架选型和玩法类似我用的是基于CDP协议的方案它可以直接获取页面的渲染截图和可访问性树不需要额外注入脚本接口也比较稳定。比较关键的选型是模型底座。建议优先选择支持自定义对话模板、能够把图片和文本拼接输入的开源多模态模型参数量根据自己的显存情况来单机多卡能跑7B级别就已经可以验证大部分思路。推理后端推荐用那种支持批量预填充和连续批处理的服务这样在评测几百个任务时效率会高很多。代码结构上我建议把系统拆成三个模块浏览器控制层、感知增强层和决策推理层。浏览器控制层负责所有页面操作感知增强层负责把截图、AOM、HTML索引合成带标记的输入决策推理层负责调用模型生成动作。三个模块之间用统一的“页面状态对象”传递数据减少耦合。4.2 推理主循环实现下面给一个逻辑层面的伪代码展示主循环怎么串起来。真实项目里肯定要加异常处理、重试机制和记忆管理但核心骨架是清晰的。def run_agent(task, page, model, max_steps30): memory [] for step in range(max_steps): # 1. 获取当前页面状态 screenshot page.screenshot() # 截图 aom page.accessibility_tree() # 可访问性树 target_element_ids, marked_image render_marks(screenshot, aom) html_index extract_compact_html(page) # 轻量HTML索引 # 2. 组装提示词 prompt build_prompt( tasktask, marked_imagemarked_image, aom_snapshottrim_aom(aom, target_element_ids), html_indexhtml_index, memorymemory, ) # 3. 模型推理输出结构化动作 action model.generate(prompt) # 返回 dict: action_type, target_id, value # 4. 执行动作转换坐标基准 if action[target_id] in target_element_ids: box get_bounds_by_id(aom, action[target_id]) viewport_x box.x / scale_factor viewport_y box.y / scale_factor execute_action(page, action[action_type], viewport_x, viewport_y, action[value]) # 5. 记录关键状态到工作记忆 memory.append(summarize_step(action, page.url, page.title)) # 6. 判断任务是否完成 if is_task_finished(page, task): return success return failed有几个地方需要注意。render_marks这一步不是简单画框它需要过滤掉不可见元素。判断不可见有两个办法一是看AOM里是否标记为不可见二是用元素边界框是否在视口内来判断。滚动场景下视口外的元素如果也生成标记模型可能会尝试点击屏幕外的位置导致操作失效。所以在渲染标记时我会先保留当前视口内的元素同时把“向下滚动”作为一个额外动作让模型决定。execute_action里最容易被忽略的是下拉框选择。很多网页的下拉是自定义组件光调用原生select方法没有用需要先点击下拉框再等待选项列表渲染出来然后点击对应选项。这类动作模型其实很难一次学会建议在数据合成时专门构造一批下拉交互的轨迹让模型看到“点击下拉后出现选项列表”这个视觉变化和最终选择动作之间的关系。4.3 评测跑通与重复实验环境准备好后第一次评测我建议先不要直接跑全部任务而是挑一二十个有代表性的任务做冒烟测试。理由很简单WebArena的Docker环境涉及多个站点、域名解析和Cookie设置任何一个环节配置不对模型可能从头到尾都在404页面里打转成功率直接归零。先跑小批任务能快速暴露环境问题。正式评测时需要注意两个统计细节。第一个是搜索策略的粒度模型解码时的温度参数建议调低必要时使用多次采样取多数投票能明显提升单任务的成功率。第二个是评估口径WebArena官方采用的是任务级成功率每个任务只算一次结果多次重试后取最优值会失真。我看到的团队为了刷高榜往往在失败后重复运行同一任务直到成功为止这样得到的数字在论文里没有一致性价值真正合理的做法是多轮独立实验后取平均值并且固定随机种子和温度。在一次完整的评测中几百个任务通常要跑几个小时到十几个小时。这个环节非常考验工程耐心因为中间一个站点因为网络波动挂了整批任务就白跑一大半。我习惯把任务列表拆成小批次每跑完一批就保存中间结果这样即使后续任务失败也可以续跑不需要从头开始。5. 常见问题与排查技巧实录跑这类系统的过程中我遇到过不少让人头皮发麻的问题这里挑几个典型场景把现象、原因和处理方式列出来也算给大家省点时间。5.1 坐标与标记的“幽灵误差”现象模型明明在截图上选对了标记框但执行点击后页面没有任何反应或者点到了隔壁元素上。排查之后发现问题几乎都出在坐标基准没对齐上。截图尺寸和视口坐标之间存在缩放系数有些自动化框架还允许设置全屏截图和视口截图两者尺寸完全不同。还有一个更隐蔽的问题AOM给出的是视口内的相对坐标页面滚动后绝对坐标变了需要叠加scrollX和scrollY。典型问题可能原因解决思路点击偏移截图缩放系数未统一统一所有坐标转换入口除以scale滚动后点错未叠加滚动偏移加上window.scrollX / scrollY标记框重叠相邻元素包围盒重叠过滤叶子元素外扩边框控制在2像素内高亮像素干扰标记边缘遮挡文字标记渲染成半透明外边框不填充元素内部我的习惯是每次巡检时打印“模型选中的目标ID”“换算后的视口坐标”“最终点击的DOM元素标签名”三行日志对比这三者是否一致出问题时很快就能定位到底是在感知层、换算层还是执行层出了问题。5.2 多轮累积误差与恢复机制网页操作天然是一个多步决策过程任何一个错误动作都可能把智能体带到错误的页面状态里。误差累积有两种典型表现一是任务进行到后半段时模型开始重复执行已经做过的操作比如反复把同一个商品加进购物车二是模型对当前页面的理解彻底跑偏把登录页当成了注册页。针对重复操作工作记忆是主要的解决手段。每次执行完关键动作后把动作结果用一句话记录下来在下一轮提示词里显式声明“你已经做过这些事”。针对页面理解跑偏更有效的办法是引入一个恢复机制当模型连续两轮生成的动作为空、或者连续点击同一个位置超过两次脚本强制刷新页面回到已知状态然后重新生成规划。这个机制在真实环境里非常重要因为很多网站的交互反馈有时快有时慢模型可能需要额外等待但重复点击同一个按钮不会让页面变得更好。5.3 评测阶段的隐蔽坑评测中很多看起来是模型能力的问题其实是环境问题。最常见的坑是等待机制。有些页面操作之后需要异步加载数据比如加入购物车后右上角购物车图标更新如果脚本没有显式等待网络空闲截图可能还停留在旧页面模型的下一步判断自然就错了。我会在每次动作之后强制使用一个“等待DOM稳定”的辅助函数检查最近一秒内页面变化是否停止再做下一步截图。还有一个坑是登录状态。WebArena里有些任务依赖已登录的用户会话如果会话过期模型会遇到登录墙而它自己往往没有能力处理登录流程。处理方式是在评测开始前先统一登录并持久化Cookie把登录墙这个变量从评测中彻底移除。另一个坑是任务描述里的时间依赖比如“查看明天日程”这类任务网站数据里的日期是动态生成的如果你的任务列表缓存了旧日期就会持续失败。检查这类问题的方法是回到站点人工访问一遍当前状态确认和任务描述是否一致。从我的实际经验来看把这类系统的成功率从个位数拉到可复现的阶段难度并不在模型训练这一个环节而在于感知、决策、执行、恢复这四个环节的咬合是否紧密。OpAgent登顶WebArena本质上不是某一个模块的胜利而是整个系统把所有细节都扣对了。如果你也打算沿着多模态网页智能体这个方向做下去我的建议是先跑通一个最小闭环然后一项项排查上面这些隐蔽问题。等到某一天你在完全没见过的新站点上也能稳定完成长任务链条时你大概就能体会到榜单数字只是结果真正值钱的是那个经得住折腾的系统本身。