开头:
这两周我一直在折腾一件事:让 AI 编码代理不仅能生成代码,还能替我把那些藏在 GUI 里的按钮点掉。事情起因很简单,我有个构建流程,最后一步必须在某个旧版配置工具的图形界面里手工切换选项、点“确定”。代码让代理写好了,脚本也能跑,但它没法替我去点那个按钮。整个过程就卡在一处,特别憋屈。所以我自己动手做了个免费 AI 编码代理:支持操控 GUI 和 MCP 协议,单文件运行,不依赖云端,不上传业务数据。
这个项目解决的最核心问题,是传统编码代理能力的边界。市面上的编码代理大多活跃在终端里:改代码、跑命令、读日志,一套流程行云流水。可真到需要操作桌面软件、填表单、点菜单、拖滑块的时候,它们就完全没招了。我的想法很直接:既然要全流程自动化,编码代理就该能处理图形界面,也能通过 MCP 标准协议挂接外部工具,而不是把所有能力都自己闭门造车。这篇内容适合两类人:一类是把 AI 代理接进真实业务流、被 GUI 环节卡住的朋友;另一类是想了解 MCP 协议怎么落地、怎么写能操控桌面的代理工具的人。
1. 只会在终端里跑命令的编码代理,撑不起完整工作流
1.1 传统编码代理的工作边界到底在哪
如果你用过 GitHub Copilot、Codex 或者各种本地跑的开源编码代理,应该能发现一个共同点:它们的主场是终端。代码补全、文件编辑、执行 shell 命令、跑测试用例、报错分析,这摊事它们做得确实不错。能力边界也画得非常清楚——凡是能通过命令行完成的,代理都能碰;凡是必须靠鼠标在图形界面里完成的,代理就无能为力了。
这个边界一开始不明显。因为大部分开发者日常面对的都是终端环境,git 操作、编译命令、包管理器,全是文本流。我们天然就活在一个“命令行可以搞定一切”的世界里,于是默认 AI 编码代理也不需要碰 GUI。可现实是,完整的工作流从来不只是代码层面的事。公司内部的旧管理系统、产品配置工具、各种检测仪的配套软件,甚至很多工业软件,它们的界面全都停留在桌面 GUI 时代,没有命令行接口,也没开放 API。代码能写,命令能跑,可最后一步“在界面上把人该干的活干了”始终断链。
1.2 “代码写好了,但按钮没人点”的尴尬场景
我举几个真实遇到的例子。第一个是通信设备配置工具,界面是国内某厂商自己封装的那类巨型面板,参数藏在好几层 Tab 里。命令行版工具不是没有,但功能不全,很多高级选项只能界面操作。第二个是办公自动化场景,某个内部 OA 系统没有开放接口,数据导入只支持在网页里点上传、点确认,页面还全是异步加载,光靠 curl 模拟请求根本行不通。第三个是我之前在调试设备固件时用的上位机软件,串口连接、协议配置、波形查看,全靠鼠标点。
这三类场景有个共同点:开发量不大,但卡住就全卡住。传统做法是让人坐在电脑前,按照操作手册一遍一遍点,或者用 UI 自动化脚本的 API 一把梭。可 UI 自动化脚本是脆弱的,窗口大小变了、分辨率换了、控件位置动了,脚本立刻崩。而且写这套脚本本身就要投入时间,说到底还是在“人工产出自动化方案”,不是让智能代理自己完成闭环。
1.3 我的取舍:不如让代理自带“眼睛”和“手”
想明白这个痛点后,我做了个设计决策:不给用户单独做一个“GUI 自动化脚本工具”,而是把 GUI 操控能力直接内置到 AI 编码代理的底层动作集里。代理天生就“看得见”屏幕信息,也“够得着”鼠标键盘,同时还拥有一组标准化的 MCP 工具调用能力。这样无论是写代码、跑命令,还是点界面、调外部服务,都在同一个 Agent 循环里,由模型自己判断下一步该用什么方式执行。
代价肯定是有的。桌面操控比命令行操作复杂得多,会出现坐标偏移、控件识别失败、权限拦截、窗口失焦这些新问题。但收益也很明显:一旦把 GUI 能力补齐,AI 编码代理就能从“会写代码的终端助手”升级成“能真正操办事情的工作流执行体”。我做项目时一直坚持一个原则:能不开新窗口就不开新窗口,能把所有事情收敛到一次对话里完成,就绝不打断用户切换到另一个工具。GUI 操控和 MCP 接入,都是为这个目标服务的。
2. 单文件运行:把项目壳和运行时焊在一起的选型记录
2.1 为什么我坚持必须单文件
项目从第一天起就定了硬性约束:交付物必须是一个可执行文件,双击就能跑,不需要用户安装 Python,不需要配置环境变量,不需要 pip install 任何依赖。理由很现实。第一,目标用户里有很多非开发者,比如售前、实施工程师、测试工程师,让他们配一套 Python 环境再跑代理,门槛直接劝退一半人。第二,单文件在分发侧最省心,网盘丢一个文件、群里发一个文件,对方双击就完事。第三,很多客户现场内网隔离,只有 Windows 桌面环境,没有包管理器,更不允许临时装一堆依赖。单文件是我能想象到的最低的交付摩擦。
当然,单文件也会带来问题。最直接的是体积——纯 Python 项目打成单文件,动辄几十上百 MB。但这不妨碍我坚持这个方向,因为体积大只是下载慢,而环境依赖复杂是根本用不起来。两害相权,我选体积。
2.2 三条技术路线,我为什么选了嵌入式 Python
单文件方案在技术圈其实有几条常见路线,我做了个对比,选型过程供你们参考。
| 方案 | 体积 | 生态 | 跨平台 | 我的评价 |
|---|---|---|---|---|
| PyInstaller 打包嵌入式 Python | 偏大,基础项目 50MB 起 | 极好,Python 生态随便用 | Windows 最好,Linux/macOS 需分别打包 | 适合需要视觉识别、OCR、协议客户端的项目 |
| Go 静态编译 | 很小,10MB 以内 | 需要自己实现或找第三方库 | 极好,原生静态编译 | GUI 识别和 MCP 客户端生态需要自己造轮子 |
| Bun 编译单文件可执行 | 中等 | JS/TS 生态丰富 | 支持主流平台 | 适合 Web 技术栈团队,但桌面操控部分资料少 |
我最终选择了 PyInstaller 路子,核心原因只有一个:我的代理需要大量现成的视觉算法库、OCR 引擎、UI 自动化库和 MCP 客户端库。这些在 Python 生态里最成熟,踩过的坑也最全。MCP 的客户端有现成 SDK,Windows 上的 UIA 自动化有 pywinauto 这类库,屏幕截图和图像匹配有 Pillow、OpenCV。如果用 Go 从零实现这些,工程量会大出好几倍,而且调试成本全在我自己身上。
2.3 打包后的实测表现与体积优化
我用自己的项目做了一次完整的打包和体积实测。基础功能打包出来大概 110MB,主要被 Python 运行时、UI 自动化库和 MCP SDK 占掉了。后来我做了三件事瘦身:把不常用的语言模型和 OCR 模型拆成按需下载的独立文件;用 UPX 压缩可执行段;把项目代码里 import 的部分做了一遍清理,去掉没用到的第三方库。瘦身之后基础包降到 62MB,首次启动后模型按需加载,日常启动在 1 秒左右。Windows 10 和 Windows 11 上实测都能稳定跑,Windows 7 我没有专门适配,因为系统自带的 UIA 接口在 Win7 上有不少兼容坑。
这里有个我自己踩过的坑:PyInstaller 打出来的单文件,默认是运行时解压到临时目录再执行。如果代理里引用了需要定位项目路径的代码,比如读取配置文件、加载模型文件,直接写相对路径容易找不到文件。正确做法是通过sys._MEIPASS这个特殊变量来获取运行时解压目录,所有资源文件统一放在打包目录的_internal下。我第一次打包完,运行到一半报找不到模型文件,排查半天才发现是这个原因。
3. GUI 操控的核心链路:视觉识别加 UI 树加事件注入
3.1 代理“看懂屏幕”的完整流程
GUI 操控听起来神秘,拆开其实就是三步:理解屏幕上有什么、定位目标控件、向目标控件发起交互。我把这条链路做成了代理内置的一组原语动作,模型只需要输入“打开设置窗口,把分辨率改成 1920x1080”,代理会自动规划并执行屏幕理解、控件定位、事件注入。
理解屏幕这步我用两路信息融合。第一路是操作系统的可访问性接口,Windows 上走 UIA/MSAA,macOS 上走 AX API。这条接口能直接拿到窗口树、按钮文本、输入框的值、滑块的当前值,全是结构化数据,精确度极高。第二路是视觉识别,截取当前屏幕图像,做 OCR 文字提取和模板目标检测。可访问性接口拿不到文本的图形区、自绘控件、游戏画面,就得靠视觉识别兜底。
3.2 控件定位:能不用坐标就不用坐标
很多初次接触桌面自动化的朋友上来就想用鼠标坐标,这我能理解,毕竟 PyAutoGUI 点起来很爽。但坐标方案有个致命缺陷:分辨率一变就全歪。DPI 缩放从 100% 改成 150%,原坐标全部偏移;窗口从双屏拖到单屏,坐标直接跑出屏幕。我的项目里,坐标只是最后兜底选项,优先级完全放在控件定位上。
具体流程是这样的:优先通过可访问性树拿到目标控件的句柄和矩形位置,例如按钮的“Name”和“BoundingRectangle”属性。如果可访问性树里拿不到,就退到视觉识别:OCR 找到文字区域中心,模板匹配找到图标位置。再不行,就根据可访问性树的窗口矩形,结合语义推导出一个相对坐标。整个过程里代理拿到的不是“x=800, y=600”这种裸坐标,而是“窗口‘设置’里的按钮‘应用’”这种语义描述,位置信息只是执行层的附带产物。这样窗口挪了位置、换了分辨率,语义不变,代理照样能找到。
3.3 实操示例:让代理打开记事本并自动完成输入保存
我用一个最简单但完整的示例说明整个链路。假设用户对代理说:打开记事本,输入“Hello from AI 编码代理”,保存到 D 盘根目录的 hello.txt。
代理内部执行顺序是:
- 调用
open_app动作,启动 notepad.exe,等待窗口出现。 - 调用
get_ui_tree获取当前窗口的可访问性树,定位到文本编辑区。 - 调用
set_text动作,把内容写入编辑区。这里我优先用 UIA 的 ValuePattern 直接设置文本,比模拟逐字键盘输入快而且稳定。 - 调用
menu_click,找到“文件”菜单,展开后定位“另存为”,点击。 - 等另存为对话框出现,定位文件名输入框,填入“hello.txt”,定位“保存”按钮,点击。
- 如果系统弹出“是否覆盖”确认框,读取框中文本,判断后点击“是”。
整个流程里代理没有用一次硬编码坐标,全部由可访问性树驱动。实测在 Windows 11 雷电模拟器窗口、高 DPI 缩放环境下也能正确完成。
3.4 权限和焦点问题,这个坑必须提前说
GUI 操控最常翻车的地方不是识别,而是权限和焦点。当目标程序以管理员权限运行时,普通权限的代理无法向它发送输入事件,点击会石沉大海。我处理的方式是:检测到目标进程提权时,弹出提示让代理自身以管理员权限重启。第二个问题是焦点抢夺。如果用户在代理执行 GUI 操作期间切走了窗口,鼠标键盘输入就全部落到错误的窗口上。我的方案是输入注入走 Windows SendInput 而不是普通的点击事件模拟,并且每步执行前会重新确认目标窗口前置。即便如此,我依然强烈建议在无人值守场景下运行 GUI 任务前锁屏关掉抢焦点的东西——这是靠经验换来的教训。
4. MCP 接入:让编码代理通过标准协议长出手脚
4.1 MCP 到底是什么,一句话说明白
MCP,全称 Model Context Protocol,模型上下文协议。它解决的是模型和外部工具之间的连接标准化问题。以前每个 AI 工具要对接一个外部系统,都得自己写一套接口;现在大家统一按 MCP 协议暴露工具,Agent 侧按同一套协议调用,工具生态就能一次性打通。你可以把它理解成 USB-C 口:谁做成了 C 口,谁就能插上各种外设,不需要每买个键盘就换套插头。
MCP 协议的核心是三层架构:MCP Server 负责把某个工具能力暴露为一系列“工具”(tools),MCP Client 负责和 Server 通信,Agent 则通过工具清单知道当前有哪些可用能力,然后按需发起调用。协议层走 JSON-RPC,主要动作就两个,tools/list获取工具清单,tools/call发起一次工具调用。
4.2 我的代理如何加载 MCP 工具
我在项目里做了一个统一的 MCP 配置入口,用户只需要准备一个 JSON 配置文件,声明要加载哪些 MCP Server,代理启动时就会自动连接并拉取工具清单。
下面这是一个本地文件输出工具的标准配置示例,对应的是把内容流式输出到文件这种场景:
{ "mcpServers": { "file-writer": { "command": "python", "args": ["-m", "mcp_server_file_writer", "--workspace", "D:/ai-output"], "env": { "LOG_LEVEL": "INFO" } } } }配置说明很直白:command是要启动的 MCP Server 命令,args是参数,env是环境变量。代理启动时看到这个配置,就会拉起该进程,通过 stdio 或 SSE 通道建立 JSON-RPC 连接。连接成功后,代理拿到这个 Server 对外暴露的“write_file”“append_line”“list_workspace”等工具,模型会根据用户指令决定是否调用。
4.3 Agent 侧的工具调用决策逻辑
很多第一次接触 MCP 的人会问:模型怎么知道什么时候该用哪个工具?答案藏在两个地方。一个是工具描述,MCP Server 在tools/list返回时会给每个工具带一段 description。另一个是模型的工具调用规划能力,代理收到用户指令后,会把指令拆解成执行计划,然后按计划逐个调用工具。
我自己写 MCP Server 时有一条强烈建议:description 一定要写得像“人话”。打个比方,write_file如果只写“write a file”,模型能在很多场景下猜对用法。但如果你写“write content to a new file in the configured workspace; use this when the user asks to save text, create a document, or export output”,模型几乎不会误调用。description 写得好,比你在代码里加注释还有用,因为它直接影响模型决策。
4.4 MCP 连接故障排查的基本顺序
MCP 连接失败是我收到反馈最多的问题,排错顺序基本固定:
- 确认
command在系统 PATH 中能直接执行。很多配置写成"command": "node",但代理进程跑在无 PATH 环境的服务状态下,node 根本找不到。改成绝对路径通常立刻解决。 - 确认
args里的模块路径或脚本路径存在。用相对路径时,工作目录一变就容易挂,全部换成绝对路径更稳。 - 确认启动命令不加
--debug也能运行。调试开关常会改变 Server 进程的日志输出模式,导致 JSON-RPC 通道被污染。 - 用 MCP Inspector 类工具单独连接一次 Server,如果单独连都连不上,那问题一定出在 Server 自身配置,而不是 Agent。
这套顺序我在项目里直接做成了内置诊断面板,用户遇到连接问题点一下就能看到卡在哪一步。
5. 令我意外的真实玩法:逆向分析、设计稿转码、企业系统自动化
5.1 逆向与调试:IDA、x32dbg、Cheat Engine 的 MCP 桥接
项目发布后我观察到一个非常突出的使用方向:逆向分析和漏洞研究。社区里有人给 IDA Pro 写了 MCP 插件,让代理能直接读取反汇编结果、查看交叉引用、列出函数列表。还有人给 x32dbg 写了 MCP 插件,把调试器的断点状态、寄存器值、调用栈暴露给代理。最具代表性的是 Cheat Engine 桥接 MCP,代理可以通过协议直接搜索当前进程内存、读取数值、修改地址。
这个玩法让我意识到一件事:MCP 的“工具”不必局限于业务 API,任何有操作界面的软件都可以被桥接成 MCP 服务。GUI 操控解决的是“代理能点按钮”,MCP 解决的是“代理能拿到结构化数据”。两者结合的效果才是完整的:MCP 负责高效读取数据和下发精确操作,GUI 操控负责处理那些没有接口的遗留界面。
5.2 设计稿转代码:Figma MCP 授权的那些坑
设计稿直接生成代码是另一个热门场景。社区里有人把 Codex 接入 Figma MCP,让代理读取设计稿的图层数据、样式变量和切图资源,然后生成前端代码。这个思路很顺,但授权问题很典型:Figma MCP 插件要求用户生成 Personal Access Token,很多用户直接给了拥有全部权限的 token。实际上只需要给到特定文件的只读权限就够用了,权限给大了风险陡增。
我周边朋友实测下来的经验是:连接前先确认 MCP 插件要求的权限范围,只授予单文件或单项目的读权限;token 放在配置文件里不要提交到仓库;用完可以在 Figma 后台直接吊销重建,成本极低。
5.3 企业系统和嵌入式场景:SAP GUI、玲珑 GUI、STM32 GUI
企业软件自动化比想象中需求更大。SAP GUI 至今有大量操作只在桌面客户端里能完成,没有公开 API 的模块非常多。代理通过 GUI 操控去点 SAP GUI 的菜单、填事务代码、提取屏幕字段,这一套流程稳定跑通后,能替代不少重复录入工作。注意这种场景最好先和信息化部门确认合规边界,别踩管理红线。
嵌入式方向也有不少接法。STM32 图形界面框架开发时,界面资源和控件树比较规整,用可访问性树拿参数很方便。还有个叫玲珑 GUI 的嵌入式方案,社区里有人给它的模拟器写了 MCP Server,让代理从需求描述直接改界面文案和布局参数,跑一遍模拟器就能看到效果,比人工改 C 代码快很多。
5.4 通用平台接入:Dify 浏览器 MCP、nuclei GUI scanner、ruoyi-vue-pro
通用平台方向的接入给我的启发最大。Dify 这类 AI 工作流平台接上浏览器 MCP 后,代理可以直接操作网页表单、完成登录、点击按钮提取页面数据,整个流程在编排里就能串起来。nuclei GUI scanner 这类安全扫描器本身有图形界面,代理接管后,可以从扫描结果里提取漏洞详情、生成报告、联动修复任务。ruoyi-vue-pro 合并 MCP 功能则是后端开发平台直接暴露代码生成工具给代理,让“生成增删改查代码”变成一句自然语言指令的事。
这些案例放在一起看,结论非常清晰:MCP 的价值不在某个具体工具,而在于它让代理的能力边界从“代码”扩展到了“任意可以被标准协议封装的服务”。不管对方是 IDE、调试器、设计工具、扫描器还是企业系统,只要有一个 MCP Server,代理就能成为它的操作者。
6. 我的实测翻车记录:从问题里提炼的几条硬规律
6.1 症状:MCP 工具时有时无,偶尔连接成功偶尔失败
有一次我在调试过程中不断重启代理,每次的结果都不一样。上午能连上的 MCP Server,下午就报连接拒绝。排查到最后发现,问题出在端口复用上:我同时跑了两个本地 MCP Server,它们共用了同一个端口配置,后启动的进程拿不到端口,就静默退出。解决方式是为每个本地服务分配独立端口,并在配置里加上启动日志文件,通过日志判断进程到底是启动失败还是连接中断。
6.2 症状:GUI 操作能识别控件,但点击位置偏了
有一次代理做桌面自动录入,控件识别全部正确,可点击时就是点不到按钮中心。后来查出来是 DPI 缩放的问题:系统缩放 125%,可访问性树拿到的矩形坐标和物理屏幕坐标之间差了个缩放系数。很多 UI 自动化库已经把缩放换算做好了,但我的项目里有一处自定义的视觉识别路径没换算,导致偏移。修复就一句话:把目标控件的坐标统一转换到物理像素坐标系,再交给事件注入模块。
6.3 症状:单文件启动后被杀毒软件拦了
这是单文件项目几乎躲不掉的问题。PyInstaller 打包的程序在部分安全软件里会被标记为未知程序,严重时直接拦截执行。我处理的办法有几条:首次启动时弹说明引导用户加白名单;配置了代码签名证书后误报率会明显下降;同时把项目源码公开,让安全团队能快速核查行为。这个过程没什么技巧,就是要做很多解释工作。
6.4 症状:长任务执行到一半,代理和 GUI 状态脱节
这个坑最隐蔽。代理执行一个包含 30 步 GUI 操作的任务,前 20 步很顺利,第 21 步窗口弹出了一个模态对话框,把后续控件都挡住了。代理的截图显示界面没有变化,但可访问性树发生了变化,导致后续点击全部打偏。我后续的改进是增加一步“可访问性树状态比对”,每次点击前先把当前树和步骤开始时保存的树做 diff,发现新增模态框就先处理模态框,再继续原任务。这一条改进直接让长任务成功率从 71% 提到 93%。
翻车记录整理到最后,我发现所有问题的根因就一句话:界面和调用方之间存在“其他变化因素”。要么是分辨率缩放,要么是弹窗挡住了,要么是端口被抢了。做代理工具,本质上是在和这些不可控因素较劲。每次翻车都不可怕,把它变成诊断逻辑里的一条规则,项目就稳定一分。
我自己的体会是,这类工具适合慢慢迭代。最初版本只支持 GUI 操控,MCP 是后加的,单文件化也经历了三轮重制。每次改动都会带来新的兼容性问题,但只要诊断路径清楚、日志足够详细,问题总能收敛。如果你也想做类似项目,我建议从最小闭环开始:先让自己的代理能打开一个记事本、输入一段文字、存成一个文件,再逐步加上 MCP 工具和更多桌面控制能力。这个最小闭环跑通,后面的事情就只是时间和需求问题。