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 的大脑皮层!


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


相关推荐
蓝创工坊Blue Foundry28 分钟前
图片文字提取到 Excel:批量任务如何先定义要交付的字段
运维·服务器·开发语言·数据库·自动化·ocr·excel
寒水馨1 小时前
Windows下载、安装ollama-v0.32.1(附安装包OllamaSetup.exe)
windows·llm·大语言模型·llama·本地部署·ollama·模型运行
曾阿伦1 小时前
Windows 下运行 Hadoop 并部署到 AWS EMR 指南
hadoop·windows·aws
见合八方2 小时前
【噪声系数】高偏SOA噪声系数测试方法
网络·自动化·soa·光通信·激光雷达·半导体光放大器
曦尧2 小时前
Microsoft Ontology Playground:面向 Fabric IQ 的零后端本体可视化学习平台
ai·自动化
王维同学3 小时前
[原创][Windows C++]Explorer Shell 扩展、图标覆盖与 COM 服务器定位
c++·windows·注册表
AlanBruce3 小时前
从串口仪表到西门子PLC:摩尔信使MThings的多协议设备接入能力
自动化·plc·modbus·mthings·摩尔信使·西门子s7
广州灵眸科技有限公司13 小时前
xfce桌面触摸校准:基于灵眸科技EASY-EAl-Orin-Nano
数据库·windows·科技
微硬创新15 小时前
耐达讯自动化 CC-Link IE 转 PROFINET网关在污水厂智能化升级实战
人工智能·物联网·网络协议·自动化·信息与通信
Ctrl+Z侠18 小时前
.NET WebApi Windows / Linux Docker 全链路压测与瓶颈定位 0 到 1 教程
linux·windows·.net