最近圈子里聊AI编码代理的人越来越多,Claude Code、Codex、Cursor这些工具我也重度用了一阵子。直到有一天,一个需求把我卡住了:代码改动AI能搞定,但改完之后要自己在测试环境里登录系统、点页面、开表单,做一套冒烟验证——这一串GUI操作,几乎没有现成的agent能完全代劳。折腾了几个方案之后,我决定自己写一个。
这个项目最终做成了一款免费开源的AI编码代理,核心卖点就三个:能操控GUI界面、完整支持MCP协议、整个程序打包成单文件直接运行。所以这篇文我不打算写什么"产品发布稿",而是把整个设计和实现过程摊开来讲,包括我为什么这么设计、踩了哪些坑、实测效果如何。内容会比较长,但对想自己动手做类似工具、或者正在给团队选型AI编码代理的朋友,应该有不少能直接拿走的东西。
1. 为什么我要做这个"能动手"的AI编码代理
1.1 现有编码代理的"看不见的手"
现在市面上主流的AI编码代理,本质上都是跑在终端里的agent。它们能读你的项目文件、改代码、执行命令行、跑测试、看错误日志,这一套在纯后端开发生态里确实很强。但我用了几个星期之后发现一个明显的边界:它们对图形界面基本是瞎的。
举个真实例子。我手头有个老项目需要做一次小升级,前端界面逻辑改了,涉及一个流程表单的字段调整。代码层面AI改得很干净,但你依然要自己打开系统、用测试账号登录、一步步点到那个页面去验证改动是否生效。这种"打开系统-登录-导航-操作-截图确认"的工作,在大多数编码代理眼里根本不存在,因为它们的观测通道只有文本和命令行输出。
这不是某个厂商做得不够好,而是架构上的一种盲区。终端型agent的所有感知都建立在结构化文本上,GUI世界的像素、控件、窗口状态,对他们来说是不可见的。如果你面临的实际工作流里有很大一部分是"改完代码之后还要在界面上确认效果",那现有的编码代理体验就永远差了一口气。
1.2 项目定位与三条硬约束
想清楚痛点之后,我给自己定了三条硬约束,既是产品定位,也是筛选需求的手段:
- 免费开源:不搞闭源收费,代码仓库直接公开,任何人可以拿去用、改、内部分发。
- 单文件运行:分发形态是一个可执行文件,拷过去就能跑,不要求目标机器装一堆依赖。
- 同时支持GUI操作和MCP协议:这是和主流终端型agent最大的差异化,也是这个项目最核心的工作量来源。
定这三条约束是有原因的。免费是为了降低尝试门槛,单文件是为了让"能操控GUI"这件事真的可以被普通开发者用起来——如果还需要安装Python环境、配置一堆动态库,那"能操作界面"这个卖点就废了一半。GUI和MCP两件事绑在一起,则是因为我想做一个真正能落到"实际工作流"里的agent,而不是又一个只能在终端里自嗨的玩具。
1.3 不重复造模型,我写的是"调度中枢"
刚立项的时候有人问我:你是不是要去微调一个模型,或者自己训练识别UI的模型?我说不是。这个项目真正的核心技术不在模型侧,而在调度中枢。
我的定位是:模型用现成的,可以是本地跑的、也可以是云端API,我只需要把"屏幕观测、工具调用、行动执行、结果汇总"这几个环节串起来,写成一个高效的、可扩展的agent框架。至于最终决策用什么模型,那是可配置项,我不绑定任何一家。
技术栈上我选了Rust。原因也很朴素:单文件分发这件事,Rust做静态编译很成熟,跨平台时不用带解释器或运行时;而且并发处理和系统API调用的生态比较顺手,正好是我后面要做GUI操控时的硬需求。老实说,如果只用Python写原型,一周就能跑通,但距离"一个文件拷过去能跑"的交付态还差得很远。用Rust写,前期开发慢,后期分发爽。
2. GUI操控能力:给代理一双眼睛和能点击的手
2.1 三层结构:观测、决策、执行
让AI代理操作GUI,我没有选择那种"AI自己画一个界面然后模拟操作"的不切实际路线,而是采用了最贴近真实用户行为的感知-决策-执行闭环,拆成三个独立模块:
- 观测层:负责"看"。采集当前屏幕内容、当前活跃窗口的控件树、鼠标键盘状态。输出是一份结构化的"屏幕摘要",包含控件类型、位置、文本、可用状态。
- 决策层:负责"想"。把观测结果和用户指令一起丢给大模型,让它决定下一步动作。动作被严格限制在一张预定义的行为列表里:点击、双击、右键、输入、按键、滚轮、等待、截图、读取文本。
- 执行层:负责"做"。把模型输出的动作翻译成真实的系统级鼠标键盘事件注入到目标窗口,操作完成后再次触发观测,形成闭环。
这个设计里最关键的是第一层。因为GUI操作的本质问题是"AI不知道屏幕上有什么",只有把屏幕变成可读的数据,后面的决策和执行才有意义。
2.2 观测层的两套方案:无障碍树和OCR兜底
观测层我同时做了两条路,按优先级使用。
第一路是系统无障碍API读取界面元素树。Windows上有UI Automation,macOS上是Accessibility API,Linux桌面则走AT-SPI。这相当于让AI直接"阅读"界面的DOM结构,能拿到控件的准确类型、坐标、名称、状态,精确度非常高。大多数主流桌面应用、浏览器页面、以及基于Qt/Electron/WPF的应用都能从中拿到有效信息。这一步做得好,AI的点击操作基本不会点偏。
第二路是屏幕截图加OCR作为兜底。无障碍树不是万能的:有些游戏、自绘界面、远程桌面画面根本不给树,或者树是空的。这时候就只能靠截图,然后对截图像素做文本识别和元素定位。我内置了一个轻量级的OCR引擎,并在程序里做了区域裁剪和对比度增强的预处理,让OCR在小控件文本上也能达到可用的准确率。
两套方案切换的规则很简单:先尝试无障碍树,失败或超时则自动降级到OCR模式。对用户来说,这个切换是无感的,但决策层拿到的观测数据会标记来源,这样模型就知道自己看到的到底是"结构化控件"还是"图片识别结果",置信度会相应调整。
2.3 执行层的跨平台差异与权限处理
执行层是整个项目里最"脏活累活"的部分,因为三套操作系统对模拟输入的态度完全不一样。
Windows最直接,通过SendInput注入鼠标键盘事件,绝大多数窗口都会正常响应。macOS比较麻烦,需要给程序授予辅助功能权限,否则事件被系统拦截;而且macOS对输入注入有额外的安全机制,目标应用如果拒绝自动化,事件会失败。Linux则是"窗口管理器说了算",X11下稳定,Wayland下很多合成器禁止全局输入注入,只能退而求其次用辅助技术的接口。
我在设计之初就把权限问题放到了非常靠前的位置。程序第一次启动时会让用户对各个平台做一次"授权自检",明确告诉你哪些能力可用、哪些被系统拦截了。这一点很重要,因为很多AI工具一上来就静默失败,用户根本不知道是模型决策错了,还是系统不允许操作。
执行层另外引入了一个"节奏控制器"。真实GUI操作不是瞬间完成的,菜单要展开、窗口要刷新、按钮要进入可点击状态,如果AI按照代码执行的速度疯狂点击,屏幕根本反应不过来。我实测发现,必须强制在两次动作之间保留几百毫秒的动态间隔,而且在点击之后、下一次观测之前,要等到界面"稳定"下来:一般用窗口内容变化检测来做,比对前后两帧截图,变化小于阈值才认为界面稳定了。这一步做得不好,再聪明的模型也操作不了一个"一直在闪"的界面。
2.4 安全边界:能操作不代表可以乱操作
让AI接管鼠标键盘,等于把一台机器的控制权交给了模型。这个风险不能指望模型自觉,必须在工程层面对冲。我在系统里做了四条防线:
- 操作确认模式:默认开启,AI每次准备执行"高风险动作"(如删除、覆盖、提交表单)前,必须弹窗征求人类确认。也可以改成全自动模式,但需要显式关闭保护。
- 只读沙箱模式:只允许AI观测和截图,不允许注入任何输入事件,适合让AI先"看"再让人类决定要不要让它"动"。
- 目标窗口锁定:可以指定只允许操控某个窗口或进程。AI不会跑到窗口外面乱点,避免出现"改着代码突然去点开了浏览器"这种失控情况。
- 完整操作日志:每个注入的动作、每帧观测结果、每轮模型决策都记录在案。出问题时能完整回放,而不是靠猜。
我始终觉得,AI编码代理这类工具,能力边界可以激进,但权限边界必须保守。特别是GUI操控,一旦出事就是直接影响用户机器上的真实环境。宁可多做几次确认,也不能让用户有一次"被工具坑了"的糟糕体验。
3. MCP接入:让代理长出外挂工具
3.1 MCP到底是什么,为什么编码代理必须支持它
MCP全称Model Context Protocol,是Anthropic提出的一种开放协议,后来被整个AI工具生态接纳了。如果你看过一些比喻,最贴切的就是AI界的"USB-C接口":模型不需要为每一个数据库、每一个浏览器、每一个业务系统单独定制接入方式,只要遵循同一个协议,就能连接各类"外部工具"或"数据源"。这就像你的手机不需要为每个设备造一个专用充电口,一个USB-C口就能接各种外设。
对编码代理来说,MCP的意义比普通人理解得更大。因为编码代理的核心工作不只是"写代码",而是要理解代码背后涉及的整个系统。真实的开发任务里,你需要查数据库、调API、看日志、搜文档、发HTTP请求、操作版本库。如果没有MCP这类标准化接入能力,你就得为每个数据源去写定制插件,或者让用户在配置文件里手写一大堆本地脚本。有了MCP,工具生态是共享的,今天有人写了一个好的数据库MCP服务器,整个社区的agent都能直接用。
我在设计这个项目时把MCP支持列为核心能力之一,就是这个原因:AI编码代理的竞争力,很大程度上等于它能接入的工具生态的丰富程度。
3.2 内置MCP客户端的设计实现
这个项目内置了一个完整的MCP客户端,而不是"部分兼容"或者"只读支持"。具体实现了两块:
- 传输层:同时支持stdio(本地进程间通信)和SSE(HTTP服务端事件流)两种标准传输方式。stdio适合接本地的、随agent一起启动的MCP服务器;SSE适合接远程部署的工具服务,比如团队成员共享的一个数据库查询服务。
- 协议层:完整实现了工具发现(tools/list)、工具调用(tools/call)、资源读取、提示词模板等核心方法。启动时agent会自动向每个已注册的MCP服务器发送握手请求,拿到能力清单,并把它们合并进模型可见的工具列表里。
交互流程上我做了"先发现、后授权、再调用"三段式。工具发现阶段只获取名称、描述和参数Schema,不实质性执行任何操作;授权阶段,用户可以选择对某个MCP工具全自动放行、每次确认、或者完全禁用;调用阶段才真正把参数发给MCP服务器执行,并把结果结构化回传给模型。
为什么要这么设计?因为MCP服务器的能力千差万别:有的是查数据库的,有的是读文件系统的,有的是操控浏览器的。如果agent启动后不分青红皂白地把所有工具都交给模型,等于把一把万能钥匙给了它,这既不安全也对用户不公平。所以我把"能否调用某个MCP工具"这个决定权始终保留在人类这一侧。
3.3 实测:让AI连着数据库和界面一起干活
MCP支持不是纸面功能,我拿一个接近真实工作的场景做了完整验证。场景是这样的:一个基于RuoYi-Vue-Pro风格的后台管理项目,我需要AI帮我完成一次"按关键词查数据库记录、然后到界面上对应的表单去核对"的联调任务。
具体链路是:我先在agent的配置里注册了一个数据库查询类的MCP服务器,再打开后台管理系统的GUI界面。任务指令是"从数据库查出所有状态为待审核的记录,然后到界面上的列表页看看这些记录是否都正常显示"。agent拿到指令后,自己做了几件事:
- 通过MCP调用数据库查询接口,拿到待审核记录的ID列表。
- 切换到GUI操作模式,读取当前浏览器窗口的无障碍树,找到列表页表格的位置。
- 点击搜索框、输入筛选条件、触发查询。
- 重新观测界面,比对结果和数据库查到的ID,输出差异报告。
整个过程约三分钟,中途我只在"执行搜索"这个动作上点了一次确认。这个例子的价值在于:它展示了MCP和GUI操控不是两个孤立的功能,而是可以串成一条完整工作流的能力。这也正是我坚持要把两个功能放在同一个agent里的原因——分开的能力到处都是,合起来才能解决真实问题。
3.4 与现有生态的兼容状况
因为MCP是标准协议,这个客户端天然能和社区里已经成型的MCP服务器配合。我在测试中直接接过的有:数据库类MCP、浏览器控制类MCP、设计稿转代码类MCP、以及几个头部项目里常见的文件读写和命令行工具MCP。基本上只要是遵循官方SDK写的MCP服务器,把启动命令写进配置文件就能用。这一点省了我大量精力,也让我更确信做标准协议是正确选择。
接Codex生态里那种Figma授权类MCP的时候倒是踩过一个坑:有些MCP服务器需要OAuth授权跳转,但跳转会拉起默认浏览器,结果agent本身又在操控GUI,两个进程会打架。我的处理是给MCP调用加了一个"阻塞模式",在执行需要外部授权流程的MCP工具时,暂停一切GUI注入动作,等授权完成后自动恢复。这个细节看起来小,但实际使用中极其重要,否则就是各种莫名其妙的互相抢焦点。
4. 单文件运行:交付形态的工程取舍
4.1 "拷过去就能跑"为什么是刚需
单文件运行这个特性,做之前觉得无所谓,做之后发现它是这个工具被团队接受的关键因素。道理很简单:大部分开发者最烦的就是装环境。
我在公司内部测试的时候,一开始给出的是一个zip包,里面有几个动态库和一份Python辅助脚本。结果反馈最多的就是"这个依赖怎么又报错了""Python版本对不上""给我个绿色版吧"。后来我改成单一可执行文件:下载、双击、跑起来,配置也是自动生成的。反馈立刻变了,大家开始关心功能本身,而不是环境问题。
单文件的价值不只是"方便"这么简单。在企业内网环境、隔离网络、客户现场这些场景,能拷贝的单个可执行文件几乎是唯一可行的分发方式。你不可能要求客户机器上装好某语言的运行时、一套依赖和一个连你自己都说不清的配置。单文件交付意味着你把自己的完全体打包在了一起,而不是把一半的运行环境问题丢给了用户。
4.2 打包方案:静态编译与依赖内嵌
用Rust做单文件有个天然优势:只要目标平台有对应工具链,静态编译出来的二进制不依赖系统的动态链接库。但实际操作中没那么容易,我重点处理了三类事情:
- GUI库和系统API绑定:我最终没有选择那种重量级GUI框架,而是直接用系统原生API做窗口和控件渲染,并把外部依赖降到了最低。这让最终产物不需要额外携带任何GUI运行时库。
- 内置模型的嵌入:轻量OCR模型和本地推理参数设置,全部以资源形式编译进二进制里。代价是体积变大,但换来的是运行时完全离线,装完就能用。
- 动态插件外置:MCP服务器本身不内嵌,它们仍然是独立的子进程或远程服务,agent只在配置里记录启动命令。这样避免了把大量无用的工具都塞进一个文件里,保持了单文件体积在一个可接受的范围。
按照不同目标平台,我分别只出一个二进制,比如Windows平台就是agent.exe,macOS是agent,Linux也是agent。整包大小大概在90MB左右,比很多IDE插件还小,对于同时包含AI推理、GUI操控和MCP客户端的工具来说,这个体积我认为是合理的。
4.3 启动速度和常驻内存间的平衡
单文件还有一个容易被忽略的工程细节:启动速度。如果你每次打开工具都要加载一个巨大的二进制和解压内嵌资源,用户体感会很差。我把资源类数据做了惰性加载:obviously需要时再把OCR模型等大块资源从文件里映射到内存,而不是程序一启动就全部加载。实测空载启动时间压在1秒以内,OCR模型按需加载后再加半秒,这个数据对GUI工具来说是可以接受的。
常驻内存控制上也有取舍。模型和GUI控件树观测都比较吃内存,我在不影响功能的前提下做了一些缓存策略,比如相同区域的截图短时间内复用、OCR结果做短时缓存、无障碍树只在界面变化时重新拉取。对整个应用来说,空闲时大概占170MB左右内存,操作高峰期会上浮一些,考虑到它身兼数职,这个占用不算离谱。
4.4 跨平台三件套的差异化处理
单文件运行不代表代码一套通吃。三套系统在打包细节上差别很大:Windows有代码签名要求,macOS有公证和Gatekeeper拦截,Linux有发行版碎片化。我的做法是给每个平台单独发一个编译产物,并在项目文档里写清楚每个平台需要解除的限制和授权步骤。虽然看起来"不统一",但对用户来说,实际上比一个到处受阻的通用包更友好。
5. 实测记录、翻车复盘和剩下的事
5.1 三个典型场景的真实跑分
功能都做完之后,我拿几个相对典型的场景做了标准化测试。数据不敢说多权威,但绝对真实。测试机器是同一台笔记本,模型走的是同一家云端API,GUI模式默认开启确认,MCP注册了数据库、浏览器控制和一个命令行工具。
| 场景 | 操作内容 | 结果 | 耗时 |
|---|---|---|---|
| 后端项目冒烟测试 | 改完代码后自动启动服务,访问本地接口并核对返回JSON | 全流程通过,无人工参与 | 约2分10秒 |
| 后台管理界面数据核对 | 按客户要求查数据库,然后在网页列表页逐项核对记录 | 通过,中途确认了一次搜索动作 | 约3分半 |
| 桌面工具界面自动化 | 启动一个本地桌面小工具,重复执行导入-处理-导出的操作 | 基本稳定,但有一处分辨率适配不稳需要重试 | 约4分钟 |
第三个场景暴露的问题我先记着,后面会展开说。总体而言,在需要GUI操作的任务里,这个工具能把过去"必须人盯着"的重复操作降级为"偶尔确认一次"的半自动任务,实际情况符合我立项时的预期。
5.2 翻车记录:分辨率、OCR误判、权限弹窗
这个项目开发过程中翻过的车,值得记录的不少。写出来给后面做类似项目的人参考。
第一辆是分辨率适配。我在自己的显示器上调得稳稳当当,换了台不同分辨率的机器一跑,坐标全部漂移。原因很直接:我早期直接把无障碍树里的坐标当作绝对坐标传给了鼠标注入,忽略了显示器缩放比例和DPI变化。修复方式是统一做一个"逻辑坐标到物理坐标"的转换层,所有动作都经过这个转换层下发,而不是在使用处临时换算。
第二辆是OCR误判导致点了错误按钮。我在一个界面上的**"保存并提交"和"保存为草稿"**两个按钮靠得很近,OCR把小号字体识别错了,AI直接点了提交。虽然单测场景是测试环境,但这件事给我提了个醒:OCR识别结果必须带置信度,低于阈值时要明说"无法确定"而不是猜一个。我后来加了一条规则,当OCR置信度低于设定值时,决策层必须要求人工指定或放弃操作。
第三辆是权限弹窗被AI"吃掉"。macOS上第一次跑辅助功能授权时,系统弹出授权对话框,结果AI以为那是任务弹窗,直接点掉了。这个问题非常黑色幽默,但也很现实:当一个工具能操控GUI时,它连系统权限确认框都能当操作对象。修复措施是在进入"授权流程检测"状态时暂停所有GUI动作,任何系统级弹窗优先交给人工处理。
5.3 还想做的事:插件化、模型无关、更细的GUI理解
当前版本还谈不上完美。下一步我优先做三件事。
插件化是第一个方向。现在GUI动作集是内置的,用户不能扩展。我想把"动作"体系改成插件化接口,让有需要的人可以自己写自定义动作,比如调用特定软件的API、执行某个IDE的快捷键组合。这让agent的能力可以跟着团队的实际需求长出来,而不是我预设什么就是什么。
模型无关是我一直在坚持的原则。目前决策层可以对接多个常见模型提供方(本地和云端的都在配置里支持了),但不同模型的工具调用格式差异比较大,我还想再抽象一层,让切换模型时的适配成本进一步降低。这样用户想省成本用轻量模型跑简单任务,想冲效果换强模型跑复杂任务,都不用改任何工作流。
更细的GUI理解是技术上的长远目标。现在的观测层能识别控件、能OCR,但对"界面布局语义"的理解还比较浅。比如一个卡片信息区域包含了多个字段,AI现在能逐个读,但未必能理解"这些字段是一个整体"。我打算在观测层引入布局分析,把界面按视觉区块做聚类,让决策层看到的不只是零散控件,而是一张有结构的"界面地图"。这一步如果能做成,GUI操控的准确率和任务复杂度上限会有明显提升。
这个项目从立项到现在,我最直观的感受是:AI编码代理的下一个竞争点,不在"谁能写更多代码",而在"谁能把代码之外的真实操作链路也接管过去"。终端里的代码和屏幕上的界面,是一个完整工程工作流的两端,只盯着一端做,永远差一口气。
最后说点实在的。我把这个工具免费开出来,就是想让更多人能用上"AI既能看代码、又能动手开界面"的完整形态。如果你也在做类似的东西,或者在你自己的项目里接MCP、做GUI自动化,欢迎直接用我这套框架当底座,省掉那些我已经踩过的坑。单文件、GUI操控、MCP这三件事能拼在一起跑通,本身就是一次很值得的尝试。