ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CUA智能体实战:从像素到点击的界面操作自动化

CUA智能体实战:从像素到点击的界面操作自动化 我调试过最让人窒息的一个Bug是我自研的CUA在自动登录时连续四次把账号密码填进了隔壁的注册表单。明明提示词里写了“点击登录”屏幕上也有巨大的“登录”按钮模型就是执着地认定了另一个长得几乎一模一样的输入框。那一整晚我都在想同一个问题所谓“会操作电脑的智能体”难点其实从来不在听懂人话而在于像人一样真正读懂了屏幕并且能在千变万化的界面里稳住手脚。这篇内容围绕的“CUA”指的就是 Computer Using Agent计算机使用代理。它和普通聊天式Agent最关键的区别是它不再只对着对话框输出文字而是直接拥有“眼睛”和“手”——通过看屏幕截图理解当前界面状态再通过模拟鼠标键盘动作去操作真实软件。最适合读这篇文章的人是准备做AI自动化、正在研究智能体落地、或者被RPA脆弱性坑到想换方案的朋友。我会尽量把原理、选型、翻车细节和可复现代码都写清楚。1. CUA为什么突然走红它正好补上了“图形界面”这块硬骨头1.1 智能体一直缺的那层“眼”和“手”过去两年各类Agent框架层出不穷但大部分人做的还是“API型智能体”给模型接上搜索引擎、数据库、代码解释器模型通过工具调用完成任务。这套玩法的上限很明显——工具暴露多少它就只能干多少活。而现实世界里大量软件根本没有开放API尤其是企业内部系统、老旧桌面软件、银行柜面、设计工具它们唯一的交互出口就是图形界面。RPA机器人流程自动化曾经想解决这个问题。我早年也用过几款主流RPA工具思路基本都是录制操作轨迹、固定元素选择器、按既定路径回放。问题在于只要界面版本一更新按钮位置稍作调整脚本马上就崩维护成本高到离谱。说白了RPA追求的是“流程固定”而真实用户界面的唯一不变之处就是“永远在变”。CUA走的是另一条路它不依赖固定的坐标或元素ID而是依赖视觉理解。模型看到截图能认出“这是一个登录按钮”能判断“弹窗遮住了表单”能理解“这个下拉框当前选中了错误的选项”——然后才决定下一步动作。这种能力来自多模态大模型的成熟本质上等于给智能体装上了人类水平的视觉认知。1.2 CUA、RPA和传统脚本到底差在哪我把三者的差异整理过一张对比表做技术选型时可以直接参考维度传统自动化脚本RPACUA找元素方式固定坐标、选择器录制路径、UI元素识别视觉语义、自然语言描述界面变动时直接失败大概率失败需重新录制可通过视觉重新识别跨应用能力弱中强开发成本低但维护高中等初期集成成本高需要API经常需要通常不需要不需要核心风险脆弱流程粘连模型幻觉、动作不确定性我见过很多团队上来就纠结“CUA到底能代替RPA吗”这个问题问早了。现实是CUA现在还做不到100%可靠但它的定位不是RPA的简单替代品而是把自动化从“脚本化”推进到“意图化”的一层新基座。它适合的场景是那些流程多变、无法枚举、但人类可以通过看图立刻上手的任务。我自己的判断是未来两年内CUA不会消灭API Agent也不会完全取代RPA但它会变成智能体落地中最重要的一条腿。因为能让你用自然语言操作任何软件的Agent才是普通用户真正需要的“数字员工”。2. 搭建一个最小可用的CUA架构设计、环境准备与动作协议2.1 整体架构四层各司其职我构建的最小CUA按数据流划分成四个层级每一层只做一件事感知层负责获取屏幕截图。通常我会固定分辨率比如1440x900并禁止系统缩放保证模型看到的像素与真实坐标是一一对应的。推理层多模态大模型接收截图和任务目标推理当前界面状态输出结构化动作指令。这是CUA的“大脑”但也是不确定性的主要来源。执行层把动作指令翻译成真实的鼠标键盘操作比如移动鼠标、点击、输入文本、按下快捷键。校验层每次动作执行后重新截图通过图像对比或OCR确认操作是否生效再进入下一轮循环。看到的很多开源项目和商业产品底层都是这套四层结构。关键差异在于每层怎么选型、动作协议怎么设计、错误恢复怎么做这才是决定稳定性的细节。我推荐新手先跑通最小闭环不要一上来就堆功能。最小闭环只需要一个能截图、一个能调用视觉模型、一个能模拟输入的三段脚本整个核心循环不超过200行代码。2.2 环境准备沙箱永远是第一优先级我强烈建议在隔离环境里开发CUA尤其是最初测试阶段。因为动作循环一旦失控它可能乱填表单、误删文件甚至触发一些破坏性操作。我自己的标准配置是 Docker 容器加远程桌面# 以Ubuntu 22.04为例安装桌面环境和VNC服务 docker run -d --name cua-lab \ -p 5901:5901 \ -p 6901:6901 \ ubuntu:22.04 # 进入容器后安装基础组件 apt update apt install -y \ xfce4 xfce4-goodies \ tigervnc-standalone-server \ novnc websockify \ xdotool scrot \ python3 python3-pip # 设置固定分辨率启动VNC vncserver :1 -geometry 1440x900 -depth 24 # 通过noVNC网页访问浏览器打开 http://localhost:6901这样做的理由有几个第一容器坏了随时可以重建避免一次错误操作毁掉宿主机环境第二VNC的远程桌面可以让我们肉眼观察CUA的一举一动调试时非常有用第三固定分辨率直接规避了坐标映射中最头疼的比例换算问题。执行层我用的是 xdotool 加 scrot因为它们轻量、依赖少适合容器环境。在Windows下则推荐 pyautogui 或 Windows API但开发逻辑完全一致只是接口名称不同。2.3 动作协议把“点击”“输入”变成模型能输出的结构化JSONCUA的核心交互协议是我自己设计的一套极简动作JSON动作类型参数说明clickx, y, button鼠标点击某个坐标button 默认 lefttypetext模拟键盘输入一段文本keykey, chord按某个键或组合键如 key: s, chord: [ctrl]scrolldx, dy滚动鼠标滚轮waitms等待指定毫秒screenshotremark请求重新截图用于校验或作为兜底模型每轮输出一个JSON数组程序按顺序执行。例如要让CUA点击屏幕上某个按钮并输入内容模型可能返回[ {action: click, x: 720, y: 452}, {action: type, text: helloexample.com}, {action: key, key: Tab}, {action: type, text: MyPassword123}, {action: key, key: Return} ]这个协议看起来简单但它有一个重要设计原则动作必须显式拆开。不要把“点击登录并输入密码”压成一个复合动作因为模型如果对某个中间状态判断错了你很难定位到底哪一步出错。动作越原子化排查时就越容易。主循环的伪代码大致是这样def cua_main_loop(task: str, max_steps: int 30): for step in range(max_steps): screenshot take_screenshot() response model.chat( system你是CUA只能输出JSON动作数组, user[ f任务{task}\n当前步骤{step}, screenshot ] ) actions parse_json(response) for action in actions: if action[action] screenshot: continue execute(action) if is_task_done(screenshot): break这套框架后来我加了校验层、重试层、回滚机制但核心骨架到现在都没变过。3. 从像素到可执行动作截图解析与坐标映射的硬核链路3.1 坐标问题为什么是最容易翻车的环节如果你的CUA在Docker里测试一切正常但一到真机就乱点十有八九是坐标换算出了问题。屏幕截图是一张位图模型看到的是像素输出的也是像素坐标。但不同设备的屏幕分辨率、缩放比例、DPI全都不同同样的“登录按钮”在1440x900下可能是(720, 452)在1920x1080下就变成了(960, 542)在2K屏开125%缩放时更麻烦模型看到的截图尺寸可能与实际坐标系不一致。这也是我坚持在沙箱和固定分辨率下开发的原因。固定分辨率意味着截图尺寸恒定坐标映射没有歧义——你看到的就是你能点的。而在真机上一定要在CUA启动时探测屏幕分辨率和缩放因子将模型输出的像素坐标乘以校正系数才能映射到真实屏幕坐标。3.2 我推荐“锚点偏移”而不是直接输出绝对坐标如果模型每次直接输出一个绝对坐标稍微滚动页面、窗口移动位置整个坐标就全废了。我后来改成了一种更稳定的方案锚点加偏移。具体做法是让模型面对屏幕时先识别目标控件周围的稳定视觉锚点——比如“黑色右侧栏”“底部状态栏”“页面上方的黄色按钮”。再基于这个锚点描述目标位置例如“在‘提交’按钮下方80像素处”。执行层拿到这句描述后再通过目标检测或图像匹配去定位锚点坐标然后偏移得到实际点击坐标。你也可以让模型输出百分比坐标即相对截图宽高的位置{action: click, x_percent: 50.0, y_percent: 50.2}执行层再换算成实际像素。这个方案的好处是即使截图尺寸变化只要目标在版面中的相对位置没变依然能准确定位。但遇到需要精确对齐的小元素时百分比坐标误差还是会变大所以更精细的操作还是需要视觉锚点加局部识别来辅助。3.3 视觉语言模型输出坐标时的“两步法”实践我观察到的经验是直接问模型“按钮在哪个位置”它往往能给你一个接近正确答案的坐标但不会精确到像素。更大的问题在于界面元素密集时模型会选择错误的视觉焦点明明屏幕上只有一个“登录”按钮它却告诉我“登录”在表单下方实际那里是“立即注册”。于是我把定位任务拆成了两步第一步让模型描述目标元素的视觉特征“那个背景色为深蓝色、位于页面右侧、上面写着‘提交订单’的矩形”。这相当于让模型先确认目标是什么样子、大致在哪里。第二步让模型在最新截图上输出该元素的精确包围框格式为{x: 720, y: 452, width: 80, height: 36}执行层将包围框中心和预期点击偏移量结合起来计算出最终点击坐标。相比直接输出点坐标包围框给了模型更多的容错空间——它哪怕把框画粗一点只要中心点仍在元素内部点击就不会失败。如果你希望秒级响应当然可以只用一步让模型直接输出点坐标。但如果你追求成功率请务必采用两步法。这多出来的100毫秒延迟比一次点击失败后重新恢复整个流程要便宜得多。另外一个很实用的细节是在每次截图传给模型前我会用一个提示词模板固定它的输出格式并在解析JSON时做严格校验。模型偶尔会输出多余的解释文字、字段名写错、甚至把坐标写成字符串程序必须优雅处理这些情况而不是直接崩溃。解析失败时我会让模型“重新输出符合schema的JSON”通常再有一两次就能拿到合法结果。4. 实测记录让CUA独立完成三项真实任务理论说再多不如直接扔几个真实任务进去跑。我在搭建完最小系统后挑了三件日常工作中常见的操作让CUA全自主执行中间不进行人为干预只在失败规定次数后停止。4.1 任务一填写在线报名表单并提交这个任务的流程是打开一个报名页面依次填入姓名、手机号、邮箱选择场次勾选同意条款点击提交。执行过程大体顺利模型能够通过标签文本找到输入框点击后正确填入文本。但中途出现两个小问题第一个是场次选择使用了下拉框CUA第一次点击之后只是展开了选项列表没有选中任何项直接去点“提交”了第二个是“同意条款”的复选框位置偏小模型点到了旁边的文字区域导致提交后被页面拦截弹出错误提示。这两个问题最终是通过增量校验解决的模型每执行完一个动作都会重新截图发现状态没有变化时自动补一次修正动作。整个任务实际耗时2分07秒初次提交失败一次第二次成功。4.2 任务二把演示文稿导出为PDF这个任务考验的是菜单栏的深层导航能力。正常情况下需要点击“文件”菜单、找到“导出”、选择格式、等待导出弹窗、确认保存路径。CUA在“文件”菜单这一步就卡了两次。第一次是模型看到的截图里“文件”菜单在顶部菜单栏但实际点击后弹出了最近文件列表而不是导出选项第二次是它误把侧边栏的“文件”标签当成了菜单栏的“文件”点击后进入了文件浏览视图。我复盘日志后发现引导模型关注“窗口标题栏下方的那一行菜单”而不是“左侧边栏里的图标”之后导航路径就正常了。最终它花了3分44秒完成任务比人工操作慢了大约十倍但全程不需要人盯着。4.3 任务三从网页批量整理信息到表格这个任务我让CUA打开一个资讯站点把前三篇文章的标题和发布日期整理进一个本地表格文件。看起来简单但涉及滚动加载、跨窗口切换、焦点管理实际复杂度不低。CUA的完成度让我意外它能识别“加载更多”按钮点击后等待新内容出现再继续提取。但它在一个弹窗推荐广告上反复纠结了很久每次都试图直接提取弹窗内容最后是我在任务描述里加入了“忽略非正文区域”的提示它才绕过弹窗继续执行。三篇文章最终全部提取完成耗时2分51秒。相比人工操作大约三分钟这个速度已经接近人类。4.4 三项任务的汇总分析任务完成率人工干预次数总耗时主要瓶颈在线报名表单第一次失败后成功02分07秒下拉框选择逻辑、复选框误点演示文稿导出PDF100%03分44秒菜单导航理解、界面语义冲突网页信息整理100%02分51秒弹窗干扰、动态加载等待这组数据给我的直接结论是CUA处理“内容输入”类任务已经很可用处理“界面导航”类任务还需要人工安抚。好消息是大多数错误并非模型能力不足而是流程缺乏约束。比如下拉框没选中本质上是因为模型没有“动作后必须验证状态”的约束意识弹窗干扰则是缺少“先识别遮挡物再决定主动作”的规则。这些约束完全可以通过提示词和代码逻辑补上。5. 高频翻车点与排查清单我把踩过的坑都复盘了一遍5.1 界面变化导致坐标漂移现象是CUA第一次点击成功但第二次同样的任务就点到空白处。排查后发现原因是窗口位置发生了偏移模型基于上一次记忆的坐标直接点击没有重新分析截图。解决办法是每次动作前强制使用最新一轮截图的视觉输出禁止模型沿用历史坐标。同时在执行重要点击前增加一次区域聚焦截图确认目标仍在预期位置。5.2 页面还在加载动作已经执行这是最典型的时序问题。页面点击后出现loading模型没有等待条件就继续输入内容导致输入框尚未渲染所有输入都被扔进空白区域。我最初用固定sleep解决但不同页面加载速度差异巨大固定等待要不就太慢要不就太短。最终我改成“条件等待”执行完一个动作后如果预期要出现某个元素或文本程序先轮询截图直到元素出现或超时再进行下一步。例如“点击登录后等待页面标题变为‘控制台’”这就比盲目等3秒靠谱得多。5.3 弹窗打断主流程弹窗是CUA实测中遇到率最高的干扰源广告弹窗、更新提示、权限申请五花八门。模型如果执着于当前主任务会无视弹窗的存在直接操作结果点到了弹窗上主流程直接崩掉。我开发了一套弹窗处理规则每次动作前如果截图中央区域出现与任务无关的浮层模型必须优先处理弹窗点击其右上角关闭按钮或者选择“稍后提醒”。这个规则看似简单但把整体成功率提升了约40%。5.4 键盘焦点丢失导致输入内容错位点击一个输入框然后准备输入文字这时候如果界面焦点被某个系统通知抢走输入的字符会跑到别的地方去。排查这种问题很难因为截图看起来一切正常但实际输入位置已经错误。我的解法是在每次type动作前先强制执行一次click动作并传入输入框中心坐标作为聚焦动作然后再输入内容。聚焦和输入必须绑定为同一个动作组不要分开。5.5 相同文本多目标歧义屏幕上如果同时存在“确认”按钮和“确认并继续”按钮模型可能把两个当成同一个目标。每当遇到需要点击特定文本时我要求模型除了返回坐标外还必须返回它所识别到的完整按钮文本内容程序会做一次字符串校验防止点到错误的相似按钮。5.6 动作执行过快导致系统无响应虚拟机的软件响应速度远不如真机CUA连续快速点击时界面可能进入假死状态。后来我在每个动作之间加入了50到200毫秒的动态间隔并在连续输入大段文本时按句子粒度分批输入保证UI线程有足够时间处理事件。5.7 缺乏回滚机制导致雪崩式错误最严重的一次CUA误把订单表单里的“删除”按钮当成“编辑”点击了导致数据被清空。这事让我彻底明白了CUA必须有回滚能力无论感知再准、模型再强动作执行前都要假设自己有可能会错。现在我的架构里增加了一个“动作前快照”机制每次执行重要操作前都保存截图和界面状态一旦后续校验失败可以恢复回滚点重新来一遍。5.8 安全与误操作边界最后一条也是最重要的一条。CUA拥有了操作真实系统的能力就意味着一个幻觉可能导致不可逆的破坏。我给自己列了几条铁律第一默认在沙箱环境运行第二涉及删除、发送、付款、修改权限等高危动作时必须人工确认第三限制单次任务的最大动作步数和运行时间避免失控循环第四全部动作留日志事后可审计。6. 从这套CUA经验里沉淀下来的三个方向6.1 短期记忆让CUA不再每次“重新做人”很多次失败都源于模型没有记住自己已经做过什么。它刚填完用户名下一步到了密码框却又去点击用户名输入框重新输入。引入短期记忆后系统会维护一个“已完成操作清单”模型每一步都能看到历史动作不仅减少了重复操作还让它在误操作后更容易推断“刚才那步点错了应该回退”。6.2 自纠错循环比更强的模型更划算有时换个更大参数量的模型错误率确实会下降但引入的成本和延迟是不可接受的。我更推荐的做法是允许模型犯错但强制它在每个关键动作后用截图校验结果一旦发现状态没有如期变化就自动执行“观察新状态、调整动作、重试”的闭环。数据表明自纠错循环能把任务成功率从60%拉到90%以上而这部分提升完全来自工程手段不是模型能力。6.3 权限分级与人类确认制度CUA能否规模化落地核心不在技术水平而在治理边界。我的建议是给CUA定义三级权限普通操件点击、输入、滚动自动执行敏感操作删除、修改配置、发送消息必须弹出确认框高风险操作批量修改、涉及资金的动作直接禁止并转交人工处理。这套分级机制不复杂但在生成式模型的非确定性面前它是守住底线的唯一依靠。最后说一个我踩了很久才悟出来的小经验想把CUA调到稳定可用的状态不要先急着换更强的视觉模型先解决执行层的确定性。动作校验、坐标锚定、状态回滚这些工程细节一个都不解决的话模型再聪明也白搭。我当时最有效的改动是给了CUA一个“重新截图确认”的保留动作——每当它怀疑自己的判断就可以先截一张图再看一眼。这一个小小的“免死金牌”比任何提示词优化都能显著提升稳定性和最终成功率。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进