Agent Engineering 课程阅读
首页/课程八 · GUI Agent/行动空间:动作 DSL 与解析校验

在 GitHub 查看原文

本页目录

Lesson 03 — 行动空间:动作 DSL 与解析校验

本课目标:设计受限动作 DSL(click(n)/type(n, text)/scroll(dir)/back()/finish(answer)),手写解析器+校验器+执行器,理解「模型能说什么」决定安全与可控的上限。非法动作不崩溃,转结构化错误回注重试。

学完你能回答:「GUI agent 的行动空间该怎么设计——自由代码还是受限 DSL?为什么浏览器场景必须用 DSL?」——答案是受限 DSL:可校验、可白名单,非法动作转结构化错误回注,不是崩溃。


0. 行动空间 = 决定模型能对世界施加什么

L02 解决了「agent 怎么看见页面」(观察空间),本课解决「agent 怎么表达动作」(行动空间)。这是观察-行动接口的另一半:

观察空间(L02)              行动空间(本课)
───────────                  ───────────
页面 → 元素编号列表           元素编号 + 动作意图 → DSL → 执行
决定 agent 能感知什么         决定 agent 能操作什么
放什么进上下文窗口            允许说什么动作

🎯 核心认知:行动空间不是「让 LLM 自由发挥」这么简单——它是安全与可控的上限。行动空间越宽(自由代码),表达力越强但越危险;越窄(受限 DSL),越可控但越笨。浏览器场景里,一次错误的 click 可能跳到恶意域、提交了不该提交的表单——所以这里可控优先于表达力


1. 流派对比:三种行动空间

行动空间 形式 表达力 安全性 取舍
自由代码(CodeAct 式) LLM 输出 Python/JS 直接执行 最强 🚫 最差——浏览器场景能干任何事(跳转/提交/下载执行),一个恶意 prompt 就完蛋 适合沙箱完好的 Code Agent(第七门课 L06),不适合直接控浏览器
受限 DSL(本课选它) click(3) / type(3, "查询") / scroll(down) / back() / finish(答案) 够用 ✅ 可校验(编号存在吗/参数合法吗)+ 可白名单 + 可拦截敏感动作 表达力受限但浏览器任务用这几招就够
视觉坐标点击(视觉派专属,L05 讲) LLM 输出 (x, y) 坐标点击 所见即所得 🚫 难校验(坐标落在哪事先不知)、grounding 难 配合 SoM 标注变成「点编号」才可控

选 ② 的理由

  1. 可校验click(99) 当页只有 9 个元素时,校验器直接拒——编号不存在。自由代码做不到这种前置校验。
  2. 可白名单:动作集合固定(5 种),想加新动作必须改代码——天然限制了 LLM 的「越权冲动」。
  3. 可拦截敏感动作(L07 安全主线):DSL 是结构化的,click 的目标元素能查 href 是否在域名 allowlist——自由代码跳转了都不知道。

📖 SoM(Yang et al. 2023, arXiv:2310.11441)证明给图片画编号标注能让 VLM 精确指对象——视觉派的坐标点击一旦配合 SoM 编号,就退化成「点编号」,和本课的 DSL click(n) 同构。所以本课的 DSL 是文本/视觉两派的共同底座,L05 视觉路线会复用编号概念。


2. DSL 设计

5 个动作,覆盖浏览器任务的绝大多数操作:

动作 语法 语义 校验规则
click(n) click + 元素编号 点击编号 n 的元素 n 是正整数且 ≤ 当前页元素数
type(n, text) type + 编号 + 文本 清空后输入 text 到编号 n n 合法 + text 非空且 ≤ 长度上限
scroll(dir) scroll + up/down 页面滚动 dir ∈
back() back 无参 浏览器后退
finish(answer) finish + 答案文本 结束任务并提交答案 answer 非空

为什么是这 5 个:click/type 是核心交互,scroll 处理长页面(观察空间只看到视口内元素,要 scroll 才能看到更多),back 是回退(L06 可靠性用),finish 是任务终止信号(agent 循环的退出条件)。不设 goto(url)——直接跳转任意 URL 是安全风险(L07 域名 allowlist 才允许),导航通过点击链接完成。

DSL 文法(正则解析)

