Agent Engineering 课程阅读
首页/课程八 · GUI Agent/可靠性工程:GUI Agent 为什么脆

在 GitHub 查看原文

本页目录

Lesson 06 — 可靠性工程:GUI Agent 为什么脆

本课目标:给 L04 的 agent 加可靠性层——失败模式清单 + 循环检测(观察哈希)+ 重试预算 + 回退(back+重新规划)+ 人工接管点。用刁难页(假按钮/死链/相似按钮)量化加固前后成功率与步数。

学完你能回答:「GUI agent 为什么不可靠?你怎么加固?」——答案是失败模式清单 + 循环检测用观察哈希(连续 N 步不变→强制换策略)+ 回退 + 人工接管,加固前后有刁难页 before/after 数字。


0. L04 的 agent 在刁难页上有多惨

L04 的循环在 L00 的「友好页」上 5 步完成。但真实网站不友好——故意/无意设置各种陷阱。本课的 tricky.html 造了四类陷阱:

① 假按钮:三个「提交」按钮,只有蓝色 primary 的真响应,另两个点了没反应
② 死链:href="#" 的链接,点了不跳转(可能滚到顶)
③ 相似按钮:三个文本一样的按钮,agent 分不清该点哪个
④ 需点对才显内容:点对按钮才显示目标内容(点错永远拿不到)

裸 agent(L04)遇到这些会怎样?循环打转——连续点假按钮,观察不变,但 agent 不知道自己卡住了,继续点同一个假按钮,直到步数上限耗尽,任务失败。

🎯 核心认知:GUI agent 的脆不是「偶尔失败」,是「失败了自己不知道还在继续」。可靠性层的核心不是「防止失败」(失败不可避免),而是「检测到卡住并换策略」。这是从第七门课 Reflexion 学来的——失败不可怕,可怕的是失忆地重复失败。


1. 失败模式清单

失败模式 症状 检测信号 对策
① 等待不足 元素没找到就操作 TimeoutError auto-wait + 显式等待(L01)
② 元素没找到 选择器飘了/DOM 变了 Locator 超时 重新观察 + 换选择器
循环打转 连续点同一个没反应的元素 观察哈希连续 N 步不变 强制换策略(back/scroll/换元素)
④ 弹窗劫持 点不到下面的元素 点击后观察不变 + 有遮罩 检测遮罩并先关弹窗(L01)
⑤ 误点相似元素 三个「提交」点错 动作后观察无预期变化 回退 + 用更具体的选择器
⑥ 死链 href=# 点击不跳转 点击后 URL 不变 检测 URL 未变→换入口

本课重点治 ③ 循环打转(最常见最致命)和 ⑤ 误点相似元素(刁难页核心陷阱)。


2. 循环检测:观察哈希

连续 N 步(默认 N=2)观察的哈希不变 → 判定循环打转 → 强制换策略

为什么用观察哈希而不是动作哈希:agent 可能连续做不同动作但页面没变(点假按钮A→点假按钮B→点假按钮A,动作在变但观察不变)。观察哈希抓的是「页面状态没变」这个本质信号——不管 agent 怎么折腾,页面没动就是卡住了。

def detect_loop(history, n=2):
    """连续 n 步观察哈希相同 → 循环。"""
    if len(history) < n + 1:
        return False
    recent_hashes = [hash(h.full_obs) for h in history[-(n+1):]]
    # 最近 n 步(不含当前)的哈希全相同 → 打转
    return len(set(recent_hashes[:-1])) == 1

强制换策略

检测到循环后不是重试(重试就是继续打转),而是换策略

换策略动作 适用场景
back() 当前页是死胡同,后退到上一页重新选
scroll(down) 目标元素可能在视口下方
注入「你已卡住,请尝试其他元素」教训 让 LLM 重新决策(Reflexion 精神)
人工接管点 多次换策略仍卡住 → 暂停等人工(L07 安全主线联动)

🎯 这就是第七门课 Reflexion 的「失败→语言化教训→注入重试」在 GUI 场景的落地:循环检测 = 失败检测,换策略 + 教训注入 = 语言化反馈。区别是 Reflexion 重试整个任务,本课在循环内即时换策略——更轻量。


3. 流派对比:怎么发现卡住

