摘要: OpenAI 说 agent 能操作界面了,我没光看热闹,在隔离沙箱里搭了个假后台,让 agent 自己去批量调价。结果它跑得飞快,对账时发现零星几个商品库存被清零------它把"编辑"点成了"删除",还把确认弹窗当成了流程下一步。本文复盘完整事故链路,拆三个根因(视觉噪声、上下文缺失、默认猜测),给出三层护栏方案和适用边界。
文章目录
-
- 一个认真的沙箱实验
- 现象:跑得飞快,日志一片绿
- 排查:回放每一步点击
- [根因:界面是给人眼设计的,agent 在猜](#根因:界面是给人眼设计的,agent 在猜)
- 三层护栏:我后来怎么修的
- 什么时候可以放手
- 总结
一个认真的沙箱实验
上一篇文章刚聊完 OpenAI 说 agent 能操作界面 这回事。光看发布页不过瘾,我决定自己上手验证一下------在隔离沙箱里搭了个假后台,让 agent 自己进去干活。
这里先交代清楚:是沙箱,不是生产。所有数据都是假的,任务也是我自己布置的,就是为了看 agent 操作界面到底靠不靠谱。事实证明,这个决定非常正确。
现象:跑得飞快,日志一片绿
假后台是个典型的商品管理页:表格、每行有编辑和删除按钮、顶部有批量操作入口。任务很简单:把列表里一小批商品的价格整体上调一成。
agent 干得确实快,几分钟就把活"干完"了。页面日志一片绿,没有一条报错。我当时心里还嘀咕:computer use 这波可能真成熟了。
真正让我冷汗下来的是对账环节。我按沙箱的备份一对比,发现零星几个商品的库存变成了 0------不是我让它改的价格,是库存,被清零了。
排查:回放每一步点击
沙箱的好处是可以完整回放。我把 agent 的操作记录翻出来,一行一行看,最后定位到这样一条链路:
text
列表页(每行两个按钮:编辑 / 删除)
↓ agent 的点击坐标错位
点到相邻行的"删除"按钮
↓ 系统弹出确认框:"确定删除该商品?"
agent 把弹窗里的"确认"当成流程的下一步
↓ 直接点了
该行被删除,库存显示归零
问题出在两个环节的叠加:点错了行,以及把确认弹窗当成了必经流程。

第一个错,回放里看得很清楚------agent 的点击坐标落到了相邻行的"删除"按钮上。我猜是截图里表格行距不够,按钮靠得近,模型按坐标定位时偏了一格;第二个错更隐蔽------在 agent 眼里,弹窗里的"确认"就是"继续",它不知道这一步不可逆。
根因:界面是给人眼设计的,agent 在猜
复盘完这条链路,我最大的感触一句话:界面是给人眼设计的,agent 在猜。
拆开说,有三个机制放大了这次事故:
视觉噪声。 截图里全是角标、状态色、装饰性元素,模型分不清哪些是数据、哪些是摆设。两个按钮之间靠间距和 hover 状态区分,这些对 agent 来说都是弱信号。
上下文缺失。 我看到"删除"两个字,脑子里会自动补全"删了不可恢复,这是生产数据,不能乱点"。agent 没有这个上下文,它看到的只是两个名字相似的按钮。
默认猜测。 当界面不提供机器可读信号时,模型只能按普遍网站的常见布局去猜。单按钮弹窗在绝大多数网站里是"下一步"的意思,于是它就点了。
说白了:人眼会再三确认的高危按钮,agent 按概率就点了。
这个差异,拿人和 agent 摆在一起看更清楚:
| 维度 | 人 | agent |
|---|---|---|
| 视觉理解 | 靠颜色、间距、经验判断按钮层级 | 靠截图 + 可访问性树"猜" |
| 歧义处理 | 不确定时会犹豫、会再看一眼 | 按普遍网站的习惯默认猜测 |
| 按钮意图 | 结合业务上下文理解"删除"的后果 | 只读到两个名字相似的按钮 |
| 确认行为 | 高危操作会反复确认 | 单按钮弹窗 = "下一步",直接点 |
三层护栏:我后来怎么修的
翻车之后,我在沙箱里给界面加了三层护栏,然后让 agent 重新跑同一个任务,同一套护栏下连着重跑了好几轮,没再出现同类误点。
第一层:写操作走结构化通道。 批量调价这种事,直接给 agent 一个 API,让它提交"商品 ID + 新价格"的 JSON,别让它逆着 UI 去点。这样 agent 根本碰不到那些高危按钮。
json
// 给 agent 的结构化通道:批量调价
POST /api/products/batch-price
{
"items": [
{ "productId": "P1001", "pricePercent": 10 },
{ "productId": "P1002", "pricePercent": 10 }
]
}
运行结果:
text
HTTP 200
{ "updated": 2, "failed": 0 }
agent 不再需要"看"界面,也就不存在点错行的问题------这是最彻底的一层护栏。
边界说清楚:这个方案的前提是系统有可用的结构化接口。很多老后台没有,只能从第二层做起。
第二层:高危控件加机器可读信号。 给删除按钮补语义,让 agent 在可访问性树里能读到"这是删除、不可逆"。这一步主要依赖 WAI-ARIA 1.2 规范里的 aria-label、role 这类属性:
html
<button
class="row-action"
aria-label="删除商品(不可逆,需二次确认)"
data-action="delete"
data-irreversible="true"
>
删除
</button>
运行结果(可访问性树视角):
text
button name="删除商品(不可逆,需二次确认)"
data-action="delete"
data-irreversible="true"
配合第一层,agent 至少能从"读到的信息"里知道:这一步不可逆。
第三层:流程强制人工确认。 凡是写操作,agent 只能"准备好",不能"直接执行"。它把操作清单列出来,人来点最后的确认。
这一层的实现不复杂,但很反直觉:它把 agent 的工作流从"一路跑到底"改成"分段交棒"。agent 每完成一个批次的准备动作,就停下来等一个确认信号;人看过清单点一下"执行",它才继续下一个批次。整个沙箱环境里我用的浏览器是 Playwright 1.49 驱动,agent 的操作对象是本地起的演示后台(不连任何真实数据库),确认信号走的是页面上的一个模拟闸门按钮。
这层最丑最慢,但最可靠。OpenAI 在 Codex 里也是这么干的------默认带 auto-review 和确认策略,官方都默认"agent 不能裸操作"。
什么时候可以放手
三层做完,我要划一条边界,免得矫枉过正:
只读操作可以放手。 查询、搜索、分析数据这类不改状态的活,让 agent 自己跑,出错了重来就行。
写操作必须人盯。 改数据、删数据、发消息、转账,任何不可逆动作,至少留一层人工确认。等 agent 的可靠性和你的监控能力证明它值得信任了,再逐步放权。
沙箱永远是验证场。 想让 agent 操作界面,先在隔离环境跑几轮,看它在哪里犯错。我这次要是直接在真系统上试,清零的就不止是沙箱里的假库存了。
总结
这次沙箱演练把"为 AI 设计界面"从概念变成了刚需。
上一篇我说语义层是"让 AI 看得懂",当时还是推论;这次翻车是实证------界面在可访问性树里长什么样,直接决定 agent 会不会点错。以后谁跟我说"agent 操作界面已经很成熟了",我就把这次的回放日志给他看。