上周 Ai2(Allen AI研究所)放出了 MolmoWeb,一个开源的视觉 Web Agent。跟以前那些靠解析 HTML DOM 来操作网页的方案不同,MolmoWeb 直接看截图,跟人一样看屏幕,然后决定点哪里、打什么字。

我花了一天时间把它跑通了。这篇文章记录整个过程:从环境搭建到模型加载,再到实际让它完成一个网页搜索任务。踩了几个坑,也有一些超出预期的地方。

为什么截图方案比DOM解析好用

传统的 Web Agent(比如用 Playwright + LLM 的那些方案)需要把整个网页的 HTML 结构序列化成文本,喂给大模型。问题是,一个普通网页的 DOM 树动辄上万 token,又臃肿又容易出错,网站改个 class 名你就得重新适配。

MolmoWeb 的思路简单粗暴:截一张图,丢给视觉模型,让模型自己看懂页面上有什么,然后输出下一步操作。

实际好处有两点。第一是省 token,一张 1280×720 的截图经过视觉编码器处理后,消耗的上下文远比序列化整个 DOM 少。第二是不怕页面改版,你不依赖任何 CSS selector 或 XPath,页面长什么样就看什么样。

当然也有明显的短板:纯视觉方案在遇到被遮挡的元素、需要滚动才能看到的内容时,处理起来比较笨,得像人一样先滚动、再看。后面实测部分会说到这个问题。

环境准备

MolmoWeb 有两个版本:4B 和 8B 参数。4B 版本用 4-bit 量化后大概吃 6GB 显存,一张消费级显卡就能跑。我用的是 RTX 4090,跑 8B 全精度也没压力,但这里用 4B 做演示,降低门槛。

安装依赖

pip install transformers>=4.48.0 accelerate bitsandbytes jinja2 Pillow requests torch

几个注意点: - bitsandbytes 在 Windows 上安装可能报错,建议用 WSL2 或者直接在 Linux 上搞 - transformers 版本必须 >= 4.48.0,老版本不支持 MolmoWeb 的 processor

加载模型

from transformers import AutoProcessor, AutoModelForImageTextToText, BitsAndBytesConfig
import torch

CHECKPOINT = "allenai/MolmoWeb-4B"

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
    bnb_4bit_use_double_quant=True,
)

model = AutoModelForImageTextToText.from_pretrained(
    CHECKPOINT,
    trust_remote_code=True,
    quantization_config=bnb_config,
    device_map="auto",
)

processor = AutoProcessor.from_pretrained(
    CHECKPOINT,
    trust_remote_code=True,
    padding_side="left",
)

第一次加载会从 HuggingFace 下载模型权重,4B 量化版大概 2.5GB。国内网络可能需要设 HF_ENDPOINT 镜像。

核心逻辑:看截图、想一步、做一步

MolmoWeb 的工作循环很直白:

  1. 给它一个任务描述(比如"在 arXiv 上搜索 attention is all you need")
  2. 给它当前浏览器的截图
  3. 它输出两样东西:一段思考过程(THOUGHT)和一个动作(ACTION)
  4. 执行动作,截新的图,重复上面的循环

支持的动作类型:

goto(url)          # 导航到指定网址
click(x, y)        # 点击屏幕坐标(归一化到 0.0-1.0)
type("text")       # 在当前焦点元素里打字
scroll(direction)  # 滚动页面,up 或 down
press("key")       # 按键,比如 Enter、Tab
new_tab()          # 开新标签页
switch_tab(n)      # 切换到第 n 个标签页
go_back()          # 返回上一页
send_msg("text")   # 把结果告诉用户

Prompt 模板

from jinja2 import Template

MOLMOWEB_THINK_TEMPLATE = Template("""
# GOAL
{{ task_description }}

# PREVIOUS STEPS
{% for action in past_actions -%}
## Step {{ action['index'] }}
THOUGHT: {{ action['thought'] }}
ACTION: {{ action['action'] }}
{% endfor %}
# CURRENTLY ACTIVE PAGE
Page {{ page_index }}: {{ page_title }} | {{ page_url }}

# NEXT STEP
""")

这个模板把任务目标、历史操作记录和当前页面信息拼在一起。模型会根据这些上下文,决定下一步该干什么。

推理函数

import re

def run_inference(prompt, image, max_new_tokens=300):
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": prompt},
                {"type": "image", "image": image},
            ],
        }
    ]
    inputs = processor.apply_chat_template(
        messages,
        tokenize=True,
        add_generation_prompt=True,
        return_tensors="pt",
        return_dict=True,
        padding=True,
    )
    inputs = {k: v.to(model.device) for k, v in inputs.items()}

    with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):
        output = model.generate(**inputs, max_new_tokens=max_new_tokens)

    generated_tokens = output[0, inputs["input_ids"].size(1):]
    return processor.decode(generated_tokens, skip_special_tokens=True)


