跳到主要内容

Skyvern 全景:这是什么 / 怎么转 / 从哪读

30 秒导读: Skyvern 是一个用「大语言模型 + 计算机视觉」自动化浏览器工作流的开源引擎。 它最核心的一招:不写死 XPath/CSS 选择器——而是给页面上每个可交互元素临时打一个编号 unique_id,把「带编号的元素清单 + 截图」喂给 LLM,LLM 只回「点 id=42、往 id=17 里输入 'hello'」,Skyvern 再把编号翻译回真实的 Playwright 定位器去操作。页面改版、换皮肤都不影响, 因为编号是每次现抓的。本章只做导航与心智模型,不钻任何单一机制的实现(细节留给各章)。


1. 这是什么(零基础也能懂)

一句话定义: Skyvern 让你用自然语言描述一个网页任务(「登录后把发票下载下来」),它自己 看页面、决定点哪、填什么,一步步把任务做完。

它解决谁的什么问题。 传统的网页自动化(Selenium / Playwright 脚本)有个老毛病:你得为 每个按钮、每个输入框写死一条选择器(//div[@id='login-btn'])。网站一改版,选择器全失效,脚本 集体崩。Skyvern 把「找元素」这件事交给 LLM 的视觉与语义理解,于是:

传统脚本Skyvern
人工为每个元素写死 XPath/CSS运行时现抓元素、现打编号
页面改版 → 选择器失效 → 脚本崩页面改版 → 重新抓一遍就行
只能跑「见过的」固定页面没见过的页面也能试着操作
逻辑写在代码里逻辑写在自然语言目标里

给谁用。 想批量填表、抓数据、跑重复网页流程,又不想维护一堆脆弱选择器的人;需要「网页 Agent」能力嵌进自己产品的开发者。

用起来什么样。 公共入口是 from skyvern import Skyvern(见 skyvern/__init__.py:15 的 lazy import),两个主方法 run_task(一次性任务)和 run_workflow(编排好的多步流程):

# 示意,非源码。真实签名见 skyvern/library/skyvern.py:294 run_task / :351 run_workflow
from skyvern import Skyvern

skyvern = Skyvern(api_key="...") # 或 Skyvern.local() 连本地引擎
run = await skyvern.run_task( # 给一句话目标 + 起始网址
prompt="登录并把最近一张发票下载下来",
url="https://example.com/login",
)
print(run.status, run.output) # 跑完拿状态和产出

一句话直觉。 把它想成一个「只会看屏幕、不会读源码」的实习生:你把网页截图和一份 「屏幕上有哪些可点的东西(每个都编了号)」的清单递给他,他指着说「点 3 号、往 8 号里打字」, 你替他真去点。他永远不需要知道那个按钮的 HTML 长什么样。

本节不出现底层代码细节;目标只是让完全没接触过的人知道「这东西是干嘛的」。


2. 顶层全景(它大概怎么转)

2.1 三层主干 + 三个横切子系统

Skyvern 的骨架是一条感知 → 决策 → 执行的流水线,外面套三个横切子系统。先看这张图 (从上往下是一次「思考-行动」的数据流向):

┌─────────────────────── 横切子系统 ───────────────────────┐
│ │
│ ①多引擎 ②工作流引擎 ③规划器 │
│ RunEngine workflow blocks task_v2 │
│ (选哪种大脑) (Block 编排多步) (一句话→自主拆解) │
│ │
└────────────┬──────────────┬───────────────┬─────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 真实网页 │─抓─▶ │ 感知层 │─喂─▶ │ 决策层 │─出─▶ │ 执行层 │
│ (Playwright │ │ webeye/ │ 元素 │ forge/ │ 动作 │ webeye/ │
│ 控制的浏览器)│ ◀─操作───────────────────────────────────────── │ actions/ │
└──────────────┘ │ scraper │ 清单 │ agent.py │ JSON │ handler.py │
▲ │ +截图 │ +截图│ (Agent 循环) │ │ (动作分发) │
└──────────────┴──────────────┴──────┴──────────────┴──────┴──────────────┘
操作真实浏览器(点/填/滚)后回到「抓」,循环

怎么读这张图: 中间那条横带(网页 → 感知 → 决策 → 执行 →(操作)→ 网页)是主血管, 一次 step 就是走一遍;上方三个横切子系统决定「用哪种大脑」「要不要编排成多步」「要不要先自动 拆任务」。

2.2 部件一句话职责

部件干什么在哪(文件 : 符号)
感知层 · 抓取器unique_id、抽可交互元素树、截图skyvern/webeye/scraper/scraper.py:190 scrape_website
感知层 · 页面模型装抓取结果:id→元素/CSS/frame 的映射表skyvern/webeye/scraper/scraped_page.py:170 ScrapedPage
决策层 · Agent中央循环:建 prompt → 调 LLM → 拿动作 → 逐个执行skyvern/forge/agent.py:355 ForgeAgent
决策层 · 单步一次 step 的完整逻辑skyvern/forge/agent.py:1316 agent_step
执行层 · 分发器按动作类型查表,调对应 handlerskyvern/webeye/actions/handler.py:1231 ActionHandler
执行层 · id→定位器unique_id 翻译回真实 Playwright 定位器skyvern/webeye/utils/dom.py:52 resolve_locator
①多引擎六种「大脑」的枚举与分岔skyvern/schemas/run_enums.py:14 RunEngine
②工作流引擎30 种 Block 类型编排多步skyvern/schemas/workflows.py:417 BlockType
③规划器Skyvern 2.0:一句话目标自主拆成子任务skyvern/services/task_v2_service.py:481 run_task_v2
公共入口from skyvern import Skyvern 的 SDK 门面skyvern/library/skyvern.py Skyvern.run_task / run_workflow

2.3 主线走一遍(高层,不进代码)

一次 step(Agent 的一个「思考-行动」回合)端到端是这样的——这条线是理解整个项目的钥匙, 每一环的实现细节分别落在 01/02/03 章:

①抓取页面 ②喂给 LLM ③拿到动作 JSON ④落到真实元素 ⑤判断是否完成
───────── ───────── ───────────── ───────────── ─────────────
给每个可交互元素 把「元素树 + 截图」 LLM 回一段 JSON: 按 element_id 查 问 LLM/看信号:
打 unique_id, 连同任务目标塞进 [{action:click, id_to_css_dict, 目标达成了吗?
抽成精简元素树, prompt,发给大模型 element_id:42}, resolve_locator 没完 → 回①抓新页
顺手截一张图 {input,id:17,...}] 翻成 Playwright 面,进下一 step
定位器,真去点/填
│ │ │ │ │
scrape_website build_and_record parse_actions ActionHandler check_user_goal
(scraper.py) _step_prompt + (parse_actions.py) .handle_action _complete /
LLM 调用(agent.py agent.py:1606 (handler.py) complete_verify
:1514) (agent.py)

一句话概括这条线:看(抓页面)→ 想(问 LLM)→ 做(执行动作)→ 查(是否完成),不完成就再看一遍。 所谓「不写死选择器」的魔法,全压在 ①的「打编号」和 ④的「按编号翻译回定位器」这一对上。


3. 阅读地图(从哪读、怎么读)

建议由浅入深按 01 → 06 顺序读。每章一句话导览:

顺序章节一句话导览何时读它
101-perception-scraper.md感知层:domUtils.js 怎么给元素打 unique_idscrape_website 怎么把一页网页压成「元素树 + 三张映射表 + 截图」想懂「LLM 到底看到了什么」
202-agent-loop-planning.md决策层:execute_step / agent_step 的单步循环——建 prompt、调 LLM、解析动作、逐个执行、验证完成想懂「Agent 一个回合怎么转」
303-action-execution.md执行层:ActionHandler 分发表、resolve_locator 把 id 翻回定位器、点击/输入/下拉的容错技巧想懂「模型说的话怎么精确落地」
404-engines-cua.md引擎家族:DOM 规划器(skyvern-1.0)vs 计算机使用 CUA(OpenAI/Anthropic/UI-TARS)——同一循环里 agent_step 如何按 RunEngine 分岔想懂「除了看 DOM 还能怎么驱动」
505-workflow-blocks.md工作流引擎:30 种 BlockType(导航/提取/循环/条件/发邮件…)如何拼成多步流程、参数怎么在 Block 间流转想编排复杂的多步任务
606-skyvern2-planner.md2.0 规划器:task_v2 如何把一句话大目标自主拆成一串导航/循环/提取子任务并自动编排想懂「一句话怎么变成一整套流程」

只想快速抓重点? 读完本章 §2 的三张图 + §4 的巧妙之处,已经能讲清「Skyvern 是什么、 核心原理是什么」。要动手改代码再按上表下钻。


4. 巧妙之处(读者该带走的精华)

这几条是 Skyvern 区别于「又一个 Playwright 封装」的设计决策,每条给出可核对的锚点:

  1. 用临时 unique_id 当「模型 ↔ 真实 DOM」的握手协议。 抓取时 domUtils.js 给每个元素 element.setAttribute("unique_id", ...) (skyvern/webeye/scraper/domUtils.js:1660-1662);常量定义 SKYVERN_ID_ATTR = "unique_id"(skyvern/constants.py:5)。LLM 全程只跟这个编号打交道, 执行时 resolve_locator 再用 [unique_id='42'] 选择器把它翻回 Playwright 定位器 (skyvern/webeye/utils/dom.py:52)。这是「不写死 XPath」的技术底座。

  2. 动作分发用「注册表」而非巨型 if-else。 ActionHandler 维护一张 _handled_action_types 字典,靠 register_action_type 把 30 种 动作(点击/输入/下拉/上传/滚动/切标签…)挂进去 (skyvern/webeye/actions/handler.py:4015-4040)。加新动作 = 加一行注册,不动分发主干。

  3. 一套 agent_step 循环喂多种「大脑」。 同一个 agent_step 内按 RunEngine 分岔:DOM 规划走 LLM 出 JSON 动作,CUA 引擎 (OpenAI/Anthropic/UI-TARS/Yutori)走各自的原生「计算机使用」协议 (skyvern/forge/agent.py:1449-1485;引擎集合 CUA_ENGINESskyvern/schemas/run_enums.py:23)。换模型家族不用重写循环。

  4. CUA 任务自动关掉「完成验证」以省钱提速。 一个不显然的细节:execute_step 里若 engine in CUA_ENGINES 就强制 complete_verification = False——因为 CUA 每步只出一个动作、极少幻觉「假装完成」,再跑一次 验证纯属浪费(skyvern/forge/agent.py:656-657)。

  5. 两层任务模型:一步循环(task_v1) + 会自主拆解的规划器(task_v2)。 task_v2 不直接操作页面,而是像项目经理:把大目标拆成导航/循环/提取等子任务再交给底层循环去跑 (_generate_navigation_task / _generate_loop_taskskyvern/services/task_v2_service.py:1725 / :1411)。这就是「Skyvern 2.0」自主编排的由来。


5. 边界与心智模型提醒

  • 它「看」的是抽出来的元素树 + 截图,不是完整 HTML。 抓取阶段会裁剪(trim_element_tree)、 只留可交互与可见元素(skyvern/webeye/scraper/scraper.py)。所以模型看不到被过滤掉的东西—— 这是能力也是盲区,细节见 01 章。
  • unique_id 是每次抓取现打的、易变的。 正因为易变,它对页面改版鲁棒;但也意味着编号不能跨 step 复用——每个 step 都要重抓。
  • 不是所有引擎都「读 DOM」。 CUA 系(计算机使用)更接近「纯看截图点坐标」,DOM 规划器才依赖 元素树。选哪种由 RunEngine 决定,取舍见 04 章。

本章到此为止只给全景与心智模型;任何一环「具体怎么实现」都请翻对应章节。


6. 总代码地图(agent 的跳转表)

覆盖全项目关键文件与真实符号名,按「感知 → 决策 → 执行 → 横切 → 入口」排;行号会漂,优先用 符号名 grep 定位

主题文件路径符号名
抓页面 · 主入口skyvern/webeye/scraper/scraper.pyscrape_website / scrape_web_unsafe
抓页面 · 元素树构建skyvern/webeye/scraper/scraper.pyget_interactable_element_tree / trim_element_tree
打 unique_id(浏览器内 JS)skyvern/webeye/scraper/domUtils.jsbuildElementTree 区(setAttribute("unique_id", …))
id 属性常量skyvern/constants.pySKYVERN_ID_ATTR
页面模型 · 映射表skyvern/webeye/scraper/scraped_page.pyScrapedPage(id_to_element_dict / id_to_css_dict / id_to_frame_dict)
决策 · Agent 类skyvern/forge/agent.pyForgeAgent
决策 · 外层执行skyvern/forge/agent.pyexecute_step
决策 · 单步循环skyvern/forge/agent.pyagent_step
决策 · 建 prompt + 截图skyvern/forge/agent.pybuild_and_record_step_prompt
决策 · 完成判断skyvern/forge/agent.pycheck_user_goal_complete / complete_verify
动作解析(JSON→Action)skyvern/webeye/actions/parse_actions.pyparse_actions / parse_cua_actions / parse_anthropic_actions
执行 · 分发器skyvern/webeye/actions/handler.pyActionHandler.handle_action / register_action_type
执行 · 典型 handlerskyvern/webeye/actions/handler.pyhandle_click_action / handle_input_text_action / handle_select_option_action
执行 · id→定位器skyvern/webeye/utils/dom.pyresolve_locator / SkyvernElement
动作类型枚举skyvern/webeye/actions/action_types.pyActionType
①多引擎枚举skyvern/schemas/run_enums.pyRunEngine / CUA_ENGINES
②工作流 Block 类型skyvern/schemas/workflows.pyBlockType
②工作流 Block 实现skyvern/forge/sdk/workflow/models/block.pyBlock 各子类
③2.0 规划器skyvern/services/task_v2_service.pyrun_task_v2 / run_task_v2_helper
③规划器 · 子任务生成skyvern/services/task_v2_service.py_generate_navigation_task / _generate_loop_task / _generate_extraction_task
公共 SDK 入口skyvern/library/skyvern.pySkyvern.run_task / Skyvern.run_workflow / Skyvern.local
浏览器驱动 · 状态skyvern/webeye/browser_state.pyBrowserState
浏览器驱动 · 工厂skyvern/webeye/browser_factory.pyBrowserFactory