1. 这个11万star的项目到底在拆什么第一次看到这个项目的star数我以为是某个前端框架或者深度学习库。点进去才发现它干的事情特别“朴素”——把软件界面拆成结构化的数据让AI能看懂屏幕上有什么按钮、什么输入框、什么菜单。听起来简单但真正上手跑通之后我发现它解决的是一个长期被低估的痛点让自动化脚本从“瞎点”变成“看着点”。传统UI自动化是什么路子你写个脚本告诉它“点击坐标(320, 480)”或者“找到文本为‘提交’的按钮”。前者换个分辨率就废了后者遇到动态渲染或者多语言就抓瞎。而这个项目的思路是先把当前屏幕的UI结构完整提取出来转成类似HTML的层级树再交给AI去理解“这个界面是干什么的”“下一步该点哪里”。这就好比以前你是蒙着眼睛在房间里摸开关现在有人给你开灯了还递给你一张房间布局图。这个项目适合谁三类人最应该关注一是做RPA机器人流程自动化的开发者你们肯定被元素定位折磨过二是做AI Agent的团队想让智能体操作桌面软件但苦于没有好的感知层三是测试工程师尤其是做跨平台UI测试的这个工具能帮你省掉大量写选择器的时间。我花了大概两周时间在Windows、macOS和Linux三个平台上都跑了一遍踩了不少坑也总结了一些官方文档里没写的经验。下面就从环境准备开始一步步拆解怎么把这个“拆软件神器”真正用起来。1.1 它和传统UI自动化工具的本质区别很多人第一反应是这不就是Appium、Selenium那套东西吗我一开始也这么想但实际用下来发现区别很大。Appium和Selenium的核心是“驱动”它们通过WebDriver协议去操控浏览器或移动端你需要告诉它具体操作哪个元素。而这个项目的核心是“解析”它不负责点击只负责把当前界面的UI结构完整地、结构化地提取出来。打个比方Selenium像是一个遥控器你按哪个键它执行哪个动作而这个项目像是一台扫描仪它先把整个房间扫一遍生成一张3D地图然后你或者AI看着地图决定怎么走。这个区别决定了它的应用场景更偏向“感知层”而非“执行层”。你可以把它和任何自动化框架结合使用——用它的解析结果来驱动pyautogui做点击或者喂给大语言模型做决策。另一个关键区别是跨平台能力。传统方案里Windows用UIAutomationmacOS用Accessibility APILinux用AT-SPI三套完全不同的接口。而这个项目在底层做了抽象对外暴露统一的接口。我实测下来同一段解析代码在三个平台上都能跑虽然底层调用的API不同但返回的数据结构是一致的。这对需要跨平台部署的团队来说省掉了大量适配工作。1.2 拆解出来的数据长什么样光说概念太虚直接看数据。我在Windows上打开一个记事本运行解析命令后得到的是一个JSON格式的树形结构。每个节点包含这些字段控件类型Button、Edit、Menu等、名称、坐标位置左上角和右下角、是否可见、是否可用、子节点列表。比如记事本的“文件”菜单解析出来大概是这样的{ type: MenuItem, name: 文件, bounds: {left: 10, top: 30, right: 60, bottom: 55}, visible: true, enabled: true, children: [ {type: MenuItem, name: 新建, bounds: {...}}, {type: MenuItem, name: 打开, bounds: {...}} ] }这个结构的好处是AI可以直接理解“文件菜单下面有新建和打开两个选项”而不需要去猜像素。你可以把这个JSON喂给GPT-4问它“我想保存文件应该点哪里”它能准确告诉你点“文件”然后点“保存”。我试过用纯截图让GPT-4识别准确率大概七成但用这个结构化数据准确率能到九成五以上。原因很简单截图有歧义文字没有。2. 环境搭建三个平台各自的坑官方文档给的安装步骤看起来很简单就几行命令。但我实际跑下来每个平台都有各自的坑而且这些坑在文档里基本没提。下面按平台分开说你可以直接跳到对应的部分。2.1 Windows权限和依赖库的隐形门槛Windows上安装本身不复杂pip install就能搞定。但第一次运行的时候我遇到了两个问题。第一个是权限如果你要解析的窗口是以管理员权限运行的那你的Python脚本也必须以管理员身份运行否则拿不到UI结构。这个坑很隐蔽因为报错信息不会直接说“权限不足”而是返回一个空的树。我一开始以为是代码写错了排查了半天才发现是权限问题。第二个是依赖库。这个项目在Windows上依赖comtypes和pywin32这两个库在安装时可能会因为Python版本或者系统架构32位 vs 64位出现兼容性问题。我的建议是直接用Python 3.9到3.11之间的版本太新或太旧都容易出问题。另外如果你用的是虚拟环境确保在虚拟环境里也安装了这些依赖不要只装在全局环境里。还有一个实际使用中的细节Windows的UI Automation接口在解析某些老旧软件比如用Delphi或VB6写的程序时返回的控件树会非常扁平所有控件都堆在一层没有层级关系。这种情况下你需要结合坐标信息来判断控件之间的包含关系。我写了一个简单的后处理函数根据bounding box的包含关系重建树形结构效果还不错。2.2 macOS辅助功能权限的授权流程macOS的坑主要在权限上。系统有一个“辅助功能”权限任何想读取UI结构的程序都必须获得这个权限。第一次运行脚本时系统会弹窗提示你去“系统偏好设置 安全性与隐私 隐私 辅助功能”里勾选你的终端或者IDE。但问题是如果你用的是iTerm或者VS Code可能需要手动把它们的可执行文件拖进去而不是在列表里勾选。我遇到的一个诡异问题是明明已经授权了但脚本还是返回空树。后来发现是因为macOS的权限缓存机制——授权之后需要重启终端或者IDE才能生效。这个在官方文档里没写我是试了三次才发现的。另外如果你在CI/CD环境里跑比如GitHub Actions的macOS runner默认是没有辅助功能权限的需要额外配置这个后面会细说。还有一点macOS的Accessibility API在解析某些原生应用时性能会比Windows差一些尤其是控件数量多的时候。我实测解析一个复杂的Xcode界面大概需要2到3秒而同样复杂度的Windows界面只要0.5秒左右。如果你对实时性要求高可能需要做缓存或者增量解析。2.3 LinuxAT-SPI总线的配置细节Linux上的坑最多因为桌面环境太碎片化了。这个项目在Linux上依赖AT-SPIAssistive Technology Service Provider Interface你需要确保系统里安装了at-spi2-core和相关的Python绑定。在Ubuntu上命令大概是sudo apt-get install at-spi2-core python3-pyatspi但装完之后还需要设置环境变量。默认情况下AT-SPI总线可能没有启动你需要手动启动它或者在桌面环境的启动脚本里加上export GTK_MODULESgail:atk-bridge export OOO_FORCE_DESKTOPgnome这两个环境变量的作用是让GTK应用和LibreOffice等程序把UI信息暴露给AT-SPI。如果不设置你会发现只能解析到很少的控件大部分应用返回的都是空树。我一开始在Xfce桌面环境下跑怎么都拿不到Firefox的UI结构后来换成GNOME才正常。所以如果你在Linux上遇到问题先确认桌面环境是不是GNOME或KDE这两个的兼容性最好。另外Linux上还有一个“无障碍总线”的概念。AT-SPI通过D-Bus通信如果你的系统D-Bus配置有问题也会导致解析失败。可以用dbus-monitor命令来调试看看总线上有没有AT-SPI的消息。这个排查过程比较繁琐但一旦配好后续就很稳定了。3. 核心API的调用逻辑与参数调优环境配好之后下一步就是调用核心API。这个项目对外暴露的接口不多但每个接口都有一些参数值得细说。我用下来感觉默认参数能覆盖80%的场景但剩下20%的场景需要你理解参数背后的逻辑才能调好。3.1 解析入口函数的选择策略项目提供了两个主要的解析函数一个是解析整个桌面包括所有打开的窗口另一个是解析指定窗口。我强烈建议用后者原因有两个一是性能解析整个桌面在窗口多的时候非常慢二是准确性指定窗口能避免其他窗口的干扰。指定窗口的方式有两种通过窗口标题或者通过进程ID。窗口标题的方式更直观但有个问题如果标题是动态的比如浏览器标签页会变你就需要每次重新获取。进程ID的方式更稳定但需要你先找到目标进程的PID。我的做法是结合使用先用进程名找到PID再用PID解析窗口。这样即使窗口标题变了也不影响解析。还有一个细节解析函数有一个max_depth参数控制解析的深度。默认是无限深但实际用的时候建议设成5到8层。为什么因为很多软件的UI树非常深尤其是Web应用嵌入在桌面客户端里的情况解析到十几层的时候返回的数据量会爆炸而且大部分深层节点对你做自动化决策没有帮助。我一般设成6层既能拿到主要控件又不会让数据量太大。3.2 过滤与裁剪让返回数据更干净原始解析结果里有很多“噪音”控件比如不可见的、被禁用的、纯装饰性的容器。这些控件对AI决策没有帮助反而会干扰判断。项目提供了一个filter参数可以按条件过滤。我常用的过滤条件有三个visibleTrue只保留可见控件enabledTrue只保留可用控件type not in [Pane, Group]排除纯容器控件加上这些过滤之后返回的数据量能减少60%到70%而且AI的理解准确率会更高。我做过对比测试不过滤的情况下GPT-4判断“下一步点哪里”的准确率大概是85%过滤之后准确率提升到93%左右。原因很简单噪音少了关键信息就更突出。还有一个裁剪技巧如果你只关心某个区域内的控件可以用bounds参数指定一个矩形区域只解析这个区域内的控件。这在处理复杂界面时特别有用比如你只想操作一个对话框就可以把解析范围限制在对话框的坐标范围内避免解析整个主窗口。3.3 坐标系统的坑逻辑像素与物理像素坐标系统是很容易踩坑的地方尤其是高DPI屏幕。在Windows上如果系统缩放是150%那UI Automation返回的坐标可能是逻辑像素而pyautogui用的可能是物理像素。这两个不一致的话你解析出来的控件位置和实际点击位置就会对不上。我遇到的具体问题是解析出来的按钮坐标是(100, 200)但用pyautogui点击(100, 200)却点到了别的地方。后来发现是因为缩放比例的问题。解决方案是在解析的时候获取系统的DPI缩放比例然后把逻辑坐标乘以缩放比例转换成物理坐标。Windows上可以用ctypes.windll.shcore.GetScaleFactorForDevice(0)来获取缩放比例macOS上可以用Quartz.CoreGraphics的相关接口。Linux上相对简单因为大部分桌面环境的缩放是在合成器层面处理的AT-SPI返回的坐标通常就是物理坐标。但如果你用的是Wayland而不是X11情况会更复杂因为Wayland的安全模型不允许程序随意获取全局坐标。这种情况下你可能需要依赖窗口管理器提供的接口或者改用X11会话。4. 和AI模型对接的实战方案解析出UI结构只是第一步真正的价值在于让AI基于这个结构做决策。我试过三种对接方案各有优劣下面分别说。4.1 直接喂JSON给大语言模型最简单的方式是把解析出来的JSON直接塞进GPT-4的prompt里然后问它“我想完成XX操作应该点哪个控件”。这个方案的好处是零训练成本开箱即用。但有几个注意事项第一JSON不能太大。GPT-4的上下文窗口虽然大但塞太多数据会影响推理速度和准确率。我的经验是控制在2000个token以内大概对应50到80个控件。如果控件太多需要先做过滤和裁剪。第二prompt的设计很关键。我试过几种写法效果最好的是这种结构先描述当前界面的整体功能比如“这是一个文本编辑器的主界面”然后列出可操作的控件用简洁的格式比如“按钮保存位置左上角”最后给出任务描述。这样GPT-4能快速定位到相关控件。第三要处理模型返回的格式。我让GPT-4返回一个JSON包含action点击、输入、滚动等和target控件名称或坐标。然后在代码里解析这个JSON调用pyautogui执行。这个流程跑通之后准确率能到90%以上但偶尔会有幻觉比如返回一个不存在的控件名称。我的做法是加一层校验如果返回的控件名称不在解析结果里就重新问一次或者降级到坐标点击。4.2 本地小模型的微调思路如果你对延迟敏感或者不想依赖外部API可以考虑用本地小模型。我试过用Llama 3 8B做微调训练数据就是“UI结构JSON 任务描述 → 操作指令”这样的三元组。训练数据可以从实际使用中收集每次人工操作之后把当时的UI结构和操作记录下来积累几百条之后就能微调了。微调后的模型在特定软件上的准确率能到85%左右虽然不如GPT-4但延迟低很多而且不需要联网。不过微调的成本不低需要一张显存至少16GB的显卡训练时间大概几个小时。如果你的场景比较固定比如只操作某几个特定软件微调是值得的如果场景多变还是用大模型更灵活。4.3 混合方案规则引擎兜底纯AI方案有个问题不稳定。同样的界面模型可能这次判断对了下次就错了。为了提高稳定性我在AI决策之外加了一层规则引擎。规则引擎处理那些高频、固定的操作比如“点击保存按钮”“关闭当前窗口”。这些操作不需要AI判断直接用规则匹配就行。规则引擎的实现很简单维护一个“控件名称 → 操作”的映射表解析出UI结构后先查表如果命中就直接执行不命中再交给AI。这样能把AI的调用频率降低70%左右整体稳定性也提升了不少。我实测下来混合方案的端到端成功率能到95%以上而纯AI方案大概在85%到90%之间波动。5. 踩坑实录那些文档没告诉你的问题这一部分是我在实际使用中遇到的最棘手的几个问题以及排查过程。如果你也遇到类似情况可以按这个思路来。5.1 解析结果为空从权限到总线的完整排查链路解析结果为空是最常见的问题可能的原因有五六种。我总结了一个排查顺序按这个顺序走基本能定位到问题。第一步检查权限。Windows上确认脚本是否以管理员身份运行macOS上确认辅助功能权限是否已授权并重启了终端Linux上确认AT-SPI总线是否启动。这一步能解决60%的问题。第二步检查目标窗口是否在前台。有些系统只允许解析前台窗口的UI结构如果目标窗口被其他窗口遮挡解析会返回空。解决办法是用SetForegroundWindowWindows或activatemacOS把目标窗口激活。第三步检查依赖库版本。这个项目的不同版本对依赖库的要求不一样比如某个版本要求pywin32305你装的是304就会出问题。用pip list确认一下版本必要时升级或降级。第四步检查系统语言和区域设置。这个坑很隐蔽某些系统在非英语区域设置下UI Automation返回的控件名称可能是乱码或者空字符串。解决办法是把系统区域设置改成英语美国或者用控件的类型和坐标来识别而不是依赖名称。第五步如果以上都没问题那就是目标软件本身不支持无障碍接口。有些软件尤其是游戏和某些专业工具用的是自绘UI不暴露标准的无障碍接口。这种情况下这个项目就无能为力了只能退回截图OCR的方案。5.2 动态界面的解析时机问题很多软件的界面是动态的菜单展开、对话框弹出、加载动画这些都会改变UI结构。如果你在界面还没稳定的时候就解析拿到的数据是不完整的。我遇到过一个典型场景点击“文件”菜单后菜单项有一个淡入动画大概300毫秒。如果在动画过程中解析只能拿到部分菜单项。解决方案是加一个“等待稳定”的逻辑解析之前先连续解析两次比较两次的结果是否一致。如果一致说明界面稳定了如果不一致等200毫秒再试。这个逻辑我封装成了一个函数调用起来很方便。实测下来加上这个等待之后解析准确率提升了15%左右。还有一个相关的问题某些操作会触发界面刷新刷新过程中解析会拿到旧数据。比如点击“刷新”按钮后列表内容会更新但更新是异步的。这种情况下你需要等待某个特定控件出现或消失而不是简单等固定时间。我一般用“轮询超时”的方式每隔100毫秒解析一次直到目标控件出现或者超过5秒超时。5.3 多显示器与虚拟桌面的坐标偏移多显示器环境下坐标系统会变得复杂。Windows上主显示器的左上角是(0,0)副显示器可能在左边负坐标或右边正坐标。如果你用pyautogui点击它用的是虚拟桌面的坐标系而UI Automation返回的可能是相对于某个显示器的坐标。这两个不一致的话点击就会偏移。我遇到的具体问题是在双显示器环境下解析出来的按钮坐标是(100, 200)但实际点击却点到了主显示器的对应位置而不是副显示器上的按钮。解决办法是获取每个显示器的偏移量然后把控件坐标转换成虚拟桌面坐标。Windows上可以用win32api.EnumDisplayMonitors来获取显示器信息macOS上可以用NSScreen.screens。虚拟桌面比如Windows的“任务视图”是另一个坑。虚拟桌面里的窗口UI Automation可能无法正确解析因为窗口实际上不在当前桌面上。这种情况下你需要先切换到目标窗口所在的虚拟桌面再解析。这个操作在Windows上可以用IVirtualDesktopManager接口来实现但需要额外的COM调用稍微有点复杂。6. 性能优化从秒级到毫秒级的调优经验默认配置下解析一个中等复杂度的界面大概需要500毫秒到1秒。如果你需要高频解析比如做实时监控这个速度是不够的。我通过几个优化手段把解析时间降到了100毫秒以内。6.1 增量解析与缓存策略全量解析每次都要遍历整个UI树开销很大。但实际上大部分界面的变化是局部的可能只是某个文本框的内容变了或者某个按钮的可用状态变了。增量解析的思路是只解析发生变化的子树其他部分用缓存。实现方式有两种一是监听UI事件Windows上有UIAutomation的事件接口macOS上有AXObserver当事件触发时只重新解析受影响的节点二是定期做全量解析但两次解析之间做diff只更新变化的部分。第一种方式更高效但实现复杂第二种方式简单一些适合对实时性要求不那么高的场景。我用的是第二种方式每500毫秒做一次全量解析然后和上一次的结果做diff。如果diff很小比如只变了几个节点就只更新那几个节点如果diff很大比如界面切换了就全量更新。这样在界面稳定的情况下实际的计算量很小解析时间能降到50毫秒左右。6.2 并行解析与异步IO另一个优化思路是并行化。UI树的各个子树之间是独立的可以并行解析。Python里可以用concurrent.futures.ThreadPoolExecutor来实现。我试过开4个线程解析时间从800毫秒降到了250毫秒左右。但要注意UI Automation的接口不一定是线程安全的多线程调用可能会出问题。我的做法是主线程负责遍历树结构把叶子节点的解析任务分发给线程池。这样既利用了并行又避免了并发访问同一个接口。异步IO是另一个方向。如果你用的是asyncio框架可以把解析操作包装成异步任务这样在等待解析结果的时候可以同时做其他事情。不过UI Automation的底层是同步的包装成异步只是表面上的异步实际执行还是阻塞的。真正要异步需要用多进程或者C扩展。这个成本比较高一般场景下用线程池就够了。6.3 裁剪解析范围的具体参数前面提到过用bounds参数裁剪解析范围这里展开说一下具体怎么设。假设你要操作一个对话框对话框的坐标是(400, 300)到(800, 600)。你可以把bounds设成{left: 400, top: 300, right: 800, bottom: 600}这样只解析这个区域内的控件。但有个细节有些控件的bounding box可能部分在区域内部分在区域外。默认情况下只要控件的中心点在区域内就会被解析。如果你希望更严格可以设置strict_boundsTrue这样只有完全在区域内的控件才会被解析。我一般用默认的非严格模式因为部分在区域内的控件往往也是相关的。裁剪之后解析时间能减少50%到80%具体取决于界面复杂度。我实测过一个复杂的设置界面全量解析要1.2秒裁剪到只解析当前标签页之后只要200毫秒。这个提升非常明显。7. 实际应用场景的落地案例说了这么多技术细节最后分享几个我实际落地的场景让你更直观地理解这个工具能干什么。7.1 跨平台软件测试的自动化巡检我帮一个团队做过跨平台桌面软件的自动化测试。他们的软件有Windows、macOS、Linux三个版本以前每个平台都要写一套测试脚本维护成本很高。用这个工具之后测试脚本只需要写一套解析UI结构然后根据控件名称和类型来断言。比如“检查主窗口是否有‘保存’按钮”“检查点击‘新建’后是否弹出了对话框”。这个方案的好处是跨平台一致。同一段测试代码在三个平台上都能跑因为解析出来的数据结构是一样的。而且因为是基于UI结构而不是坐标所以对分辨率和缩放的适应性也很好。他们原来跑一轮回归测试要2个小时现在只要40分钟而且误报率从15%降到了3%以下。7.2 给RPA流程加上“眼睛”另一个场景是RPA。传统的RPA流程很脆弱网页改个版、软件换个皮肤流程就断了。我帮一个做财务自动化的团队改造了他们的RPA流程原来是用坐标点击现在改成先解析UI结构找到目标控件再点击。改造之后流程的稳定性大幅提升原来每周都要修几次现在一个月都不用动。具体做法是在RPA的每个步骤之前先调用解析函数拿到当前界面的控件列表。然后用一个简单的匹配算法按名称和类型匹配找到目标控件获取它的坐标再调用pyautogui点击。如果找不到目标控件就抛出异常并截图方便排查。这个改造大概花了三天时间但后续节省的维护时间远超这个投入。7.3 辅助工具让AI帮你操作复杂软件最后一个场景比较有意思我做一个辅助工具帮助不熟悉复杂软件的用户完成操作。比如某个专业的视频编辑软件菜单层级很深新手找不到功能。这个工具的做法是用户用自然语言描述想做什么比如“把视频导出成MP4格式”工具解析当前UI结构喂给GPT-4让GPT-4生成操作步骤然后自动执行。这个场景对准确率要求很高因为操作错了可能会丢失数据。我的做法是加一个“确认”步骤AI生成操作步骤后先展示给用户看用户确认后再执行。这样既降低了风险又让用户学到了操作路径。实测下来GPT-4在视频编辑软件上的操作准确率大概80%加上用户确认之后实际成功率能到95%以上。8. 一些零散但重要的经验最后分享几个零散的经验都是我在实际使用中总结的不一定系统但都挺实用。关于版本选择这个项目更新很频繁但新版本不一定稳定。我的建议是用比最新版晚一个版本比如最新是2.5你就用2.4。这样既不会太旧又避开了最新版可能有的bug。另外锁定依赖库的版本很重要不要用pip install -U随便升级很容易出现兼容性问题。关于日志解析失败的时候日志非常重要。我建议在代码里加上详细的日志记录每次解析的耗时、返回的控件数量、过滤前后的差异。这样出问题的时候能快速定位是解析本身的问题还是过滤逻辑的问题。我一般用Python的logging模块把日志输出到文件方便后续分析。关于测试这个工具本身也需要测试。我写了一个简单的测试脚本用几个常见的软件记事本、计算器、浏览器做基准测试每次升级版本或者改代码之后跑一遍测试确认解析结果没有大的变化。这个习惯帮我避免了好几次因为升级导致的意外问题。关于社区这个项目的社区很活跃Issues里有很多有价值的讨论。我遇到的大部分问题在Issues里都能找到答案。建议在使用之前先花半个小时翻一翻最近的Issues看看有没有你可能会遇到的坑。另外项目的Discussions板块也有很多实战经验分享比官方文档更接地气。关于安全这个工具能读取UI结构意味着它能“看到”屏幕上的所有内容包括敏感信息。如果你在共享环境或者生产环境使用一定要注意权限控制避免泄露敏感数据。我的做法是在解析之前先过滤掉包含敏感关键词的控件比如“密码”“账号”或者对解析结果做脱敏处理。这个在金融、医疗等场景下尤其重要。关于扩展这个项目的架构设计得不错很容易扩展。比如你可以自定义解析器支持特定的软件或者自定义过滤器按自己的需求裁剪数据。我写过一个自定义解析器专门处理某个用Qt写的软件因为默认的解析器拿不到Qt的控件树。扩展的方式是继承基类实现几个关键方法然后在配置里注册。这个在官方文档里有说明但例子不多需要自己摸索一下。
阅读完成 · 觉得有帮助?