Skyvern 2.0 规划器:从一句话目标到自主编排
30 秒导读: README 说 Skyvern 用「a swarm of agents(一群 agent)」去理解网页、规划并执行——这句话的真身就是本章讲的
task_v2。它不是真有很多进程在跑,而是一个外循环:拿到你一句话的大目标,每一轮先看当前页面,让 LLM 规划「下一步该干的小目标」,把这个小目标当场物化成一个工作流 block(导航 / 提取 / 循环)去执行,再把结果塞回历史、进入下一轮,直到 LLM 判定目标达成或不可能。
本章建立在前面几章之上,只讲新增的那层自治外循环,不重复它们的内部:
- 感知(把网页变成 LLM 能读的东西)见 01-perception-scraper.md
- 单步 Agent 循环与动作规划见 02-agent-loop-planning.md
- 动作如何精确落到真实元素见 03-action-execution.md
- Block 如何编排多步骤见 05-workflow-blocks.md
1. 这是什么(零基础也能懂)
一句话定义: task_v2(代码里也叫 observer cruise / Skyvern 2.0)是一个会自己写工作流的 agent——你只给一句话目标,它自己一步步把工作流「生长」出来并执行完。
它和 task_v1 的区别,是本章的核心。 用一句话说清:
- task_v1(单任务): 你给一个明确任务("在这个页面上填表并提交"),它在固定的 step 循环里把这一件事做完。任务边界是你划的。
- task_v2(自主规划): 你给一个大目标("帮我在这三家店里比一款笔记本的价格,选最便宜的"),它自己决定要分成哪些小任务、每个小任务是什么类型、按什么顺序做。任务边界是它自己划的。
给谁用 / 解决什么问题: 给那种「一句话说得清、但拆成步骤很烦」的网页活。你不想(也没法)预先画好工作流图,就把大目标丢给它,让它边看边想边做。
用起来什么样: 对外就是一句话 + 可选的起始 URL。
# 示意,非源码:task_v2 对外的最小心智模型
run = skyvern.run_task(
prompt="在 example-shop 上找到评分最高的三款机械键盘,比较价格,返回最便宜的那款",
engine="skyvern-2.0", # 走 task_v2 规划器,而不是 v1 单任务
)
# 剩下的拆解、导航、提取、比较,全由规划器自己完成
一句话直觉: 把它想成一个边走边画流程图的人。他手上只有一句话目标,每走到一个新页面就 停下来想「下一格该画什么框」,画完一个框(block)就立刻执行,看结果,再想下一格——而不是像 v1 那样照着一张已经画好的图走。
本节不碰底层。记住一件事就够了:v2 = 在工作流引擎之上,加一层「LLM 动态生成下一个 block」的自治外循环。
2. 顶层全景(它大概怎么转)
2.1 一张图看懂外循环
规划器的心脏是 run_task_v2_helper 里的一个 for i in range(max_iterations) 循环(skyvern/services/task_v2_service.py:744)。每一轮(iteration)都做同样四件事:
一句话大目标 (user_prompt)
│
▼
┌─ ──────────────────────────────────────────┐
│ 每一轮 iteration(最多 50 轮) │
│ │
│ ① 观察 抓取当前页面 DOM + 截图 │
│ (scrape_website) │
│ │ │
│ ▼ │
│ ② 规划 LLM 读 页面+历史+大目标 │
│ → 判定 达成? 放弃? 还是 │
│ 下一个 mini goal + task_type │
│ (prompt: task_v2.j2) │
│ │ │
│ 达成 ──┴── 放弃 │
│ │ │ │
│ ▼ ▼ 否则继续 ▼ │
│ summarize terminate │
│ (完成) (终止) ③ 物化 │
│ 把 mini goal 变成 │
│ 一个 workflow block │
│ (_generate_*_task) │
│ │ │
│ ▼ │
│ ④ 执行 block │
│ (block.execute_safe) │
│ │ │
│ ▼ │
│ 结果写入 task_history │
│ 成功则再跑一次完成校验 │
│ (task_v2_check_completion) │
└──────────────────────────┬──────────────────┘
│ 回到 ① 进入下一轮
▼
达成 / 放弃 / 超步数 / 超轮数 → 终态
怎么读这张图: 从上往下是一轮的四步「观察 → 规划 → 物化 → 执行」,右边的分叉是三种提前退出(达成、放弃、超预算)。只要没退出,就回到 ① 重新观察,进入下一轮。
2.2 部件一句话职责
| 部件 | 干什么 | 在哪里 |
|---|---|---|
initialize_task_v2 | 建空工作流 + workflow run 的壳,把 task_v2 挂上去 | task_v2_service.py:261 |
initialize_task_v2_metadata | 开跑前先让 LLM 推断起始 URL 和工作流标题 | task_v2_service.py:384 |
run_task_v2 | 外层入口:铺好 SkyvernContext、兜底异常、收尾清理浏览器 | task_v2_service.py:481 |
run_task_v2_helper | 核心主循环:观察→规划→生成→执行,逐轮推进 | task_v2_service.py:604 |
_generate_navigation_task | 把「导航类」mini goal 物化成 NavigationBlock | task_v2_service.py:1725 |
_generate_extraction_task | 把「提取类」mini goal 物化成 ExtractionBlock | task_v2_service.py:1645 |
_generate_loop_task | 把「循环类」mini goal 物化成 ForLoopBlock | task_v2_service.py:1411 |
_generate_goto_url_task | 生成一个纯跳转的 UrlBlock(第 0 轮开局用) | task_v2_service.py:1763 |
handle_block_result | 看 block 执行结果,决定工作流 run 是否要取消 / 刷新状态 | task_v2_service.py:1316 |
mark_task_v2_as_* | 五个终态落库:completed / failed / terminated / canceled / timed_out | task_v2_service.py:1849+ |
task_v2.j2 | 规划 prompt:读页面判定下一步、是否达成、是否放弃 | forge/prompts/skyvern/task_v2.j2 |
task_v2_check_completion.j2 | 完成校验 prompt:每次 block 成功后复核大目标是否真达成 | forge/prompts/skyvern/task_v2_check_completion.j2 |
2.3 主线走一遍( 高层,不进代码)
- 你给一句话(+ 可选 URL)。
initialize_task_v2建一个空工作流——注意,此刻工作流里一个 block 都没有。 initialize_task_v2_metadata先让 LLM 猜「该从哪个 URL 开始、这活儿叫什么名字」。- 进入主循环。第 0 轮通常先生成一个跳转 block 把浏览器带到起始页。
- 之后每一轮:抓当前页 → 规划 prompt 吐出「下一个 mini goal + 它是 navigate/extract/loop 中的哪一种」→ 把它物化成对应 block 追加进工作流 → 执行 → 结果进历史。
- 规划 prompt 哪一轮说「user_goal_achieved=true」,就总结产出、收尾完成;说「should_terminate=true」就判定不可能、终止。
- 撞到轮数上限或步数上限,则失败收场。
关键认知:工作流是被这个循环一轮一轮「长」出来的,不是预先定义好的。 这就是 v2 相对 v1 的本质增量。