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_steps=30): 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( task=task, marked_image=marked_image, aom_snapshot=trim_aom(aom, target_element_ids), html_index=html_index, memory=memory, ) # 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,本质上不是某一个模块的胜利,而是整个系统把所有细节都扣对了。如果你也打算沿着多模态网页智能体这个方向做下去,我的建议是先跑通一个最小闭环,然后一项项排查上面这些隐蔽问题。等到某一天,你在完全没见过的新站点上也能稳定完成长任务链条时,你大概就能体会到,榜单数字只是结果,真正值钱的是那个经得住折腾的系统本身。
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_steps=30): 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( task=task, marked_image=marked_image, aom_snapshot=trim_aom(aom, target_element_ids), html_index=html_index, memory=memory, ) # 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,本质上不是某一个模块的胜利,而是整个系统把所有细节都扣对了。如果你也打算沿着多模态网页智能体这个方向做下去,我的建议是先跑通一个最小闭环,然后一项项排查上面这些隐蔽问题。等到某一天,你在完全没见过的新站点上也能稳定完成长任务链条时,你大概就能体会到,榜单数字只是结果,真正值钱的是那个经得住折腾的系统本身。