Gemini 2.0 AI操控屏幕:不到300行搭建自动化闭环 1. 从会聊天到会动手AI操控屏幕到底改了什么第一次看到 Gemini 2.0 接管浏览器的那段演示我盯着屏幕愣了几秒。它不是给我一段操作教程也不是吐出一段 Playwright 脚本让我自己跑而是自己截屏、自己判断下一步该点哪里、自己把鼠标移过去点下去然后看结果、再决定下一步。整个过程没有一行 CSS 选择器没有 XPath没有>def norm_to_pixel(norm_x, norm_y, screen_w, screen_h): x int(norm_x / 1000 * screen_w) y int(norm_y / 1000 * screen_h) return x, y但魔鬼在细节里。第一多显示器环境下screen_w和screen_h要是主显示器的尺寸如果目标窗口在副屏上你还得加上副屏的偏移量。第二系统缩放比如 Windows 的 125% 缩放、Mac 的 Retina 屏会让逻辑分辨率和物理分辨率不一致截图 API 拿到的尺寸和鼠标 API 接受的坐标系可能不是同一个这一步不做处理点出来的位置会差个百分之二三十看着不多但点一个 16px 的小图标就是完全点不中。我的处理办法是截图之后立刻记录这张图的宽高把它作为换算基准而不是去查系统分辨率。因为截图和后续的坐标换算用的是同一个尺寸缩放带来的偏差就自然抵消了。这个思路我用了之后跨设备跑同一套代码基本没再出过坐标漂移的问题。2.3 动作空间设计动作越少稳定性越高Gemini 这套能力暴露出来的动作类型通常包括这几类点击某个坐标、在某个坐标输入文本可选是否回车、滚动指定方向和幅度、按键比如按下 Enter、Esc、CtrlA、以及等待。数量不多但组合起来能覆盖绝大多数桌面操作。动作少是有意为之。每多一种动作模型选择错误的概率就上升一点你的解析分支也多一层。我自己的经验是能用一个通用动作解决的就别拆成两个。比如输入文本这个动作里带一个是否回车的参数就比单独再做一个按回车的动作更省事因为模型在填完表单后紧接着回车是高频组合操作让它一次性输出能减少一轮交互。滚动动作的参数设计要特别注意。用像素值描述滚动距离在不同应用里表现不一致有的应用是平滑滚动有的是整屏翻页。比较稳的做法是用滚轮格数wheel clicks作为单位一格大概是几十像素模型更容易理解往下滚三格这种量级。如果你用像素值模型可能会给出一个夸张的数字导致页面直接飞到最底部反而丢失了要看的中间内容。还有一个容易被忽略的点动作执行后的等待时间。点击之后页面不会立刻响应模型如果马上截下一张图可能截到的是加载中的白屏或旧状态于是它又会重复点击陷入死循环。我现在固定在每个动作后加一个短等待几百毫秒并对点击后立刻截图这种模式做特殊处理——宁可贵一点也别让它重复操作。2.4 为什么看屏幕比读 DOM更通用传统 RPA 和爬虫依赖的是页面结构拿到 DOM 树、找到元素、触发点击事件。这条路精确、快、不消耗视觉 token但它有个硬伤它只对能被程序读到的界面有效。什么意思一个网页你还能用无头浏览器去读它的 DOM但一个本地桌面软件、一个远程桌面里的老系统、一个用 Canvas 画出来的界面、甚至一个视频播放器里的自定义控件DOM 这套就完全失效了。这些场景下屏幕上明明有你可以点的东西程序却看不见。这就是为什么很多企业内部的老系统自动化至今还得靠人肉。看屏幕这条路把所有界面拉到了同一个抽象层级只要它能显示在屏幕上AI 就能看到只要能接收鼠标键盘输入AI 就能操作。这个通用性带来的价值在企业内部那些没有 API、没有开放接口、界面还特别老的系统上体现得淋漓尽致。我见过一个场景某套内部报表系统只能通过远程桌面访问之前要么人工导出要么写一堆脆弱的图像识别脚本现在直接用这套方案配好环境就能跑。代价当然也有识别精度不如 DOM 定位受分辨率、主题、字体渲染影响速度慢成本高。所以我的判断是——有 API 优先用 API有稳定 DOM 优先用 DOM什么都没有的时候AI 操控屏幕是那张底牌。它不是替代方案是补位方案。3. 手把手搭一个最小可跑闭环3.1 工具链选型与依赖清单先说选型思路。截图和输入控制这部分Python 生态里最省事的是pyautogui跨平台安装即用缺点是性能一般、对多显示器支持得靠mss这类库来补。如果你只做浏览器自动化Playwright或者Selenium的截图能力更干净滚动和点击也更可控但代价是它只能管浏览器管不了本地软件。我建议按你的目标场景二选一别想着一个库通吃。模型侧你需要一个能调用的 Gemini 接口并且要确认它支持 computer use 那套工具调用。这里提醒一句这个能力在模型版本上是有区分的不是所有 Gemini 型号都开放接入前务必查一遍官方文档里对应能力的状态和地域可用性别写完代码发现根本调不通。依赖大概是这样pip install google-genai pyautogui mss pillowmss用来做高性能截图比 pyautogui 自带的截图快不少pillow负责图片编码和缩放。如果你要处理多显示器mss能直接拿到每块屏幕的句柄比 pyautogui 清晰。注意pyautogui默认有个防误触机制鼠标移动到屏幕左上角会触发异常中断长时间跑任务时这个机制会误伤记得在初始化时关掉它。3.2 截图与坐标换算的落地实现截图这一步我习惯把图先缩放到一个固定宽度再送模型比如 1280 或 1024。原因有两个一是降低 token 消耗二是让模型对画面尺寸有个稳定预期。缩放之后要记住缩放比例因为换算回真实坐标时要用到。import mss from PIL import Image import io def capture(scale_width1280): with mss.mss() as sct: monitor sct.monitors[1] # 主显示器 shot sct.grab(monitor) img Image.frombytes(RGB, shot.size, shot.rgb) real_w, real_h img.size ratio scale_width / real_w new_size (scale_width, int(real_h * ratio)) img img.resize(new_size, Image.LANCZOS) buf io.BytesIO() img.save(buf, formatPNG) return buf.getvalue(), real_w, real_h换算函数要把上面的real_w, real_h传进去def to_pixel(norm_x, norm_y, real_w, real_h): return int(norm_x / 1000 * real_w), int(norm_y / 1000 * real_h)这里有个细节缩放用的real_w是主显示器的物理宽而鼠标 API 接受的坐标在开了系统缩放的情况下可能是逻辑坐标。如果你发现点击位置系统性偏移比如总是往左上偏八成就是这个原因解决办法是用ctypes调 Windows 的SetProcessDpiAwareness或者在 Mac 上用逻辑分辨率做基准。这个坑我在两个不同项目里都遇到过每次都得重新确认一遍当前系统的缩放状态。3.3 主循环从指令到动作执行主循环是整个方案的心脏逻辑不复杂但边界条件要写清楚。核心是一个 while 循环每轮做四件事截图、调模型、解析动作、执行动作。def run_agent(goal, max_steps15): history [] for step in range(max_steps): img_bytes, rw, rh capture() resp call_gemini(goal, img_bytes, history) action parse_action(resp) if action is None: break if action[type] done: break execute(action, rw, rh) history.append(describe(action)) time.sleep(0.8) return historyhistory这个变量很重要它记录了模型已经做过的动作在下一轮 prompt 里带上能有效避免模型忘记自己点过什么然后反复点同一个地方。但 history 不能无限增长太长会撑爆上下文我一般只保留最近 5 到 8 步更早的用一句话概括比如已完成登录。执行动作这部分点击用pyautogui.click(x, y)输入用pyautogui.write(text)配合pyautogui.press(enter)滚动用pyautogui.scroll(-3)。有个小技巧输入中文的时候pyautogui.write经常失灵因为它模拟的是按键序列中文字符没法直接映射到按键。解决办法是用剪贴板——先把文本写进剪贴板再模拟CtrlV粘贴。import pyperclip def type_text(text): pyperclip.copy(text) pyautogui.hotkey(ctrl, v)这一招在处理中文、特殊符号、长文本的时候特别管用比逐字符模拟稳得多。3.4 护栏这些操作必须提前拦住这套东西威力大风险也大。它能点就意味着它能点错它能输就意味着它能输错。我在任何一次跑真实任务之前都会加几道护栏这不是可选项是必需项。第一道是动作白名单。只允许点击、输入、滚动、按键这几类任何超出范围的调用直接拒绝执行。模型有时候会幻觉出一个不存在的动作或者试图调用系统级命令白名单能挡住这类越界行为。第二道是危险区域拦截。屏幕上总有一些地方不该点——关闭按钮、删除按钮、支付确认按钮、系统托盘。可以用坐标区间框出来命中就拒绝。更稳妥的做法是让模型在每次点击前说明我要点击的是什么把这个描述也做一遍关键词匹配命中敏感词就停下来问人。第三道是步数上限和循环检测。设一个最大步数我一般设 15 到 20超了就停。同时检测重复动作如果连续三步都是点击同一个坐标、或者截图内容几乎没变化说明卡住了直接中断而不是继续烧 token。第四道是敏感操作二次确认。凡是涉及提交表单、发送消息、确认删除这类不可逆操作在真正执行前必须人工确认一次。我在测试阶段吃过亏——任务描述写得含糊模型自己理解成要提交结果把一条测试数据发进了正式环境。提示跑无人值守任务之前先在沙箱环境里完整跑通把每一步的截图存下来复盘你会发现在自动化里我以为它会这样和它实际这样之间的差距往往比你想象的大。4. 把成功率从六成拉到九成调优细节4.1 任务描述怎么写才不容易跑偏模型对任务描述的理解程度直接决定成败。我最开始写的描述是帮我登录并下载报表跑出来的结果是它在登录页反复横跳因为登录这个词太宽泛模型不知道用哪个账号、点哪个按钮、要不要处理验证码。改写之后是这样第一步在用户名字段输入 xxx第二步在密码字段输入 xxx第三步点击登录按钮第四步等待页面加载完成第五步找到顶部导航栏的报表菜单并点击。步骤拆得越细模型的决策负担越小成功率上升得非常明显。但也不能细到每一下点击都写死那就退化成传统脚本了失去了这套方案的灵活性。我的经验是描述目标状态和关键路径把中间的细节留给模型判断。比如把这份表格里金额大于 5000 的行筛选出来并复制到新表格这就是一个合适粒度的描述——它说清了要什么但没说具体点哪个按钮、用什么方式筛选。还有一个实用技巧是在描述里给出终止条件的判断方式当看到页面上出现导出成功的提示时任务结束。模型有了明确的成功判据就不会在任务已经完成后还在东摸西摸。4.2 分辨率、缩放与坐标漂移的系统性处理前面讲过坐标系这里讲怎么防止它在长任务里慢慢漂。漂移的来源主要有两个一是窗口大小变化任务跑到一半窗口被最大化或者被系统弹窗挤压屏幕内容位置全变了二是页面滚动滚动之后原本记在 history 里的坐标就失效了。对付窗口变化我采取的策略是强制固定窗口状态。任务开始前就把目标窗口调到固定大小和位置跑任务期间用脚本锁住禁掉最大化、监听窗口变化事件。如果检测到窗口尺寸变了直接暂停任务而不是硬跑。对付滚动导致的坐标失效核心原则是每轮重新截图、重新定位绝不复用上一轮的坐标。history 里记录的是做了什么动作不是点在哪里这样即使页面滚动了模型也会基于新截图重新判断该点哪儿。这个设计看起来低效但它是避免点了半天没点中的关键。另外提一句高分屏。Retina 或者 4K 屏上截图拿到的物理分辨率可能是逻辑分辨率的两倍这时候如果换算基引用错了点击位置会偏到姥姥家。我的做法是在程序启动时做一次标定——截图一张用工具找出一个已知位置的元素验证换算是否准确不准则调整基准。这一步花不了两分钟能省掉后面一堆诡异 bug。4.3 动作校验与重试怎么知道它点成功了模型说我点了不等于真的点中了。页面可能还没渲染完元素可能被遮挡点击可能落空。所以每执行一个动作我都做一次轻量校验。最通用的是截图差异检测。动作前后各截一张图算一下两张图的差异比例如果几乎没变化说明这个动作大概率没生效标记为可疑并让模型重新决策。这个方法的缺点是有时候点击确实生效了但界面就是没变比如点了输入框只是获得焦点所以差异检测只能作为参考不能作为唯一判据。更精准的是让模型自己判断。下一轮截图发给模型时prompt 里附上上一步你点击了 X请判断是否成功如果没成功请给出替代方案。模型看到新截图往往能自己识别出哦刚才那个是弹窗广告没关得先关掉。这种自我纠错能力是这套方案相比传统脚本的最大优势。重试策略上我给每个动作最多两次重试机会且第二次重试必须换方式比如第一次点坐标第二次改成先聚焦再用键盘导航。连续失败就中断上报不要无限重试那只会烧钱不解决问。4.4 延迟和成本这笔账得算清楚延迟主要来自三块截图编码、模型推理、动作后等待。截图编码几十毫秒可以忽略。模型推理是主要开销一次调用视图片大小和模型型号通常在几秒量级复杂页面更慢。动作后等待我现在固定 800 毫秒这个值是我在太快截到旧状态和太慢浪费时间之间试出来的平衡点。成本这块要看你用的模型定价。假设一轮消耗 1000 个图片 token 加几百个文本 token一个十步的任务就是十轮总量摆在那里。想省钱有几个方向降分辨率图片 token 大致和面积成正比从 1920 降到 1024 能省一大半、裁剪区域只截关心的那部分屏幕、减少无谓的截图轮次能靠固定流程走完的就别每步都问模型。但我要提醒一句别为了省钱把分辨率降得太狠。我试过把截图压到 640 宽结果模型识别小字体和小图标的准确率断崖式下跌反而因为反复重试花了更多 token。降本要在成功率不掉的前提下做本末倒置就亏了。5. 常见问题与排查技巧实录5.1 高频问题速查表我把实际跑任务时遇到的高频问题整理成一张表遇到问题先来这里对照能省不少时间现象常见原因排查与解决点击位置系统性偏移坐标系基准错误或系统缩放用截图尺寸做基准必要时做 DPI 标定输入中文变成乱码或丢失模拟按键无法映射中文改用剪贴板粘贴方式输入同一个按钮反复点动作后等待不足截到旧状态增加等待时间加重复动作检测任务跑到一半卡住弹窗遮挡或页面跳转未识别在 prompt 里加先处理弹窗的指令模型给出的动作无法解析返回格式和解析逻辑不匹配加入解析失败兜底和日志记录多显示器点击错屏只按主屏坐标计算明确目标屏幕句柄加上偏移量长时间运行后越来越慢history 无限增长撑爆上下文只保留最近几步早期步骤做摘要这张表里的每一条我都是在真实项目里被坑过之后才加进去的。尤其是截到旧状态和上下文膨胀这两条一开始完全没意识到debug 的时候百思不得其解。5.2 我踩过的几个坑第一个坑是对模型能力的过度信任。我一度觉得模型能看懂屏幕那它应该什么都能干。结果在一个页面结构特别复杂的后台系统上它把导出按钮和退出登录按钮搞混了因为它俩挨得近、样式相似。后来我在任务描述里明确写了按钮的文字内容问题才解决。教训是描述里能给的锚点都要给别指望模型自己分辨。第二个坑是忽略了网络延迟对时序的影响。有个任务是点击提交后等页面反馈我设的等待是 1 秒本机测试没问题一到网络慢的环境就翻车——页面还没返回结果模型就以为提交失败了又点了一次结果提交了两遍。现在我的做法是用界面上的明确信号做等待条件比如等某个元素出现、等某个文字消失而不是用固定时间。第三个坑是日志记太粗。早期我只记执行了点击动作出问题根本查不出点了哪儿。现在每一步都存一张标注了点击位置的截图和当时的模型输出回看的时候一目了然。这个习惯帮我定位过好几次明明录屏看着没问题、但就是失败了的诡异情况——往往是点击落点偏了几个像素。第四个坑是在错误的场景强行用这套方案。我开始有点上头什么都想让它干包括一些有现成 API 的操作。后来发现凡是能用 API 的用 API 又快又稳AI 操控屏幕应该留给那些实在没别的办法的场景。把工具用在刀刃上比什么都用 AI 更专业。5.3 用代码行数统计工具核实不到300行标题里那个不到300行到底怎么算的这里正好可以聊聊代码行数统计工具很多人对这个行数是有误解的。常见的统计工具有几个cloc是老牌工具能区分代码行、注释行、空行tokei是 Rust 写的速度快输出友好scc是 Go 写的还会算复杂度。用法都很简单tokei ./src --exclude *.json cloc ./src --exclude-extjson关键在于统计口径。同一个项目不同的统计方式结果能差出一倍。算不算空行算不算注释算不算配置文件和生成的代码不到300行通常指的是核心逻辑的实际代码行把空行、注释、三方依赖、样板代码都排除掉之后的结果。如果你把所有文件加一块用wc -l数数字会大得多。所以看到XX 行实现这种说法先别急着惊叹或者质疑先问清楚统计口径。我这个项目实测下来最小闭环的纯逻辑代码确实在 250 到 300 行之间但加上错误处理、日志、护栏、配置之后轻松翻到 800 行以上。这不是标题党是最小可运行和可用之间的正常差距。理解这一点你评估自己项目工作量的时候就不会被误导。我还习惯在项目里配一个统计脚本每次提交前跑一遍看看代码增长趋势。有时候不知不觉就堆了一大坨重复代码统计工具能及时提醒你该重构了。6. 场景边界与后续扩展别把它当万能药6.1 真正适合落地的几类场景这套方案最舒服的场景有几个共同特征界面稳定、步骤明确、有明确成功判据、出错代价可控。内部系统的数据录入和导出是典型代表。这类系统往往没 API、界面老、但流程固定人来操作轻车熟路。交给 AI 之后一次配好可以反复跑人从重复劳动里解放出来。我参与过的一个场景是每天从三套内部系统里各导一份报表再汇总以前一个人二十分钟现在设个定时任务自动跑人只需要看结果。跨应用的流程串联也很合适。比如从一个软件里读数据填到另一个软件的界面里中间还要做点格式转换。这种活人工做得又慢又容易出错AI 操控屏幕能把它串起来。还有一类是数据采集和监控定时去看某个界面上的数字有没有变化变化了截图存档这种低强度的重复检查特别适合交给它。再就是辅助操作不是全自动而是人在旁边看着AI 打辅助。比如它负责填写一大堆表单字段人负责最后审核提交。这种半自动模式对准确率要求没那么苛刻容错空间大是很好的入门实践。6.2 明确不该碰的场景有些场景我看到就会直接劝退省得浪费彼此时间。高频、低延迟要求的操作别用这套。模型推理本身就有秒级延迟你让它去抢购、去高频交易、去操作游戏响应速度根本跟不上而且这类场景对准确性要求极高一次点错损失巨大。涉及不可逆重要操作的要格外谨慎比如资金转账、正式合同提交、大批量数据删除。这类操作即使加了几道确认我依然建议保留人工最终确认环节别全自动。界面频繁变化的场景也要评估清楚。如果目标页面每天都在改版那模型可能今天能点中明天就点不中维护成本未必比传统脚本低。这种情况得先确认界面变化的频率和幅度再决定。对数据安全有严格要求的场景同样要想清楚。截图意味着把屏幕内容传给了第三方模型如果屏幕上涉及敏感信息这条路可能根本走不通。这一点必须在方案设计初期就评估不能等出了问题再补救。6.3 后续可以怎么扩展最小闭环跑通之后往下走有几个方向值得投入。加入记忆和技能复用。把成功的操作序列沉淀成模板下次遇到类似任务直接调用只在细节上问模型。这样既省 token 又提准确率。类似这个系统的登录流程我跑过一百遍直接走固定脚本这种优化是提升效率的关键。做多模态的错误恢复。现在模型出错时基本是重来未来可以让它根据错误提示信息有些是文字有些是弹窗图标判断具体原因针对性恢复而不是盲目重试。接入更细的屏幕理解能力比如让它不仅能点坐标还能识别界面上的语义元素这是一个输入框、那是一个下拉菜单这样操作会更接近人的认知方式鲁棒性也会更好。最后分享一个我自己的习惯每跑一个新任务我都会先手动把整个流程走一遍并录屏然后对照录屏去看 AI 的执行过程哪一步和人的操作不一样那一步就是需要优化的地方。这个笨办法帮我在好几个项目里提前发现了隐患。工具再好用最后还是得靠人对业务的理解去驾驭它AI 操控屏幕也一样——它把手交了出去但脑子和判断还得是你自己的。