在讨论“AI龙虾到底应该用哪个技能实现浏览器的自动化”之前,我先交代一下背景:我们内部有一个智能体项目,代号就叫“AI龙虾”,核心任务之一是把大量重复的浏览器操作交给模型去跑。这个过程中踩过的坑、试过的方案、最后沉淀下来的选型思路,我觉得值得拿出来聊聊。如果你也在做类似的事情——不管你是给智能体接技能,还是自己在写自动化脚本,这篇内容应该能帮你少走一点弯路。
先说结论:不存在一个“唯一正确”的技能,真正靠谱的做法是让AI龙虾在一个可编排的框架里,把协议级控制、DOM级定位、流程级编排这三层能力组合起来。只看单一技能,要么遇到动态页面就扑街,要么就困在固定脚本里,根本发挥不出“AI”的泛化能力。下面我拆开讲。
1. 为什么“技能选型”直接决定自动化项目的生死
1.1 先搞清楚AI在自动化里扮演的角色是什么
很多人有个误解:以为AI龙虾像人一样“看着”屏幕,理解页面内容,然后模拟鼠标键盘去操作。真做起来你会发现,纯视觉驱动的方案(直接截图然后让模型输出坐标点击)在静态页面还能勉强跑,一碰到列表加载、弹窗、懒加载、动态样式,马上就乱。
这里的本质问题是:技能选型决定了AI的感知基座和操作基座是什么。所谓“感知”,就是AI怎么知道当前页面上有什么;“操作”,就是AI用什么方式让页面发生变化。如果你给AI龙虾接的感知层只有一张截图,那它在面对复杂页面时,信息量是不够支撑稳定决策的。我们要做的,不是让AI学会“像人一样看”,而是让AI在正确的信息层上做判断——这比训练模型本身更影响成败。
1.2 三种信息层决定了三种技能路线
我习惯把浏览器自动化技能分成三个层级:
- 视觉层:通过截图、图像识别获取页面状态,模型的优势是能应对完全未知的界面,但速度慢、稳定性差、误点率高。
- 结构层:通过DOM树、ARIA属性、元素选择器来定位和操作,速度快、精准度高,但要求页面有可读的语义结构。
- 协议层:通过浏览器调试协议或WebDriver协议直接下发指令,几乎不依赖“看”,完全走通道,稳定性最高,但开发量也最大。
不同方案适合不同场景。视觉层适合“陌生系统快速试探”,结构层适合“已知系统的自动化回归”,协议层适合“需要并发和精确控制的批量任务”。AI龙虾要做得稳,不能只挂一种,更多时候是先结构层,失败再降级到视觉层,所有跨页面的状态迁移由协议层兜底。
1.3 技能接口的设计比技能本身更重要
就算你选了再好的底层库,如果AI龙虾接入的方式是“让模型自由地写一大段Python脚本然后执行”,很快你就会遇到问题:模型写的选择器过期了、它自己造的等待逻辑不生效、两个并发任务之间共享了全局状态。我见过太多项目倒在第一步,不是AI不行,而是接口太糙。
正确的做法是把“浏览器自动化”封成一个受限但可控的技能接口,比如暴露给AI的就三个动作:查找元素、等待状态、执行操作。模型只负责组合这些原子动作,真正的容错、重试、超时都在技能内部实现。这样AI的能力边界是清晰的,出问题也好排查。选技能的核心,其实是选一套能让模型安全调用、不容易把浏览器搞炸的接口设计,而不仅仅选一个库。
2. 主流技能路线全景:到底有哪几条路可走
2.1 协议级方案:稳定性的上限最高
先说协议级。什么是协议级?就是绕过“模拟人操作”这个层面,直接通过浏览器提供的调试端口,发送JSON格式的命令,比如“打开一个标签页”“执行一段JavaScript”“等待某个网络请求完成”。这种方式最接近浏览器引擎本身,稳定性极高,CPU和内存占用也远低于完整模拟操作。
它的典型用法是:启动一个浏览器实例时开启调试端口,然后AI龙虾通过WebSocket连上去,直接调用浏览器内部能力。你可以把一个网址作为参数传进去,也可以主动监听控制台日志、网络响应、页面错误,这些信息比截图丰富得多。适合的场景包括:需要同时跑几十个页面的批处理、需要在页面加载完之前就干预的接口测试、对失败率要求极高的线上巡检。
但这个方案的短板也很明显:它对AI来说是“不直觉”的。你让大模型直接写调试协议的命令,它很容易写出错误参数。所以在实际项目里,我会在协议层上面再包一层语义化动作库,不让模型面对裸协议。协议级是“骨架”,不直接暴露给AI,但所有高层技能最终都会落到这层来执行。
2.2 结构化DOM方案:AI最顺手的中间层
中间这一层是我日常用得最多的,也是给AI龙虾的主力技能。这种方案不依赖截图识别,而是把当前页面的可交互元素整理成一份紧凑的文本清单,送给模型。清单里每个元素都带有一个短期有效的ID或路径,模型只需要说“点击ID为17的按钮”或“在ID为32的输入框填入某内容”即可。
这个方案的优点有两个。第一是快,因为不需要截图、不需要图像模型,一次操作只有一次结构化解析和一次指令下发。第二是可解释,模型每一步的操作理由都能从清单里追溯,出了问题你知道它在想什么。技术上它是通过注入一段脚本到页面里,把当前可见的链接、按钮、输入框、下拉框全部枚举出来,过滤掉隐藏元素,生成精简视图,再映射到真实DOM操作上。
要注意的是,这个方案对页面结构有一定要求。如果目标网站大量使用Canvas渲染,或者所有操作都是自定义绘图组件,那结构化清单里可能什么都拿不到。另外,一些框架渲染出来的元素没有可读的文本信息,只有一串样式名,这时候需要辅助脚本去补充可访问性属性(aria-label之类)才靠谱。
2.3 纯视觉方案:该用的时候别犹豫
别急着否定纯视觉。虽然它稳定性差、调用成本高,但它在某些场景下是唯一能上手的。比如一个新接进来的业务系统,前端又老又乱,DOM结构全是嵌套几十层的表格,标准的选择器根本没有,甚至点击区域是用绝对定位的透明层覆盖的。这时候AI龙虾最有效的切入方式是:截图 + 视觉定位 + 模拟操作。
我的经验是,视觉方案要前置一个成本预估步骤:先让AI判断这个页面能不能用结构化清单解析,解析不了再进入视觉模式。毕竟一次视觉调用的延迟和费用通常是结构化的几十倍,如果所有操作都走视觉,项目跑不起。视觉方案还需要配合一套坐标映射逻辑,因为截图尺寸和浏览器视口尺寸不一定一致,模型标出来的坐标必须经过换算之后才能真正落到元素上,这个细节很多人会漏掉。
另外,视觉方案的模型选型要特别在意“小目标检测”能力。如果模型连页面里一个14号字的按钮都看不清,那基本没法用。我用下来的体感是,选一个分辨率支持高一点的模型,并且截图时做局部放大,比直接喂整页截图靠谱得多。
2.4 RPA技能:被低估的补充角色
还有一类传统RPA方案,很多人觉得AI都来了,这玩意应该过时了。实际我反而觉得,RPA是AI龙虾技能组合里很有价值的一环,尤其是当目标系统根本不是网页,而是桌面客户端的时候。许多企业内部系统还保留着老式的客户端界面,你就算再厉害也拿不到它的DOM,只能靠坐标和窗口句柄去操作。
RPA技能跟前面几种的配合方式是:AI负责“编排”,RPA负责“执行”。AI决定先做什么后做什么,但具体到那个老客户端的字段怎么填、按钮怎么点,就交给RPA脚本。这也引出一个通用原则:不要在同一个维度上跟工具死磕,能用最低成本完成任务的方式就是好方式。如果某个操作只需要固定三步,写死一个RPA流程比交给AI临场发挥稳定得多。
不过RPA的维护成本和环境兼容性是个问题,屏幕分辨率变了、窗口位置变了都可能直接跑飞。所以我在项目里把RPA技能收敛成一个“轻量技能”,只暴露少量的固定动作,比如激活窗口、点击坐标区域、读取区域文本,不让AI随意去编新的坐标操作。
3. 实操:让AI龙虾自动完成一个后台表单录入任务
3.1 先定义一个具体到能落地的场景
理论说太多容易飘,我拿一个实际做过的任务来走一遍完整链路。目标系统是一个模拟的业务后台(暂且叫“某运营平台”),AI龙虾需要每天自动登录,进入订单管理页,筛选出“待处理”状态的订单,然后对每一条订单执行回访备注填写、状态改为“已联系”。这活儿以前是运营同事花了两个小时手工干的。
拆解下来,这个任务涉及的关键点包括:登录时的输入框、筛选条件的下拉框、结果列表的动态加载、每条订单对应的操作按钮、弹窗确认。这已经不算最简单的任务了,但也不至于复杂到需要多智能体协作。把它作为“AI龙虾技能实测”的基准场景,再合适不过。
拆任务的时候我习惯画这样一个思维链条:登录 → 跳转 → 筛选 → 等待列表 → 逐条处理 → 保存 → 登出。注意,这七个步骤里的每一步都需要一种“确定性”,比如“等待列表加载完成”就必须定义成“出现某个特定元素或某个网络请求返回”,而不是简单Sleep几秒。这个确定性定义的工作,比写自动化代码本身技术含量更高。
3.2 技能配置的核心代码骨架
我用的是一个基于协议层封装的结构化操作库,AI龙虾能调用的动作被我收敛成了几个:navigate(跳转)、query(查询可交互元素)、click(点击元素)、fill(填写输入框)、wait(等待条件出现)。给AI的上下文里,只放query结果和少量系统状态,不让它直接面对千奇百怪的页面报错。
下面是一个典型调用片段(伪代码风格,基于常见实践的补充):
# 技能内部实现示意 def handle_task_step(step): if step.action == "query": elements = page.get_interactive_elements() return compact_view(elements) elif step.action == "click": locator = resolve_locator(step.target_id) locator.scroll_into_view_if_needed() locator.click(timeout=5) elif step.action == "fill": locator = resolve_locator(step.target_id) locator.clear() locator.fill(step.value) elif step.action == "wait": return page.wait_for_condition(step.condition, timeout=10)AI龙虾拿到的是类似这样的结构化指令流:第一步调用query,拿到所有可点击元素和输入框的清单;第二步选定输入框ID,调用fill;第三步选定按钮ID,调用click。每一步的返回值都会被拼接成文本反馈给模型。这个过程中如果某一步超时,技能内部会自动尝试滚动页面后重试一次,还不行就返回一个带错误码的错误信息,模型看到后就能调整策略,比如退出重新登录再走一遍。
3.3 参数选择与意想不到的细节
很多细节会在刚开始做的时候被忽略,但恰恰是它们决定了稳定性。
第一是选择器的有效期。结构化清单里的元素ID不是永恒有效的,页面一刷新就变。所以我在设计技能接口的时候,给每个元素ID绑定了一个短期票据(后面简称为引用ID),任何操作执行前都先校验这个引用还存不存在,不存在就提示AI重新query。这会增加一点点调用次数,但能避免大量“元素失效”的莫名其妙报错。
第二是等待条件的建模。我强烈建议把“页面加载完成”这个条件从“某个元素出现”升级为“某个元素处于可交互状态”。有些列表加载完只是元素出现在DOM里,但还在渲染中,点上去没反应。这就要用到元素可见且未禁用且不处于动画中的综合判断。
第三是登录态的管理。不要每次任务都走完整的账密登录,让AI龙虾在一个专门的配置环境里先登录一次,保留登录态,后续任务直接复用。这能把整个自动化流程时间缩短一半以上。当然前提是目标系统允许会话保持,且安全策略允许。
我记得第一次跑通这个任务时,以为大功告成,结果第二天列表页改版了。第二天一上班发现报错率飙升,日志里全是“找不到元素”。这个体验让我彻底明白一件事:只要目标系统在前端持续迭代,自动化方案就必须设计成“容忍变化”的,而不是试图固定住某个版本。于是后来我把query层做成了“语义兜底”:如果找不到某一类按钮,就把页面里所有按钮的文本和位置都返回给AI,让AI根据语义自己挑。事实证明,这个设计比精确匹配健壮得多。
3.4 运行时的观察与人工介入机制
自动化跑起来之后,观察和兜底同样重要。我给AI龙虾配了一个轻量级的运行看板(其实就是一个日志页面),实时显示三类信息:当前步骤、最近一次query返回的元素概览、操作记录的截图存档。这些信息不是为了好看,是为了快速定位问题。
一旦单次任务连续失败超过三次,系统会自动进入“低风险人工接管”模式:把当前页面截图、上下文日志、历史操作记录打包,推给运营同事,由人来决定是继续还是终止。这里的关键认知是:AI自动化不是追求100%无人介入,而是把需要人工介入的次数降到一个可接受范围。对于批量任务,哪怕只有80%能自动完成、剩下20%需要人工盯,也已经比纯人工省下很多时间。
再分享一个小细节:重试策略不是简单的“错了就再来一次”,而是要区分错误类型。如果是网络波动导致超时,重试有效;如果是业务规则变了(比如某个字段改成了必填),重试一百次也一样失败。所以我在技能返回的错误信息里加了错误分类字段,AI龙虾可以根据分类决定重试还是马上停下来寻求帮助。这个逻辑很重要,能防止系统在错误方向上空转。
4. 实测中的高频故障与排查心得
4.1 高频故障速查表
这段内容是我觉得最有分享价值的部分。直接上表:
| 故障现象 | 根因分析 | 排查方向 | 实战建议 |
|---|---|---|---|
| 元素找到了,但点击无效 | 元素被其他透明层遮挡 | 检查覆盖元素的定位样式 | 先滚动到可视区,再检查z-index |
| 页面一直停在某个加载状态 | 等待条件定义错 | 查看网络请求是否已返回 | 改用“网络空闲”或“特定元素可交互”作为条件 |
| 首次运行正常,第二次开始报错 | 登录态过期或全局状态污染 | 检查Cookie和缓存隔离 | 每个任务使用独立浏览器上下文 |
| 输入框填入后内容丢失 | 输入事件没触发框架的响应 | 看是否是受控组件 | 必要时模拟完整按键事件,而不只是设值 |
| 弹窗位置不稳定 | 页面有动态布局 | 查看弹窗是否被重新渲染 | 每次点击弹窗操作前重新query一次 |
| 不同浏览器跑出来结果不同 | 浏览器版本差异 | 检查所用框架与浏览器版本匹配 | 锁定浏览器版本来跑,减少不确定性 |
| AI反复选错元素 | 元素清单太冗长 | 检查是否给AI太多无关信息 | 精简清单,只保留当前区域的可操作元素 |
| 页面打开就崩溃或无响应 | 资源占用过高 | 检查是否并发任务过多 | 限制单浏览器实例的并发标签数量 |
这只是我遇到的高频问题,完整清单远不止这些。但即使只优化这八项,自动化任务的稳定率也能从60%拉到85%以上。再往上走,就需要引入更多“预测性”机制了,比如根据历史成功率自动降低某些任务的并发等级。
4.2 稳定性设计的三个原则
踩过这么多坑之后,我总结出三个稳定性设计原则,基本成了我做所有浏览器自动化项目的标配:
原则一:所有操作都带保护。这里的保护包括超时、重试、失败降级。没有保护的操作只适用于演示,不适用于正式任务。要给每个原子动作都设置明确的超时阈值,宁可多等一秒,也不能永久挂死。
原则二:上下文必须隔离。每个自动化任务都应该跑在独立的浏览器上下文里,Cookie、缓存、存储完全隔离。否则上一个任务的脏数据会影响下一个任务。这一条我是在跑批处理时被坑过一次才彻底记住的,那次三个任务互相污染登录态,报错信息五花八门。
原则三:可观测性优先于功能优化。任务跑成功了不奇怪,关键是你能否快速定位失败的那一步。我要求所有步骤都要留下结构化日志,包括操作前页面状态、操作后页面变化、耗时、截图。这比堆功能重要得多。很多项目稳定不下来,就是因为出了问题只能靠肉眼一遍遍看录像回放,效率太低了。
4.3 为什么“技能组合拳”比“单一技能”更值得投入
最后回答标题里那个问题。AI龙虾到底应该用哪个技能?我的答案非常明确:不要把所有鸡蛋放在一个技能篮子里。我做过的自动化项目里,凡是只用结构化元素定位的,遇到老系统就卡住;只用纯视觉的,成本和误判率又让人抓狂;只用协议层的,模型一行命令写错就得排查半天。真正能打的是组合方案。
用技术语言说,就是“感知层多模态 + 动作层统一接口 + 策略层模型决策”。感知层可以从结构化清单、截图中选择信息;动作层只暴露有限的原子操作;策略层由大模型根据当前情况选择怎么组合这些动作。这个架构的好处是,任何一个底层技能升级或替换,都不会影响整体结构。将来如果有新的自动化通道出来,我只需要把它封装成同一个接口,AI龙虾的技能列表里多一项选择就行。
所以如果你让我给一个选型建议,我会说:主力走结构化DOM查询,兜底走视觉定位,底层全部统一到协议层,接口一律原子化、带超时、带重试、带可观测日志。这不是什么高深理论,就是我做了一轮又一轮项目后踩出来的经验。按照这个思路搭,哪怕你的目标系统下周就改版,你的AI龙虾也能稳稳扛住大部分变化。
我个人在实际操作中的体会是:浏览器自动化这件事,真正的难点从来不是“哪个库更好用”,而是你能不能把一个开放、混乱的浏览器环境,改造成AI能稳定发挥的封闭环境。技能选型只是起点,后面要做的工程化工作才是大头。如果只挑一个最重要的习惯,我会建议大家从第一天开始就给所有自动化任务录好日志、存好截图,这些沉淀下来的数据,会在后面优化模型策略时发挥超乎想象的价值。