1. CUA到底要解决什么:从“会聊天的AI”到“会干活的AI”
最近在技术社区里,“cua”这个缩写出现的频率明显高了起来,很多朋友第一次看到它时都以为是拼写错误,其实它指的是 Computer-Using Agent,也就是“计算机使用智能体”。如果你看过网上那些“让AI自己打开Excel处理数据”“让AI帮忙登录系统、导出报表”的演示视频,那背后的主角基本都是这类技术。在我看来,CUA最大的价值不是多了一个新名词,而是把AI从“只能在一个对话框里输出文字”的状态,往前推了一大步,推到了“可以直接坐在电脑前操作软件”的状态。这篇文章我想围绕CUA,把我自己动手搭建、实测、踩坑的过程完整拆出来,给正在做AI Agent、RPA替代方案,或者想给办公自动化场景找一条新路子的朋友做一个参考。
要理解CUA,得先回到一个老问题上:AI到底能替人干什么?过去很长一段时间,大语言模型能做的只是“生成内容”——写邮件、写代码、总结文档,这当然很有用,但用户真正消耗时间的地方,很多是发生在软件操作层面的。比如从ERP里导出数据、在OA里走一个审批流程、把PDF里的表格粘贴到Excel里重新排版。这类工作的共同特点是:操作路径明确、重复度高、没有创造性,但又必须有人坐在电脑前一步步点。过去我们对付这种场景,靠的是脚本和RPA,为什么CUA会成为一个更值得关注的解法?我下面把这两种传统思路的边界说透。
1.1 传统API方案和RPA各自卡在哪里
先看API方案。它的逻辑很干净:软件方开放接口,你用代码直接调。问题在于,真正承载业务的一堆老系统根本没有API,或者API覆盖的操作范围非常有限。我有一次想自动化一个内部客户管理系统的“导出客户列表”功能,翻了几十页文档发现这个接口根本没有对外暴露,最后只能回到“模拟人点鼠标”这条路。API方案还有一个隐性成本:你每对接一个新软件,就要重新读一遍对方文档,这些工作量一点不比人工操作省。
再看传统RPA。它的思路是“录制一套动作脚本,然后回放”,这在界面结构非常稳定的场景下确实能跑,比如固定的网页表单、固定的Windows窗体。但一旦界面改版、分辨率变化、弹窗时机不对,脚本就会成片地失灵。更麻烦的是,很多老系统用的是非标准控件、图片按钮、嵌入式PDF,RPA的选择器根本抓不到。我见过一个团队维护了上百条RPA流程,每个季度光修选择器就要花掉两个人一周的时间。这说明RPA本质上是在“预设界面”的前提下做自动化,它缺少对界面内容的理解能力。
1.2 CUA换了一个全新的解题角度
CUA的思路其实特别朴素:人是怎么操作电脑的?人看屏幕,理解界面上有什么,然后决定点击哪里、输入什么,操作完再看一眼屏幕确认结果。那如果给AI接上“眼睛”和“手”,让它也走一遍这个循环呢?CUA的核心闭环就是四步:截图感知界面内容,多模态模型理解当前状态并做决策,输出一个具体的鼠标键盘动作,执行动作后再次截图验证结果。这个闭环不断重复,直到完成用户给定的目标。
和传统RPA相比,CUA最大的差异在于:它不预设界面的结构,而是把“理解界面”这件事交给视觉模型实时完成。也就是说,哪怕软件换了皮肤、按钮位置变了,只要屏幕上的信息还能被模型看懂,它就有机会重新规划路径。从理论上讲,只要一个软件是人能操作的,CUA就能操作——只要这个软件的操作本质是“看屏幕+鼠标键盘”。
1.3 CUA和当前主流“Agent”的区别
现在市面上很多Agent产品,本质是“调用工具型Agent”:你给模型一把计算器工具、一个数据库查询工具、一个发邮件的函数,模型根据用户指令决定调用哪个。这种Agent依赖的是预先封装好的接口。CUA则完全面向图形界面:它不依赖后端接口,也不依赖开发者的工具schema,它把所有软件都还原成“屏幕像素+操作事件”。我把三者的能力特征整理成一张表,方便对比参考:
| 维度 | 工具调用型Agent | 传统RPA | CUA |
|---|---|---|---|
| 依赖条件 | 需要开放API或预置工具 | 界面结构稳定、选择器可用 | 软件能在屏幕上渲染 |
| 感知对象 | 结构化数据 | DOM元素/控件坐标 | 整张屏幕画面 |
| 泛化能力 | 受工具范围限制 | 较差,改版即失效 | 理论较强,取决于模型能力 |
| 使用门槛 | 需要开发接入 | 需要录制和维护流程 | 需要配置模型和运行环境 |
| 典型场景 | 数据查询、文本生成 | 高频表单批量提交 | 跨应用、跨系统的复杂操作 |
从这张表能看出来,CUA并不是要取代所有方案,而是把“无法用API自动化、又无法用RPA稳定自动化”的那部分场景补上了,这正是它热度持续走高的根本原因。
2. 拆开CUA的黑盒:感知、规划、执行三个组件一个都不能少
如果只是把CUA当作一个“截图+模型+鼠标键盘”的三件套,那做出来的东西大概率只能在演示视频里跑。真正能稳定完成的CUA项目,至少要把感知、规划、执行三个环节各自的工程难点想清楚。下面我逐层拆解。
2.1 感知层:把一整个屏幕变成模型真正“看得懂”的信息
感知层的第一件事是截图,但截图远没有想象中那么简单。首先是分辨率问题:一台普通的1080p显示器,一帧画面大约是200万像素,直接塞给视觉语言模型做全图理解,token消耗非常高,而且很多模型的视觉编码器对超大图片的处理并不可靠。实际工程里通常需要先做降采样,或者把屏幕切成若干区域分别理解,再或者先让OCR提取出屏幕上的文字和坐标,作为图像的补充输入进行整合。
第二个坑是系统缩放。Windows系统默认的“显示缩放”往往是125%或150%,这会导致截图尺寸和实际鼠标坐标对不上。比如屏幕物理分辨率是1920×1080,但系统按125%渲染,那么逻辑坐标和物理坐标之间就差了1.25倍。这个问题我在后文会专门展开,因为它是新手最容易踩的第一个坑。第三个坑是动态界面:很多页面在加载中、动画播放中、弹窗淡入中这三种状态下的画面差异很大,如果模型看到的是“半成品”截图,它给出的操作指令也必然失真。所以感知层不能只做“截一张图”,而是要连着做“截图—判断画面是否稳定—稳定后再理解”。
2.2 规划决策层:把大目标拆成一步一步的实时决策
规划层负责回答“下一步做什么”。早期我做这种自动化时,总想让模型在一开始就把所有步骤一次性规划出来,比如“第一步打开浏览器,第二步访问网址,第三步点击登录……”,实践证明这条路行不通。原因是真实环境充满意外:弹窗可能挡住按钮、密码输入框可能提前出现、网络可能变得很慢。一旦实际情况和规划不一致,整条前置规划就全废了。因此现在主流的做法是走ReAct循环,也就是“推理→行动→观察”交替进行:模型只决定当前这一小步,执行后立刻通过截图观察结果,再根据新状态重新决策。这种机制下,上一步的失误并不会导致全局崩溃,模型有机会在下一步修正回来。
上下文记忆也属于规划层的关键内容。超过十五步的操作任务,模型必须记住自己从哪来、已经完成了什么、当前卡在哪一步。工程上通常把完整的多轮交互历史喂给模型,但代价是token量持续膨胀。更经济的方式是维护一个结构化的“任务进度摘要”:每一轮结束后,让模型用一段话总结当前状态,下一轮只带这个摘要,而不是把所有历史截图全部重新喂一遍。实测下来,这样的方案在保持一定成功率的前提下,能把长任务的token成本压缩将近一半。
2.3 执行层:模型的动作空间如何定义,反馈闭环怎么建立
执行层直接影响CUA能不能“落得下手”。目前主流有两种动作空间:一种是像素坐标级,模型直接输出目标位置的x、y坐标,然后由自动化框架执行移动和点击;另一种是语义级,模型输出“点击名称为‘保存’的按钮”这类指令,由底层的UI理解模块把指令转换成一个控件位置。像素级的好处是逻辑简单、不需要适配不同应用的控件树,但坐标一旦因为窗口移动、界面变化而偏移,就很容易点错。语义级更稳定,但工程实现成本高,尤其面对非标准自绘控件时,底层模块可能根本识别不出“保存”按钮在哪里。
我个人的建议是:项目初期先做像素级,把模型本身的逻辑能力验证通过,再考虑是否引入语义级定位。闭环验证环节也必不可少,通常的做法是记录动作执行前后的两张截图,计算它们之间的差异程度,结合OCR识别结果判断界面是否产生了预期变化。比如点击一个“下一步”按钮后,如果截图上出现了“请填写邮箱”的提示,系统就知道动作生效了;如果两张截图几乎没变化,那就说明点击可能落在了一个无效区域,需要触发重试或者上报错误。没有这层反馈验证,CUA就只是一个“盲操作的机械手”,出错了根本不知道。
3. 从零搭一个最小可用的CUA Demo:选型、代码与运行流程
这一节我给出一个能够完整跑通的CUA最小实现路径,不依赖任何商业产品。核心思路是用一个Python主循环串联“截图→调模型→解析动作→执行→再截图”这几个步骤,让AI在一台Windows虚拟机上完成“打开计算器并计算2468×1357”这个小任务。麻雀虽小,五脏俱全,跑通它之后,换到其他应用和任务只是改目标描述的问题。
3.1 环境准备与基础技术选型
先列一下我用的技术栈,都是公开通用的方案,你也可以根据自己的实际情况替换。视觉理解部分用的是当前主流的视觉语言模型,支持图片+文本输入,能输出格式化内容即可,例如GPT-4o、Gemini、Qwen-VL等。桌面控制部分用Python结合pyautogui和pynput这两个库,前者负责定位鼠标、点击、滚轮和键盘输入,后者负责监听全局按键事件,方便随时中断自动化流程。运行环境我强烈建议放在Windows虚拟机里,而不是直接跑在主力开发机上。原因很朴素:自动化程序一旦因为模型误判而疯狂点击,你能一键恢复虚拟机快照,而不是眼睁睁看着自己的开发环境被弄得一团糟。
为了更好地模拟真实办公环境,我建议在虚拟机里固定显示分辨率为1920×1080,并把Windows显示缩放设置为100%。这一步能规避大多数坐标偏移问题。同时安装一个常用输入法和计算器应用,方便后续扩展任务。
3.2 主循环代码的结构与运行流程
下面是一个最小可用的Python主循环,代码不长,但已经包含了一个CUA系统的骨架:
import time import json import pyautogui import base64 from io import BytesIO from PIL import Image # 这里替换为你选择的视觉语言模型的调用封装 from vlm_client import request_vlm TARGET = "打开Windows计算器,计算 2468×1357,并读出结果" ACTION_PROMPT = """ 你是计算机操作助手。你可以看到屏幕截图。 你的任务目标是:{target} 可用动作(每次只能输出一个): - click x y : 点击屏幕坐标(x, y) - input text : 输入文本 - key key_name : 按下快捷键,key_name如 win, enter, esc - wait : 等待界面稳定 - done : 任务已完成 输出必须是严格的JSON: {{"action": "click", "params": {{"x": 960, "y": 540}}}} 约束: 1. 只根据当前截图上的真实内容行动,不要假设界面上存在某个按钮。 2. 每次只执行一个动作,不要连续点击。 3. 执行完动作后,系统会自动截图供你观察结果。 4. 如果同一动作连续失败3次,输出 {{"action": "error"}}。 """ MAX_STEPS = 40 def take_screenshot_bytes(): img = pyautogui.screenshot() buf = BytesIO() img.convert("RGB").save(buf, format="JPEG", quality=80) return buf.getvalue() def execute_action(action, params): if action == "click": pyautogui.click(params["x"], params["y"]) elif action == "input": pyautogui.typewrite(params["text"], interval=0.02) elif action == "key": pyautogui.hotkey(*params.get("modifier", []), params["key"]) elif action == "wait": time.sleep(0.8) return time.time() def run_cua(target=TARGET, max_steps=MAX_STEPS): history = [{"role": "user", "content": f"任务目标:{target}"}] step = 0 while step < max_steps: step += 1 screen = take_screenshot_bytes() # 将截图、动作约束和历史上下文一起交给模型 response = request_vlm(screen, ACTION_PROMPT.format(target=target), history) try: parsed = json.loads(response) except json.JSONDecodeError: parsed = {"action": "wait", "params": {}} action = parsed.get("action") params = parsed.get("params", {}) if action == "done": print("任务完成,模型认为已经达到目标状态") break if action == "error": print("模型报告连续失败,需要人工介入") break execute_action(action, params) # 动作后留出渲染时间,再进入下一轮截图判断 time.sleep(0.5) if __name__ == "__main__": run_cua()这段代码的核心逻辑就是那个while循环。每一轮循环,系统做三件事:截图发给模型、模型返回一个动作、系统执行动作。前面提到的“反馈闭环”,靠的是每一轮循环都会重新截图,让模型看到执行动作后的真实界面状态。注意我在代码里加了一个MAX_STEPS的上限,防止模型陷入无限循环。这类保护机制在真正跑起来的时候比任何优化都重要。
3.3 任务执行过程中模型最可能的走法
跑起来后,整个执行路径大致是这样的:第一轮,模型看到当前桌面截图,判断出需要打开开始菜单,于是按下Win键;第二轮,截图显示开始菜单搜索框,模型输入“计算器”;第三轮,截图显示搜索结果,模型点击“计算器”应用图标;第四轮,截图显示计算器窗口,模型逐个点击数字键2、4、6、8、乘号、1、3、5、7、等号;最后一轮,模型从屏幕上的结果区域识别出计算结果,输出done。整个过程可能稳定地在10步左右跑完。
但这里必须说明:上面这个路径是“理想状态”。实际执行时,模型可能会点错、输入法可能没有切换成英文、计算器窗口可能没有出现在最前面,这些都会导致路径偏离。设计CUA系统的一个重要心态就是“接受每步都有误差”,依靠循环反馈来不断修正,而不是指望模型每一步都对。后面我会具体讲几个我真实踩过、也真实花时间排查过的坑,这些比顺利的流程更有参考价值。
3.4 为什么prompt要这样设计
给模型的prompt不是随便写的,我试过好几版,最后固定成上面那套模板,原因有三点。第一,动作空间必须收敛。如果不明确告诉模型只能用这几种动作,它会自由发挥,输出一些无法执行的指令。第二,界面交互必须闭环。prompt里反复强调“执行后系统会自动截图”,是为了让模型理解它处于一个连续的决策环境中,每一次动作都会带来新的观察,而不是一次性做完所有事。第三,触底保护必须内建。明确要求连续失败3次就输出error,这个规则极大地降低了模型在异常状态下反复折腾同一个无效动作的概率。
4. 实测中的翻车记录:三个典型问题与完整排查过程
光讲顺利路径,对实际做项目帮助不大。这一节我按“现象→排查链路→修复方案”的方式,把我印象最深的三个CUA实测问题完整写出来,这些都是常规文档里不会讲的细节。如果你正在做类似项目,大概率会碰到其中至少一个。
4.1 坐标偏移:屏幕缩放与坐标系不一致导致的幽灵Bug
第一次跑通demo的第二天,我把同样的代码放到另一台笔记本上运行,结果模型开始“胡乱点击”:它明明在截图上看到了按钮,点击位置却总是偏到按钮下方。我最初以为是模型能力问题,后来静下来做了两步排查,才定位到根因。
第一步,我打印出截图尺寸和pyautogui返回的鼠标坐标范围,发现截图是1920×1080,鼠标坐标范围也是1920×1080,看起来没有矛盾。第二步,我把模型点击前的截图和点击后的截图叠在一起对比,发现同样的逻辑坐标,第一台机器点准了,第二台机器偏了大概四分之一屏幕。这时候我才想到去查Windows显示缩放,果然第二台笔记本默认是125%倍率。也就是说,截图是以物理像素绘制的,而鼠标坐标系统经过了系统缩放层的换算,两者不在同一个坐标系里。
修复方案是双管齐下:在虚拟机里直接把显示缩放改为100%,让逻辑坐标和物理坐标一致;同时我在代码里加了一个环境检测函数,启动时自动读取当前系统的缩放比例,如果非100%,则把所有点击坐标除以缩放因子后再传给pyautogui。从那以后,坐标偏移动的问题基本消失了。这个排查过程看起来简单,但新手往往会在“模型理解错界面”这个方向上浪费大量时间,实际上问题在操作系统层面。
4.2 加载时序:模型总是点在半成品页面之上
第二个高频问题出在页面或应用加载的异步过程上。模型看到一个窗口正在打开,就立刻自信地输出了点击指令,但此刻窗口内容还在渲染,按钮根本没出现在目标位置。于是点击落空,下一轮模型看到画面没有变化,又尝试点击同一个位置,连续几次后就彻底乱了流程。
排查时我发现,问题不在模型,而在于“截图时机”。动作执行后系统立刻截屏,画面上可能还停留着上一个窗口的半透明残影或者loading动画,模型无法判断界面是否已经稳定。修复思路也不复杂:在每轮动作执行后增加固定等待时间,给应用留出渲染窗口;同时给模型提供wait动作选项,让它自己决定是否需要等待。工程上还加了一个简单检测——截图中如果识别到“加载中、请稍候、spinner动画”这类典型加载信号,系统直接拦截并延迟到信号消失后再进入决策轮。
这个坑给了一个很深刻的经验:CUA的很多问题并不是模型不够聪明,而是交互链路中缺少“帧率控制”。人操作电脑时会本能地等界面稳定,但模型没有这个本能,它只能依赖我们给它设计的节奏。
4.3 模型幻觉:一本正经地点击一个根本不存在的按钮
第三个问题最危险:模型“看见”了一个界面上根本不存在的按钮。现象是截图里明明没有“确认”按钮,模型却输出点击坐标,而且是在一个空白区域点了一下。最开始我百思不得其解,后来把模型的完整思考过程打印出来才明白,它的文本先验太强:当任务涉及“提交订单”时,模型“猜”界面上应该有一个“提交”按钮,于是它选择性地忽略了截图里没有这个按钮的事实,直接编了一个坐标出来。
这是大模型在视觉任务上的典型幻觉问题,文字先验压过了视觉输入。针对这个情况,我改了三层。第一层是prompt硬约束,明确写“只基于截图里真实可见的元素行动,看不到就是看不到,不要猜”。第二层是要求模型先描述界面再给动作——这一步不能省,让模型先输出它在图中识别到的一组控件名称和坐标,然后再基于这些控件决定动作,相当于加了一道“视觉校验”。第三层是设置失败重试上限,同一个动作连续失败三次,系统就不再让模型自行尝试,而是把现场截图和过程日志保存下来,转交人工处理。加了这三层之后,幻觉点击的情况大幅下降,但并没有完全消失,这也是目前CUA落地时仍然需要人工兜底的主要原因。
5. 从Demo到交付:可靠评测、成本控制和安全边界一个都不能少
跑通一个demo和交付一个能用的CUA项目之间,距离比大多数人想象的要大。如果只是内部自用,前面几章的流程已经足够。但要是想把CUA作为一种能力接入业务流程,有几件“看不到的功课”必须提前做,我在这章把最重要的三块讲清楚。
5.1 先搭评测集,再谈优化
我和不少同行交流过CUA项目的失败案例,发现高度一致的问题:大家一上来就追求“把复杂任务跑通”,跑通了就开始欢呼,然后发现下一次运行成功率忽高忽低,根本不敢上线。问题的根源是缺少一个可量化的评测集。正确的做法是,在项目启动的第一周就建立20到30个真实任务的评测集合,覆盖不同软件、不同难度,每个任务记录五个核心指标:任务成功率、任务平均步数、单步操作错误率、平均耗时、以及人工介入次数。后续每换一次模型、每改一次prompt,都拿这套评测集跑一遍,对比前后数据再决定是否上线。
这套评测集的建立会让CUA的开发方式发生质变。原来是在“感觉它的表现变好了”,之后是在“数据告诉我它变好了”。我自己的项目里,有一版prompt改动让成功率从58%提到76%,靠的正是这种量化评测,否则那版改动可能就埋没在各种主观感受里了。
5.2 成本与响应速度的平衡
CUA的运行成本比普通对话Agent高出一个量级。普通对话一次可能只消耗几百个token,CUA一个任务几十步,每一步都要传截图给视觉模型,一张图可能上千个token,整个任务下来几万token是家常便饭。如果业务量每天上千次,成本会迅速变成一个需要认真规划的问题。
控制成本有三个常用手段。第一个是任务分层:先用一个便宜的小模型判断“当前界面属于什么状态”,再决定是否需要调用强模型做完整推理。很多界面状态是稳定的,不需要大模型每次从头理解。第二个是技能缓存:如果某个操作路径被成功执行过,系统可以把路径保存下来,下次遇到相同任务时直接回放固定步骤,只在出现异常时才切换回模型推理。第三个是模型选型的梯度化:简单任务用轻量级视觉模型,只有在复杂任务中才启用更强的模型。这三个手段组合使用,能把单位成本压到原来的40%左右,同时保持大部分任务成功率不下降。
5.3 安全与权限:让AI操作电脑之前,先想好怎么叫停
CUA让模型直接控制鼠标键盘,本质上是把关机、删文件、发邮件这些操作能力都交给了模型。没有边界控制,一旦模型误判或幻觉,后果可能是灾难性的。我在自己项目里建立了三层防护,分享出来供参考。
第一层是环境隔离:所有CUA任务默认跑在独立的虚拟机或远程桌面环境中,与生产网络、核心数据存储都做严格隔离,即使模型把整个虚拟机折腾崩溃,也只影响这一个隔离实例。第二层是动作白名单:对敏感操作进行系统级拦截,比如访问特定目录、执行命令行、调用删除类快捷键,一律禁止;即使模型发出了这些指令,执行层也会直接拒绝。第三层是人工接管机制:系统检测到高频点击、非预期弹窗、长时间重复动作等异常信号时,自动暂停并请求人工确认。我甚至加了一个全局急停快捷键,任何时刻按下就能终止整个自动化进程。这三层防护合在一起,才让我敢把CUA从实验环境推向相对正式的内部流程。
落到最后一句话上,我个人的体会是:CUA最让人兴奋的地方,不是它现在的成功率已经超过人类,而是“通用计算机操作”第一次变成了一个可以通过数据持续优化的机器学习问题。这个方向还远没到开箱即用的成熟期,但如果你愿意从单应用、单任务、固定环境开始,老老实实搭评测集、跑真实验证、维护反馈闭环,你手上这个不起眼的demo会一步步长成真正替你省时间的自动化能力。