news 2026/9/17 4:45:26

Gemini 2.0 AI操控屏幕:不到300行搭建自动化闭环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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_wscreen_h要是主显示器的尺寸,如果目标窗口在副屏上,你还得加上副屏的偏移量。第二,系统缩放(比如 Windows 的 125% 缩放、Mac 的 Retina 屏)会让逻辑分辨率和物理分辨率不一致,截图 API 拿到的尺寸和鼠标 API 接受的坐标系可能不是同一个,这一步不做处理,点出来的位置会差个百分之二三十,看着不多,但点一个 16px 的小图标就是完全点不中。

我的处理办法是:截图之后立刻记录这张图的宽高,把它作为换算基准,而不是去查系统分辨率。因为截图和后续的坐标换算用的是同一个尺寸,缩放带来的偏差就自然抵消了。这个思路我用了之后,跨设备跑同一套代码基本没再出过坐标漂移的问题。

2.3 动作空间设计:动作越少,稳定性越高

Gemini 这套能力暴露出来的动作类型通常包括这几类:点击某个坐标、在某个坐标输入文本(可选是否回车)、滚动(指定方向和幅度)、按键(比如按下 Enter、Esc、Ctrl+A)、以及等待。数量不多,但组合起来能覆盖绝大多数桌面操作。

动作少是有意为之。每多一种动作,模型选择错误的概率就上升一点,你的解析分支也多一层。我自己的经验是,能用一个通用动作解决的就别拆成两个。比如"输入文本"这个动作里带一个"是否回车"的参数,就比单独再做一个"按回车"的动作更省事,因为模型在填完表单后紧接着回车是高频组合操作,让它一次性输出能减少一轮交互。

滚动动作的参数设计要特别注意。用像素值描述滚动距离在不同应用里表现不一致,有的应用是平滑滚动,有的是整屏翻页。比较稳的做法是用"滚轮格数"(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 pillow

mss用来做高性能截图(比 pyautogui 自带的截图快不少),pillow负责图片编码和缩放。如果你要处理多显示器,mss能直接拿到每块屏幕的句柄,比 pyautogui 清晰。

注意:pyautogui默认有个"防误触"机制,鼠标移动到屏幕左上角会触发异常中断,长时间跑任务时这个机制会误伤,记得在初始化时关掉它。

3.2 截图与坐标换算的落地实现

截图这一步,我习惯把图先缩放到一个固定宽度再送模型,比如 1280 或 1024。原因有两个:一是降低 token 消耗,二是让模型对画面尺寸有个稳定预期。缩放之后要记住缩放比例,因为换算回真实坐标时要用到。

import mss from PIL import Image import io def capture(scale_width=1280): 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, format="PNG") 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_steps=15): 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 history

history这个变量很重要,它记录了模型已经做过的动作,在下一轮 prompt 里带上,能有效避免模型"忘记自己点过什么"然后反复点同一个地方。但 history 不能无限增长,太长会撑爆上下文,我一般只保留最近 5 到 8 步,更早的用一句话概括(比如"已完成登录")。

执行动作这部分,点击用pyautogui.click(x, y),输入用pyautogui.write(text)配合pyautogui.press('enter'),滚动用pyautogui.scroll(-3)。有个小技巧:输入中文的时候pyautogui.write经常失灵,因为它模拟的是按键序列,中文字符没法直接映射到按键。解决办法是用剪贴板——先把文本写进剪贴板,再模拟Ctrl+V粘贴。

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-ext=json

关键在于统计口径。同一个项目,不同的统计方式结果能差出一倍。算不算空行?算不算注释?算不算配置文件和生成的代码?"不到300行"通常指的是核心逻辑的实际代码行,把空行、注释、三方依赖、样板代码都排除掉之后的结果。如果你把所有文件加一块用wc -l数,数字会大得多。

所以看到"XX 行实现"这种说法,先别急着惊叹或者质疑,先问清楚统计口径。我这个项目实测下来,最小闭环的纯逻辑代码确实在 250 到 300 行之间,但加上错误处理、日志、护栏、配置之后,轻松翻到 800 行以上。这不是标题党,是"最小可运行"和"可用"之间的正常差距。理解这一点,你评估自己项目工作量的时候就不会被误导。

我还习惯在项目里配一个统计脚本,每次提交前跑一遍,看看代码增长趋势。有时候不知不觉就堆了一大坨重复代码,统计工具能及时提醒你该重构了。

6. 场景边界与后续扩展:别把它当万能药

6.1 真正适合落地的几类场景

这套方案最舒服的场景有几个共同特征:界面稳定、步骤明确、有明确成功判据、出错代价可控

