news 2026/9/16 21:30:16

视觉驱动浏览器自动化:Qwen2.5-VL与Claude Computer Use实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
视觉驱动浏览器自动化:Qwen2.5-VL与Claude Computer Use实战

一直觉得,让AI自己看屏幕、自己动鼠标键盘把活干完,才是“AI替我打工”的真正形态。以前搞RPA要写一堆选择器、定位符,页面稍微改个class就废了;后来用脚本调用各种接口,又受限于平台开放程度。直到我把 browser-use、Qwen2.5-VL 和 Claude 的Computer Use模式整合到一条流水线里,才找到一种更接近“真人操作”的自动化方案——AI靠“看”屏幕理解界面,再用自然语言决定点哪里、输入什么,整个流程不需要任何第三方插件或平台API授权。

这个项目要解决的核心问题很直接:把“视觉理解”和“操作执行”打通,让大模型直接驱动浏览器完成端到端任务。相比传统RPA,它不依赖页面结构;相比普通脚本,它能应对动态变化的界面。适合正在做AI Agent、自动化测试、RPA替代方案,或者单纯想折腾一套“电脑自己干活”系统的开发者参考。下面我把这套工作流的搭建过程、技术选型逻辑和踩过的坑完整梳理一遍。

1. 项目整体设计与技术选型思路

1.1 为什么选“视觉驱动”而不是传统DOM解析

传统自动化方案,比如Selenium、Playwright,核心思路是“定位元素”——你得告诉工具“这个按钮的id是btn-submit”或者“这个输入框的name是username”。这在页面稳定、结构清晰的时候没问题,但遇到以下场景就非常痛苦:

  • 前端频繁改版,class和id说换就换,脚本跟着返工。
  • 页面用了大量Canvas、WebGL或者Shadow DOM,DOM树里根本找不到可定位的节点。
  • 目标系统是老旧的后台管理系统,表格嵌套七八层,选择器写出来又臭又长。

browser-use 这类工具换了个思路:把页面的可见区域变成一张“图”,让视觉模型去理解图上有什么、用户想干什么,然后直接输出操作坐标或操作指令。哪怕页面上是个画布绘制的自定义按钮,只要人眼能看到、能点击,视觉模型就能操作。这种方案把“自动化”的门槛从“懂前端”降到了“会描述”,非常贴合普通人操作电脑的直觉。

1.2 模型分工:Qwen2.5-VL 做“眼睛”,Claude Computer Use 做“大脑”

这套流水线里,我同时用到了两个视觉语言模型,它们各有分工,不是简单的二选一。

Qwen2.5-VL 是目前开源阵营里视觉理解能力很能打的一个模型。它的优势在于对中文界面、复杂图表的识别效果好,而且可以通过Ollama或者vLLM本地部署,数据不用出内网。我在项目里主要用它做页面状态理解——把截图丢给它,让它描述当前界面有哪些可操作元素、布局结构如何。

Claude 的 Computer Use 模式则是另一套东西。它不只是在“看图说话”,而是能根据用户下达的目标,自主拆解步骤,输出类似“移动鼠标到位置(450, 820),左键单击,然后输入文字xxx”的操作序列。也就是说,Qwen2.5-VL 负责感知,Claude Computer Use 负责决策和动作编排。

这里要说明一个关键点:browser-use 本身是一个开源库,它提供了浏览器控制能力(打开网页、截图、执行点击输入等),而Qwen2.5-VL和Claude的Computer Use是两种可以接入的“大脑”。我把它们组合起来,并不是什么神秘的黑科技,核心就是让browser-use 把截图喂给模型,模型返回操作指令,browser-use 再执行指令,循环往复直到任务完成。

1.3 为什么不直接全用Claude Computer Use

有人会问:既然Claude Computer Use 能自己看屏幕、自己操作,为什么还要引入Qwen2.5-VL?

说实话,Claude Computer Use在英文界面、标准Web应用上的表现确实惊艳,但在实际使用中我遇到几个问题:

  • 对中文长文本的识别偶尔会丢字,尤其是小字号字体。
  • 对国内一些样式复杂的后台系统(比如Ant Design Pro、Element UI 混搭的老项目)理解不够精准,经常把表格里的“操作”列认成普通文本。
  • API 调用成本较高,如果每一步都让Claude去理解截图,一个简单任务可能消耗很多token。

