Agent Engineering 课程阅读
首页/课程八 · GUI Agent/最小 GUI Agent:文本路线跑通

在 GitHub 查看原文

本页目录

Lesson 04 — 最小 GUI Agent:文本路线跑通

本课目标:把 L02 观察 + L03 动作装进 observe→think→act 循环,跑通第一个会操作浏览器的 agent。手写循环 + prompt 设计 + 上下文滑动窗口裁剪 + mock LLM 路径保证零 API 可跑。

学完你能回答:「browser agent 的循环和手写 ReAct 有什么同与异?」——同构是 observe→think→act,差异是观察是活页面(每步变)、动作改变环境状态、每步观察很大要做滑动窗口裁剪。


0. 从「手」到「手脑协同」

L01 给了稳的手(BrowserSession),L02/L03 给了观察和行动的接口。但它们是被动的——要人调用。本课把它们装进一个主动循环:agent 自己看页面、自己决定动作、自己执行,循环到任务完成。这是第一个完整的 GUI agent。

        ┌─── observe(L02 page_to_obs)─────────┐
        │  页面 → 元素编号列表 + 正文摘要          │
        ↓                                        │
   think(LLM:任务+历史+观察+动作表 → 输出 DSL)  │
        │                                        │
        ↓                                        │
   act(L03 parse+validate+execute)─────────────┘
        │
        ↓
   观察变化 → 回到 observe(直到 finish 或步数上限)

🎯 核心认知:这个循环和第七门课手写的 ReAct 循环同构——都是 think→act→observe 的迭代。但有两个本质差异,决定了 GUI agent 的循环要专门设计:观察是「活页面」而非工具返回值,动作会「改变环境状态」而非只读。


1. 与手写 ReAct 的同构与差异

维度 ReAct(agent-lessons/03) GUI Agent 循环(本课)
循环结构 think→act→observe observe→think→act→observe
观察来源 工具返回值(静态字符串) 活页面(每步重新生成,操作后会变)
动作效果 调工具拿信息(只读多) 改变环境状态(点击导航、输入改变表单)
观察体积 小(工具返回几百字) 大(页面元素列表+摘要,数百 token/步)
终止条件 工具返回答案 finish(answer) 或步数上限
上下文膨胀 轻(每步观察小) 重(每步观察大,N 步后窗口爆)

两个差异的处理

  1. 活页面:每步 page_to_obs() 重新生成观察,编号每步重分配(L02 已处理)——agent 不能跨步引用旧编号,每步都看新观察决策。
  2. 上下文膨胀:每步观察几百 token,10 步就几千——必须做滑动窗口裁剪(本课核心工程点)。

2. 上下文滑动窗口裁剪

这是 GUI agent 循环最关键的工程决策,也是上下文工程主线在本课的直接体现:

全量保留(天真版):               滑动窗口(本课):
步1观察(300tok)                    步1观察(300tok)  ← 全量
步2观察(300tok)                    步2观察(300tok)  ← 全量
步3观察(300tok)                    步3观察(300tok)  ← 全量(最近 N=3 步全量)
...                                步1观察 → 压缩为「步1: click(3) 点击了链接A」(摘要)
步10观察(300tok)                   步2观察 → 压缩为「步2: type(1,"x") 输入了查询」(摘要)
= 3000 tok 全塞,后期窗口爆        = 最近3步全量 + 更早步只留动作摘要

策略: - 最近 N 步(默认 N=3)保留完整观察(元素列表+摘要)——agent 需要近期上下文做决策。 - 更早的步只保留动作摘要步k: click(3) → 点击了链接A)——丢弃大观察,留动作轨迹。 - 任务描述 + 可用动作表始终在 prompt 头部——不裁剪。

🎯 这就是第七门课「上下文工程」母题在 GUI 循环的落地:窗口有限(上下文预算),观察大(信息多),必须取舍——留近期全量、压早期为摘要。和 RAG 的 top-k 截断、记忆的衰减策略是同构问题。


3. Prompt 设计

每步给 LLM 的 prompt 结构:

【任务】对比 LangGraph 最近 release 的版本号和日期

【可用动作】
  click(n)      点击编号 n 的元素
  type(n, text) 在编号 n 输入 text
  scroll(dir)   滚动(up/down)
  back()        后退
  finish(答案)  完成任务,提交答案

