ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

AI Agent桌面自动化实战:让大模型看见屏幕并操控电脑

AI Agent桌面自动化实战:让大模型看见屏幕并操控电脑

如果你问一个经常用AI的人,AI到底能替自己干哪些活,得到的答案大概率是:写文案、改代码、查资料、做翻译。这些事有一个共同点——都发生在对话框里,AI负责“说”,你负责“做”。但现实中的工作,绝大多数不在对话框里:整理下载目录、批量重命名文件、把表格里的数据录进网页、每天定时备份文件夹……这些重复、琐碎、规则明确的事,恰恰最适合交给自动化系统,却恰恰是传统AI最“够不着”的地方。这篇文章是我自己动手实践“AI Agent赋能桌面自动化”的一份真实记录,核心目标很简单:让AI从“只会回答”变成“能够操作”——它要能看见屏幕、移动鼠标、敲击键盘、规划步骤,最终替我把一件完整的事情做完。如果你也想让AI助手不再只是个“聊天框”,希望这篇实战演练能给你一条能直接落地的路径。

1. 对话框里的AI和桌面自动化的差距,到底差在哪

1.1 AI能答,但不能做

我在刚开始做这个项目的时候,最先意识到的问题是:大语言模型天然是“文本进、文本出”的闭环。你让它“帮我写一份周报”,它能给出结构完整的模板;你让它“把下载文件夹整理一下”,它大概率只能告诉你“建议按文件类型创建子文件夹,然后将对应文件移动进去”——然后就没有然后了。

这不是模型笨,而是交互模态决定的。对话框里不存在“操作”的对象,模型既看不到你的屏幕,也没有手去点鼠标。它能做的只是基于语言常识给出建议。可现实任务是靠“动作”完成的:打开窗口、选中文件、右键、移动、确认弹窗。这些动作链一旦中断,整件事就做不成。所以我一直觉得,真正让AI产生生产力跃迁的,不是它说话更聪明,而是它能不能把话转化成实际操作。这一步跨出去,就相当于给AI装上了手和眼睛。

1.2 桌面自动化Agent需要补上的三块拼图

一个能操作桌面的Agent,至少要同时具备三块能力,缺一块都跑不起来:

  • 感知:能看到屏幕上的窗口、图标、按钮、文字。没有感知,一切动作都是盲猜。
  • 决策:把“整理下载文件夹”这样的高层目标,拆解成一个个原子操作,并决定操作顺序。
  • 执行:把决策落实到鼠标点击、键盘输入、窗口切换等真实动作上。

这三块拼图正好对应人的三种器官:眼睛、大脑、手。传统自动化(比如按键脚本)只解决了“手”的问题,用固定坐标和固定流程跑,一旦界面变了就崩。而AI Agent真正有价值的地方,是让“大脑”参与进来,根据实时看到的屏幕内容做动态决策。它不是背流程,而是看一步走一步。

1.3 什么样的任务才值得交给它

不是所有任务都适合桌面Agent。我在实践后总结了一个判断标准,用一张表就能说清:

适合交给Agent的任务特征不适合交给Agent的任务特征
重复执行、步骤固定、操作对象明确需要强专业判断,如医疗诊断、法务审核
操作过程中有清晰的视觉反馈纯后台计算,无界面可观察
需要跨多个软件协同涉及敏感数据且需要严格审计的流程
规则会偶尔变化,纯脚本难以维护毫秒级实时响应,如交易下单
人可以远程看着它操作后果不可逆,如删除生产数据库

简单说,凡是“你盯着屏幕能做、但不想亲自做”的活,都是Agent的候选任务。理解了这个边界,后面选技术方案才不会跑偏。

2. 技术底座怎么选:视觉方案对比传统UI自动化

2.1 传统UI自动化的三条死胡同

做桌面自动化的技术方案不止一种,我早期都试过,最后才定下视觉方案。先说为什么老路子走不通。

第一类是坐标脚本。用录制工具记下鼠标点击的像素坐标,回放时按固定坐标执行。优点是简单,缺点是脆弱:窗口稍微挪一下、分辨率改一下、任务栏多一个图标,坐标就全废了。维护成本比写脚本本身还高。

第二类是UI控件树方案。通过系统辅助功能接口读取界面上的控件层级,定位按钮、输入框后再操作。这个方案比坐标稳定,但严重依赖软件本身是否暴露控件信息,很多第三方工具、老软件、自绘界面根本读不到任何控件,等于瞎了。

第三类是OCR加固定模板。截屏后用OCR识别文字位置,再按预设模板点击。这个方案对“文字按钮”有效,但界面布局一变,模板就要重做,本质上还是在写死规则,只是把坐标换成了文字位置。

