UFO 源码实战 (3):它怎么“点”鼠标的?通过源码掌握 Windows 自动化控制

前言

在上一篇中,我们揭秘了 UFO 的"火眼金睛":它通过 Set-of-Marks (SoM) 技术,把屏幕上的每个按钮都标记上了 ID。

当 GPT-4V 看着满屏的红框说:"我觉得应该点击 5号 按钮"时,UFO 的大脑工作就结束了,轮到**"四肢" (Execution Layer)** 登场了。

  • 它是瞬间把鼠标"瞬移"过去的吗?

  • 它是怎么输入文字的?

  • 如果点歪了怎么办?

今天这一篇,我们深入 ufo/automator 模块,拆解 Action Execution 的全过程,带你掌握 Windows 自动化控制的核心代码。


🦾 第一步:指令翻译 (从文本到动作)

GPT-4V 返回给 UFO 的通常是一段 JSON 格式的字符串,比如:

JSON

复制代码
{
  "thought": "I need to save the file.",
  "control_label": "5",
  "action": "click"
}

UFO 的控制器首先要把这个"文本指令"翻译成 Python 的函数调用。这部分逻辑通常在 AppAgent 的响应解析部分。

关键流程

  1. 提取 ID :拿到 control_label: "5"

  2. 查表 :回到我们在上一篇建立的 control_map(ID 到控件对象的映射表)。

  3. 锁定对象 :找到 ID 为 5 的那个 pywinauto 控件对象。


🖱️ 第二步:鼠标点击的玄学 (click vs click_input)

这是新手最容易踩坑的地方。在 ufo/automator/ui_control/controller.py (或类似文件) 中,你会发现 UFO 并没有使用普通的点击方法。

源码核心逻辑:

Python

复制代码
# 伪代码:UFO 的点击逻辑封装

def execution_click(element):
    # 1. 确保控件可见并置顶
    element.set_focus() 
    
    # 2. 高亮一下(为了让用户知道我要点这里了,视觉反馈)
    highlight_element(element) 
    
    # 3. 核心点击操作
    # 注意:这里用的是 click_input() 而不是 click()
    element.click_input(coords=(x, y)) 

这里的门道 (必考题):

pywinauto 提供了两种点击方式,UFO 为什么选 click_input

  • click() (消息级):直接给窗口发一个 Windows Message (WM_CLICK)。

    • 优点:快,鼠标光标不需要移动,可以在后台运行。

    • 缺点 :很多现代 UI(如 WPF, UWP, Electron)根本不理会这种假消息,点击经常无效。

  • click_input() (硬件模拟级)

    • 原理真实的移动鼠标光标到指定坐标,按下物理左键,再松开。

    • 优点兼容性无敌,和真人操作一模一样。

    • 缺点:会独占你的鼠标,操作期间你不能动。

UFO 为了保证在各种软件里都能跑通,果断选择了模拟真实硬件的 click_input()


⌨️ 第三步:键盘输入的艺术

除了点击,UFO 还需要打字。这在源码中对应 type 动作。

Python

复制代码
def execution_type(element, text):
    # 1. 先点一下输入框,确保焦点在里面
    element.click_input()
    
    # 2. 输入文字
    # with_spaces=True 保证空格不会被吞掉
    element.type_keys(text, with_spaces=True)

特殊按键处理:

你可能会在源码里看到类似 {ENTER} 或 ^a (Ctrl+A) 的字符串。这是 pywinauto 的特殊语法。

UFO 在这里做了一层封装,允许 GPT 输出 "Press Enter",然后内部转化为 type_keys("{ENTER}"),从而实现回车、删除、复制粘贴等操作。


🛡️ 第四步:安全与高亮 (Visual Feedback)

UFO 有一个非常贴心的设计:在点击之前,会在屏幕上画一个框或圆圈,闪烁一下

这不仅仅是为了酷炫,更是一种 Safety Check (安全机制)。

代码位于 ufo/automator/ui_control/screenshot.py 的绘图工具中。

源码逻辑

  1. 获取目标控件的 RECT (矩形范围)。

  2. 在执行 click_input 之前,调用 GDIPyQt 绘制一个半透明的红色矩形覆盖在目标上。

  3. time.sleep(0.5) 暂停半秒。

  4. 清除矩形,执行点击。

为什么这么做?

  • 调试:如果你发现红框画歪了,说明上一轮的坐标识别有问题。

  • 介入:如果你发现 UFO 要点击"删除文件",这半秒的延迟给了你惊呼和拔电源的机会(笑)。


🚧 异常处理:如果不动了怎么办?

源码分析不仅要看正常流程,还要看它怎么处理"翻车"。

controller.py 中,通常包裹着大量的 try...except 块:

Python

复制代码
try:
    control.click_input()
except ElementNotFoundError:
    # 控件找不到了?可能是界面刷新了
    print("Error: Element not found, triggering re-observation...")
    # 触发重新截图,重新识别流程
    return "FAIL_RETRY"
except Exception as e:
    # 其他未知错误
    print(f"Unknown error: {e}")

UFO 引入了自我修正机制:如果动作执行失败,它不会直接报错退出,而是会将"执行失败"作为反馈传回给 GPT-4V。

GPT-4V 收到反馈后,往往会说:"哦,可能是菜单没展开,那我先点一下菜单,再点按钮。"


📝 总结与实战建议

通过分析 UFO 的动作执行源码,我们学到了 Windows 自动化的三个黄金法则:

  1. 模拟真实 :尽量使用 click_input() 模拟物理硬件,而不是发消息,以获得最大兼容性。

  2. 状态确认 :操作前必须 set_focus(),操作后最好检查结果。

  3. 视觉反馈:给用户展示机器人的"意图"(高亮),是提升 AI Agent 体验的关键。

实战作业:

打开你的 UFO 源码,找到 controller.py,尝试修改一下 type_keys 的速度(pywinauto.timings),看看能不能让 UFO 打字快得像黑客帝国一样!

下期预告:

现在 UFO 能看能动了,但它怎么记得住之前的操作?如果任务很长(比如"把所有未读邮件整理到Excel"),它怎么规划步骤?

下期文章:《UFO 源码实战 (4):拆解 prompter 模块,看微软如何调教 GPT-4V 》,我们将深入 AI 的大脑皮层!


点赞是免费的,但知识是无价的。我们下期见! 🤖


相关推荐
石像鬼₧魂石1 小时前
Windows 靶机渗透完整流程(新手版)
windows
herinspace2 小时前
管家婆软件中如何运用商品副单位
运维·服务器·数据库·windows·电脑
北京阿尔泰科技2 小时前
冬季安全用电监测案例
安全·自动化
北京耐用通信2 小时前
突破协议壁垒:耐达讯自动化Ethernet/IP转CC-Link网关在工业互联中的核心应用
人工智能·网络协议·安全·自动化·信息与通信
扫描电镜2 小时前
扫描电镜选购指南:智能、稳定与自动化的综合考量
人工智能·自动化·扫描电镜·自动扫描电镜
嫂子的姐夫2 小时前
01-selenium
爬虫·python·selenium·自动化
橙子味de巧克力4492 小时前
【DataGrip】JetBrains 专业数据库 IDE!全流程管理 + 永久补丁(详细安装指南)
ide·windows
IDOlaoluo3 小时前
RedisStudio-en-0.1.5.exe 安装步骤 详细教程(附安装包)
windows
天庭鸡腿哥3 小时前
输入鸡和马,解suo至尊版!
android·windows·visual studio·everything