【动作历史】
  步1: click(2) → 点击了「LangGraph 发布列表」
  步2: click(7) → 点击了「下一页」(翻到第2页)
  步3: click(3) → 点击了「LangGraph-v0.8.0」(进详情)

【当前观察】(最近一步,全量)
  可交互元素:
  [1] link "返回列表"
  [2] textbox "搜索框"
  ...
  正文摘要:LangGraph-v0.8.0 发布于 2024-08-15...

【你的动作】(输出一个动作,如 click(3) 或 finish(答案))

LLM 输出一行 DSL → L03 解析校验执行 → 观察变化 → 下一轮。

步数上限与 finish

  • 步数上限(默认 max_steps=12):防死循环。达到上限强制以当前最佳答案 finish。
  • finish 触发:LLM 觉得拿到了答案就 finish(答案),循环退出。这是 agent 自主判断的终止信号。

4. mock LLM 路径:零 API 可跑

任务书硬约束:测试不打真实 API。本课的 agent 循环必须能离线跑。设计:

  • MockLLM:预录动作序列,按步数返回对应的 DSL 字符串。模拟「一个会做任务的 LLM」。
  • 真实 LLM 路径ChatZhipuAI(glm-4),有 API key 且 --real 时启用。
  • 两条路径接口一致(都接收 prompt 返回文本),agent 循环不感知差异。
MockLLM 预录(针对「翻页取证」本地任务):
  步1: type(3, "LangGraph")      输入搜索词
  步2: click(4)                   点搜索按钮
  步3: click(7)                   翻第2页(点页码2链接)
  步4: click(3)                   点第1条结果进详情
  步5: finish(版本 v0.8.0, 日期 2024-08-15, ...)  提取完字段,finish

🎯 这保证 CI/离线/无 API key 都能验证「循环本身跑得通、上下文裁剪生效、步数控制工作」。真实 LLM 跑出的动作序列可能不同(模型可能多翻几页、先点错再纠正),但循环机制一致——这正是「机制验证用 mock、效果验证用真实」的分工。


5. 落地清单

本课是 agent 循环课,无 research-assistant 代码改动(落地在 L09)。产出:

文件 说明
README.md(本文件) ReAct 同构差异 + 滑动窗口 + prompt 设计 + mock 路径
code.py 最小 GUI agent 循环 + MockLLM + 滑动窗口 + 本地任务跑通
test_pages/ 复用 L00/L01 的页(code.py 指向它们)
exercise.md 练习

验收

# 起本地服务(L00 的 search/detail)
cd gui-agent-lessons/00_overview/test_pages && python -m http.server 8765

cd gui-agent-lessons/04_text_agent

# mock 路径(零 API,CI 可跑)
python code.py

# 预期输出:
#  - agent 循环 5 步完成本地任务:搜索→翻页→进详情→finish 提取答案
#  - 每步打印:观察摘要 + think 输出的动作 + 执行结果
#  - 滑动窗口裁剪可见:步4时步1的观察已压缩为动作摘要
#  - 任务成功:finish 的答案含版本号+日期

⚠️ 需 playwright + L00 本地服务。mock 路径零 API;--real 路径需 ZHIPUAI_API_KEY


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

  • 评估主线:本课产出了第一个「可跑通的 agent」——L08 mini-benchmark 评的就是这个循环(及 L06 加固版)的「成功率/步数」。本课的验收「本地任务跑通」是评估主线有了被评对象。mock 路径保证评估可复现(不依赖真实 LLM 的随机性)。
  • 观察-行动接口主线:本课把观察(L02)和行动(L03)装进循环合拢——observe→think→act 的闭环在此首次跑通。编号在循环里流转:观察生成编号→think 引用编号→act 执行编号。滑动窗口裁剪是上下文工程主线在循环内的落地——「窗口有限,留近期全量、压早期摘要」。

🎯 面试话术

「browser agent 循环我手写过——和 ReAct 同构但观察是活页面、动作改变环境状态。最关键的工程是上下文滑动窗口:每步观察几百 token,10 步就爆,我做最近 N 步全量+更早步只留动作摘要的裁剪。mock LLM 路径保证零 API 可复现验证循环机制,真实 LLM 验证效果。这是上下文工程母题在 GUI 循环的直接落地。」