这三条死胡同的共同问题:它们都是“人先预判所有可能性,再写死应对逻辑”。但真实桌面状态千变万化,穷举是穷举不完的。

2.2 视觉方案的本质变化:让Agent拥有“眼睛”

视觉方案的思路完全不同。它不关心底层控件接口,不依赖固定模板,只做一件事:像人一样看屏幕。把当前屏幕截下来,交给有视觉理解能力的大模型,让模型直接根据图像内容判断“这是什么窗口、哪里有按钮、下一步该点什么”。

这个思路的本质变化,是从“穷举状态”变成“实时感知状态”。Agent不再需要预先知道界面的每一种长相,它只需要每走一步就重新看一眼,然后基于当前画面决策。这就解决了传统方案最大的痛点:界面变了怎么办?——界面变了我重看一眼就行。

我实测下来,视觉方案对“屏幕上有清晰文字和按钮”的操作场景识别得非常准。它最擅长的是那些人类一眼就能看明白的界面,比如文件管理器、设置页、表单页;最怕的是图形密集、无文字提示的纯视觉界面。所以在设计任务时,我会尽量把操作对象放在有文字标识的界面里,这是提高成功率的重要窍门。

2.3 动作执行层:为什么用模拟键鼠而不是系统原生接口

看清楚了屏幕,接下来要动手。执行层我选了最“笨”也最通用的方案:模拟鼠标键盘输入,也就是在系统层面注入点击、移动、键入事件。

有人可能会问:为什么不直接调用系统自动化接口?原因是接口这条路只对支持辅助功能的软件有效,覆盖面太窄。而模拟键鼠是操作系统提供的通用能力,任何软件都能接收输入,和真人操作完全一致。缺点是速度没有原生接口快,精确度也依赖坐标计算,但对于“帮人省去重复劳动”这个目标,完全够用。

这个选择就像请人代驾:你不是让代驾去拆车的控制系统,而是让他像你一样握方向盘、踩油门。虽然笨,但任何车都能开。

3. 从零搭建一个能“看见并操作”桌面的Agent

3.1 环境准备:依赖越少越好

这个项目我全程用的是 Python 3.11,加几个轻量库,不到一百行核心代码就能把主体逻辑跑通。依赖列表极简:

组件用途
截图库抓取屏幕画面,传给视觉模型
键鼠模拟库移动鼠标、点击、输入文字、滚动
视觉理解模型根据截图内容输出下一步动作
图像比较工具动作前后截图差异分析,用于验证执行是否生效

我不建议上来就套重型Agent框架。框架封装程度高,出了问题你很难判断是模型的问题还是框架调度的问题。自己写一个二十行的循环,反而能把每一步都看得明明白白,等跑通了再考虑要不要引入框架。

3.2 把鼠标键盘封装成Agent的工具箱

Agent不能直接调用操作系统,它只能通过我们暴露给它的“工具”来操作。这一步非常关键:工具设计的好坏,直接决定Agent能不能稳定执行。

我先写了一批最底层的工具函数,每个函数只做一件事,参数明确,返回值可读:

# tools.py —— 把桌面操作封装成 Agent 可用的工具 import time import pyautogui from PIL import Image def screen_shot(): """截取当前屏幕,返回图像对象""" img = pyautogui.screenshot() return img def click(x: int, y: int): """移动到指定坐标并单击""" pyautogui.moveTo(x, y, duration=0.2) pyautogui.click(x, y) time.sleep(0.5) return {"result": "clicked", "position": (x, y)} def type_text(text: str): """逐字输入文字""" pyautogui.typewrite(text, interval=0.05) return {"result": "typed", "length": len(text)} def press_key(key: str): """按下按键,如 enter、ctrl+c""" pyautogui.press(key) return {"result": "pressed", "key": key} def scroll(amount: int): """滚动鼠标滚轮,正数向上,负数向下""" pyautogui.scroll(amount) return {"result": "scrolled", "amount": amount}

每个工具函数就是Agent的一只手。函数越原子越好,不要写一个“整理文件”这种大而全的工具,否则Agent的决策能力就浪费了。让它自己用click、type_text、press_key去组合出“整理文件”的完整动作链,这才是Agent应该干的事。

3.3 Agent主循环:观察、思考、行动

工具准备好之后,核心逻辑就是一个循环:截图传给模型,模型返回一个动作,执行动作,记录结果,再截图验证,直到模型判定任务完成。

