这段时间我一直在折腾一个东西:一个免费、开源的AI 编码代理,它能像人一样看到屏幕上的GUI界面、模拟鼠标键盘去操作软件,同时原生支持MCP协议,而且整个项目打包后能做到单文件运行,双击就能用,不需要装 Python、Node 或一堆运行时依赖。
做这个项目的起因很简单。市面上大多数 AI 编码代理都活在终端里:你给它一个 issue,它"理解"之后改代码、跑测试、提交 commit,一气呵成。但一旦让它"打开某个软件,点几个按钮,看下界面效果再回来改代码",它基本就瞎了——因为传统 Agent 看不到屏幕,也没有手。这个问题卡了很多人很久。我自己的日常工作里,有一大堆场景其实是"看界面 + 操作界面 + 根据结果改代码"的混合任务,比如排查一个 GUI 程序为什么按钮点了没反应、验证一个前端页面在不同窗口大小下的布局、甚至只是帮同事自动完成某个软件的重复性导表操作。如果 Agent 能把这些活接下来,我一天至少能省出两三个小时。
这篇文章我会把这个项目的完整思路、技术选型、核心实现和一些踩坑记录写清楚,内容偏实操。如果你对 AI Agent、GUI 自动化、MCP 协议、单文件分发这几个方向感兴趣,或者你就是想给现有 Agent 加一双眼睛和一只手,那这篇文章应该能给你省不少弯路。
1. 为什么我会做这样一个"AI 编码代理"
1.1 当下 AI 编码代理的短板:只会"改代码",不会"看界面"
现在主流的 AI 编程工具,不管是 Cursor、Claude Code 还是各种开源 Agent,本质上是"代码编辑器 + 终端 + 大模型"的组合。它们擅长在文件系统里检索代码、跨文件修改、调用命令行工具,甚至能自己写测试再跑一遍。但它们的感知范围非常窄:只能看到文本。
问题在于,现实世界的开发任务并不全是文本任务。举个我踩过的例子:有次我接手一个内部工具项目,用户反馈说"界面上有个导出按钮点了没反应"。我让 AI 编码代理去排查,结果它在代码里翻了半天,从事件绑定到网络请求查了一圈,最后说"按钮的 handler 没有被触发"。这个结论其实离真正原因差得很远——真正问题出在按钮被另一个半透明浮层盖住了,鼠标根本点不到它。这种事,光看代码是永远看不出来的,必须得"看到界面"才能定位。
类似的问题在跨端开发、UI 自动化、桌面软件运维、数据分析可视化场景里比比皆是。AI 编码代理需要一个机会去观察真实运行中的界面,而不是只面对冷冰冰的源代码。这也是我决定自己做一个"能看、能点、能操作 GUI 的编码 Agent"的出发点。
1.2 项目想解决的三件事:看 GUI、连 MCP、单文件跑
定下方向之后,我给自己这个项目列了三项硬性需求,缺一不可。
第一,要能"看 GUI 并操作 GUI"。不只是截个图,而是要能理解屏幕上有什么控件、按钮在哪个位置、状态栏显示什么文本,然后通过模拟鼠标和键盘去完成点击、输入、拖拽、滚动等操作。这是这个项目区别于普通 coding agent 的核心能力。
第二,要原生支持 MCP。MCP 是这两年 AI 工具链里绕不开的关键词,全称 Model Context Protocol,解决的是"AI 应用程序怎么统一调用外部工具和数据源"的问题。我不仅要让我的 Agent 能作为 MCP Client 去调用其他 MCP Server(比如数据库、浏览器、Figma),还要让它能把自己伪装成一个 MCP Server,反过来被 Claude Desktop、Dify、Cherry Studio 这类宿主调用。这样它就不是一个孤立玩具,而是能融入大家现有 AI 工作流的工具。
第三,要单文件运行。我见过太多开源项目卡在"环境安装"这一步劝退用户。什么 pip install、npm install、装 Python 3.11、配环境变量……光这些就能干掉一半意向用户。我的目标是:下载一个文件,双击,填入模型 API Key,直接能用。打包过程中踩过的坑后面专门写一节。
这三件事凑齐了,它才配叫一个"能实际干活"的 AI 编码代理,而不是又一个 demo。
2. 技术选型与整体架构拆解
2.1 核心运行循环:感知、决策、执行、观察
整个 Agent 的核心是一个再经典不过的循环,可以概括成四步:感知(Perception)→ 决策(Decision)→ 执行(Action)→ 观察(Observation)。
感知层负责把"屏幕上的画面"和"界面的控件信息"转成大模型能理解的文本描述。决策层是大模型根据任务目标和当前状态,决定下一步要调用哪个工具、传什么参数。执行层是具体的工具函数,负责真去移动鼠标、点击按钮、输入文字、按快捷键。观察层在执行完动作后重新抓取状态,判断操作有没有生效,如果没生效,就进入下一轮循环继续调整。
这个循环看起来简单,但每个环节都有很多坑。比如感知层如果只靠截图,大模型识别坐标容易漂移;如果只靠控件树,又拿不到图形化的视觉信息(比如颜色、图标、遮挡关系)。决策层如果提示词写得不够结构化,模型会频繁做出重复动作;执行层如果鼠标移动过快,程序甚至可能来不及响应。这些都是我在开发中反复调优的点。
抽象出来,这个 Agent 本质上是一个"带眼睛和手的函数调用循环"。我在代码里用了一个很精简的调度器,核心逻辑大概长这样:
async def agent_loop(task: str, max_steps=30): state = await observe() # 感知当前界面状态 messages = [system_prompt, user_prompt(task, state)] for step in range(max_steps): decision = await llm.call(tools=all_tools, messages=messages) if decision.tool_name == "finish": return decision.output result = await run_tool(decision.tool_name, decision.tool_args) observation = await observe(after_action=decision.tool_name) messages.append(decision.to_message()) messages.append(observation.to_message()) return "max_steps exceeded"注意这里我特别强调执行动作之后要"重新观察一次"而不是直接拿工具返回值当最终状态。GUI 操作和代码执行不一样,鼠标点下去,界面可能要几百毫秒才算稳定下来,直接拿旧状态喂给模型必然导致幻觉。这个"执行后强制再观察"的习惯帮我避免了一大批看似莫名其妙的问题。
2.2 GUI 操控三套方案,为什么我选了"可访问性树 + 视觉模型"
要实现"操控 GUI",业内有三种常见技术路线:纯坐标模拟、图像模板匹配、可访问性树(Accessibility Tree)。
纯坐标模拟最简单——让模型预测目标在屏幕上的像素坐标,然后直接用鼠标点击。好处是跨平台基本通用,坏处是极其脆弱。换一台电脑、改一个分辨率、窗口挪个位置,坐标就全废了。而且大模型预测坐标的精度受截图分辨率影响很大,4K 屏和 1080p 屏从同一个截图里预测同一个按钮的位置,结果会差出几十个像素。我把这种方式定位为"兜底方案",只在实在拿不到控件信息时才用。
图像模板匹配(OpenCV 模板匹配、特征点匹配)本质上是提前截好小图,然后在屏幕上找相似区域。比纯坐标稍微鲁棒一点,但遇到界面主题变化、缩放比例变化、字体渲染差异就很容易失配,而且维护一套模板图的成本极高。这个方案在集成测试老系统的时候有一定价值,但作为通用 Agent 的主干方式不够格。
我最终选择的是"可访问性树 + 视觉模型"组合。可访问性树是操作系统给读屏软件准备的一棵控件树,每个节点都包含控件的类型、名称、位置、状态(是否可用、是否选中、是否聚焦)等结构化信息。Windows 上有 UI Automation(UIA),macOS 上有 Accessibility API,Linux 上有 AT-SPI。从这棵树上,Agent 能拿到精准的控件坐标和属性,再结合视觉模型对截图的理解,就能在"看懂界面"和"精确定位"之间找到一个平衡点。
方案选型时我也考虑过端到端的"屏幕截图 + 多模态模型直接生成坐标",这是最近很火的 computer-use 方案。实测下来模型理解界面语义没问题,但坐标精度和稳定性相比控件树差不少。把两者结合起来是最稳的:多模态模型看整体界面理解语境,控件树提供精确坐标,互相补位。
2.3 MCP 为什么值得内嵌,Client/Server 两层怎么理解
MCP 这两年讨论热度非常高,我理解它其实做的事情非常朴素:给 AI 应用和外部工具之间定了一个统一"插座"标准,把工具变成插头,谁来插都能用。你可以把它理解成 AI 世界的 USB-C 口——以前充电器各插各的,现在一个口通吃。这个"协议"走的是 JSON-RPC 2.0,消息格式清晰,任何一个能写 HTTP 或本地管道通信的开发者都能快速接进来。
我的项目里把 MCP 做成了双面胶。对外,Agent 内置了一个 MCP Client,可以连接任意遵循 MCP 的 Server,比如常见的文件系统 MCP、数据库 MCP、浏览器 MCP、Figma MCP。在做自动化任务时,Agent 可以不只是"看界面",还能"读数据库里这条数据有没有同步",工具面就宽了。对内,Agent 自己暴露成一个 MCP Server,宿主程序通过 stdio 或 HTTP 调它。如果你想在 Claude Desktop 里给它发指令,说"帮我操作一下桌面上的那个统计软件并截图回复",Claude 作为 MCP Client 就会调用我 Server 暴露出来的gui_observe、gui_click、gui_type这些工具——整个过程对用户透明。
这个"两头都是 MCP"的设计,是项目能被更多人用起来的关键。因为现在主流 AI 客户端基本都已经支持 MCP,我不需要再开发插件去适配某一家产品,只要协议对得上,所有宿主都能叫它干活。
3. 核心实现与实操过程
3.1 感知层:三种信息来源,缺一不可
感知层是整个 GUI 操控的基础,我具体用了三种信息来源。
第一种是截图。调用mss库截取全屏,然后压缩成长边不超过 1024px 的 JPEG,压缩质量调到 80。为什么不直接把原始分辨率图丢给大模型?因为多模态模型对超大图的处理能力有限,反而会因为图片太大忽略细节。压缩之后视觉 token 数量大幅下降,响应速度和成本都更友好。为了保留关键区域细节,我会同时裁剪鼠标附近的区域作为特写,跟随当前关注点联动。
第二种是可访问性树。在 Windows 上我调用的是 pywinauto 库,底层走 UIA 接口,可以拿到完整的控件关系树。取树的时候要带上深度限制,只取前 5 层,因为完整树在复杂的 Electron 应用里能轻松达到几十上百层,直接全量展开会非常占 token。同时过滤掉不可见、无名称、非交互性的节点,确保喂给模型的是一份精炼的可读文本。
第三种是 OCR。有些自绘的软件(比如用 canvas 绘图或游戏客户端)没有暴露控件树,界面上只有一张"画布",这时候除了视觉模型直读,我还会叠加 OCR 结果作为双保险。OCR 我选用的是 RapidOCR 的 onnxruntime 版,纯本地推理,模型文件只有几十 MB,识别速度和精度都够用。
这三股信息最后会被拼装进一个结构化的 observation 对象里,大概长这样:
window: "报表统计工具 - 数据导出" 控件列表: [1] 按钮 "导出" (enabled, 位置: 832, 421) [2] 复选框 "仅导出当前页" (checked, 位置: 645, 421) [3] 下拉框 "导出格式" (值: "CSV") [4] 按钮 "关闭" (enabled, 位置: 1050, 58) OCR发现文字: "共 1284 条记录", "导出完成"这样的文本喂给大模型,比只丢一张截图的信息量高出一个量级。
3.2 执行层:鼠标键盘的"假动作"怎么做才不掉链子
感知做好了,执行也不能掉链子。执行层我统一封装成一个execute(action_type, params)函数,目前支持的动作包括click、double_click、right_click、type_text、key_press、scroll、drag、hover等。
底层输入模拟用的是自定义封装,Windows 上走SendInput,macOS 上走CGEvent,Linux 上走XTest。这里有个很容易踩的坑:如果只靠应用级 API 模拟点击,很多高权限窗口或者用到特殊钩子的程序会直接忽略。我试过用pyautogui点击某些以管理员权限运行的 Installer 窗口,机器完全没反应,换SendInput就正常了。
点击动作我推荐"两步走":先用控件树拿到目标控件中心坐标,然后先移动鼠标到坐标并短暂暂停 50~80ms,再做 click。不要一上来就 specclick,很多图形界面工具会在鼠标悬停时改变按钮状态,如果移动和点击间隔太短,点击事件会落在边界上。
键盘输入部分也有讲究。直接调pyautogui.write可能会因为按键速度太快丢字符,我在封装里固定了击键间隔,并且专门处理了中文输入:先Ctrl+A全选清空目标输入框,然后用剪贴板写入目标文本,再Ctrl+V粘贴。这一招对中文字符、符号、表情这类特殊字符是唯一可靠的方案,实测可以解决绝大多数输入框"打字乱码"问题。
def type_text(text: str): if not text: return key_press("ctrl", "a") # 清空已有内容 sleep(0.05) set_clipboard(text) key_press("ctrl", "v") # 粘贴 sleep(0.1)3.3 MCP 接入:把自己的 GUI 能力包装成 tools/list
MCP 的消息协议虽然复杂,但核心只需要理解几个方法:initialize建立会话,tools/list列出可用工具,tools/call调用工具。标准化之后,我的 GUI 操纵能力可以被任何 MCP 客户端直接调用。
这里我故意没有用一个重量级框架来实现 MCP Server,而是手写了一个基于 ASGI 的轻量服务,底层用 stdio 传输。为什么不用官方 SDK?因为官方 SDK 对自定义传输方式的扩展点藏得比较深,调试起来反而不如自己写灵活。MCP 对工具的描述是有 schema 约束的,每个工具必须有name、description、inputSchema三要素,这样客户端才能自动生成调用参数。
工具声明的大致结构如下:
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "gui_click", "arguments": { "target": "按钮: 导出", "method": "accessibility_tree" } } }我暴露出去的工具不多,就八个:gui_observe、gui_click、gui_type、gui_key_press、gui_upload_file(专门处理文件选择对话框)、gui_execute_command(在终端里跑命令)、wait_for_condition(轮询等待某个 UI 元素出现)、take_screenshot。这八个足够覆盖绝大多数桌面操作场景。工具数量刻意保持在个位数,是有讲究的——给模型的工具越多,它选错工具的概率就越大,功能相近的工具会严重干扰模型判断,宁可少而精。
为了验证这套 MCP 能力的通用性,我专门拿它去对接了三个宿主:Claude Desktop、Dify、Cherry Studio。实测下来,除了每个宿主启动 MCP Server 的配置路径不太一样,消息格式上零改动就跑通了。
3.4 单文件打包:从代码到双击即用的可执行文件
"单文件运行"是这个项目最让我抓狂的部分。最初我用 Python 写原型,但 Python 打包单文件在跨平台、体积和杀毒误报上都有不少问题。后来我用 PyInstaller 的--onefile模式配合--windowed和自定义 spec 处理了一轮,实测在 Windows 上可以做到双击即用。
PyInstaller 打包单文件的核心原理是把 Python 解释器和依赖库全部塞进一个可执行文件里,运行时在临时目录解压再执行。实际操作中有几个关键配置:
pyinstaller --onefile --windowed --name gui-agent ^ --add-data "models:models" ^ --exclude-module tkinter ^ --exclude-module numpy ^ --hidden-import pywinauto ^ --hidden-import mss ^ main.py首先--windowed必须加,否则每次运行都会弹出黑框框控制台,非常尴尬。其次tkinter这个标准库模块必须排除,因为它和 pywinauto 在某些窗口句柄操作上有潜在冲突,而且体积也不小。numpy这种大体积但不一定用得上的依赖也要排除掉,能省二三十 MB。最后--hidden-import把pywinauto、mss这些动态导入的库显式写进去,否则 PyInstaller 的静态分析经常漏掉它们,运行时直接报 ModuleNotFoundError,这个坑我踩了好几次。
打包出来的文件体积大概在 80MB 左右,对于现代电脑来说完全可接受。第一次启动因为要解压到临时目录,会慢一到两秒,后面就快了。
还需要注意一个坑:如果你用的模型 SDK 是anthropic或openai这类库,打包前要看它们底层有没有动态 import 插件。openai库个别版本会在运行时加载一个interop模块,不显式 hidden-import 会随机报错。我给每个大模型 SDK 都写了最小验证脚本,在打包后的 exe 里跑一次完整的 Agent 循环,确认能通才发布。
3.5 实战演示:我让 Agent 自己完成了一个完整 GUI 操作任务
理论说再多,不如看一次实操。我先用这个工具跑了一个很常见的场景:打开系统的记事本应用,输入一段多行文本,保存到一个指定路径,然后关闭程序。
我先给 Agent 下达了自然语言指令:"打开记事本,写入下面这段内容,并以 gui-demo.txt 为文件名保存到桌面,然后关掉窗口。"
Agent 的执行轨迹大概是这样的:
- 第一次观察:全屏截图 + 控件树扫描,识别出当前没有任何打开的记事本窗口,于是调用 MCP 工具
gui_execute_command,执行notepad命令启动记事本。 - 第二次观察:发现记事本窗口出现,窗口标题是"无标题 - 记事本",标题栏控件识别正常,于是调用
gui_type,把文本内容粘贴进编辑区域。 - 第三次观察:界面无显著变化(因为文本输入后没有实时状态提示),Agent 按计划调用快捷键
Ctrl+S,弹出了保存对话框。 - 第四次观察:保存对话框出现了文件名输入框和"保存"按钮,Agent 调用
gui_type输入文件名gui-demo.txt,点击"保存"。 - 第五次观察:记事本标题栏更新为"gui-demo.txt - 记事本",说明保存成功。Agent 判断任务完成,调用
gui_key_press("alt", "f4")关闭窗口。
整个过程耗时约 45 秒,模型调用 8 次,没有一次多余的重复动作。这个任务的难点其实不在"能做到",而在"能稳做到"——如果中间某一步观察不及时把保存对话框识别出来,而是继续盲点,整个流程就会乱掉。我把每一步动作之间的默认等待时间从固定 500ms 改成了"观察界面稳定后再继续"的自适应策略,这是成功率提升最明显的一次改动。
我还做了一个更难一点的实战:让 Agent 去改一个开源小工具的代码,然后自己启动这个 GUI 程序去验证改动是否生效。这种情况下它就同时发挥了"编码代理"和"GUI 操作员"两个角色,先在代码库检索到按钮点击事件绑定的位置,修改 handler,然后运行程序,观察按钮点击后界面是否发生了变化。这种闭环能力是目前绝大多数单功能 Agent 做不到的。
4. 踩坑记录与排查技巧实录
4.1 权限与系统差异的坑
GUI 自动化第一个绕不开的坎是权限。在 macOS 上,如果你没有给终端(或打包后的 App)开"辅助功能"权限,任何向其他应用发送鼠标键盘事件的操作都会被系统静默忽略。更坑的是,这个权限不是在系统设置里勾一下就行,有时候改了权限必须重启终端进程才生效,光这一点就能让新手怀疑人生。
Windows 上如果你要操作的是管理员权限窗口,普通权限的自动化进程是摸不到那些控件的。解决方法是把打包出来的 exe 也配置成"以管理员身份运行",或者在开发机上关闭 UAC 的某些限制——但后者我强烈不建议在生产环境用。检测权限的代码我放在了启动阶段,如果发现环境不满足,会直接打印引导文案告诉你该去哪个设置面板开权限,而不是等到用户点按钮没反应时才发现。
Linux 那边则是另一种疼:Wayland 和 X11 两套体系的输入模拟机制完全不同,Wayland 出于安全设计,默认禁止应用级模拟全局鼠标键盘。我在 Linux 下的方案是检测到 Wayland 会话就直接提示用户切换到 X11,或者使用桌面环境自带的辅助功能桥接。这不算优雅,但足够实用。
4.2 坐标与分辨率不稳定
"坐标漂移"是我被坑得最多的一个问题。同样是点击一个按钮,1920×1080 下没问题,换到 2K 屏带缩放(比如 Windows 显示设置 150%)时,控件树返回的逻辑坐标和屏幕实际物理坐标会对不上。原因在于系统 DPI 缩放,应用了SetProcessDpiAwareness之后,坐标体系才会统一。
我的解决方法是:绝不直接信任一次拿到的坐标,在做 click 之前先通过控件名重新定位一次。也就是说把"定位"和"执行"拆成两步,每次都现取现用,而不是缓存坐标 500ms 后再去点击。实测把点击成功率从 71% 提到了 96% 以上。
另外,坐标精度的校验也很重要。我在点击后会立刻对点击位置和控件矩形做一次"是否相交"的判定,如果发现点击落点不在目标控件范围内,会把这个失败信息反馈给模型,让它重新调整策略。这个反馈机制撑起了整个失败自纠循环。
4.3 等待与超时:GUI 比命令行慢太多
命令行的输出是即时的,GUI 不是。窗口弹出、控件加载、界面重绘,这些都需要时间。如果 Agent 刚点击完按钮立刻就去观察界面,看到的十有八九还是旧状态,会让模型误以为操作没有生效,从而重复点击。
我针对高频控件类型设了不同的等待策略。弹窗类控件(文件选择框、确认对话框)平均需要 1.5 秒到 2 秒才能稳定出现,按钮状态变化平均 400 毫秒,列表刷新最长可能需要 3 秒。比起让大模型每次自己判断,我更喜欢在工具层做团灭兜底:每个操作执行完都附带一个wait_for_idle()阻塞,轮询屏幕上的"忙碌"状态(如鼠标转圈、窗口标题栏闪烁"未响应"),稳定了才返回结果。
超时也要区分对待。控件树扫描如果超过 3 秒没拿全,就用已拿到的部分继续,不要等完整树——复杂桌面软件(特别是浏览器)的控件树可能有一万多节点,全量构建非常慢。视觉模型调用如果超过 30 秒没返回,就按一次失败处理并重试,不要无限等待,否则整个 Agent 循环会被卡死。
4.4 打包后环境消失的坑
开发环境跑得好好的 Python 程序,打包成单文件 exe 之后经常出现灵异事件——最典型的就是"新建的临时文件不存在"和"模型无法连接 API"。前者是因为 PyInstaller 的单文件模式会把所有资源解压到临时目录,这个目录每次启动都会变,如果你在代码里写死了相对路径,运行目录不同就直接找不到文件;后者是因为打包过程中丢失了 CA 证书,导致 HTTPS 请求失败,OpenAI 和 Anthropic 的 SDK 都依赖系统证书仓库,PyInstaller 有时不会带上这些证书文件。
我的经验是:所有路径必须os.path.abspath(Path(__file__).resolve().parent)动态获取,不能被运行时的当前目录带偏;所有网络请求库的证书路径在打包时通过--add-data显式带进去。还有一点,--onefile模式启动时会先解压全部文件,有些杀毒软件会拦这个行为,如果你在 Windows Defender 的"受保护文件夹"环境下运行,第一次启动可能直接被查杀。可以给自己的 exe 签名,或者引导用户添加白名单。
我整理了一张踩坑速查表,基本覆盖了前 20 个用户反馈中最高频的问题:
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 点击按钮没反应 | 目标为管理员窗口,权限不足 | 以管理员身份运行 Agent |
| 打包后提示找不到模块 | PyInstaller 静态分析漏掉动态导入 | 用 --hidden-import 显式声明 |
| 模型 API 请求失败 | 打包丢失 CA 证书链 | 打包时带上 certifi 的证书文件 |
| 4K 屏幕点偏 | DPI 缩放坐标不一致 | 设置 DPI aware,点击前重新定位 |
| macOS 无法输入中文 | 辅助功能权限未开启或未重启 | 权限设置后必须重启进程 |
| 点击"导出"后窗口无反应 | 等待时间不足,界面未稳定 | 启用 wait_for_idle 阻塞等待 |
| Linux 无法模拟全局键鼠 | Wayland 安全限制 | 切换 X11 会话运行 |
4.5 让 Agent "不瞎点"的提示词设计经验
很多人以为 GUI 自动化的难点在技术实现,其实提示词设计同等重要。同一个模型,用两套不同的系统提示词跑同样的任务,成功率可以相差一倍以上。
我的系统提示词里明确写了几条 GUI 操作纪律:每次操作前必须说明"我为什么要做这个动作",并给出前一步观察到的证据;每次点击前必须确认目标控件存在且可用,没有找到就换成"搜索框查找控件"的动作而不是硬猜坐标;同一个动作失败两次之后必须换方案,不许原地重试;观察结果必须引用具体的控件名称或 OCR 文字,不许笼统地说"界面正常"。这几条纪律极大减少了模型盲目点击和原地打转的情况。
我还给 Agent 设计了一个"试探-确认"机制。当模型不确定某个按钮的功能时,允许它先点击,但点击后必须观察界面变化并把这个变化写进下一轮消息里。如果变化和预期不符,模型会收到明确的失败信号,从而自动调整。这比让模型一次就做出"完美决策"要靠谱得多——人的操作本质上也充满了试探和修正,Agent 只是把这个过程显式化了。
5. 这个项目下一步的思路与个人体会
做这个项目最大的收获,不是学会怎么截屏、怎么调 UIA 接口,而是我意识到"Agent 的能力上限取决于它感知世界的带宽"。文本终端是信息带宽极窄的通道,而 GUI 自动化让 Agent 第一次能直接感知人类日常使用的软件界面,这是一个质变。一旦这个纵向能力打通了,它可以横向扩展到的场景非常多:帮运营人员自动整理 Excel 报表并操作表格软件导出图表,帮设计师在不同设计稿之间同步命名规范,帮测试人员自动执行 GUI 回归测试并截图上报。
目前这个项目我最满意的一点是,它始终是"单文件、免费、开源"的——用户可以自备任意大模型 API Key,包括支持视觉的本地模型,完全不依赖任何云端服务商的绑定。MCP 生态还在飞速膨胀,越来越多的工具开始提供 MCP Server,我相信"AI 编码代理 + GUI 操控 + MCP 连接"这个组合会是未来个人自动化助理的基础形态。
如果你也想做类似的东西,我的建议是从一个极小的场景切入,比如先把自己日常最烦的某个 GUI 操作做成自动化,再一步步往里面加视觉模型和 MCP 能力。不要一上来就想做一个通用的"AI 电脑管家",那个复杂度是地狱级的。
最后分享一个我在实际使用中发现的小技巧:当 Agent 操作完一个 GUI 程序后,让它自己截一张图发你确认,比让它"汇报操作过程"高效得多。人眼看图永远比读文字报告快,这也是我把截图工具做成 Agent 标配而不是可选项的原因。这个习惯现在也成了我所有 AI 自动化任务里的默认约定。