MolmoWeb 本地部署指南:用视觉模型替代 DOM 解析做网页自动化

16 阅读1分钟

最近在调研 Web Agent 方案,发现一个有意思的开源项目——Ai2 的 MolmoWeb。跟常规用 Playwright 抓 DOM 的方式不同,MolmoWeb 对浏览器截图做视觉理解,直接输出点击坐标和操作指令。我把它在本地跑通了,这篇文章整理下部署过程和使用体验。

DOM 解析方案的痛点

做过 Web 自动化的应该都被 CSS Selector 折腾过。你写了一套 XPath 定位规则,网站前端一改版,全部报废。更头疼的是,很多单页应用的 DOM 结构动态生成,拿到的 HTML 跟你在页面上看到的根本对不上。

还有一个成本问题:把一整个页面的 DOM 树序列化成文本喂给 LLM,一个普通网页动不动上万 token,钱烧得快不说,还经常因为上下文太长导致模型"看花眼"。

MolmoWeb 的做法是绕过 DOM,直接拿截图让视觉模型判断该操作哪里。页面改版不影响,token 消耗也少得多。一张 1280×720 截图编码后,占用的上下文远比序列化整个 DOM 小。

环境搭建

硬件要求

MolmoWeb 有 4B 和 8B 两个版本。4B 做 4-bit 量化后大概占 6GB 显存,一张 RTX 3060 就能跑。下面用 4B 版本演示。

安装依赖

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

注意两点:

  • transformers 版本得 >= 4.48.0,低了不支持 MolmoWeb 的 processor 格式
  • bitsandbytes 在 Windows 原生环境装不上,用 WSL2 或 Linux

加载模型

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 的核心循环分四步:

第一步, 给模型一个任务描述,比如"在 arXiv 搜索某篇论文"。

第二步, 截取当前浏览器画面,传给模型。

第三步, 模型输出思考过程(THOUGHT)和操作指令(ACTION)。

第四步, 执行操作,截新图,回到第二步。

支持的操作指令:

goto(url)          # 打开网址
click(x, y)        # 点击,坐标归一化到 0.0-1.0
type("text")       # 在焦点输入框打字
scroll(direction)  # 滚动,up 或 down
press("key")       # 按键,Enter/Tab 等
new_tab()          # 开新标签页
switch_tab(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 首页截图做了个测试,任务是搜索 "Attention Is All You Need" 这篇论文。

from PIL import Image

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 后准确率有明显提升。官方推荐 1280×720 或更高。

中文网页识别率不高。 我试过百度搜索页,模型能找到搜索框,但对中文搜索结果的理解很粗糙,偶尔点错链接。MolmoWeb 训练数据以英文网站为主,中文场景得等社区微调版本。

长任务容易出问题。 操作超过 10 步之后,模型偶尔会"忘记"目标,重复之前的操作。可以在 prompt 里放更完整的历史记录来缓解,但会增加 token 开销。

和 Playwright + GPT-4o 方案的对比

维度MolmoWeb 4BPlaywright + GPT-4o
部署成本本地免费API 按量计费
推理速度约 2-3 秒/步1-5 秒/步(看网络)
中文支持
页面改版适应不依赖 DOM,改版不影响依赖 selector,改了就挂
复杂任务10 步内稳定上下文窗口大,长链路更稳

MolmoWeb 的核心卖点是本地部署、零 API 成本、不怕页面改版。短板在于小模型推理能力有限,偶尔犯低级错误。

训练数据

Ai2 同时开源了训练数据集 MolmoWebMix,包含三部分:

  • 36K 条人类操作轨迹——众包工人用 Chrome 插件录制的真实浏览行为,覆盖 1100+ 网站
  • 合成操作轨迹——用 accessibility tree agent 自动生成,量级大于人类数据
  • 220 万条截图问答对——教模型理解网页截图内容

这是目前公开的最大 Web Agent 训练集。如果你想自己微调,这份数据能省大量标注工作。

适用场景判断

内部测试自动化是个不错的切入点,特别是你不想维护一堆 CSS selector 的 E2E 场景。数据采集也值得试,对页面结构经常变的网站做信息提取,视觉方案比 DOM 方案省心。另外做 RPA 原型验证也行,快速跑通某个流程看看能不能自动化,再决定要不要投入做完整方案。

但别放在生产环境的关键路径上。4B 模型会犯错——比如把"取消"按钮当成"确认"来点。测试阶段这是 bug,生产环境就是事故了。

快速上手

git clone https://github.com/allenai/molmoweb-examples.git
cd molmoweb-examples
pip install -r requirements.txt
python run_agent.py \
    --task "Search for 'transformer architecture' on Google Scholar" \
    --screenshot initial_page.png

模型权重在 HuggingFace:allenai/MolmoWeb-4Ballenai/MolmoWeb-8B。训练数据和评测工具也一起放出来了。


MolmoWeb 不是万能的。中文弱,长任务不稳,小模型推理有上限。但模型权重、训练数据、评测工具全部开源了。你能拆开来看它怎么训的,也能在它基础上改。跟那些只卖 API 的黑盒 Web Agent 比,这是一个能动手改造的底座。

代码和数据都在 GitHub 和 HuggingFace 上,下载下来跑一遍,比看文章管用。