# agent_loop.py —— 最小实现:观察-思考-行动 from tools import screen_shot, click, type_text MAX_STEPS = 20 history = [] task = "把下载文件夹里的文件按类型整理到对应子文件夹" for step in range(MAX_STEPS): screenshot = screen_shot() action = ask_model(screenshot, history, task) # 调用视觉模型 result = execute_action(action) history.append({"step": step, "action": action, "result": result}) if action["tool"] == "finish": break

这里的灵魂在ask_model这个函数的输入设计上。模型不是一个自由聊天的角色,它是一个“只会调用工具的操作员”。我在系统提示词里严格规定了它的输出格式和规则,这是整个系统稳定性的基石:

你是运行在用户电脑上的桌面自动化助手。 你的唯一沟通方式是通过工具调用输出 JSON,禁止输出解释性文字。 可用工具: - screen_shot: 截取当前屏幕 - click(x, y): 点击屏幕坐标 - type_text(text): 输入文字 - press_key(key): 按下按键(如 enter, ctrl+c) - scroll(amount): 滚动鼠标滚轮 - finish(summary): 任务结束 每轮只能输出一个工具调用,动作完成后观察新截图再继续。 如果某个动作重复3次仍没有效果,换一种方式尝试。 执行之前先确认目标元素在截图中的位置;不确定时先放大截图再决定。 注意:不要假设上一步已经成功,必须从最新截图中获取信息。

看到规则里的“禁止输出解释性文字”了吗?这个设计很关键。如果允许模型先用文字解释再输出动作,它在解释过程中很容易“跑题”,而且解析输出也会变得复杂。强制JSON输出,让Agent保持“动作密集”状态,每轮都必须往前走一步。

3.4 坐标系换算:截图像素和屏幕像素的鸿沟

第一版代码跑起来之后,我遇到一个很基础的坑:模型在截图上看到了一个按钮,给出的坐标是(650, 420),但实际点击却偏了,位置明显不对。

问题出在截图尺寸和屏幕逻辑尺寸不一致。很多桌面系统默认开启了缩放显示,比如150%缩放,截图返回的是物理像素,而鼠标API接收的是逻辑像素,两者存在固定比例差。解决方式是在截完图后做一次坐标换算:

# 假设截图尺寸为 image_w x image_h,屏幕逻辑尺寸为 screen_w x screen_h scale_x = screen_w / image_w scale_y = screen_h / image_h def to_screen_pos(img_x, img_y): return int(img_x * scale_x), int(img_y * scale_y)

如果你用的是常见截图方式,建议先打印一下截图尺寸和屏幕逻辑尺寸到底一不一致,不一致就按比例算,不要直接拿截图坐标去点击。这个坑不解决,后面所有动作都是歪的。

4. 实战演示:让Agent替我把下载文件夹里的散乱文件归好类

4.1 给Agent的第一个真实任务

理论讲完,直接上实战。我给Agent分配的任务是:把下载文件夹里的文件按类型整理到对应子文件夹。当时那个文件夹里大概有60多个文件,混合着PDF、图片、压缩包、安装程序、Word文档,乱成一团。

第一步是让Agent打开文件管理器并定位到目标文件夹。我给它提供的不是一个“自动定位文件夹”的高层工具,而是最底层的操作能力。Agent的做法是:先打开文件管理器,然后一路点击左侧的目录树,最后在搜索框里输入文件夹路径,回车,截屏查看结果。整个过程大约用了10步,每一步它都会停下来看一看,确认没有点错再继续。

这个环节的亮点不在于快,而在于“错了会纠正”。我回放日志时发现,Agent第一次点击目录树时点偏了,落到了相邻的文件夹上,但它截图后立即发现当前界面不对,于是右键返回上一级,重新定位。这在传统坐标脚本里是不可想象的——脚本只会按预定坐标走,点错了也不会知道。

4.2 Agent是怎么一步一步规划和执行的

进入目标目录后,Agent的规划是这样的(我直接从运行日志里摘的):

  1. 截屏观察当前文件列表中都有什么类型的文件;
  2. 决定按扩展名分组,因为这是最清晰、最不容易产生歧义的分类维度;
  3. 依次创建PDF、图片、压缩包、安装程序、文档这几个子文件夹;
  4. 从列表第一个文件开始,逐个选中并移动到对应子文件夹;
  5. 全部处理完,截屏确认每个子文件夹里文件数量与预期一致。

这里有一个细节值得说:创建文件夹时,Agent选择了右键菜单里的“新建文件夹”,而没有用键盘快捷键。表面上看这是个笨办法,但它的理由很实际——右键菜单在当前界面一定可见,快捷键可能因窗口焦点不同而失效。视觉Agent不是追求最优路径,而是优先选择“确定性最高”的路径。对自动化系统来说,这恰恰是非常可贵的品质。