所以我的做法是:先用本地部署的Qwen2.5-VL做第一轮页面解析和元素识别,顺便过滤掉无关信息;然后把精简后的页面结构描述、用户目标、可选操作列表发给Claude Computer Use,让它做决策。这样既发挥了Claude的推理长处,又降低了API消耗,还能在本地把敏感数据先过滤一遍,对很多企业内网项目来说,这条链路更可行。

2. 环境搭建与核心依赖配置

2.1 基础环境与依赖安装

我是在一台Linux服务器上加Windows宿主机配合调试的,但整个项目跨平台没太大问题。先列出核心依赖:

# Python 3.10+ pip install browser-use pip install playwright playwright install chromium # 如果本地部署Qwen2.5-VL,推荐用Ollama ollama pull qwen2.5vl:7b # 或者使用vLLM部署(适合更大规模并发) pip install vllm

这里有个细节:browser-use 依赖 Playwright 来驱动 Chromium 浏览器,但它的封装层做得更贴近AI操作,不是让你写选择器,而是暴露出browser.action.click(x, y)browser.action.type(text)这类接口。它把“浏览器控制”这个脏活累活都包好了。

安装完成后,先跑一个最简单的冒烟测试,看浏览器能不能正常起来:

from browser_use import Browser import asyncio async def test(): browser = Browser(headless=False) page = browser.get_page() await page.goto("https://example.com") await page.screenshot(path="test.png") await asyncio.sleep(3) await browser.close() asyncio.run(test())

如果这一步能成功生成截图,说明Playwright和Chromium的驱动链路没问题。大部分人卡在这一步是因为 Chromium 的依赖缺失,建议先跑一下playwright install-deps

2.2 接入Qwen2.5-VL做视觉理解模块

Qwen2.5-VL 的接入方式取决于你是本地部署还是调用API。我本地用 Ollama 跑,因为安装最省事。Ollama 的接口兼容 OpenAI 格式,所以可以直接用 OpenAI SDK 去调。