CLICK  = r'click\((\d+)\)'                    # click(3)
TYPE   = r'type\((\d+),\s*"(.*)"\)'           # type(3, "查询词")
SCROLL = r'scroll\((up|down)\)'               # scroll(down)
BACK   = r'back\(\)'                          # back()
FINISH = r'finish\((.*)\)'                    # finish(答案是...)

LLM 输出一行文本,解析器用正则匹配→生成结构化 Action 对象。匹配失败 = 非法动作。


3. 校验器 + 结构化错误回注

本课的灵魂设计:非法动作不是崩溃,是结构化错误回注给模型重试

LLM 输出: click(99)            (当前页只有 9 个元素)
  ↓ 解析: Action(type=click, idx=99)
  ↓ 校验: idx=99 > 9 → 非法
  ↓ 不抛异常,返回: {"ok": False, "error": "元素编号 99 不存在,当前页只有 1-9 号元素"}
  ↓ 错误信息喂回 LLM: 「上一步动作非法:...。请重新选择合法编号。」
  ↓ LLM 重试: click(3) ✅

为什么这样设计:

方案 后果
非法动作直接抛异常 agent 循环崩溃,任务中断——一次幻觉就全盘失败
非法动作静默忽略 LLM 不知道自己错了,继续犯同样的错
结构化错误回注(本课) LLM 得到反馈,自我纠正——这是 ReAct「观察回注」思想在错误处理上的应用

🎯 这和第七门课 Reflexion 的精神一致:失败不可怕,把失败的语言化反馈喂回去,下一轮就能改进。本课把它用在动作校验上——非法动作 = 一次小失败,错误信息 = 语言化反馈。


4. 执行器

校验通过的动作交给执行器,执行器调 L01 的 BrowserSession 落地:

动作 执行
click(n) 取 L02 elements[n-1] 的 selector → session.click(selector)
type(n, text) session.type(selector, text)
scroll(down/up) session.page.mouse.wheel(0, ±800)
back() session.page.go_back()
finish(answer) done=True,返回 answer

执行器也要兜底——元素可能已 detach(页面变了),捕获异常转结构化错误(和校验错误同构),让 L04 agent 循环统一处理。


5. 落地清单

本课是行动空间设计课,无 research-assistant 代码改动(落地在 L09)。产出:

文件 说明
README.md(本文件) 三种行动空间对比 + DSL 设计 + 校验回注 + SoM 导读
code.py DSL 解析器 + 校验器 + 执行器 + 单测(合法/非法/边界)
exercise.md 练习

验收

cd gui-agent-lessons/03_action

# DSL 单测(零 API,纯解析校验)
python code.py

# 预期输出:
#  - 合法动作解析:click(3)/type(3,"x")/scroll(down)/back()/finish(ans) 全部解析成功
#  - 非法动作:click(99)(编号越界)/foo(1)(未知动作)/click(abc)(参数非整数)→ 结构化错误
#  - 执行演示(需 playwright + L00 服务):click 进详情、finish 提取答案

⚠️ 单测部分零依赖纯 Python 可跑;执行演示需 playwright + L00 本地服务。


6. 本课在两条主线上的位置

  • 评估主线:本课不直接产指标,但「结构化错误回注」直接影响 L08 评估的「步数效率」——非法动作不崩、能重试,agent 才不会因一次幻觉就任务失败。L06 会量化「加固后非法动作重试成功率」,对照的就是本课的校验回注机制。
  • 观察-行动接口主线:本课是行动空间的正式开篇。L02 给了编号(观察),本课让编号成为动作目标(click(n))——观察→行动的桥梁在编号上合拢。L04 把 observe(L02)+ act(本课)装进循环,就是第一个完整 GUI agent。本课的「可校验可白名单」也是 L07 安全主线的伏笔——动作层拦截的前提是动作是结构化的 DSL 而非自由代码。

🎯 面试话术

「行动空间我用受限 DSL 而非自由代码——click(n)/type(n,text)/scroll/back/finish 五招,浏览器场景里可校验可白名单比表达力重要。非法动作不崩,转结构化错误回注重试,这是 Reflexion 的精神用在动作校验上。编号 n 直接用 L02 观察空间里的元素编号,观察→行动在编号上合拢。这层 DSL 也是 L07 安全的伏笔——动作结构化了才能在执行前拦截恶意跳转。」