4.3 执行中的两个小技巧

第一次跑通之后,我又连续跑了三轮,在迭代中总结出两个对识别率影响极大的技巧。

技巧一:先切换到列表视图再开始操作。文件管理器默认可能是图标视图,图标大、文字小,模型识别文件名时容易把相邻的图标文字搅在一起。切到详细信息列表视图之后,每个文件独占一行,文件名从行首开始,识别错误率明显下降,点击坐标也更好对齐。

技巧二:不要逐个文件拖拽,而是多选同类文件后统一操作。逐个拖拽对Agent来说有两个难点:一是拖拽终点坐标要求极高,二是拖出去的过程中容易误触发其他窗口。我的做法是:让Agent按住Ctrl逐个点选同一类型的文件,然后右键选择“移动到”,在弹出的对话框里选择目标文件夹。每一步都有清晰的视觉反馈,稳定性比拖拽高一个量级。

4.4 结果与代价

最终这轮任务跑了大约11分钟,一共执行了34步动作,中途出现过一次小失误:一个名字叫“报告_v2.pdf”的文件,因为文件名里带“v2”,被Agent误判成了版本管理类,放进了“文档”文件夹而不是“PDF”文件夹。我后来让Agent自己复查一遍,它通过对比“文件大小、扩展名、目标文件夹命名”三者的关系,很快发现了这个错误并自动移正了。

说实话,这个速度比人手动操作慢得多,手动整理最多2分钟。但它解决的问题不是“快”,而是“你不用坐在电脑前”。你只要把任务下发给它,它就能在你离开电脑的这段时间把活干完。对真正有“大量重复性整理需求”的人来说,这个时间换得值。

5. 实测翻车记录:Agent自动化中那些高频坑

任何Agent系统第一版都不可能稳定,我不止一次看着它在我面前表演“花式跑偏”。这章把实战中踩得最深的几个坑和排查思路完整写出来,希望你遇到时不用再从零排查。

5.1 坐标漂移:分辨率缩放和一屏多窗惹的祸

现象是:Agent在截图里看到的按钮坐标是(650, 420),但点击实际落到了(975, 630)附近,完全不在按钮上。我一开始以为是模型识别错了,后来排查半天才发现是DPI缩放导致截图分辨率与鼠标逻辑坐标不一致。

排查思路建议按顺序走:先对比截图尺寸和系统逻辑分辨率;再确认有没有多显示器且缩放比例不同;最后检查Agent的窗口是否和鼠标所在的显示器一致。这四步能过滤掉90%的坐标偏斜问题。解决方案就是前面写的to_screen_pos换算函数,统一坐标系后再下发动作。

5.2 幻觉动作:模型说“已点击”,实际没点

这个坑非常隐蔽。有一次Agent输出“click(500, 320)已点击保存按钮”,但我在日志里对比前后两张截图,发现画面几乎没有任何变化。原因是语言模型在训练数据里见过大量“点击保存后窗口关闭”的常识,于是它凭常识生成了“应该已经关闭”的预测性文本,跳过了真实的观察环节。

排查这件事,不能只靠截图“肉眼看差异”,我把动作前后的截图做了一次像素级比较。如果两张图几乎一模一样,就认为动作没有产生可观察变化,系统会强制把“状态未变化”这个信息塞回模型输入,要求它重新观察并纠正。这个机制上线后,幻觉动作大幅减少。

5.3 死循环:反复执行同一个失败动作不退出

有这么一次,Agent需要确认某个文件是否已经移动到位,但它反复打开同一个文件夹、反复“查看”,行动就是没有进展。回放日志时我才发现,目标文件夹在视图下方的滚动区之外,Agent的截图里根本看不到它,但模型没有意识到需要滚动,一直在原地打转。

排查后发现,模型的重试策略太死板:失败后它倾向于用完全相同的动作再试一次,而不是切换策略。我加了三条规则:第一,全局步数上限,超过20步自动停止;第二,同一工具连续失败3次必须换工具;第三,连续两次动作结果相同且任务未推进时,强制调用滚动或搜索来打破僵局。这三条组合拳之后,我基本没再见过无限循环。

5.4 弹窗抢焦点:一切计划被一个弹窗打乱

这是一个典型的“非目标状态干扰”。Agent正按部就班点击表单时,右下角突然弹出一个系统通知,或某个软件升级弹窗跳到了前台。Agent的下一步点击按原坐标执行,结果全点到了弹窗上,后续流程直接崩掉。

