在 AI Agent 蓬勃发展的今天,Web 自动化领域迎来了一个颠覆性的解决方案——Skyvern。它摒弃了传统 RPA(机器人流程自动化)依赖 DOM 选择器和 XPath 的脆弱模式,转而采用「视觉大模型(Vision LLM)+ 计算机视觉」的方式来理解和操作网页。
本文将深入解析 Skyvern 的设计理念、核心架构以及这种视觉驱动方案的优缺点,帮助你全面了解这一新兴的 Web Agent 技术。
在了解 Skyvern 之前,我们先回顾传统 Web 自动化面临的核心问题:
传统方案(如 Selenium、Playwright 脚本)依赖 CSS 选择器或 XPath 定位元素:
# 传统方式:依赖固定的选择器
driver.find_element(By.ID, "submit-btn").click()
driver.find_element(By.XPATH, "//div[@class='form-container']/button[1]").click()
「问题」:网站 UI 稍有改动,脚本就会失效。
为网站 A 编写的脚本无法直接用于网站 B,即使它们的功能完全相同(如登录、搜索)。每个网站都需要单独开发和维护。
面对验证码、动态加载、条件分支等场景,传统脚本需要大量的 if-else 逻辑,难以应对意外情况。
Skyvern 的核心理念可以用一句话概括:「让 AI "看懂" 网页,而不是 "解析" 网页」。
维度 | 传统方案 | Skyvern |
|---|---|---|
「理解方式」 | 解析 DOM 结构 | 视觉感知页面 |
「定位元素」 | CSS/XPath 选择器 | 视觉坐标识别 |
「决策逻辑」 | 预编写脚本 | LLM 实时推理 |
「泛化能力」 | 单站点定制 | 零样本跨站点 |
「1. 零样本学习(Zero-shot Learning)」
Skyvern 能够在从未见过的网站上运行,通过视觉元素映射到操作,无需针对特定网站编写代码。
「2. 抗布局变更」
由于不依赖预定义的选择器,网站 UI 改版不会导致自动化流程失效——只要人类能看懂,Skyvern 就能操作。
「3. 自然语言驱动」
用户只需用自然语言描述目标,Skyvern 自动规划并执行:
# Skyvern 方式:自然语言描述意图
skyvern.run_task(
url="https://example.com",
prompt="登录账号 admin,密码 123456,然后搜索'测试用例'"
)
Skyvern 的架构借鉴了 BabyAGI 和 AutoGPT 等自主 Agent 的设计,但专门针对 Web 交互场景进行了优化。
┌─────────────────────────────────────────────────────────────┐
│ 用户请求 │
│ (URL + 自然语言任务描述) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ API 接口层 │
│ (FastAPI Server) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 任务编排服务 │
│ (任务调度、状态管理、会话管理) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Agent 执行引擎 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ReAct 循环 (核心) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 观察 │ -> │ 思考 │ -> │ 行动 │ ──┐ │ │
│ │ │Observe │ │ Think │ │ Act │ │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │ │
│ │ ▲ │ │ │
│ │ └───────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────────────┐ ┌──────────────────────────┐
│ 浏览器环境 │ │ Vision LLM 服务 │
│ (Playwright) │ │ (GPT-4o/Claude 3.5等) │
│ - 页面渲染 │ │ - 视觉理解 │
│ - 截图获取 │ │ - 动作规划 │
│ - 动作执行 │ │ - 数据提取 │
└──────────────────────┘ └──────────────────────────┘
Skyvern 的核心是一个持续的 「ReAct(Reasoning + Acting)」 循环:
1. 获取当前页面截图
2. 提取页面可访问性树(Accessibility Tree)
3. 整合历史操作上下文
4. 构建发送给 LLM 的 Prompt
这一阶段的关键是将视觉信息转化为 LLM 能够理解的格式。Skyvern 不仅发送截图,还会提取页面的结构化信息辅助理解。
LLM 接收到的输入:
- 当前页面截图
- 页面元素列表及其位置
- 用户的目标描述
- 历史操作记录
LLM 输出:
- 任务是否完成?
- 如果未完成,下一步应该做什么?
- 具体操作的目标元素和参数
Skyvern 支持的动作类型:
动作 | 说明 | 示例 |
|---|---|---|
Click | 点击元素 | 点击"提交"按钮 |
Input | 输入文本 | 在用户名框输入 "admin" |
Select | 下拉选择 | 选择"中国"选项 |
Scroll | 页面滚动 | 向下滚动 500 像素 |
Wait | 等待 | 等待页面加载完成 |
Upload | 上传文件 | 上传身份证图片 |
为了更清晰地理解 Skyvern 的工作机制,本节详细介绍发送给 LLM 的请求结构、响应格式以及核心 Prompt 模板。
Skyvern 使用 OpenAI 兼容的多模态 API 格式发送请求:
{
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "【系统提示词 + 任务上下文 + 页面元素列表】"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
}
}
]
}
],
"temperature": 0.1
}
「关键字段说明」:
model: 使用支持视觉的模型(如 gpt-4o、claude-3-5-sonnet)content: 多模态内容数组,包含文本(Prompt)和图像(截图)temperature: 设置较低值以获得更确定性的输出Skyvern 使用 Jinja2 模板引擎动态生成 Prompt。以下是核心模板 extract-action.j2 的简化结构:
你是一个浏览器自动化 AI 代理。根据用户目标和当前页面状态,生成下一步操作。
## 任务信息
- **用户目标**: {{ navigation_goal }}
- **完成标准**: {{ complete_criterion }}
- **数据提取目标**: {{ data_extraction_goal }}
- **当前 URL**: {{ current_url }}
## 用户提供的数据
{{ navigation_payload_str }}
## 操作历史
{{ action_history }}
## 当前页面元素列表
以下是页面上可交互的元素(ID 用于定位):
{{ elements }}
## 可用操作类型
- CLICK: 点击元素
- INPUT_TEXT: 输入文本
- SELECT_OPTION: 选择下拉选项
- UPLOAD_FILE: 上传文件
- HOVER: 鼠标悬停
- WAIT: 等待加载
- SCROLL: 滚动页面
- SOLVE_CAPTCHA: 解决验证码
- COMPLETE: 任务完成
- TERMINATE: 终止任务
## 输出要求
输出严格的 JSON 格式,不包含 Markdown 代码块或注释:
{
"user_goal_stage": "当前进度描述",
"user_goal_achieved": false,
"action_plan": "即将采取的行动计划",
"actions": [...]
}
LLM 返回的 JSON 响应结构如下:
{
"user_goal_stage": "正在填写登录表单",
"user_goal_achieved": false,
"action_plan": "填写用户名和密码后点击登录按钮",
"actions": [
{
"action_type": "INPUT_TEXT",
"element_id": 15,
"text": "admin@example.com",
"reasoning": "在用户名输入框中填写邮箱地址",
"input_or_select_context": {
"intention": "输入登录邮箱",
"is_required": true,
"is_search_bar": false
}
},
{
"action_type": "INPUT_TEXT",
"element_id": 18,
"text": "********",
"reasoning": "在密码框中输入密码"
},
{
"action_type": "CLICK",
"element_id": 22,
"reasoning": "点击登录按钮提交表单",
"click_context": {
"thought": "表单已填写完毕,可以提交"
}
}
]
}
「Actions 数组中每个动作的字段」:
字段 | 类型 | 说明 |
|---|---|---|
action_type | string | 操作类型(见上方列表) |
element_id | int | 目标元素的唯一标识符 |
text | string | 输入的文本内容(INPUT_TEXT 时必需) |
option | object | 选择的选项(SELECT_OPTION 时必需) |
file_url | string | 上传文件的 URL(UPLOAD_FILE 时必需) |
reasoning | string | AI 对该操作的推理说明 |
Skyvern 支持的全部操作类型:
class ActionType(str, Enum):
# 基础交互
CLICK = "click" # 点击元素
INPUT_TEXT = "input_text" # 输入文本
SELECT_OPTION = "select_option" # 选择下拉选项
CHECKBOX = "checkbox" # 复选框操作
UPLOAD_FILE = "upload_file" # 上传文件
DOWNLOAD_FILE = "download_file" # 下载文件
# 鼠标操作
HOVER = "hover" # 鼠标悬停
DRAG = "drag" # 拖拽操作
MOVE = "move" # 鼠标移动
LEFT_MOUSE = "left_mouse" # 鼠标左键按下/抬起
# 键盘操作
KEYPRESS = "keypress" # 按键操作
# 页面控制
SCROLL = "scroll" # 页面滚动
GOTO_URL = "goto_url" # 跳转 URL
RELOAD_PAGE = "reload_page" # 刷新页面
CLOSE_PAGE = "close_page" # 关闭页面
WAIT = "wait" # 等待操作
# 特殊处理
SOLVE_CAPTCHA = "solve_captcha" # 解决验证码
# 任务状态
COMPLETE = "complete" # 任务完成
TERMINATE = "terminate" # 终止任务
NULL_ACTION = "null_action" # 空操作
EXTRACT = "extract" # 提取数据
为了让 LLM 更准确地理解页面元素,Skyvern 会附加额外的上下文信息:
「输入框/选择框上下文 (InputOrSelectContext)」:
{
"intention": "输入用户的邮箱地址", # 意图描述
"is_required": True, # 是否必填
"is_search_bar": False, # 是否为搜索栏
"is_location_input": False, # 是否为地址输入框
"is_date_related": True, # 是否与日期相关
"date_format": "YYYY-MM-DD", # 日期格式
"is_text_captcha": False # 是否为文本验证码
}
「点击上下文 (ClickContext)」:
{
"thought": "需要展开下拉菜单查看更多选项",
"single_option_click": False
}
以下是一个登录任务的完整 LLM 交互流程:
「第一轮:分析登录页面」
请求 Prompt(简化):
用户目标: 登录系统
当前 URL: https://example.com/login
页面元素:
[ID: 15] input type="email" placeholder="邮箱地址"
[ID: 18] input type="password" placeholder="密码"
[ID: 22] button "登录"
[ID: 25] a "忘记密码?"
LLM 响应:
{
"user_goal_stage": "在登录页面,准备填写凭据",
"user_goal_achieved": false,
"action_plan": "填写邮箱和密码,然后点击登录",
"actions": [
{"action_type": "INPUT_TEXT", "element_id": 15, "text": "user@example.com"},
{"action_type": "INPUT_TEXT", "element_id": 18, "text": "password123"},
{"action_type": "CLICK", "element_id": 22}
]
}
「第二轮:验证登录结果」
请求 Prompt(简化):
用户目标: 登录系统
当前 URL: https://example.com/dashboard
操作历史: [填写邮箱, 填写密码, 点击登录]
页面元素:
[ID: 5] div "欢迎回来,user@example.com"
[ID: 12] nav "仪表板 | 设置 | 退出"
LLM 响应:
{
"user_goal_stage": "已成功登录,进入仪表板页面",
"user_goal_achieved": true,
"action_plan": "登录完成,任务结束",
"actions": [
{"action_type": "COMPLETE", "reasoning": "页面显示欢迎信息,确认登录成功"}
]
}
Skyvern 同时发送 「页面元素列表」 和 「截图」 给大模型,是其核心设计理念,主要基于以下原因:
「1. 互补的信息来源」
信息类型 | 优势 | 局限 |
|---|---|---|
「DOM 元素列表」 | 精确的元素属性(ID、class、placeholder)、可操作性标识、结构化数据 | 无法感知视觉布局、看不到 Canvas/图片内容 |
「页面截图」 | 真实的视觉呈现、布局关系、视觉线索(颜色、图标) | 无法直接获取元素标识符用于自动化操作 |
「2. 解决传统方案的痛点」
纯 DOM 方案的问题:
纯截图方案的问题:
「3. 融合策略示意」
截图 ──────────────────────┐
│
┌───────────────────────▼───────────────────────┐
│ Vision LLM 推理 │
│ • 截图:理解页面布局、视觉语义、上下文关系 │
│ • 元素列表:获取精确的 element_id 用于操作 │
└───────────────────────┬───────────────────────┘
│
▼
输出: {action_type, element_id}
│
▼
Playwright 用 element_id 精确操作
「4. 实际效果对比」
以「点击提交按钮」场景为例:
[ID: 42] button class="btn-primary" → 页面上有 3 个这样的按钮,LLM 无法判断哪个是目标点击坐标 (856, 432) → 分辨率变化后坐标偏移,点击失败[ID: 42] → 输出 {action_type: "CLICK", element_id: 42} → Playwright 精确点击「5. 总结」
这种设计实现了 「「视觉理解」+「精确操作」」 的完美结合:
Skyvern 使用 「Playwright」 作为浏览器自动化引擎:
这是 Skyvern 与传统方案的核心区别:
Skyvern 支持多种 LLM 后端:
使用 Skyvern 的本地浏览器功能前,需要先搭建本地服务环境。Skyvern 提供了便捷的 quickstart 命令来简化这一过程。
# 使用 pip 安装
pip install skyvern
# 或使用 pipx 隔离安装(推荐)
pipx install skyvern
Skyvern 提供了 quickstart 命令,可以一键完成本地环境配置:
skyvern quickstart
启动过程中会提示选择运行模式:
? How would you like to set up Skyvern?
> local # 本地模式(推荐,可访问内网)
cloud # 云端模式
选择 「local」 后,继续配置:
? Do you want to set up PostgreSQL in Docker? (y/n)
> y # 首次运行选择 y,会自动拉取并启动 PostgreSQL 容器
? Do you want to configure LLM settings? (y/n)
> y # 配置 LLM API
Skyvern 需要连接支持视觉的 LLM。可以通过环境变量或 .env 文件配置:
# .env 文件
OPENAI_API_KEY=sk-your-openai-key
LLM_KEY=OPENAI_GPT4O # 使用 GPT-4o
如果使用第三方 LLM 代理服务(如内部 API 网关):
# .env 文件
ENABLE_OPENAI_COMPATIBLE=True
OPENAI_COMPATIBLE_API_BASE=https://your-llm-proxy.com/v1
OPENAI_COMPATIBLE_API_KEY=your-api-key
OPENAI_COMPATIBLE_MODEL_NAME=gpt-4o
OPENAI_COMPATIBLE_SUPPORTS_VISION=True
LLM_KEY=OPENAI_COMPATIBLE
# .env 文件
ANTHROPIC_API_KEY=your-anthropic-key
LLM_KEY=ANTHROPIC_CLAUDE3.5_SONNET
macOS 没有原生 Docker,需要使用 Docker Desktop 或轻量级替代方案 「Colima」:
# 安装 Colima
brew install colima docker
# 启动 Colima(分配 4GB 内存)
colima start --memory 4
# 验证 Docker 可用
docker ps
服务启动后,可以通过以下方式验证:
# 检查服务健康状态
curl http://localhost:8000/health
# 查看 API 文档
open http://localhost:8000/docs
本地服务启动后,会在终端输出 API Key:
✅ Skyvern is running!
API URL: http://localhost:8000
API Key: skyvern_xxxxxxxxxxxxxxxxx # 记录这个 Key
将 API Key 保存到 .env 文件中:
SKYVERN_API_KEY=skyvern_xxxxxxxxxxxxxxxxx
SKYVERN_BASE_URL=http://localhost:8000
# 停止服务
# 在运行 quickstart 的终端按 Ctrl+C
# 重新启动(已配置过)
skyvern quickstart
# 选择 local → n(不重新配置 PostgreSQL)→ n(不重新配置 LLM)
# 查看日志
tail -f ~/.skyvern/logs/*.log
from skyvern import Skyvern
import asyncio
asyncdef main():
# 初始化客户端
skyvern = Skyvern(api_key="your-api-key")
# 运行任务
result = await skyvern.run_task(
url="https://www.google.com",
prompt="搜索 'Skyvern AI' 并返回第一条结果的标题"
)
print(f"状态: {result.status}")
print(f"输出: {result.output}")
await skyvern.aclose()
asyncio.run(main())
Skyvern 支持在本地浏览器中执行任务,这对于访问内网系统、保持登录状态或调试非常有用。
from skyvern import Skyvern
import asyncio
asyncdef main():
# 连接到本地 Skyvern 服务
skyvern = Skyvern(
api_key="your-api-key",
base_url="http://localhost:8000"# 本地服务地址
)
# 启动本地浏览器(headless=False 可以看到浏览器窗口)
browser = await skyvern.launch_local_browser(headless=False)
print("✅ 本地浏览器已启动")
# 获取工作页面
page = await browser.get_working_page()
# 导航到目标网站
await page.goto("https://your-internal-site.com")
# 使用 browser_session_id 关联任务到当前浏览器
result = await skyvern.run_task(
prompt="登录系统并查看数据",
url="https://your-internal-site.com",
browser_session_id=browser.browser_session_id, # 关键:绑定到当前浏览器
wait_for_completion=True,
timeout=300
)
print(f"状态: {result.status}")
# 关闭浏览器
await browser.close()
await skyvern.aclose()
asyncio.run(main())
如果需要使用本地已安装的 Chrome 浏览器(保留登录状态、Cookie 等):
from skyvern import Skyvern
# 指定本地 Chrome 浏览器路径
browser_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"# macOS
# browser_path = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" # Windows
skyvern = Skyvern(
base_url="http://localhost:8000",
api_key="your-api-key",
browser_path=browser_path # 使用本地 Chrome
)
result = await skyvern.run_task(
prompt="在已登录的状态下查看订单历史"
)
Skyvern 还支持通过 Chrome DevTools Protocol (CDP) 连接到远程浏览器:
from skyvern import Skyvern
# 连接到远程浏览器(需要浏览器开启远程调试端口)
skyvern = Skyvern(
cdp_url="ws://remote-server:9222/devtools/browser/xxxxx"
)
result = await skyvern.run_task(
prompt="在远程浏览器中执行任务"
)
Skyvern 支持将 AI Agent 操作与传统 Playwright 操作混合使用:
# 启动浏览器
browser = await skyvern.launch_local_browser(headless=False)
page = await browser.get_working_page()
# 使用传统 Playwright 操作进行精确控制
await page.goto("https://example.com")
await page.fill("#username", "admin")
await page.fill("#password", "123456")
await page.click("#login-btn")
# 切换到 AI Agent 处理复杂逻辑
await page.agent.run_task("找到最近的订单并下载发票")
# 继续使用 Playwright 操作
await page.screenshot(path="result.png")
result = await skyvern.run_task(
url="https://news.ycombinator.com",
prompt="获取首页前5条新闻",
data_extraction_schema={
"type": "array",
"items": {
"type": "object",
"properties": {
"title": {"type": "string"},
"url": {"type": "string"},
"points": {"type": "number"}
}
}
}
)
# 复杂任务可以拆分为工作流
workflow = skyvern.create_workflow([
{"type": "navigate", "url": "https://example.com/login"},
{"type": "task", "prompt": "使用账号 test@example.com 登录"},
{"type": "task", "prompt": "导航到订单页面"},
{"type": "extract", "prompt": "提取所有订单信息", "schema": {...}}
])
这是 Skyvern 最大的优势。同一段代码可以在成千上万个不同的网站上运行:
# 同一个 Prompt,适用于任何登录页面
skyvern.run_task(prompt="使用用户名 admin 密码 123456 登录")
无论是 Google 登录、GitHub 登录还是企业内部系统,Skyvern 都能自动识别登录表单并完成操作。
网站改版不会导致自动化失效:
LLM 的推理能力使 Skyvern 能够处理传统脚本难以应对的场景:
不需要为每个网站编写和维护独立的脚本,一个通用的 Prompt 即可完成任务。
Skyvern 的效果直接取决于底层 LLM 的视觉理解能力:
每一步操作都需要:
相比传统脚本的毫秒级执行,Skyvern 单步操作可能需要数秒。
每次交互都会消耗 LLM Token:
LLM 的输出具有一定的随机性:
页面截图会发送到 LLM 服务端:
场景 | 是否推荐 | 原因 |
|---|---|---|
一次性数据采集 | ✅ 推荐 | 无需维护,快速完成 |
跨站点通用任务 | ✅ 推荐 | 泛化能力强 |
高频重复任务 | ⚠️ 谨慎 | 成本和速度问题 |
实时性要求高 | ❌ 不推荐 | 延迟较大 |
高安全要求 | ❌ 不推荐 | 截图传输风险 |
维度 | Skyvern | Tarsier |
|---|---|---|
「核心原理」 | 截图 → Vision LLM | DOM 标注 → 文本 LLM |
「LLM 要求」 | 必须支持视觉 | 任意文本模型即可 |
「执行速度」 | 较慢(图像处理) | 较快(文本处理) |
「定位精度」 | 视觉坐标(可能偏移) | DOM 元素(精确) |
「Token 消耗」 | 高(图像 Token) | 低(文本 Token) |
「开箱即用」 | ✅ 完整框架 | ⚠️ 需自行封装 |
维度 | Skyvern | 传统 RPA |
|---|---|---|
「开发效率」 | 高(自然语言) | 低(编写脚本) |
「维护成本」 | 低(自动适应) | 高(频繁更新) |
「执行速度」 | 慢 | 快 |
「稳定性」 | 依赖 LLM | 确定性强 |
「适用范围」 | 广(零样本) | 窄(单站点) |
随着多模态大模型的快速发展,视觉驱动的 Web Agent 方案正在变得越来越可行:
Skyvern 代表了 Web 自动化的一个重要演进方向,虽然目前还存在一些局限,但其设计理念和技术架构值得深入学习和借鉴。
「参考资料」: