资讯动态

VLM+无头浏览器:打破接口依赖的RPA自动化新路径

发布时间:2026/10/21 5:43:10 来源:尧图企业网站定制
从“接口”这个老大哥手里抢回RPA的主动权是我最近做得最爽的一件事。过去搞自动化最怕听到的就是“这个系统没有接口”或者“接口文档又又又变了”。接口之于RPA就像那条离不开的拐杖没它寸步难行。但另一个事实是我们现在天天看的网页本身就是一种极其丰富的“接口”只不过它是给人类眼睛看的。现在有了VLM视觉语言模型再配上一个无头浏览器等于给自动化装上了一双真正会“看”的眼睛让它像人一样去操作界面接口依赖这个老大难终于可以彻底扔掉了。这篇内容不谈空泛的概念只讲我实际落地这套“VLM无头浏览器”自动化方案的全过程包括怎么选型、怎么写核心逻辑、遇到了哪些坑、又是怎么填平的。如果你也在被各类系统接口缺失、文档不完善搞得焦头烂额或者想为RPA找一条肉眼可见的出路这篇分享应该能给你一个比较清晰的方向。1. 思路拆解为什么是“VLM无头浏览器”它解决了什么1.1 RPA的老路为什么越走越窄传统RPA的玩法无非是两条路。一条是接口驱动系统给你接口你得去申请权限、研究文档、写调用代码最后跑通流程。另一条是界面驱动通过解析网页的DOM结构定位某个按钮、某个输入框然后模拟点击和输入。这两条路都有明显的天花板。接口驱动的问题在于很多系统尤其是老旧的内部管理系统压根没有对外接口或者接口做得极其敷衍字段对不上、权限边界模糊跑着跑着就报错。界面驱动的问题就更扎心了DOM结构是前端工程师随手改的今天改一个class名明天换一种布局你的选择器就彻底失效了。这种情况下程序员和RPA实施人员活的就像消防员天天在扑火而且火永远扑不完。1.2 换一个思路让机器像人一样“看”人是怎么操作网页的我们不看代码我们看屏幕。我们看到左上角有个搜索框我们点进去输入“订单号”看到右侧有个蓝色按钮我们点击这就是全部的规则。VLM视觉语言模型的核心能力就是把“看屏幕”这件事变成了机器可以理解的东西。它能理解截图里的大意知道哪个区域是导航菜单哪个是按钮哪个是表单输入框。而无头浏览器则提供了一个可以真正“打开页面”并执行操作的环境。两者一结合RPA的终点不再是某个接口而是屏幕上的任何一块像素。1.3 适用场景与真正的门槛这个方案不是万能的但它非常适合两类场景。第一类是那些只对内部员工开放、界面老旧、但逻辑相对固定的操作流程比如某个特定的数据录入、某个报表的下载。第二类是跨系统的数据搬运只要你能用眼睛看到理论上都能让VLM识别清楚。但话又说回来这套方案的门槛并不低。它不像写一个Selenium脚本那么简单它需要你具备一点机器学习的认知、需要会一点Python更重要的是你得有处理“不确定性”的能力。VLM是概率输出它不一定每次都能准确你得设计一套校验机制让它即便看错了也不会酿成大错。2. 核心技术拆解VLM与无头浏览器的选型与配合2.1 VLM模型选型开源与商业产品的平衡对于想快速落地的团队调用商业API绝对是最省事的选择。比如GPT-4o这类系列模型对网页截图的理解能力已经到了一个相当高的水平它能区分按钮状态能读出输入框的placeholder甚至能理解页面上的错误提示。基本的流程搭建商业API足够。但如果是在内部环境部署或者对数据隐私有要求就必须考虑开源模型了。当前阶段业内用得比较多的是以视觉能力见长的一些多模态开源模型例如Qwen-VL系列或者国外社区活跃度比较高的几个模型。我的个人体会是开源模型在做“点一下某个按钮”这种基础指令时表现不错但在理解复杂表格、数据提取的准确性上和顶级商业模型还是有差距。如果预算允许商业API作为主力开源模型作为备胎或批量测试阶段的主力是个比较稳妥的组合。选型上有一个核心的评判标准给定一张高保真截图模型能否准确识别出目标元素并返回坐标中心点。这一点至关重要因为后续所有操作都要依赖这个坐标点。2.2 无头浏览器选型双引擎驱动更可靠无头浏览器简单理解就是没有界面的浏览器它跑在后台但能真实渲染网页。市面上主流的选择是Playwright和Puppeteer。我个人更倾向用Playwright因为它对多浏览器的支持更好API设计也更符合Python的习惯。在实操中我会同时初始化Chromium和Firefox两个引擎。为什么因为有些老系统只兼容IE内核或者对Chromium家族有莫名的兼容性问题这时候Firefox无头模式还能顶上。作为RPA的工具永远要有Plan B。无头浏览器的核心配置有几个关键点。第一必须设置优选的视口大小比如1920x1080保证页面渲染出来和真机效果一致。第二要禁用或固定一些动态元素比如弹窗、浮动提示、还在加载中的轮播图否则VLM的识别会受干扰。第三要有一套稳定的页面加载等待策略千万不要只依赖time.sleep()要结合元素状态和网络空闲状态的判断。2.3 架构思路截图-理解-行动-校验四步闭环这套系统的本质是模仿人类的“看-想-做-查”循环。我把它拆分成四个模块视觉采集模块截图、理解推理模块VLM、行动执行模块Playwright、结果校验模块二次视觉判断。四个模块串起来就是一个完整的自动化单步流程。这里需要强调的是最终做出来的RPA不是只跑一个大循环而是把任务切成多个小步骤每步都走完“看-想-做-查”这样才能保证每一步的准确性也方便出错时定位问题。3. 实操全流程从零搭建一套视觉自动化RPA3.1 环境准备与基础框架开始写代码之前先把环境弄干净。用Python 3.10及以上版本用virtualenv或conda建一个独立环境避免依赖冲突。# 创建虚拟环境 conda create -n vlm_rpa python3.10 conda activate vlm_rpa # 安装核心依赖 pip install playwright playwright install chromium playwright install firefox pip install openai # 或其他模型SDK pip install Pillow代码结构上我习惯这样组织vlm_rpa_project/ ├── main.py # 主入口控制流程调度 ├── browser_engine.py # 无头浏览器封装 ├── vision_module.py # 视觉理解模块调用VLM ├── action_executor.py # 动作执行模块点击/输入/滚动 ├── verifier.py # 结果校验模块 └── tasks/ └── order_query.py # 具体业务任务脚本3.2 浏览器引擎封装稳住渲染的基础浏览器引擎是地基地基不稳后面的视觉识别全是空谈。我在browser_engine.py里做了这么几个关键处理# browser_engine.py 核心片段 from playwright.sync_api import sync_playwright class BrowserEngine: def __init__(self, headlessTrue, viewport_sizeNone): self.headless headless self.viewport_size viewport_size or {width: 1920, height: 1080} self.playwright None self.browser None self.page None def start(self, browser_typechromium): self.playwright sync_playwright().start() if browser_type chromium: self.browser self.playwright.chromium.launch( headlessself.headless, args[ --disable-blink-featuresAutomationControlled, --start-maximized ] ) else: self.browser self.playwright.firefox.launch(headlessself.headless) context self.browser.new_context( viewportself.viewport_size, ignore_https_errorsTrue, user_agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... ) self.page context.new_page() return self.page def shutdown(self): if self.browser: self.browser.close() if self.playwright: self.playwright.stop()注意这里几个细节。--disable-blink-featuresAutomationControlled是为了移除webdriver痕迹虽然我们做的是合法自动化但有些页面会因为这个标记而故意渲染成反爬模式导致视觉环境异常。禁止headlessTrue时不要用--headlessnew这种过度激进的模式可能造成页面显示差异让VLM识别结果和真实情况不一致。设置固定user_agent也很有必要不要在无头模式里暴露“HeadlessChrome”默认UA某些系统JS会因此走一套完全不同的渲染逻辑。3.3 视觉理解模块把“意图”翻译成“坐标”这是整个系统的灵魂。视觉模块要接收页面截图和任务指令输出对应的操作坐标。# vision_module.py 核心片段 import base64 import json from openai import OpenAI class VisionModule: def __init__(self, api_key, model_namegpt-4o): self.client OpenAI(api_keyapi_key) self.model_name model_name def screenshot(self, page, pathscreen.png): page.screenshot(pathpath) with open(path, rb) as f: return base64.b64encode(f.read()).decode() def get_action(self, screenshot_base64, instruction, step_desc): response self.client.chat.completions.create( modelself.model_name, messages[ { role: system, content: ( 你是一个视觉自动化助手。请根据当前页面的截图和用户指令 决定执行的动作。只允许输出JSON格式 {action: click|type|scroll|wait|finished|fail, target_text: 目标元素上的文字, coordinate: [x, y], input_text: 需要输入的文本, reason: 判断依据} ) }, { role: user, content: [ {type: text, text: f当前步骤{step_desc}}, {type: text, text: f任务指令{instruction}}, {type: image_url, image_url: {url: fdata:image/png;base64,{screenshot_base64}}} ] } ], max_tokens500, temperature0.1 ) result response.choices[0].message.content result result.replace(json, ).replace(, ).strip() return json.loads(result)temperature要设置得尽可能低我习惯用0.1。视觉自动化任务追求的是确定性不需要模型发挥想象力。同时指令要明确要求输出JSON结构避免解析困难。这里分享一个提高识别精度的技巧屏幕截图时要对关键区域做裁剪放大。比如如果任务是要查找侧边菜单的某个按钮在进入这一步之前先用Playwright的locator把它可能所在的区域截图下来再丢给VLM识别。这样既减少了VLM的理解压力也让识别不准的概率大幅下降。3.4 动作执行与校验闭环才可靠拿到坐标后就是执行。但点击不是点下去就结束了你得确认点得到、点对了、点完了页面有什么变化。# action_executor.py 核心片段 import time class ActionExecutor: def __init__(self, page): self.page page def click(self, coordinate, target_text): try: x, y coordinate # 先移动鼠标坐标便于截图确认 self.page.mouse.move(x, y) time.sleep(0.2) self.page.mouse.click(x, y) # 点击后等待网络空闲 self.page.wait_for_load_state(networkidle, timeout5000) return True except Exception as e: print(f点击执行失败: {e}) return False def type_text(self, coordinate, input_text): try: x, y coordinate self.page.mouse.click(x, y) time.sleep(0.5) self.page.keyboard.type(input_text, delay50) # 模拟真人逐个字符敲击 return True except Exception as e: print(f输入执行失败: {e}) return False def scroll(self, directiondown, amount500): if direction down: self.page.mouse.wheel(0, amount) else: self.page.mouse.wheel(0, -amount) time.sleep(1)delay50这行是精髓模拟人工敲击键盘的间隔因为一些系统有按键监听如果瞬间输入完会触发异常校验。坐标点击也要先移动再等待再点击这是为了让页面的hover效果先触发很多隐藏菜单和悬浮提示都需要hover状态才能正确显示。校验模块的设计我采用二次视觉校验# verifier.py 核心片段 class Verifier: def __init__(self, vision): self.vision vision def verify(self, page, expected_condition): # 截图当前页面让VLM校验是否达到预期状态 screenshot self.vision.screenshot(page) result self.vision.get_action( screenshot, instructionf请判断当前页面是否满足条件{expected_condition}, step_desc校验步骤 ) if result[action] finished: return True else: return False比如执行“点击查询按钮”后校验模块会截图当前页面询问模型“页面上是否出现了订单列表”如果模型回答否说明点击没生效或者条件有变化需要重新决策。3.5 一个完整的任务流程实战以“从某后台系统导出当日订单报表”为例跑一遍完整流程# tasks/order_query.py 核心片段 from browser_engine import BrowserEngine from vision_module import VisionModule from action_executor import ActionExecutor from verifier import Verifier def run_order_query(): engine BrowserEngine(headlessTrue) page engine.start() vision VisionModule(api_keyyour-api-key, model_namegpt-4o) executor ActionExecutor(page) verifier Verifier(vision) # 登录流程 page.goto(https://example-order-system.internal.com/login) page.wait_for_load_state(networkidle) # 输入用户名 result vision.get_action(vision.screenshot(page), 输入用户名, 登录页面-用户名输入) executor.click(result[coordinate], result[target_text]) executor.type_text(result[coordinate], demo_user) # 输入密码 result vision.get_action(vision.screenshot(page), 输入密码, 登录页面-密码输入) executor.click(result[coordinate], result[target_text]) executor.type_text(result[coordinate], demo_password) # 点击登录 result vision.get_action(vision.screenshot(page), 点击登录按钮, 登录页面-登录) executor.click(result[coordinate], result[target_text]) # 等待首页加载 page.wait_for_load_state(networkidle) time.sleep(2) if not verifier.verify(page, 页面已登录成功出现欢迎信息或工作台): print(登录可能失败需人工介入) page.screenshot(patherror_login.png) engine.shutdown() return # 进入订单查询模块 result vision.get_action(vision.screenshot(page), 点击左侧菜单中的订单查询, 首页-菜单导航) executor.click(result[coordinate], result[target_text]) page.wait_for_load_state(networkidle) # 按条件筛选 result vision.get_action(vision.screenshot(page), 点击日期选择器选择今天, 订单查询页-日期筛选) executor.click(result[coordinate], result[target_text]) time.sleep(1.5) result vision.get_action(vision.screenshot(page), 点击查询按钮, 订单查询页-查询按钮) executor.click(result[coordinate], result[target_text]) if not verifier.verify(page, 页面出现订单数据表格): print(查询未成功) engine.shutdown() return # 点击导出 result vision.get_action(vision.screenshot(page), 点击导出报表按钮, 订单查询页-导出按钮) executor.click(result[coordinate], result[target_text]) # 等待导出完成 time.sleep(5) print(任务执行完毕报表已触发导出) engine.shutdown() if __name__ __main__: run_order_query()这套流程看着不复杂但真正运行起来你会发现真正的瓶颈不在代码而在容错机制。比如如果某次VLM把“导出报表”按钮看成了“导出记录”按钮点的位置就偏了这时候就需要后面的校验步骤兜底。所以我把流程设计成多个独立的小闭环而不是一个大闭环这样某个环节出错只需要重试那一个环节不需要从头再来。4. 常见的坑与排查实录4.1 VLM“眼花”把页面加载中的图标当有效按钮怎么办最开始跑的时候经常出现模型把页面上正在旋转的加载图标识别成按钮直接点了进去结果是白屏。这个问题根源在于截图时机不对。我的做法是在执行关键操作前先强制等待所有网络请求完成再额外等待0.5至1秒让渲染稳定。Playwright的locator函数可以很好地配合比如等待某个已知元素可见后再截图。page.wait_for_selector(text订单查询, statevisible)会比通用等待策略更可靠。先把页面“熬”到歇菜了再截图模型就不容易眼花了。4.2 坐标识别准确但点击无效元素在iframe里有几次VLM返回的坐标完全正确目标元素清清楚楚就在那里但点击后无反应。最后定位发现目标内容在一个嵌套的iframe里。Playwright虽然能看到iframe内的元素但直接用坐标点击需要先切换到对应的frame。解决方法是在点击前用Playwright检测是否处于frame环境如果目标元素在frame内就先执行page.frames切换再执行坐标点击。坐标的相对偏移也要算清楚否则会点偏。4.3 页面有时弹窗有时不弹一个经典的RPA头疼问题页面右上角随时可能弹出“公告”“提示”窗口影响VLM判断。我的排坑思路是在主流程正式执行前增加一个“预清洁”动作。用视觉模块先扫一遍如果发现弹窗类元素且有关闭按钮就先点击关闭把页面环境理顺了再开始干活。这个操作其实就是在主流程开头插入几步标准动作写成独立的函数每次任务开始自动执行。4.4 模型输出解析报错VLM偶尔会输出带格式的文本比如JSON外面包着Markdown代码块标记。我在解析出加了个清理函数把json标记和反引号全部剥离。另外强约束输出格式时不要只靠提示词如果模型支持“response_format”参数就加上能从根本上避免解析异常。4.5 常见避坑速查表问题现象根本原因解决方案VLM识别结果飘忽不定截图时机不稳定页面还没渲染完先等网络idle再等关键元素visible再截图坐标准但点击无效目标在iframe或阴影DOM内检测frame切换到对应frame后再点击输入内容错乱键盘输入太快触发系统事件干扰keyboard.type加delay模拟真人输入节奏登录一直失败反自动化检测标记被识别配置User-Agent移除自动化控制标记一次跑通二次跑不通页面存在动态刷新或缓存每次运行前清空上下文和缓存必要时重启浏览器实例模型不理解复杂表格指令表述不清或截图分辨率不够高分辨率截图裁剪放大关键区域指令细化到列名5. 更进一步把流程做成可配置的“技能库”当我跑通第一套视觉RPA之后我的思考又进了一步。单条自动化脚本只是解决了单个问题真正有生产力的是把这类“视觉操作”沉淀成一个个可复用的技能模块。比如“通过日期选择器选取日期区间”是一个通用技能“点击左侧菜单中的某个模块”也是一个通用技能。把这些技能抽象成函数输入输出标准化后续面对新的业务系统时就不需要从零开始跟VLM做“对话教学”而是把已有的技能组合起来像拼积木一样搭新的流程。这个设计价值很高它能大幅降低VLM幻觉带来的不确定性。因为每一步骤之前驱动模型的是高度结构化的技能指令而不是开放式的人类语言。另一个值得投入的方向是屏幕差异对比。拿VLM识别出来的目标坐标与实际业务期望做回归测试。每次业务系统更新界面后跑一遍全量流程模型会快速定位哪些操作元素失效了。这套“视觉回归”机制替代了过去人工遍历点击找断点的痛苦实现了自动化的自身健康度监测。写在最后从我实际跑通这套“VLM无头浏览器”自动化方案之后我的一个明显感受是RPA终于从“接口的附庸”变成了“视力的主人”。过去那些因为接口缺失而搁浅的自动化需求重新被翻了出来不仅实现了而且运行稳定。这套方案也有自己的软肋比如推理成本比纯接口调用高不少单次任务耗时更长但换来的价值是——它不再怕前端改版不再怕接口废弃它适应的是真实世界里人类操作界面的方式。如果你是做自动化相关工作的也可以先拿一个低频、非关键的任务试试水感受一下“机器长了眼睛”是什么体验。别着急上生产先在本地把截图、识别、动作执行这条最小闭环跑顺再逐步扩展。时机成熟时你就会发现所谓“彻底告别接口依赖”并不是一句空话。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