最近我把自己做了一个多月的东西正式免费放了出来:一个叫guia-agent的 AI 编码代理。它的特点说起来就三条——能直接操作 GUI 界面、原生支持 MCP 协议、整个程序打包成单文件,双击就能跑。我把它定位成“能让 AI 像人一样用电脑写代码”的小工具,而不是又一个补全代码的 IDE 插件。
如果你也是那种已经受够了“把报错日志从 IDE 复制到对话框、再把 AI 给的代码粘回去”的人,这工具应该能戳中你。它适合三类人:一是想试试 Agent 但不想折腾 Python 环境和一堆依赖的人;二是已经在用 Claude Code、OpenAI Codex 这类终端型 Agent,但希望它们能“伸手”操作桌面软件的人;三是对 MCP 协议好奇、想在一个轻量级客户端里接各种 MCP 服务器的玩家。下面我会把项目的动机、架构、实操过程和踩坑经历都摊开讲,纯干货。
1. 为什么做:AI 会写代码,却不会“用电脑”
1.1 被“复制粘贴错误日志”逼疯的日子
平时写代码,我最常干的蠢事是:跑一遍测试,看到终端里一堆堆的红色报错,然后手动把那段又长又乱的 traceback 复制出来,贴给 GPT,等它分析,再把补丁粘回去编译。偶尔一次还能忍受,一天重复二十次就很崩溃。更烦的是有些报错根本不是代码逻辑问题,是环境问题——比如某个 DLL 找不到、某个端口被占用、某个配置文件路径写错。AI 光看文字很难猜出你机器上到底发生了什么,但如果它能自己打开任务管理器看进程、打开文件资源管理器检查路径、启动调试器看窗口标题,那问题就简单多了。
我当时的想法很简单:能不能做一个 Agent,给它一个任务,比如“把测试跑挂的那个问题查出来并修复”,它能自己调用终端、打开 IDE、看报错弹窗、改文件、再跑测试验证。市面上确实有很多智能体框架,但它们大多数只活在终端里,能读文件能执行命令,却看不见屏幕上的东西。而真实开发环境里有大量信息只存在于 GUI 里——IDE 的断点面板、数据库客户端的连接对话框、浏览器 DevTools 的 Network 标签,这些用终端命令很难完美替代。
1.2 市面上的 Agent 缺一块“桌面操作权”
目前常见的编码代理大致分两类。一类是 Copilot、Cursor 这种深度集成在编辑器里的,它们擅长改代码、解释代码、做补全,但不会主动去操作外部软件。另一类是 Claude Code、OpenAI Codex 这类命令行 Agent,它们能执行 Shell 命令、读写文件、调用 MCP 工具,但本质上还是“无头的”——你看不见它在干什么,它也不知道桌面上弹了个什么窗口。
去年底到今年,MCP(Model Context Protocol)突然火起来以后,情况好了很多。大家可以把文件系统、数据库、浏览器抓包工具都封装成 MCP 服务器给 Agent 调用。但我注意到一个空缺:几乎没有轻量级 Agent 能把“MCP”和“GUI 自动化”这两件事同时做好。Cherry Studio 能跑 MCP 工作流,但它不是编码代理;Dify 浏览器 MCP 能驱动浏览器,但不碰本地桌面;你当然可以用 Codex 去接 Figma 的 MCP 拿设计稿,但让它直接去操作桌面版 Figma、点图层、拖画板,它做不到。所以我决定自己写一个,既能读 MCP 生态里的各种工具,又能直接操控桌面 GUI,两边打通。
1.3 免费+单文件:降低门槛的执念
一开始我确实想用 Python 写,因为 GUI 自动化和 AI 生态的库都是 Python 最多。但后来发现,让普通用户跑一个 Python 项目简直是灾难:Python 版本不对、pip 依赖冲突、虚拟环境激活失败、PyQt 装不上……我自己也是被这些搞烦了,才决定最后一定要做成“单文件”。一个文件扔到任何 Windows / macOS / Linux 机器上,给个执行权限,跑起来就能用。这样我也可以理直气壮地免费分发——不需要服务器,不需要帮你维护环境,代码全在本地,模型 API 你自己配。对开发者来说,这是最省事的模式。
2. 架构设计:单文件、GUI 引擎、MCP 客户端怎么塞进一个二进制
2.1 选型:Go 交叉编译,一个二进制覆盖三大系统
单文件这个需求的直接答案其实有不少。Python 可以用 PyInstaller 打 onefile,但打包出来的体积巨大、启动慢,而且杀毒软件经常误报。Node 生态可以用 Bun 的bun build --compile,也能打出单文件可执行程序,但跨平台 GUI 自动化能力偏弱。我最终选了 Go,原因很简单:
- 交叉编译方便,一条命令就能出 Windows / Linux / macOS 三个平台的二进制,不依赖目标机器上的运行时。
- 编译产物就是天然的单文件,没有解释器分发的概念。
- Go 生态里调用 Windows API 和 macOS Accessibility 都有现成库,写底层桥接不会太痛苦。
- 内嵌 Ollama / OpenAI 客户端的 HTTP 逻辑非常顺手。
代价也很明显:Go 在 GUI 自动化这边的库没有 Python 的pyautogui那么开箱即用,很多底层调用得自己封装。不过这些封装做一次,以后所有功能都受益。
2.2 GUI 操控引擎:UIA、AT-SPI、OCR 兜底
让 Agent “看见”屏幕,我分了三个层次,从精确到模糊排列。
第一层是系统辅助功能树。Windows 上用 UI Automation(UIA),macOS 上用 Accessibility API,Linux 上用 AT-SPI。这一层能拿到控件树:哪个按钮叫什么、哪个输入框在什么位置、哪个窗口当前是激活的。Agent 操作时优先走这层,因为它是语义级的,不会因为屏幕缩放或分辨率变化而点错。
第二层是坐标点击和键盘模拟。如果目标软件不暴露辅助功能树(比如某些自绘界面的游戏工具、老式 Win32 程序、Electron 但没做无障碍适配的 App),就只能退而求其次:截图识别出按钮位置,然后发送鼠标点击。为了避免高 DPI 和缩放带来的坐标偏移,我做的所有坐标换算都基于虚拟屏幕坐标,并且在点击之前会先激活窗口、等 100ms 焦点稳定,这几秒延迟能救回大量误点。
第三层是OCR 兜底。遇到那种全是位图的控件、连文本都抓不到的情况,我会用内置的 OCR 把屏幕上的文字识别出来给模型看。这一步是最后手段,因为 OCR 结果不稳定,容易误导 Agent。我平时限定它只能用于读取窗口标题和报错弹窗内容,不允许仅凭 OCR 猜测就去点击。
这三层统一封装成一个GuiScreen接口,Agent 看到的是一组工具:list_windows、get_ui_tree、click、type_text、screenshot、ocr_read等。模型不需要关心底层走的是哪一层,它只需要像人一样思考“下一步该看哪里”。
2.3 MCP 客户端与模型调用的封装
MCP 是现在 AI 工具互操作的事实标准。我在 guia-agent 里实现了完整的 MCP 客户端逻辑,支持stdio类型和streamable HTTP类型(MCP 新规范已经用 streamable HTTP 取代了旧的 SSE 传输)。每个 MCP 服务器配置好之后,Agent 会在启动时自动发现它暴露的工具列表,并合并进自己的工具集。
模型调用这边我做了 OpenAI 兼容的接口,所以你可以填任意兼容端点,比如 OpenAI、DeepSeek、智谱、Ollama 的本地模型都行。配置里只要给base_url、api_key、model三项。为了支持 GUI 截图分析,我还加了对多模态模型的可选依赖:如果配的模型支持图片输入,Agent 会把截图直接发给模型,理解效果会好很多;否则它只能依赖 OCR 文本。
整个架构其实就是一个大循环:模型生成下一步动作 → Agent 执行 GUI/MCP 工具 → 观察结果 → 继续决策。单文件就是这个循环加上所有工具实现,全部编译进一个二进制。
3. 跑通第一个任务:让 Agent 打开编辑器、改代码、执行测试
3.1 配置文件:模型、MCP 服务器、GUI 权限
工具默认用guia-agent.yaml做配置。第一次运行会自动生成模板,最简配置长这样:
model: provider: openai-compatible base_url: https://api.openai.com/v1 api_key: ${OPENAI_API_KEY} model: gpt-4o mcp_servers: filesystem: command: npx args: - -y - "@modelcontextprotocol/server-filesystem" - /workspace browser-debug: command: npx args: - -y - "@playwright/mcp@latest" gui: screen_capture: auto ocr: auto confirm_before_actions: true allowlist: - "code" - "Code" - "Terminal"重点说下allowlist。这是我是故意加的“安全带”。Agent 默认只能操作名单里的窗口,不在名单的软件一概忽略。比如让它操作 VS Code 和终端可以,但不小心弹出一个银行页面或系统设置,它不会去乱碰。这个列表在 GUI 自动化场景里非常重要,否则 Agent 很可能为了完成“把那个弹窗关掉”之类的指令去点任何窗口上的关闭按钮,你根本不知道它会点到什么。
3.2 实战:一条指令完成“修复测试失败”
我用一个实际例子展示它是怎么工作的。我在一个 Java 项目里故意留了个测试失败的 bug,然后运行命令:
./guia-agent --config guia-agent.yaml --task "打开当前目录的 editor 项目,运行 './mvnw test',分析失败原因,修复代码,再跑一次确认测试通过"Agent 的决策轨迹大概是这样:
- 调用 MCP filesystem 工具,查看项目目录结构,发现是 Maven 项目。
- 启动终端窗口(Terminal 在 allowlist 里),执行
./mvnw test。 - 截图,OCR 识别出终端窗口输出有一行
BUILD FAILURE,再用 UI 树定位到终端里的输出区域,把报错信息读出来。 - 模型分析出是某个 bean 注入失败,然后通过 MCP filesystem 工具打开对应的 Java 文件,定位到
@Autowired那行,发现缺少@Qualifier。 - 修改 Java 文件后,再次运行 Maven 测试。
- 这次终端显示
BUILD SUCCESS,Agent 主动截图并总结:“测试已通过,问题是缺少限定符,已修复。”
整个过程中唯一需要我介入的,是第一次运行时 macOS 弹出了“是否允许辅助控制终端”的授权框。这个框是系统级的,Agent 自己点不了,必须人工点一下。之后就全自动跑完了。整个任务大约花了 6 分钟处理,模型调用大概 2 万 token,重点开销全在反复截图和读终端输出上。
3.3 Agent 的“内心戏”:任务拆解和决策循环
这个工具没有用复杂的 Planner 架构,就是用最直接的 ReAct 循环:每次根据当前观察到的 GUI 状态和任务目标,决定下一步。
关键点在于,它每一步的工具结果都包含“屏幕摘要”。我封装了一个observe工具,它会返回当前活动窗口的标题、UI 树摘要、最近一次截图的 OCR 文本。模型看到这些后,才能决定下一步是点击、输入还是查看文件。为了控制 token,我不会把整个 UI 树都抛给模型,而是只给“当前窗口的控件摘要”,并且对同类控件做聚合,比如“当前窗口有 3 个按钮:运行、调试、搜索”。
这个循环的优点是很直观,也容易 debug。坏处是模型的 token 消耗有点大。后来我加了“观察缓存”:如果窗口状态和上次差不多,就不重复发截图,只发一个 diff 摘要,token 能省 30% 左右。
4. 接上 MCP 生态:Figma、浏览器、IDA、Codex 们都在做的事
4.1 MCP 是什么,为什么编码代理需要它
MCP 相当于是 AI 工具的 USB-C 接口。以前每个 Agent 要接一个新工具,都得给 Agent 写一个插件。有了 MCP 之后,只要工具方提供一个 MCP 服务器,任何兼容 MCP 的客户端都能直接用。现在社区里有文件系统、SQLite、浏览器自动化、Figma 设计稿读取、蓝湖、甚至 IDA Pro、Cheat Engine、x32dbg 这类逆向调试工具的 MCP 服务器。你要是想用 Codex 接 Figma MCP 拿设计稿,或者给 x32dbg 配一个能对话的 MCP 插件,都会依赖这个协议。
对 guia-agent 来说,支持 MCP 是它区别于普通 RPA 工具的核心。RPA(机器人流程自动化)也能点界面,但它没法理解对话语义。我的 Agent 能同时做到:通过 MCP 读文件、查数据库、连接设计工具、驱动浏览器,再通过 GUI 引擎操作桌面软件。举个例子,我让它“打开 Figma 里的首页设计稿,参考它的配色,把项目里 CSS 变量改掉”。它通过 MCP 从 Figma 拉取设计信息,通过 GUI 打开浏览器看渲染结果,然后通过 filesystem MCP 改代码。这个流程以前我一个人干至少半小时,现在它几分钟能跑完,虽然偶尔要人工纠偏,但已经很接近“有个理想实习生”的感觉了。
4.2 把常用 MCP 服务器接进来
接入一个 MCP 服务器,我只需要在配置文件里增加一段。比如接上 Playwright 的浏览器 MCP:
mcp_servers: playwright: command: npx args: - -y - "@playwright/mcp@latest"启动后,Agent 的工具列表里会多出browser_navigate、browser_click、browser_snapshot这一组工具。这样 Agent 既能操作原生桌面 GUI,也能操作浏览器页面,两边互不干扰。
我对 MCP 服务器的处理做了容错:任何一个服务器启动失败,只是少一组工具,不会导致整个 Agent 崩溃。日志里会明确告诉你哪个服务器起不来,方便排查。这一点在真实使用中特别重要,因为 MCP 服务器常常因为 Node 版本、依赖缺失、环境变量没配对而失败。
4.3 和 Codex、Cherry Studio、Dify 等工具的定位差异
我复盘过自己和这些热门工具的区别,直接用表格说明:
| 工具 | 编码能力 | 桌面 GUI 操控 | MCP 支持 | 单文件分发 | 定位 |
|---|---|---|---|---|---|
| OpenAI Codex | 强 | 无 | 支持 | 需要安装 CLI | 终端型编码代理 |
| Claude Code / Cursor Agent | 强 | 无/有限 | 支持 | 需要安装 | 终端/编辑器代理 |
| Cherry Studio | 弱 | 无 | 支持,甚至自带 MCP 市场 | 有安装包但非单文件 | 聊天/工作流客户端 |
| Dify | 弱 | 有限(浏览器扩展) | 支持 | 部署在服务端 | AI 应用平台 |
| guia-agent | 中等(依赖模型) | 原生支持 | 支持 | 是 | 桌面 GUI+MCP 编码代理 |
Cherry Studio 的 MCP 市场确实很方便,但那是个偏聊天的界面,没法让你说一句“打开项目修 bug”就跑完整个调试循环。Dify 的浏览器 MCP 是服务端组件和浏览器扩展方案,同样没法操作任意桌面软件。Codex 虽然很强,但默认跑在终端环境里,你让它打开“系统设置”去改环境变量就抓瞎了,而我这个刚好补齐了这块短板。
5. 真实翻车现场:GUI 自动化的坑与排查思路
5.1 点击坐标偏移:缩放、多显示器、窗口半透明
这是我最早遇到也是最烦的问题。同样的点击指令,在 100% 缩放的屏上能点中,在 125% 缩放的 Windows 上就偏了。原因是截图坐标、虚拟桌面坐标、窗口客户区坐标三者之间换算很容易出错。后来我统一使用“虚拟屏幕坐标”,再通过 Windows DPI awareness API 声明进程支持 Per-Monitor DPI,问题才解决。如果你遇到 Agent 点了没反应或点到旁边的东西,先检查缩放设置,再检查是多显示器环境中副屏坐标可能是负数。一个经验:不要直接让 Agent 记住“按钮在 x=800, y=600”,而是先通过 UI 树找到按钮的 bounding box,再取中心点去点击,这样跟分辨率无关。
还有一个坑是窗口半透明或模糊特效。在 macOS 上如果开启了模糊背景,截图里文字边缘会带虚影,OCR 识别很容易出错,UI 树反而抓不到文本。我最后的应对是:需要精确文本时优先读取 UI 树属性,OCR 只作为 fallback,并且只截取窗口客户区而不是全屏。
5.2 权限被卡:macOS 辅助功能、Windows UIA、Linux Wayland
第一个版本让朋友在 macOS 上跑,他反馈说“Agent 说它点击了,但按钮纹丝不动”。排查半天,发现是 macOS 的辅助功能权限没授权。这类系统权限必须在“系统设置 → 隐私与安全性 → 辅助功能”里手动加上,代码是申请不到的。屏幕录制权限同理,否则截图全是桌面壁纸。Windows 上 UIA 一般不需要额外权限(除了一些需要管理员权限的软件),但如果你用 PowerShell 或某些终端模拟器,可能需要以管理员身份运行。Linux 上最头疼的是 Wayland 的安全模型,普通应用根本拿不到全局截图和全局输入事件。我在 Linux 上强制要求用户切换到 Xorg 会话,或者开启 Wayland 的实验性 portal 支持,这个只能写在文档里。这三个平台我都在启动时做了自检:如果不是管理员/授权状态,工具会明确提示缺什么,而不是假装能干活。
5.3 MCP 连接失败:stdio 环境变量、Streamable HTTP 改造
MCP 服务器最常见的问题是stdio类型启动失败。比如我接@modelcontextprotocol/server-filesystem时,如果当前用户 Node 环境是 nvm 装的,npx命令在非交互 Shell 里不在 PATH,Agent 启动 MCP 服务器就会失败。解决办法是在配置里显式写好command: /path/to/npx,或者用env字段补全 PATH。我踩过这个坑后,默认配置里会给出一个PATH示例环境变量。
另外,早期我实现了旧的 SSE 传输,后来 MCP 规范升级到 Streamable HTTP 后,很多新服务器不再提供 SSE 端点。如果你发现某个 MCP 服务器在别的客户端正常、在我这个工具里连不上,十有八九是传输协议不对。2025 年 3 月后的规范里,标准做法已经是streamable HTTP,我把默认协议改成了这版,同时兼容旧服务器的stdio。
5.4 杀软误报:单文件二进制的“原罪”
单文件可执行程序是杀毒软件的重点怀疑对象,尤其是 Go 编译的、还带着 GUI 自动化和截图功能的程序,很容易被误判为“远程访问木马”或“键盘记录器”。我拿到 VirusTotal 上跑了全引擎扫描,确实有两三款引擎会报风险标签。这没办法完全避免,只能提供源码和构建脚本,让有疑心的用户可以自行编译。如果你想自己动手打包,我在项目里放了build.sh,执行后能在dist/下生成对应平台二进制。对我来说,与其和杀软对抗,不如把透明度做足。
6. 安全边界:免费版不乱来,也不让你乱来
6.1 GUI 操作白名单与敏感信息保护
一个能操控你鼠标键盘的 AI,如果没有任何约束,想想都吓人。所以我把安全设计当成核心功能来做,而不是附加项。第一道防线就是前面说的allowlist窗口白名单。第二道防线是敏感区域冻结:Agent 的点击和键盘输入会被限制在它当前活动的应用窗口内,但如果它尝试向密码框、密钥管理界面、银行页面发送按键,我会直接拦截。简单点说,如果你开着 1Password 或者系统钥匙串窗口,Agent 从 UI 树里看到控件类型是Edit(Password)时,所有写入操作都会被拒,并提示模型“该操作为了安全已被阻止”。
6.2 MCP 工具权限分级
MCP 工具本身能力差异很大。文件系统工具能读能写,浏览器工具能上任意网站,数据库工具能执行任意 SQL。我不可能让 Agent 对所有工具都有无限权限。因此我在配置里增加了mcp_permissions字段,支持三种模式:
allow_all:全部放行,适合你自己在可信环境下用。ask_user:Agent 第一次调用某个工具时,弹确认框问用户是否允许。deny:关掉指定工具。
比如我平时常用ask_user模式,只有当我确定某个任务完全可信时才会切到allow_all。这个设计牺牲了一点自动化流畅度,但换来的是“你敢放它跑”的安全感。
6.3 数据本地化与云端 API 的选择
免费分发不意味着数据必须走云端。我的工具天然支持 Ollama 这类本地模型,你只要在配置里填:
model: base_url: http://localhost:11434/v1 api_key: ollama model: qwen2.5-coder:14b就能完全离线跑。截图、OCR 文本、UI 树这些敏感数据只会发给模型 API,而不会上传到我的服务器——因为根本没有服务器。如果你用本地模型,所有数据都不会离开机器。这是我觉得这个工具和那些闭源商业 Agent 最大的不同:你完全掌控数据流向。
最后说几句实在的
项目免费放出来已经两周了,GitHub 上 star 不算多,但有几个用户反馈让我挺有成就感:有人拿它自动整理测试环境,有人用它把重复性的 UI 操作封装成了自然语言任务。我自己的体会是,单文件分发确实是个门槛极低的选择,让很多非 Python 用户也能跑起来;但同样因为单文件,很多系统级集成做不了,比如没法像 Codex 那样直接作为 CLI 插件被其他工具调用。好在这条路还很长,MCP 生态每天都在长新的东西出来,GUI 自动化和 LLM 的结合也远没到尽头。如果你也想试试,我的建议是先别急着让它干复杂的活,从一个“打开软件、读取某个窗口、点击某个按钮”的小任务开始,把配置和权限摸熟,再逐步放权。毕竟,让一个 AI 替你操作电脑这件事,边界感和信任感都是一点点建立的。