流派 做法 取舍
① 硬编码规则检测(本课选它) 观察哈希/URL 不变/步数预算 → 规则触发换策略 ✅ 确定、零成本、实时;🚫 规则有限,覆盖不全
② 让 LLM 自己发现卡住 prompt 里问「你觉得自己卡住了吗」 ✅ 灵活;🚫 LLM 常常不自知(循环打转时它觉得自己在进步),贵
③ 混合(本课实现) 规则检测循环 + LLM 换策略决策 ✅ 规则抓信号、LLM 出策略,各取所长

选 ③ 的理由:规则检测快且确定(哈希比较零成本),但「换什么策略」需要理解任务上下文——这正是 LLM 擅长的。规则负责「发现」,LLM 负责「决策」,分工最优。


4. 可靠性层设计

本课给 L04 的 agent 循环加一层 ReliabilityLayer

agent 循环每步:
  1. observe → 拿观察
  2. 【可靠性层】检测循环(观察哈希连续 N 步不变)
     → 若循环:注入教训「你已连续 N 步卡在同一页面,请尝试 back()/scroll/换元素」
  3. think → LLM 决策(含教训时会被引导换策略)
  4. act → 执行
  5. 【可靠性层】检测动作效果(URL 变了吗/观察变了吗)
     → 若无效果且是死链/假按钮:记录,下次避开
  6. 重试预算:连续 K 次换策略仍卡住 → 人工接管点

与 frontier Reflexion 的接口

第七门课的 Reflexion 是「任务级重试」(整个任务失败后反思重来)。本课是「步级换策略」(单步卡住即时换)。两者接口:

  • 本课的「教训」可写入记忆(L01 MemoryStore),下次同类任务前 recall「这种页面容易卡在假按钮」——这是跨会话的可靠性进化。
  • 本课的步级换策略失败 K 次后,可升级为任务级 Reflexion 重试。

5. 落地清单

本课是可靠性课,无 research-assistant 代码改动(落地在 L09)。产出:

文件 说明
README.md(本文件) 失败模式清单 + 循环检测 + 换策略 + 流派对比
code.py ReliabilityLayer(循环检测+换策略+重试预算)+ 裸agent vs 加固agent 对照
test_pages/tricky.html 刁难页(假按钮/死链/相似按钮)
exercise.md 练习

验收

# 起 L00 + L06 本地服务
cd gui-agent-lessons/00_overview/test_pages && python -m http.server 8765 &
cd gui-agent-lessons/06_reliability/test_pages && python -m http.server 8767

cd gui-agent-lessons/06_reliability
python code.py

# 预期输出:
#  - 循环检测单测:连续2步观察哈希不变 → 检出循环 ✅
#  - 裸 agent 在刁难页:点假按钮打转,步数耗尽失败 ❌
#  - 加固 agent:检出循环→back/换元素→找到真入口→成功 ✅
#  - 对照表:加固后成功率↑、有效步数↑(不浪费在打转上)

⚠️ 需 playwright + L00/L06 本地服务。mock LLM 路径零 API。


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

  • 评估主线:本课产出「裸 agent vs 加固 agent」的 before/after 对照数字——这是 L08 mini-benchmark 和 L11 收益表「可靠性层收益」列的直接来源。没有本课的量化,「加固有用」就是感觉。循环检测单测也是评估主线的一部分(机制本身要可验证)。
  • 观察-行动接口主线:本课在循环里加了反馈层——观察不止喂给 think,还喂给可靠性层检测循环。这是观察空间的「二次利用」:一次观察既驱动决策,又驱动健康检测。行动也加了效果检测(动作后观察变了吗)——行动不再是「发了就完」,而是「发完看效果」的闭环。

🎯 面试话术

「GUI agent 脆在哪我有清单——等待不足/元素没找到/循环打转/弹窗劫持/误点相似元素/死链。最致命的是循环打转:agent 失败了自己不知道还在继续。我用观察哈希检测——连续 N 步页面没变就是卡住了,不是重试而是换策略(back/scroll/换元素),这是从 Reflexion 学来的步级换策略。加固前后在刁难页上有 before/after 数字:裸 agent 点假按钮打转失败,加固 agent 检出循环换入口成功。」