def parse_thought_and_action(raw_output):
    thought = ""
    action = ""
    thought_match = re.search(r"THOUGHT:\s*(.+?)(?=\nACTION:|\Z)", raw_output, re.DOTALL)
    action_match = re.search(r"ACTION:\s*(.+?)(?=\n|$)", raw_output, re.DOTALL)
    if thought_match:
        thought = thought_match.group(1).strip()
    if action_match:
        action = action_match.group(1).strip()
    return {"thought": thought, "action": action}

实测:让它在 arXiv 上搜论文

我截了一张 arXiv 首页的图,让 MolmoWeb 完成"搜索 attention is all you need 这篇论文"的任务。

from PIL import Image

# 加载 arXiv 首页截图
screenshot = Image.open("arxiv_homepage.png")

prompt = build_prompt(
    task_description="Find the paper 'Attention Is All You Need' on arXiv",
    page_title="arXiv.org",
    page_url="https://arxiv.org",
    page_index=0,
)

raw_output = run_inference(prompt, screenshot)
result = parse_thought_and_action(raw_output)
print(f"思考: {result['thought']}")
print(f"动作: {result['action']}")

实际输出:

思考: I can see the arXiv homepage. I need to find the search box to search for the paper.
动作: click(0.82, 0.06)

它准确找到了右上角的搜索框位置。接下来输入关键词、按回车、从结果列表里找到目标论文,整个流程走了 5 步。

踩坑记录

第一个坑是截图分辨率。我一开始用 640×480 的截图,模型经常点错位置。换成 1280×720 之后明显好了。Ai2 官方建议用 1280×720 或更高分辨率。

第二个坑是中文网页识别率偏低。MolmoWeb 的训练数据主要是英文网站。我试了百度搜索页面,模型能认出搜索框,但对中文搜索结果的理解比较差,有时候会点错链接。如果你的应用场景主要是中文网页,可能还得等社区做中文微调版本。

第三个坑是长任务容易迷路。超过 10 步的任务,模型偶尔会忘记自己在干什么,重复之前的操作。在 prompt 里加更详细的历史记录能缓解,但会增加 token 消耗。

跟其他方案对比

我也用过 Playwright + GPT-4o 的方案做 Web 自动化。简单对比一下:

维度 MolmoWeb 4B Playwright + GPT-4o
部署成本 本地跑,免费 按 API 调用计费
速度 4B 量化约 2-3 秒/步 看网络,通常 1-5 秒/步
中文支持 较差 GPT-4o 中文好
页面适应性 不依赖 DOM 结构 依赖 selector,改版就挂
复杂任务 10 步以内比较稳 上下文窗口大,长任务更稳

MolmoWeb 的优势在于零成本、不依赖外部 API、不怕页面改版。劣势是模型小,复杂推理能力不如 GPT-4o,中文场景也差一截。

训练数据也开源了

Ai2 把训练数据也公开了,叫 MolmoWebMix。里面有三部分。

第一部分是 36K 条人类操作轨迹,众包工人用 Chrome 插件录制的真实浏览行为,覆盖 1100 多个网站。第二部分是合成轨迹,用 accessibility tree agent 自动生成的操作路径,数量比人类数据大得多。第三部分是 220 万条截图问答对,用来教模型理解网页截图里的内容。

这是目前公开的最大的 Web Agent 训练数据集。如果你想自己微调一个 Web Agent,这份数据省了大量标注成本。

谁适合用

做内部测试自动化的可以试试,不想维护一堆 CSS selector 的 E2E 测试场景很合适。做数据采集的也值得看看,特别是对页面结构不稳定的网站做信息提取。另外 RPA 原型验证也行,快速验证某个流程能不能自动化,再决定要不要投入做完整方案。

不建议直接用在生产环境的关键流程里。4B 模型的推理能力有限,偶尔会犯低级错误(比如把"取消"按钮当成"确认"来点)。

快速上手命令汇总

# 克隆示例代码
git clone https://github.com/allenai/molmoweb-examples.git
cd molmoweb-examples

# 安装依赖
pip install -r requirements.txt

# 跑官方 demo
python run_agent.py --task "Search for 'transformer architecture' on Google Scholar" --screenshot initial_page.png

模型权重在 HuggingFace 上:allenai/MolmoWeb-4Ballenai/MolmoWeb-8B。训练数据和评测代码也一并公开了。


MolmoWeb 不是什么完美的产品。中文支持差,长任务不够稳,4B 模型的推理能力也有上限。但模型权重、训练数据、评测工具全部公开,开发者可以拆开来研究和改进。大厂的 Web Agent 都是黑盒卖 API,MolmoWeb 给了你一个能动手改的底座。

用不用取决于你的场景。不过代码和数据都放出来了,下载下来玩一下不亏。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