内部系统的数据录入和导出是典型代表。这类系统往往没 API、界面老、但流程固定,人来操作轻车熟路。交给 AI 之后,一次配好可以反复跑,人从重复劳动里解放出来。我参与过的一个场景是每天从三套内部系统里各导一份报表再汇总,以前一个人二十分钟,现在设个定时任务自动跑,人只需要看结果。

跨应用的流程串联也很合适。比如从一个软件里读数据,填到另一个软件的界面里,中间还要做点格式转换。这种活人工做得又慢又容易出错,AI 操控屏幕能把它串起来。还有一类是数据采集和监控,定时去看某个界面上的数字有没有变化,变化了截图存档,这种低强度的重复检查特别适合交给它。

再就是辅助操作,不是全自动,而是人在旁边看着,AI 打辅助。比如它负责填写一大堆表单字段,人负责最后审核提交。这种半自动模式对准确率要求没那么苛刻,容错空间大,是很好的入门实践。

6.2 明确不该碰的场景

有些场景我看到就会直接劝退,省得浪费彼此时间。

高频、低延迟要求的操作别用这套。模型推理本身就有秒级延迟,你让它去抢购、去高频交易、去操作游戏,响应速度根本跟不上,而且这类场景对准确性要求极高,一次点错损失巨大。

涉及不可逆重要操作的要格外谨慎,比如资金转账、正式合同提交、大批量数据删除。这类操作即使加了几道确认,我依然建议保留人工最终确认环节,别全自动。

界面频繁变化的场景也要评估清楚。如果目标页面每天都在改版,那模型可能今天能点中明天就点不中,维护成本未必比传统脚本低。这种情况得先确认界面变化的频率和幅度再决定。

对数据安全有严格要求的场景同样要想清楚。截图意味着把屏幕内容传给了第三方模型,如果屏幕上涉及敏感信息,这条路可能根本走不通。这一点必须在方案设计初期就评估,不能等出了问题再补救。

6.3 后续可以怎么扩展

最小闭环跑通之后,往下走有几个方向值得投入。

加入记忆和技能复用。把成功的操作序列沉淀成模板,下次遇到类似任务直接调用,只在细节上问模型。这样既省 token 又提准确率。类似"这个系统的登录流程我跑过一百遍,直接走固定脚本"这种优化,是提升效率的关键。

做多模态的错误恢复。现在模型出错时基本是重来,未来可以让它根据错误提示信息(有些是文字,有些是弹窗图标)判断具体原因,针对性恢复,而不是盲目重试。

接入更细的屏幕理解能力,比如让它不仅能点坐标,还能识别界面上的语义元素(这是一个输入框、那是一个下拉菜单),这样操作会更接近人的认知方式,鲁棒性也会更好。

最后分享一个我自己的习惯:每跑一个新任务,我都会先手动把整个流程走一遍并录屏,然后对照录屏去看 AI 的执行过程,哪一步和人的操作不一样,那一步就是需要优化的地方。这个笨办法帮我在好几个项目里提前发现了隐患。工具再好用,最后还是得靠人对业务的理解去驾驭它,AI 操控屏幕也一样——它把"手"交了出去,但"脑子"和"判断"还得是你自己的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 4:44:46

Windows更新错误0x80070020根因解析与精准修复

1. 这个错误代码不是“系统坏了”,而是Windows更新机制在喊你“检查现场” 你点开Windows设置里的“更新与安全”,点击“检查更新”,进度条走到一半突然弹出红框:“更新失败,错误代码:0x80070020”。你刷新…

作者头像 李华
网站建设 2026/9/17 4:44:34

一周实测:从Codex迁到Workbuddy,AI工作台真香?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/17 4:43:35

SPM12 fMRI预处理批处理脚本实战:从解压到平滑全流程解析

1. 为什么还写这套SPM12的批处理脚本1.1 它解决什么问题MATLAB配合SPM12做fMRI预处理,算是神经影像老牌组合了。这两年虽然fMRIPrep、Nipype这类工具越来越流行,但很多课题组的老数据、旧脚本、以及正在跑的纵向研究,仍然跑在SPM12这套流程上…

作者头像 李华
网站建设 2026/9/17 4:42:06

低空经济不是骗局,扑翼飞机的技术底牌与落地场景解析

网上关于“低空经济是不是骗局”的争论,我看了很久。每次看到这种话题,我就想起那年带学生去参加鸟蝶大赛机械创新设计大赛的场景——赛场里几十支队伍调试仿生鸟、仿生蝴蝶,碳纤维骨架和薄膜翼铺了一桌子,半夜还有人在走廊里试飞…

作者头像 李华