排查思路是:不要让Agent对“弹窗出现”毫无感知。我设计了一个轻量机制:每轮截图后,先用文本识别扫一遍全屏文字,如果发现“是否”“允许”“确定”“取消”这类弹窗特征词,先把弹窗处理掉再继续主任务。另外在任务启动前,我会手动把通知开到免打扰模式,从源头减少弹窗出现的概率。

5.5 窗口重复打开:Agent不知道自己已经打开了窗口

这个问题的本质是Agent没有“状态记忆”。它下达第一个“打开文件管理器”的动作后,如果不观察任务栏,就不知道自己已经打开了窗口;下一步它可能又去菜单里找“文件管理器”,哗啦啦开了七八个窗口。桌面一乱,后续识别率直线下降。

我想了两个办法组合解决:一是在系统提示词里强调“每次打开应用前,先截图查看桌面和任务栏,确认是否已存在该窗口”;二是在上下文里维护一个简单的“已打开窗口清单”,每执行一步就更新一次,让模型随时知道当前的窗口状态。这两个小改动,让整个系统看起来“冷静”很多。

6. 从能跑到好用:工作流设计经验与后续扩展

6.1 边界划分:确定性脚本和LLM决策各干各的

跑通基础版本后,我最大的体会是:不要什么事都让模型干。模型擅长的是“看情况判断”,不擅长“反复执行机械动作”。比如移动文件这个操作,如果让模型一步一步地点击每一个文件、再点击目标文件夹,不但慢,而且容易出错。

正确做法是把机械步骤封装成确定性函数。我后来加了一个move_files(file_list, target_dir)工具,模型只需要判断“哪些文件属于哪一类”,然后调用一次工具就完成了批量移动。这就像项目经理和员工的分工:经理负责拍板,员工负责执行。模型的决策能力是稀缺资源,动作执行这种没有智力的体力活,应该交给确定性代码。

6.2 回放日志:没有日志,排查只能靠猜

有一次任务跑到第41步失败,光看最终截图根本判断不出问题出在哪儿。从那以后我养成了一个习惯:把每一步的截图、模型原始输出、工具返回结果、动作耗时全部写进结构化日志。

字段示例用途
step13定位到具体步骤
actionclick(500, 320)看模型决策是否合理
resultclicked看动作是否执行成功
screenshot附件还原现场
elapsed2.3s排查超时问题

有了这份日志,排查效率是几何级提升。任务失败时,我从最后一步往前翻,一眼就能看出是哪一步开始偏离预期。建议所有做Agent的朋友,日志系统一定要从第一天就开始建,不要等翻车了再补。

6.3 护栏与急停:Agent必须能被随时叫停

一个能控制鼠标键盘的Agent,本质上已经拥有了操作电脑的完整权限。权限越大,越需要护栏。我设置了三层防护:第一层是全局急停快捷键,我设定连续按5次F9终止当前任务,不管模型卡在哪都强制退出;第二层是目录白名单,Agent只能读写我指定的一个工作目录,绝不触碰其他位置;第三层是操作确认,对于删除、覆盖这类不可逆动作,必须先停下让用户确认。

这些护栏不是说Agent会“恶意操作”,而是模型天然存在随机性,可能某个错误判断就会导致不可逆后果。你永远不会希望看到一个自动化程序在错误的目录里执行了删除操作。花一小时搭建护栏,能避免一万小时的后悔。

6.4 后续还能怎么玩:定时触发、多Agent、经验沉淀

基础跑通之后,扩展方向其实很丰富。我目前已经把这套东西接入了定时触发:每天下班后自动归档当天产生的下载文件,跑了一周,稳定率在九成以上。剩下的一成失败基本都来自“桌面环境被我没关的窗口干扰”这类人为因素,而不是Agent本身的问题。

多Agent协同是另一个有意思的方向:一个Agent专门负责监控屏幕任务栏,另一个负责执行主任务,第三个在出错时负责恢复现场。相当于从“单打独斗”升级成“团队协作”。还有一种方向是经验沉淀:把每次成功执行的动作轨迹保存成模板,下次遇到相似任务直接从模板起步,省去模型重新规划的时间。这条路我还在试,目前看下来潜力很大。

我个人的体会是:做这类桌面自动化Agent,真正的门槛不在模型,而在于工程细节——坐标换算了没有、动作执行后验证了没有、失败后有没有止损机制、每一步有没有日志。这些听起来很琐碎的环节,恰恰决定了Agent从“能跑”到“能信”的距离。如果你也想动手试,我的建议是先选一个特别小的场景,比如“把下载目录里的图片移到图片文件夹”,完整跑通一条路径,再逐步加复杂功能。

RELATED READING

延伸阅读

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