from openai import OpenAI client = OpenAI( base_url="http://localhost:11434/v1", api_key="ollama", # 随便填,本地不需要验证 ) def describe_screenshot(image_path: str) -> str: import base64 with open(image_path, "rb") as f: img_b64 = base64.b64encode(f.read()).decode() resp = client.chat.completions.create( model="qwen2.5vl:7b", messages=[ { "role": "user", "content": [ {"type": "text", "text": "请描述这张网页截图中所有的可交互元素(按钮、输入框、链接等),包括它们的大致位置和文字内容,用简洁的列表输出。"}, {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_b64}"}} ] } ], max_tokens=1024 ) return resp.choices[0].message.content

这块有几个调参经验:

  • max_tokens不要设太小,否则模型输出的元素列表会被截断,后面Claude就没法做准确决策。我一般设1024以上。
  • 提示词里要求“用简洁的列表输出”,比“详细描述图片内容”要实用得多。你要的是“按钮(登录), 位置(820, 450)”这种结构化信息,而不是一段散文。
  • 如果页面图片多,建议先把大图压缩到合适分辨率再喂给模型。Qwen2.5-VL 默认会缩放,但原图太糊会影响识别。

2.3 接入Claude Computer Use模式

Claude Computer Use 的关键参数是computer工具调用。拿到API Key之后,核心请求长这样:

import anthropic client = anthropic.Anthropic(api_key="your-key") def computer_step(user_goal: str, page_info: str, screenshot_b64: str): resp = client.messages.create( model="claude-3-5-sonnet-20241022", # 具体版本以官方为准 max_tokens=1024, tools=[{ "type": "computer_20241022", "name": "computer", "display_width": 1920, "display_height": 1080, "display_number": 1, }], messages=[ { "role": "user", "content": [ {"type": "text", "text": f"目标:{user_goal}\n页面信息:{page_info}\n请决定下一步操作,调用computer工具。"}, {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": screenshot_b64}} ] } ] ) return resp.content

这里返回的内容是一个工具调用指令块,里面带action(比如clicktypemove)和coordinate。browser-use 拿到这个指令后,会转换成真实浏览器操作。

需要特别说明的是:Claude的Computer Use 在API层面是模拟屏幕坐标操作,它并不关心你是真实操作系统还是浏览器。所以我把它的输出动作全部桥接到浏览器内部,而不是让它真去控制我的鼠标键盘。这样做有两个好处:

  1. 任务在浏览器沙箱里执行,出错了关掉页面就行,不会乱动其他程序。
  2. 可以录制完整的操作轨迹,方便回放和调试。

2.4 把Qwen2.5-VL和Claude Computer Use串起来

串起来是这个项目的核心工程点。我的设计是一个循环:

async def run_task(goal: str): browser = Browser(headless=False) page = browser.get_page() await page.goto("https://target-site.com") # 或者从当前状态继续 for step in range(MAX_STEPS): # 1. 截图 await page.screenshot(path="current.png") # 2. Qwen2.5-VL 理解页面结构 page_info = describe_screenshot("current.png") # 3. 把页面信息+目标发给Claude,拿到工具指令 action = computer_step(goal, page_info, read_b64("current.png")) # 4. 执行动作 await execute_action(page, action) # 5. 检查任务是否完成 if check_done(action): break await browser.close()

这个循环看着简单,真正跑起来全是细节。比如Claude返回的动作是“click(635, 450)”,但这是相对于它的显示尺寸(我设的1920x1080)的坐标,而实际浏览器窗口可能是1470x956,必须做坐标换算。browser-use里有个viewport设置,建议统一把窗口调到固定大小,减少换算误差。

3. 实操案例:让AI自动完成“查资料并生成摘要报告”

3.1 任务定义与初始配置

为了更好地展示这套工作流的实际效果,我做了一个相对完整的任务:让AI自动打开一个内部数据后台,找到“项目周报”表格,筛选出最近一周的数据,并把表格内容整理成一段摘要文本。

先定义任务目标和相关参数:

TASK_GOAL = "打开后台系统,进入项目管理页面,找到周报表格,筛选最近一周的数据,把表格内容整理成摘要" MAX_STEPS = 30 SCREENSHOT_INTERVAL = 1.5 # 每步操作后等待1.5秒再截图,防止页面动画未完成

MAX_STEPS 是个防呆参数,避免AI在某个死循环里出不来。我试过如果任务复杂,20步以内大概率搞不定,30步是一个比较安全的阈值。超过步数还没完成就强制终止并输出当前状态,方便人工接管。

3.2 关键步骤一:登录环节

登录是所有后台自动化里最常见的拦路虎。这套流程处理登录的方式很朴素:让AI自己识别用户名密码输入框,把预置的凭证填进去。

我的做法是把登录凭证作为额外提示词传给Qwen2.5-VL,让它定位输入框时知道该填什么:

LOGIN_CREDENTIALS = "用户名: admin,密码: Admin@123" prompt = f""" 这张截图是一个后台登录页面。 识别其中的用户名输入框和密码输入框,以及登录按钮的位置和文字。 需要填入的凭证:{LOGIN_CREDENTIALS} 请输出操作步骤,格式:1. 点击用户名输入框,位置(x,y) 2. 输入内容xxx ... """

这里有个小技巧:不要把密码告诉Claude Computer Use,而是让Qwen2.5-VL直接在本地完成输入动作规划,把“点击第1个输入框,输入admin;点击第2个输入框,输入密码;点击登录按钮”这些指令直接交给browser-use执行。这样敏感信息只在本地方言模型中流转,不会传到Claude的API。对安全要求高的场景,这个设计能省掉很多合规上的麻烦。

3.3 关键步骤二:动态加载表格的数据筛选

后台的周报表格通常不是静态页面,而是点击“查询”按钮后才从后端拉数据。这意味着AI操作完筛选条件后,还得等接口响应、表格重新渲染,再到下一步截图。

browser-use 提供了等待机制,我通常这样处理:

await page.wait_for_selector("table", timeout=5000) await page.wait_for_timeout(1500) # 额外等1.5秒,确保数据渲染完成 page.screenshot(path="after_filter.png")

wait_for_timeout很笨但很管用。很多前端框架的数据加载是异步的,有时候wait_for_selector返回了表格节点,但表格内容还没填充完。多等这1.5秒能明显减少“截图时候表格是空的”这种尴尬情况。

另外提醒一下:页面里有弹窗、浮层、toast提示的时候,视觉模型的判断会受影响。比如筛选按钮被二维码客服弹窗挡住,Claude Computer Use可能依然去点那个坐标,结果点到弹窗上。我加了一步前置处理:截图之前,如果检测到页面右下角有固定的客服挂件,就先用JS把它隐藏掉。

await page.evaluate(""" document.querySelectorAll('.customer-service, .support-widget, .fixed-bottom-right').forEach(el => el.style.display = 'none'); """)

这种基于页面结构的微调虽然不符合“纯视觉”的初衷,但能有效提升成功率。我的原则是:能用视觉解决的就用视觉,视觉识别不了的脏活,用一点点传统手段兜底,不影响整体架构的优雅。

3.4 关键步骤三:从表格到摘要输出

最后一步,AI需要把表格内容转化成摘要文本。这里我做了个分工调整:Qwen2.5-VL 负责把表格截图转成Markdown格式的文本数据(这一步它做得很稳定,尤其是对规整的表格),再把Markdown文本交给Claude,让它用自然语言生成最终摘要。

table_md = qwen_image_to_markdown("table_screenshot.png") summary = claude_generate_summary(table_md, "这是一周项目周报数据,请生成一份300字以内的摘要,包含整体进度、风险项和下周计划")

为什么不直接让Claude看截图?因为图表截图里的表格线、颜色干扰太多,Claude生成的文本容易遗漏数字。而Qwen2.5-VL转成Markdown后,数字就变成了纯文本,Claude再去做归纳总结就非常稳了。这也是我在实际跑项目中总结出来的最佳实践:让开源模型做OCR/结构化,让闭源模型做推理/生成。

3.5 实操结果观察

我这套流程在无人工干预的情况下,成功完成了登录、菜单跳转、条件筛选、表格读取、摘要生成全部步骤,总耗时约3分20秒,共消耗约14步操作。中途有一次因为筛选日期格式没选对(把“最近一周”理解成了当日),我在后续的提示词里加了“最近一周指过去7天,不包含今天”,成功率明显提升。

4. 常见问题与调试技巧实录

4.1 坐标偏移:点不到该点的按钮

这是最让人抓狂的问题。Claude Computer Use 是根据截图坐标来操作的,但截图可能来自浏览器窗口,也可能来自整个屏幕。如果两者尺寸不匹配,偏差会很大。

我的解决方法是固定窗口大小,并且每次截图前都执行一次对齐操作:

await page.set_viewport_size({"width": 1920, "height": 1080})

然后把Claude的display_widthdisplay_height也设成1920x1080,这样坐标比例是1:1,基本不会偏。如果还是偏,多半是浏览器外部的工具栏、滚动条占了一部分空间,这时候就往viewport里减去固定偏移量,比如Y轴减去32像素。

4.2 模型把图标识别成文字按钮

Qwen2.5-VL 对纯文字按钮识别很准,但对图标按钮(比如“搜索”放大镜图标、“设置”齿轮图标)容易犯糊涂。我的经验是:在提示词中要求它把“疑似图标按钮”也列出来,并说明图标的大致形状,再让Claude判断是否需要点击。

比如Qwen输出“页面右上角有一个放大镜图标,位于坐标(1750, 80),可能是搜索按钮”,Claude就能结合任务目标决定是否点击。这套“开源模型描述直觉,闭源模型做判断”的配合,比任何单模型都稳。

4.3 页面跳转太快,截图抓到的还是旧页面

后台管理系统经常有路由跳转动画,如果页面跳转后立刻截图,视觉模型可能还在处理上一个页面的内容,AI就会重复点击已经失效的按钮。

我加了一个简单粗暴的“页面稳定度检测”:

async def wait_for_stable(page, threshold=0.8): prev_hash = None for _ in range(10): await page.wait_for_timeout(500) screenshot = await page.screenshot() cur_hash = hashlib.md5(screenshot).hexdigest() if cur_hash == prev_hash: stable_count += 1 if stable_count >= 3: return True else: stable_count = 0 prev_hash = cur_hash return False

利用截图的MD5哈希判断页面是否还在变化。连续3次截图内容一致,才认为页面稳定了,再进入下一步操作。这比固定等几秒聪明得多,也避免了有些页面加载特别慢的时候白等。

4.4 API成本控制与令牌管理

Claude Computer Use 的调用成本不低,尤其是频繁截图+多轮推理。我在项目中做了两层优化:

  • 减少无用截图:如果Qwen2.5-VL已经输出了足够详细的页面描述,Claude就不需要再看原图,直接文字决策即可。
  • 使用缓存提示词:把系统提示词和页面结构描述放到缓存里,避免每次请求都重复计费。
system_prompt = f""" 你是一个电脑操作助手。用户的目标是:{TASK_GOAL}。 页面信息如下:{page_info} 请基于页面信息,输出一个操作指令(点击/输入/等待)。 """

把这一长串作为系统提示词传给Claude,而不是塞在用户消息里,可以触发Anthropic的提示词缓存,能省不少钱。

4.5 常见问题速查表

问题现象可能原因解决方案
点击按钮无反应元素被遮挡/坐标偏移检查弹窗遮罩,重新截图像素对齐
输入文字丢字页面用了自定义输入框(如富文本)先点击输入框,用快捷键全选再输入
模型总在重复操作没有记录已执行步骤在上下文里追加“历史操作”列表,让模型参考
页面报错空白JS执行异常结束任务,从最后一张正常截图的步骤重跑
表格识别错位表格有合并单元格让Qwen先转成HTML再处理,不要直接转Markdown
任务中途用户打断没有保存状态把每步的截图、模型输出都落盘,方便断点续跑

5. 工作流扩展方向

这整套“视觉+操作”工作流搭好之后,能做的就不只是网页自动化了。我把思路延伸了一下,发现很多场景都可以套用这套框架。

第一个联想是桌面软件自动化。既然Claude Computer Use本来就支持屏幕坐标操作,只要把浏览器模式换成系统模式,理论上可以操作本地的Excel、ERP客户端、甚至老旧的生产管理系统。不过这里要谨慎,因为桌面软件没有浏览器的沙箱保护,一旦操作错了影响面更大,我目前只在虚拟机里测试过。

第二个方向是移动端UI自动化测试。Qwen2.5-VL对手机截图的理解也不错,配合ADB控制模拟器,可以让AI去点点手机App的按钮。这套方案最大的吸引力在于:测试脚本不再需要维护复杂的UIAutomator定位器,只要告诉AI“注册一个新用户”,它自己会去摸索界面。

第三个方向是数据抓取和报表整合。我找了很多企业里的“表哥表姐”,他们每天的工作就是把A系统的数据复制到B系统,再把B系统的报表下载下来填到C系统里。用这套工作流,只要AI能“看得懂”这几个系统的界面,整个跨系统数据搬运就能自动化,而且因为是视觉驱动,哪怕哪个系统改版了,AI也能通过重新理解界面来适应。

当然,目前这套方案还不完美。视觉模型的误判率在小数点级别,一个复杂的业务流程很难一次跑通,通常需要人工在旁边盯几轮。但就像当年Selenium从“只能写死脚本”到“支持智能等待”一样,视觉驱动自动化的大方向是不会错的。我个人的体会是:门槛不在于技术,而在于你愿不愿意把每一步都记录下来,把每个失败案例变成改进的提示词。每多调优一次,这套系统就离“真正的AI助理”更近一点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 21:29:46

wigolo搜索流水线深度剖析:18引擎并行扇出、RRF融合与ML重排

wigolo搜索流水线深度剖析:18引擎并行扇出、RRF融合与ML重排 【免费下载链接】wigolo The go-to web for your AI coding agent — local-first search, fetch, crawl & research over MCP. No API keys, no cloud, $0/query. Public beta. 项目地址: https:/…

作者头像 李华
网站建设 2026/9/16 21:28:12

BFS算法解析:LeetCode 994腐烂的橘子问题

1. 问题背景与题目解析今天想和大家分享一道经典的广度优先搜索(BFS)算法题——LeetCode 994题"腐烂的橘子"。这道题看似简单,但蕴含着很多值得深入思考的算法细节,也是面试中的高频题目。题目描述是这样的:…

作者头像 李华
网站建设 2026/9/16 21:27:32

AD9653与FPGA高速连接设计:JESD204B接口、时钟树与PCB信号完整性

1. 项目概述:这不是接根线就完事的“简单连接”AD9653采集模块怎样连接FPGA底板?——看到这个标题,我第一反应不是去翻数据手册,而是先问自己:你手里的FPGA底板,是实验室里那块带JTAG下载口、几个LED和按键…

作者头像 李华
网站建设 2026/9/16 21:23:10

2026年有线耳机选购指南:从单元到参数,避开这些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 21:22:13

DGX Spark实战:从大模型微调到边缘推理的完整指南

第一次把 DGX Spark 放到办公桌上的时候,我盯着这个比 Mac mini 大不了多少的机箱看了半天。说明书上写着 1 PFLOP(FP4)AI 算力、128GB 统一内存,NVIDIA 管它叫“个人 AI 超级计算机”。我习惯性打开终端敲下nvidia-smi&#xff0…

作者头像 李华