1278 lines
57 KiB
Markdown
1278 lines
57 KiB
Markdown
# GUI Agent — Spec 规范文档
|
||
|
||
> **文档类型**: Spec(规范文档)— 描述"做什么",定义功能范围与边界
|
||
>
|
||
> **状态**: Draft
|
||
>
|
||
> **日期**: 2026-03-17
|
||
|
||
---
|
||
|
||
## 1. 项目概述
|
||
|
||
### 1.1 是什么
|
||
|
||
一个以 **MCP Server** 形式运行的 GUI 桌面操作服务,供文本模型 Agent(如 claude-code、qimingcode)通过 MCP 协议调用,实现截图识别 + 键鼠模拟来自动操作桌面应用程序。
|
||
|
||
### 1.2 解决什么问题
|
||
|
||
- 文本模型 Agent 擅长代码/文件操作,但**无法操作图形界面**
|
||
- 需要一个标准化的 MCP 服务,让文本 Agent 可以**委托 GUI 操作任务**
|
||
- 文本 Agent 在工作中遇到 GUI 场景时(验证 UI、操作浏览器、填写表单等),能无缝调用
|
||
|
||
### 1.3 核心定位
|
||
|
||
```
|
||
用户
|
||
│ 自然语言对话
|
||
▼
|
||
文本 Agent (claude-code / qimingcode / 其他) ← 决策者,理解用户意图
|
||
│ MCP 协议调用
|
||
▼
|
||
GUI Agent MCP Server (本项目) ← 执行者,操作桌面
|
||
│
|
||
├── 模式 A: 原子操作(截图/点击/输入) ← 文本 Agent 自己编排步骤
|
||
│
|
||
└── 模式 B: 完整任务(自然语言 → Agent 循环) ← GUI Agent 内部自主完成
|
||
```
|
||
|
||
**关键区分**:用户不直接与 GUI Agent 交互。文本 Agent 是"大脑",GUI Agent 是"眼睛和手"。
|
||
|
||
---
|
||
|
||
## 2. 使用场景
|
||
|
||
### 2.1 典型场景
|
||
|
||
| 场景 | 文本 Agent 做什么 | GUI Agent 做什么 |
|
||
|------|------------------|-----------------|
|
||
| **验证 UI 效果** | 改完前端代码后,调 `gui_screenshot` 截图查看效果 | 截图返回 |
|
||
| **操作浏览器** | 调 `gui_execute_task("打开浏览器访问 http://localhost:3000 并截图首页")` | 自主完成:找到浏览器 → 输入 URL → 等待加载 → 截图返回 |
|
||
| **填写表单** | 逐步调 `gui_click` + `gui_type` 填写各字段 | 执行单次操作并返回 |
|
||
| **跨应用操作** | 调 `gui_execute_task("从 Excel 复制 A1:B10 的数据,粘贴到浏览器表单")` | 自主完成多步跨应用操作 |
|
||
| **安装软件** | 调 `gui_execute_task("双击桌面上的安装包,按默认选项完成安装")` | 自主完成安装向导的多步点击 |
|
||
|
||
### 2.2 调用方角色
|
||
|
||
- **主要**: 文本模型 Agent(claude-code、qimingcode 或任何支持 MCP 的 Agent)
|
||
- **次要**: QimingClaw Electron 客户端(通过 SDK 嵌入方式集成)
|
||
|
||
### 2.3 GUI Agent 不面对的角色
|
||
|
||
- **终端用户** — 用户与文本 Agent 对话,不直接操作 GUI Agent
|
||
- 因此**不需要 CLI 交互模式**,不需要 TUI/Web UI
|
||
|
||
---
|
||
|
||
## 3. 功能需求
|
||
|
||
### 3.1 MCP 服务(核心功能)
|
||
|
||
GUI Agent 的**唯一入口**是 MCP 协议。暴露两类 Tool:
|
||
|
||
#### 3.1.1 原子操作工具(文本 Agent 自行编排)
|
||
|
||
文本 Agent 可以逐步调用这些工具,自己决定操作顺序:
|
||
|
||
| MCP Tool | 描述 | 入参 | 出参 |
|
||
|----------|------|------|------|
|
||
| `gui_screenshot` | 全屏截图 | `displayIndex?` | `{ image, mimeType, imageWidth, imageHeight, logicalWidth, logicalHeight, scaleFactor }` |
|
||
| `gui_click` | 鼠标单击 | `x`, `y`, `button?`, `coordinateMode?` | `{ success, elapsed }` |
|
||
| `gui_double_click` | 鼠标双击 | `x`, `y`, `button?`, `coordinateMode?` | `{ success, elapsed }` |
|
||
| `gui_move_mouse` | 移动鼠标 | `x`, `y`, `coordinateMode?` | `{ success }` |
|
||
| `gui_drag` | 鼠标拖拽 | `startX`, `startY`, `endX`, `endY`, `button?`, `coordinateMode?` | `{ success, elapsed }` |
|
||
| `gui_scroll` | 滚动 | `x`, `y`, `deltaY`, `deltaX?`, `coordinateMode?` | `{ success }` |
|
||
| `gui_type` | 输入文本 | `text` | `{ success, elapsed }` |
|
||
| `gui_press_key` | 按下单键 | `key` | `{ success }` |
|
||
| `gui_hotkey` | 组合键 | `keys[]` | `{ success }` |
|
||
| `gui_cursor_position` | 获取光标位置 | 无 | `{ x, y }` |
|
||
| `gui_list_displays` | 获取显示器列表 | 无 | `{ displays: [...] }` |
|
||
| `gui_find_image` | 在屏幕上查找图像 | `templateImage`, `confidence?` | `{ found, region?, confidence? }` |
|
||
| `gui_wait_for_image` | 等待图像出现 | `templateImage`, `timeout?`, `confidence?` | `{ found, region?, elapsed }` |
|
||
| `gui_analyze_screen` | 视觉模型分析屏幕 | `prompt`, `displayIndex?` | `{ analysis, imageWidth, imageHeight }` |
|
||
|
||
#### 3.1.2 完整任务工具(GUI Agent 自主执行)
|
||
|
||
文本 Agent 下发自然语言任务,GUI Agent 内部启动 Agent 循环自主完成:
|
||
|
||
| MCP Tool | 描述 | 入参 | 出参 |
|
||
|----------|------|------|------|
|
||
| `gui_execute_task` | 执行完整的 GUI 任务(同步阻塞) | `task: string`, `maxSteps?` | `{ success, result?, finalScreenshot?, steps[], error? }` |
|
||
|
||
**同步阻塞执行**:`gui_execute_task` 是标准的 MCP tool call,handler 内部 await Agent 循环完成后返回结果。利用 MCP SDK 原生机制:
|
||
|
||
| 能力 | MCP SDK 机制 | 说明 |
|
||
|------|-------------|------|
|
||
| **进度通知** | `notifications/progress` + `extra.sendNotification()` | 每步操作通过 progressToken 推送进度 |
|
||
| **请求取消** | `extra.signal`(AbortSignal) | 客户端可随时取消,handler 中监听 signal 终止 Agent 循环 |
|
||
| **连接断开** | Transport 关闭自动触发 AbortSignal | 无需手动管理 session 清理 |
|
||
|
||
**互斥执行**:桌面同一时间只能有一个 GUI 操作者。通过互斥锁(Mutex)确保同时只有一个 `gui_execute_task` 在执行,第二个调用等待锁释放后再执行。
|
||
|
||
#### 3.1.3 MCP Resources(信息暴露)
|
||
|
||
| Resource | 描述 |
|
||
|----------|------|
|
||
| `gui://status` | 服务状态(运行中、平台、权限) |
|
||
| `gui://permissions` | 当前平台权限状态 |
|
||
| `gui://audit-log` | 最近操作审计日志 |
|
||
|
||
#### 3.1.4 MCP 传输方式
|
||
|
||
| 传输 | 启动方式 | 适用场景 |
|
||
|------|---------|---------|
|
||
| **Streamable HTTP**(主模式) | `gui-agent --port 60008` | 持久化本地 HTTP 服务,多个 Agent 客户端通过 URL 连接 |
|
||
| **stdio**(备选) | `gui-agent --transport stdio` | 单个文本 Agent spawn 子进程,简单场景 |
|
||
|
||
**Streamable HTTP 主模式说明**:
|
||
|
||
参考 `qiming-mcp-stdio-proxy` 的 proxy 模式,GUI Agent 作为**持久化本地 HTTP 服务**启动:
|
||
|
||
- 监听 `127.0.0.1:<port>`,长期运行
|
||
- 多个文本 Agent(claude-code、qimingcode 等)通过 MCP URL 连接,各自独立 session
|
||
- 每个客户端连接创建独立的 `StreamableHTTPServerTransport` + MCP Server 实例
|
||
- Session 通过 `mcp-session-id` HTTP header 跟踪,支持 session 清理
|
||
- 桌面是共享资源,`gui_execute_task` 通过互斥锁确保同时只有一个在执行(见 3.1.2)
|
||
|
||
---
|
||
|
||
### 3.2 Agent 循环(`gui_execute_task` 内部实现)
|
||
|
||
当文本 Agent 调用 `gui_execute_task` 时,GUI Agent 内部启动一个自主循环:
|
||
|
||
```
|
||
收到任务文本
|
||
↓
|
||
截取屏幕 → 缩放到模型目标分辨率 → 转 JPEG base64
|
||
↓
|
||
构造多模态消息(截图 + 任务描述 + 上下文)→ 调用 LLM
|
||
↓
|
||
LLM 分析 UI 并返回 tool call(含模型格式的坐标)
|
||
↓
|
||
CoordinateResolver: 模型坐标 → 归一化 → 逻辑坐标
|
||
↓
|
||
执行桌面操作(nut.js 使用逻辑坐标)
|
||
↓
|
||
再次截图验证操作结果
|
||
↓
|
||
循环直到:LLM 判断任务完成 / 达到 maxSteps / 被 abort
|
||
↓
|
||
返回结果给调用方(通过 MCP response)
|
||
```
|
||
|
||
**pi-mono 集成方式**:
|
||
- 使用 `@mariozechner/pi-ai` 作为 LLM 调用层(多 Provider 统一接口)
|
||
- 使用 `@mariozechner/pi-agent-core` 的 tool calling 循环
|
||
- 桌面操作工具通过 pi-mono 的 **extension/tool 注册机制**注入
|
||
- 利用 pi-mono 的 **context compaction** 处理长任务的 token 溢出
|
||
|
||
**借鉴 TuriX-CUA 的三层记忆管理**:
|
||
- **Summary**:更早步骤的高度压缩摘要(由 LLM 总结),超限时做"摘要的摘要"
|
||
- **Recent**:最近完成的步骤记录 + 评估结果
|
||
- **Pending**:当前正在执行的步骤,完成后移入 Recent
|
||
- **目的**:Agent 循环每步都带截图(base64 很大),历史截图如果不压缩会迅速撑爆 context window
|
||
- 当 Recent 超预算时触发 LLM 摘要压缩:总结文字 → 移入 Summary,丢弃旧截图 base64,保留文字描述
|
||
- 记忆摘要可使用独立的更便宜模型(通过 `GUI_AGENT_MEMORY_MODEL` 配置)
|
||
|
||
### 3.3 多模型 LLM 支持
|
||
|
||
- **必须支持**: Anthropic (Claude)、OpenAI 协议
|
||
- 支持任何 OpenAI 兼容端点(Azure OpenAI、本地 vLLM 等)
|
||
- 支持多模态输入(截图图片 + 文本)
|
||
- 支持 tool calling(LLM 返回要执行的桌面操作)
|
||
- 模型配置通过环境变量或配置文件传入
|
||
|
||
### 3.4 截图与坐标系统(核心难点)
|
||
|
||
#### 3.4.1 问题本质
|
||
|
||
GUI Agent 的核心循环是"截图 → 视觉模型分析 → 输出坐标 → 执行点击"。
|
||
|
||
**关键认知**:视觉模型输出的坐标格式是**训练数据决定的**,不是推理时可以改变的。不同模型的训练方式完全不同,坐标格式各异。**坐标转换必须由系统层完成**,绝不能让模型自己计算缩放——模型做数学运算误差极大。
|
||
|
||
#### 3.4.2 三层坐标空间
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Layer 1: 物理分辨率 (Physical Pixels) │
|
||
│ macOS Retina 15": 2880 × 1800 │
|
||
│ Windows 150% DPI: 2880 × 1620 │
|
||
│ 说明: 屏幕的实际像素数 │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ Layer 2: 逻辑分辨率 (Logical Points) │
|
||
│ macOS Retina 15": 1440 × 900 (scaleFactor = 2) │
|
||
│ Windows 150% DPI: 1920 × 1080 (scaleFactor = 1.5) │
|
||
│ 说明: OS 报告的坐标空间,nut.js 操作使用此坐标 │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ Layer 3: 发送给模型的图片像素 (Image Pixels) │
|
||
│ 需要按模型要求缩放到特定尺寸再发送 │
|
||
│ 不同模型要求不同(见 3.4.3) │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
#### 3.4.3 各视觉模型的坐标格式(调研数据)
|
||
|
||
| 模型 | 坐标格式 | 坐标范围 | 推荐截图分辨率 | 说明 |
|
||
|------|---------|---------|---------------|------|
|
||
| **Claude computer_use** | 图片绝对像素 | (0, 0) ~ (imgW, imgH) | **1024×768** 或 **1280×800** | Anthropic 推荐缩放到 XGA/WXGA,模型输出该图片空间的绝对坐标。最大边不超过 1568px |
|
||
| **GPT-4o / GPT-5.4** | 图片绝对像素 | (0, 0) ~ (imgW, imgH) | 原始分辨率(最大 10.24M 像素) | OpenAI CUA 输出图片像素空间的绝对坐标 |
|
||
| **Gemini 2.5** | 归一化 0-999 | (0, 0) ~ (999, 999) | **1440×900** | Google 推荐;输出归一化网格坐标,与分辨率无关 |
|
||
| **UI-TARS** (ByteDance) | 归一化 0-1000 | (0, 0) ~ (1000, 1000) | 任意 | 输出千分比坐标,与图片实际尺寸无关 |
|
||
| **Qwen2.5-VL** | 图片绝对像素 | (0, 0) ~ (imgW, imgH) | 任意 | 输出 bbox 格式 [x1, y1, x2, y2] 的图片绝对坐标 |
|
||
| **CogAgent** | 图片绝对像素 | (0, 0) ~ (imgW, imgH) | ≤1120×1120 | 输出 box 格式的图片绝对坐标 |
|
||
| **SeeClick / ShowUI** | 归一化 0-1 | (0.0, 0.0) ~ (1.0, 1.0) | 任意 | 输出小数比例坐标,完全分辨率无关 |
|
||
|
||
**三大坐标家族**:
|
||
|
||
| 家族 | 代表模型 | 特征 |
|
||
|------|---------|------|
|
||
| **图片绝对像素** | Claude CU、GPT CUA、Qwen2.5-VL、CogAgent | 坐标与发送的图片尺寸强绑定,换图片大小坐标就变 |
|
||
| **归一化千分制** | UI-TARS、Gemini | 0-999 或 0-1000,与图片尺寸无关 |
|
||
| **归一化小数** | SeeClick、ShowUI | 0.0-1.0,与图片尺寸无关 |
|
||
|
||
#### 3.4.4 截图策略:统一分级缩放
|
||
|
||
**核心原则**:截图必须从物理分辨率缩放到逻辑分辨率(吸收 scaleFactor),否则坐标会偏移 scaleFactor 倍。逻辑分辨率过大时进一步等比缩放。
|
||
|
||
**统一分级缩放策略**(参考 TuriX-CUA,不按模型区分):
|
||
|
||
不同模型的坐标转换都会经过归一化步骤(`modelX / imageWidth` 或 `modelX / 1000` 等),数学上与截图发送的分辨率无关。因此**不需要按模型匹配不同截图分辨率**。
|
||
|
||
```
|
||
屏幕 (逻辑 1440×900, 物理 2880×1800)
|
||
│
|
||
│ 全屏截图 → 原始图片 (物理分辨率 2880×1800)
|
||
│
|
||
│ Step 1: 缩放到逻辑分辨率 (吸收 scaleFactor)
|
||
│ 2880×1800 → 1440×900
|
||
│
|
||
│ Step 2: 逻辑分辨率最长边 > 1920 时,等比缩放到最长边 1920
|
||
│ 例: 2560×1440 → 1920×1080
|
||
│
|
||
│ Step 3: 转 JPEG quality=75 进一步压缩
|
||
▼
|
||
发送给视觉模型 (base64 JPEG)
|
||
```
|
||
|
||
| 逻辑分辨率最长边 | 缩放策略 | 示例 |
|
||
|----------------|---------|------|
|
||
| ≤ 1920 | 不缩放(保持逻辑分辨率) | 1440×900 → 1440×900 |
|
||
| 1921 ~ 2560 | 等比缩放到最长边 1920 | 2560×1440 → 1920×1080 |
|
||
| > 2560 | 等比缩放到最长边 1920 | 3840×2160 → 1920×1080 |
|
||
|
||
#### 3.4.5 坐标转换链路
|
||
|
||
```
|
||
截图 (全屏, 物理分辨率)
|
||
│
|
||
│ 统一分级缩放(见 3.4.4)
|
||
▼
|
||
发送给视觉模型
|
||
│
|
||
│ 模型输出坐标 (model_x, model_y),格式由训练决定
|
||
▼
|
||
CoordinateResolver(四步转换)
|
||
│
|
||
│ Step 1: 坐标顺序修正(Gemini yx → xy swap)
|
||
│ Step 2: 模型坐标 → 归一化 (0~1)
|
||
│ Step 3: 归一化 → 逻辑坐标(相对于目标显示器)
|
||
│ Step 4: 逻辑坐标 + 显示器偏移 → 全局坐标
|
||
▼
|
||
nut.js mouse.click(global_x, global_y)
|
||
```
|
||
|
||
**Step 1 — 坐标顺序修正**:
|
||
|
||
| 坐标顺序 | 处理 | 适用模型 |
|
||
|----------|------|---------|
|
||
| `xy`(默认) | 不变,`rawX = model_x, rawY = model_y` | Claude、GPT、UI-TARS、Qwen 等 |
|
||
| `yx`(Gemini) | swap,`rawX = model_y, rawY = model_x` | Gemini 系列 |
|
||
|
||
**Step 2 — 模型坐标 → 归一化 (0~1)**:
|
||
|
||
| 坐标家族 | 归一化公式 |
|
||
|----------|-----------|
|
||
| 图片绝对像素 (Claude/GPT/Qwen) | `norm_x = rawX / imageWidth` <br> `norm_y = rawY / imageHeight` |
|
||
| 归一化 0-1000 (UI-TARS) | `norm_x = rawX / 1000` <br> `norm_y = rawY / 1000` |
|
||
| 归一化 0-999 (Gemini) | `norm_x = rawX / 999` <br> `norm_y = rawY / 999` |
|
||
| 归一化 0-1 (SeeClick/ShowUI) | `norm_x = rawX` <br> `norm_y = rawY` |
|
||
|
||
**Step 3 — 归一化 → 逻辑坐标**(所有模型统一):
|
||
|
||
```
|
||
local_x = norm_x × logicalWidth
|
||
local_y = norm_y × logicalHeight
|
||
```
|
||
|
||
**Step 4 — 逻辑坐标 + 显示器偏移 → 全局坐标**(多显示器场景):
|
||
|
||
```
|
||
global_x = local_x + display.origin.x
|
||
global_y = local_y + display.origin.y
|
||
```
|
||
|
||
> 主显示器 origin 为 (0,0),副屏有偏移(见 3.6.5)。边界校验:结果 clamp 到目标显示器范围内。
|
||
|
||
#### 3.4.6 完整示例
|
||
|
||
**场景**:macOS Retina,逻辑分辨率 1440×900,scaleFactor=2,使用 Claude computer_use
|
||
|
||
```
|
||
1. 全屏截图 → 物理 2880×1800 的原始图
|
||
2. 系统缩放到 Claude 目标分辨率 → 1280×800 的图片
|
||
3. 发送 1280×800 图片给 Claude
|
||
4. Claude 输出: "click at (640, 400)" ← 图片绝对像素坐标
|
||
5. 归一化: norm_x = 640/1280 = 0.5, norm_y = 400/800 = 0.5
|
||
6. 逻辑坐标: x = 0.5 × 1440 = 720, y = 0.5 × 900 = 450
|
||
7. nut.js: mouse.click(720, 450) ← 屏幕正中央 ✓
|
||
```
|
||
|
||
**场景**:同一屏幕,使用 UI-TARS
|
||
|
||
```
|
||
1. 全屏截图 → 物理 2880×1800 的原始图
|
||
2. 系统缩放到合理尺寸 → 1440×900 的图片
|
||
3. 发送 1440×900 图片给 UI-TARS
|
||
4. UI-TARS 输出: (500, 500) ← 归一化千分比
|
||
5. 归一化: norm_x = 500/1000 = 0.5, norm_y = 500/1000 = 0.5
|
||
6. 逻辑坐标: x = 0.5 × 1440 = 720, y = 0.5 × 900 = 450
|
||
7. nut.js: mouse.click(720, 450) ← 屏幕正中央 ✓
|
||
```
|
||
|
||
**场景**:同一屏幕,使用未知视觉模型(fallback)
|
||
|
||
```
|
||
1. 全屏截图 → 物理 2880×1800 的原始图
|
||
2. 未知模型,缩放到逻辑分辨率 → 1440×900 的图片
|
||
(必须从物理缩到逻辑,否则坐标会偏移 scaleFactor 倍)
|
||
3. 发送 1440×900 图片给未知模型
|
||
4. 模型输出: "click at (720, 450)" ← 按 image-absolute 处理
|
||
5. 归一化: norm_x = 720/1440 = 0.5, norm_y = 450/900 = 0.5
|
||
6. 逻辑坐标: x = 0.5 × 1440 = 720, y = 0.5 × 900 = 450
|
||
7. nut.js: mouse.click(720, 450) ← 屏幕正中央 ✓
|
||
```
|
||
|
||
> **为什么 scaleFactor 不需要显式出现在坐标公式中?**
|
||
>
|
||
> 因为截图从物理分辨率缩放到目标/逻辑分辨率时,scaleFactor 已被"吸收"。
|
||
> 最终 Step 2 乘的是 `logicalWidth` 而非 `physicalWidth`,这本身就是对 DPI 的处理。
|
||
> 如果跳过缩放直接把物理分辨率的截图发给模型,模型输出的坐标会比逻辑坐标大 scaleFactor 倍,点击位置会偏到屏幕外。
|
||
|
||
#### 3.4.7 截图图片约束(API 限制)
|
||
|
||
视觉模型 API 对输入图片有严格的大小和格式限制,截图必须在发送前处理:
|
||
|
||
| 模型/Provider | 单图大小限制 | 最大分辨率 | 推荐格式 |
|
||
|--------------|-------------|-----------|---------|
|
||
| Claude (Anthropic) | 基于 token 计算(~1600 token/图) | 最长边 1568px,总 ≤1.15MP | JPEG/PNG/WebP |
|
||
| GPT-4o (OpenAI) | 20MB | 最大 10.24MP;低分辨率模式 512×512 | JPEG/PNG/WebP |
|
||
| Gemini (Google) | 20MB | 无硬限,但推荐 ≤1440×900 | JPEG/PNG/WebP |
|
||
| Qwen2.5-VL | 取决于部署配置 | 建议 ≤1280×800 | JPEG/PNG |
|
||
| UI-TARS | 取决于部署配置 | 建议 ≤1280×800 | JPEG/PNG |
|
||
|
||
**截图处理管线**:
|
||
|
||
```
|
||
全屏截图(物理分辨率,PNG lossless)
|
||
│
|
||
│ 1. 缩放到逻辑分辨率(吸收 scaleFactor)
|
||
│ 物理截图必须缩放,至少缩到逻辑分辨率,
|
||
│ 否则模型坐标 → 逻辑坐标的转换会因 scaleFactor 偏移
|
||
│ 逻辑分辨率最长边 > 1920 时,等比缩放到最长边 1920
|
||
│ 使用 LANCZOS 重采样保证质量
|
||
▼
|
||
│ 2. 转为 JPEG 格式 + 调整质量
|
||
│ quality 参数平衡文件大小与清晰度
|
||
│ 默认 quality=75
|
||
▼
|
||
│ 3. 检查文件大小是否超限
|
||
│ 超限则降低 quality 重新编码
|
||
▼
|
||
│ 4. 转 base64 编码
|
||
▼
|
||
发送给视觉模型 (base64 JPEG)
|
||
```
|
||
|
||
**为什么用 JPEG 而不是 PNG**:
|
||
- 全屏截图的 PNG 通常 2-10MB,JPEG quality=75 可压缩到 100-300KB
|
||
- 视觉模型不需要像素级无损精度,JPEG 的压缩损失对 UI 识别无影响
|
||
- 大幅减少 base64 编码后的 token 消耗和网络传输时间
|
||
- 连续截图场景(Agent 循环每步都截图),累计节省非常显著
|
||
|
||
**quality 参数策略**:
|
||
- 默认 `quality=75`:清晰度与文件大小的最佳平衡
|
||
- 小文字/高密度 UI:可提高到 `quality=90`
|
||
- Token 敏感场景:可降低到 `quality=60`
|
||
- 可通过 `GUI_AGENT_JPEG_QUALITY` 环境变量覆盖(仅内部调试用)
|
||
|
||
#### 3.4.8 截图元数据
|
||
|
||
每次截图必须携带完整元数据,坐标转换依赖这些值:
|
||
|
||
```typescript
|
||
interface ScreenshotResult {
|
||
image: string; // base64 编码的 JPEG 图片数据
|
||
mimeType: string; // "image/jpeg"(默认)| "image/png"
|
||
imageBytes: number; // 编码前的图片字节数(用于检查是否超限)
|
||
imageWidth: number; // 发送给模型的图片宽度(按模型要求缩放后)
|
||
imageHeight: number; // 发送给模型的图片高度
|
||
logicalWidth: number; // 屏幕逻辑宽度(OS 坐标空间,nut.js 操作空间)
|
||
logicalHeight: number; // 屏幕逻辑高度
|
||
physicalWidth: number; // 屏幕物理宽度
|
||
physicalHeight: number; // 屏幕物理高度
|
||
scaleFactor: number; // DPI 缩放因子(物理/逻辑)
|
||
displayIndex: number; // 截图来源的显示器索引
|
||
}
|
||
```
|
||
|
||
#### 3.4.9 CoordinateResolver(坐标解析器)
|
||
|
||
```
|
||
CoordinateResolver
|
||
├── 输入: model_x, model_y, coordinateMode, coordinateOrder, screenshotMeta, displayInfo
|
||
├── 输出: global_x, global_y
|
||
├── 四步: 坐标顺序修正 → 归一化(0~1) → 逻辑坐标 → 全局偏移
|
||
└── coordinateMode/coordinateOrder 来源:
|
||
├── 内置模型配置表(已知模型自动匹配)
|
||
└── 环境变量 GUI_AGENT_COORDINATE_MODE 手动覆盖
|
||
```
|
||
|
||
**内置模型配置表**(可扩展):
|
||
|
||
| 模型名匹配规则 | 坐标模式 | 坐标顺序 | 说明 |
|
||
|---------------|---------|---------|------|
|
||
| `claude-*` | `image-absolute` | `xy` | Anthropic Computer Use API 标准格式 |
|
||
| `gpt-4o*`, `gpt-5*` | `image-absolute` | `xy` | OpenAI CUA |
|
||
| `gemini*` | `normalized-999` | **`yx`** | Google Gemini,坐标顺序是 `[y, x]` 而非 `[x, y]`,这是 Google 训练数据的固有格式 |
|
||
| `ui-tars*` | `normalized-1000` | `xy` | UI-TARS |
|
||
| `qwen2.5-vl*`, `qwen-vl*` | `image-absolute` | `xy` | 通义千问 VL |
|
||
| `cogagent*` | `image-absolute` | `xy` | CogAgent |
|
||
| `seeclick*`, `showui*` | `normalized-0-1` | `xy` | SeeClick/ShowUI |
|
||
| **未匹配(fallback)** | `image-absolute` | `xy` | 保守策略 |
|
||
|
||
> **截图分辨率不按模型区分**:统一使用分级缩放策略(见 3.4.4),不在模型配置表中配置目标分辨率。
|
||
>
|
||
> **Gemini 坐标顺序**:Gemini 输出坐标格式为 `[y, x]`(非 `[x, y]`),这是 Google 训练数据的固有格式。CoordinateResolver 必须在归一化前根据 `coordinateOrder` 做 swap。
|
||
|
||
#### 3.4.10 原子操作中的坐标处理
|
||
|
||
对于原子操作工具(`gui_click` 等),坐标由**文本 Agent** 提供。需要支持两种输入方式:
|
||
|
||
| 参数 | 说明 |
|
||
|------|------|
|
||
| `x`, `y` | 默认为**逻辑坐标**(文本 Agent 自己换算后传入) |
|
||
| `coordinateMode?` | 可选,指定坐标格式。如果文本 Agent 直接传视觉模型输出的原始坐标,需声明格式以便 GUI Agent 转换 |
|
||
|
||
#### 3.4.11 现有 Electron 内嵌版的问题
|
||
|
||
当前 `systemPrompt.ts` 中让 Agent 自己做坐标缩放计算("multiply pixel coords by 1/scale"),这是**错误的**:
|
||
|
||
- 视觉模型的坐标输出格式由训练数据决定,不是推理时可以改变的
|
||
- 让 LLM 做数学乘除运算误差大,尤其是 Retina 下多层缩放
|
||
- 正确做法:**系统层自动完成全部坐标转换,模型只管输出它训练时学到的坐标格式**
|
||
|
||
---
|
||
|
||
### 3.5 桌面操作能力
|
||
|
||
基于 **nut.js**(`@nut-tree/nut-js`):
|
||
|
||
| 能力 | nut.js API | 说明 |
|
||
|------|-----------|------|
|
||
| 截图 | `screen.capture()` | 全屏截图(始终全屏) |
|
||
| 鼠标移动 | `mouse.moveTo(point)` | 移动光标(逻辑坐标) |
|
||
| 鼠标点击 | `mouse.click(button)` | 左/右/中键 |
|
||
| 鼠标拖拽 | `mouse.drag(path)` | 从 A 拖到 B |
|
||
| 滚动 | `mouse.scrollUp/Down(amount)` | 垂直滚动 |
|
||
| 键盘输入 | `keyboard.type(text)` | 输入文本 |
|
||
| 按键 | `keyboard.press(key)` / `release(key)` | 单键按下释放 |
|
||
| 图像查找 | `screen.find(image)` / `screen.findAll(image)` | 模板匹配 |
|
||
| 等待图像 | `screen.waitFor(image, timeout)` | 等待 UI 元素出现 |
|
||
| 光标位置 | `mouse.getPosition()` | 获取当前光标坐标(逻辑坐标) |
|
||
| 显示器 | 系统 API | 获取多显示器信息(含 scaleFactor) |
|
||
|
||
---
|
||
|
||
### 3.6 操作时序与可靠性
|
||
|
||
GUI 自动化的核心挑战不只是"点哪里",还包括"什么时候点"、"怎么输入"、"出了意外怎么办"。
|
||
|
||
#### 3.6.1 操作后等待策略
|
||
|
||
**问题**:点击按钮后,UI 动画/页面跳转/弹窗需要时间渲染。如果操作后**立即截图**,截到的是过渡状态(动画中/加载中),视觉模型会误判。
|
||
|
||
**等待策略(参考 TuriX-CUA 的分层延迟机制)**:
|
||
|
||
| 层级 | 延迟 | 说明 |
|
||
|------|------|------|
|
||
| **微操作延迟** | 10-30ms | 单个鼠标事件内部(mouseDown → mouseUp 之间) |
|
||
| **连续操作间延迟** | 300-500ms | 同一步骤内多个操作之间(如先点击再输入) |
|
||
| **步骤间延迟** | 1-2s(默认 1.5s) | Agent 循环中每步操作完成后、截图验证前的等待 |
|
||
| **错误重试延迟** | 5-10s | API 限流或操作失败后的退避等待 |
|
||
|
||
```
|
||
执行操作 (click/type/etc.)
|
||
│
|
||
│ 步骤间延迟 (默认 1.5s)
|
||
│ 目的: 等待 UI 动画完成、页面渲染稳定
|
||
▼
|
||
截取验证截图
|
||
│
|
||
│ 发送给视觉模型分析
|
||
▼
|
||
决定下一步操作
|
||
```
|
||
|
||
**可配置**:通过环境变量 `GUI_AGENT_STEP_DELAY_MS`(默认 1500ms)调整步骤间延迟。
|
||
|
||
**特殊场景延迟**:Agent 循环中,LLM 可以主动选择 `wait` 动作(如等待页面加载完成),此时额外等待指定时间后再截图。
|
||
|
||
#### 3.6.2 截图时鼠标光标处理
|
||
|
||
**问题**:鼠标光标出现在截图中,可能遮挡 UI 元素(按钮文字、输入框等),影响视觉模型识别准确率。
|
||
|
||
**策略**:
|
||
|
||
| 方案 | 说明 | 采用 |
|
||
|------|------|------|
|
||
| **截图前移动光标到角落** | 简单有效,但用户能看到光标跳动 | 备选 |
|
||
| **接受光标在截图中** | 最简单,依赖视觉模型忽略光标 | **v1 采用** |
|
||
| **操作后光标归位** | 参考 TuriX-CUA 的隐形点击:操作完成后光标恢复原位 | v2 考虑 |
|
||
|
||
**v1 方案**:不主动处理光标,接受光标出现在截图中。原因:
|
||
- 主流视觉模型(Claude CU、GPT CUA)的训练数据中包含光标,能正确忽略
|
||
- 移动光标本身是一次额外操作,增加复杂度和失败概率
|
||
- 如果实测发现光标遮挡严重影响准确率,v2 再引入光标隐藏
|
||
|
||
#### 3.6.3 文本输入策略
|
||
|
||
**问题 1:CJK/非 ASCII 字符输入**
|
||
|
||
nut.js 的 `keyboard.type()` 基于键码模拟,**无法直接输入中文、日文、韩文**等需要 IME(输入法)的字符。
|
||
|
||
**解决方案:剪贴板粘贴**
|
||
|
||
```
|
||
输入文本
|
||
│
|
||
│ 检测: 是否包含非 ASCII 字符?
|
||
│
|
||
├── 纯 ASCII + 短文本 → keyboard.type() 逐字模拟
|
||
│
|
||
└── 包含非 ASCII 或长文本 → 剪贴板粘贴模式:
|
||
1. 保存当前剪贴板内容
|
||
2. 将目标文本写入剪贴板
|
||
3. 模拟 Cmd+V (macOS) / Ctrl+V (Windows/Linux)
|
||
4. 恢复原剪贴板内容
|
||
```
|
||
|
||
> **参考**:TuriX-CUA 使用 macOS Quartz 的 `CGEventKeyboardSetUnicodeString` 直接发送 Unicode 事件,但这是 macOS 专用 API。我们需要跨平台方案,剪贴板粘贴是最通用的方式。
|
||
|
||
**问题 2:长文本输入**
|
||
|
||
`keyboard.type()` 逐字模拟击键,长文本(>50 字符)存在问题:
|
||
- 速度慢(每字符需要 keyDown + keyUp 两个事件)
|
||
- 中途焦点切换会导致文本输入到错误窗口
|
||
- 可能触发系统的按键重复机制
|
||
|
||
**策略**:
|
||
|
||
| 文本特征 | 输入方式 | 原因 |
|
||
|----------|---------|------|
|
||
| 纯 ASCII,≤50 字符 | `keyboard.type()` 逐字模拟 | 最自然,兼容性最好 |
|
||
| 纯 ASCII,>50 字符 | 剪贴板粘贴 | 速度快,避免焦点丢失 |
|
||
| 包含非 ASCII 字符 | 剪贴板粘贴 | IME 兼容 |
|
||
|
||
**剪贴板恢复**:粘贴完成后必须恢复用户原有的剪贴板内容,避免破坏用户的复制粘贴工作流。
|
||
|
||
#### 3.6.4 意外弹窗处理
|
||
|
||
**问题**:操作过程中可能出现意外遮挡:
|
||
- 系统通知(macOS 通知中心、Windows Toast Notification)
|
||
- 权限请求弹窗("xxx 想要访问你的麦克风")
|
||
- 应用自身的弹窗(更新提醒、错误对话框、Cookie 同意)
|
||
- 屏保/锁屏触发
|
||
|
||
**策略(参考 TuriX-CUA:依赖 LLM 识别)**:
|
||
|
||
v1 不做自动弹窗检测,交给 Agent 循环中的视觉模型处理:
|
||
- 视觉模型截图后能看到弹窗
|
||
- 在 system prompt 中明确指导 LLM:如果发现意外弹窗/通知,优先关闭弹窗再继续任务
|
||
- 连续多步操作未有效推进(前后截图无变化),触发 stuck 检测
|
||
|
||
**system prompt 指导(gui_execute_task 内部)**:
|
||
|
||
```
|
||
如果你在截图中发现:
|
||
- 系统通知弹窗:忽略,通知会自动消失
|
||
- 权限请求对话框:点击"允许"或"确定",然后继续任务
|
||
- 应用错误弹窗:点击"关闭"或"确定",然后继续任务
|
||
- 如果弹窗阻挡了目标区域且无法关闭,报告失败原因
|
||
```
|
||
|
||
**stuck 检测**:连续 N 步(默认 3 步)操作后截图无明显变化(可通过图像相似度粗判),判定为卡死,自动终止并返回错误。
|
||
|
||
#### 3.6.5 多显示器坐标偏移
|
||
|
||
**问题**:多显示器环境下,OS 使用**全局连续坐标系统**。副屏的坐标原点不是 (0,0),而是相对于主屏有偏移:
|
||
|
||
```
|
||
┌──────────────┐┌──────────────────┐
|
||
│ Display 1 ││ Display 0 │
|
||
│ (副屏) ││ (主屏) │
|
||
│ origin: ││ origin: (0,0) │
|
||
│ (-2560, 0) ││ size: 1440×900 │
|
||
│ size: ││ │
|
||
│ 2560×1440 ││ │
|
||
└──────────────┘└──────────────────┘
|
||
```
|
||
|
||
nut.js 的鼠标操作使用**全局坐标**。如果 GUI Agent 选择了副屏(displayIndex=1),点击该屏幕上的 (100, 200) 实际需要传给 nut.js 的是 (-2560+100, 0+200) = (-2460, 200)。
|
||
|
||
**策略**:
|
||
|
||
```
|
||
CoordinateResolver 输出逻辑坐标 (local_x, local_y)
|
||
│ 这是相对于目标显示器左上角 (0,0) 的坐标
|
||
│
|
||
│ 获取目标显示器的全局偏移量 (display.origin.x, display.origin.y)
|
||
▼
|
||
全局坐标: global_x = display.origin.x + local_x
|
||
global_y = display.origin.y + local_y
|
||
│
|
||
▼
|
||
nut.js mouse.click(global_x, global_y)
|
||
```
|
||
|
||
**获取显示器偏移量**:
|
||
- macOS: `screen.getAllDisplays()` 返回的 `bounds.x/y` 即为全局偏移
|
||
- Windows: `EnumDisplayMonitors` + `MONITORINFO.rcMonitor`
|
||
- Linux X11: `XRRGetScreenResources` + `XRRCrtcInfo`
|
||
|
||
**截图也需要对应**:截图时只截取目标显示器的画面,不是整个桌面。nut.js 的 `screen.capture()` 支持指定区域参数。
|
||
|
||
#### 3.6.6 屏幕状态前置检查
|
||
|
||
**问题**:在执行 GUI 操作前,屏幕可能处于不可用状态。
|
||
|
||
| 状态 | 影响 | 处理 |
|
||
|------|------|------|
|
||
| 屏幕锁定/屏保 | 截图为锁屏画面,操作无效 | 检测到后返回错误,不继续操作 |
|
||
| 目标应用最小化 | 截图中看不到目标窗口 | 交给 LLM 判断(截图中无目标时自行处理) |
|
||
| 系统模态对话框 | 阻止其他操作 | 同 3.6.4 弹窗处理 |
|
||
|
||
---
|
||
|
||
### 3.7 安全需求
|
||
|
||
#### S1: 危险操作防护
|
||
|
||
- 阻断危险热键:`Cmd+Q`/`Alt+F4`(关闭应用)、`Ctrl+Alt+Delete` 等
|
||
- 可配置的热键黑名单
|
||
|
||
#### S2: 审计日志
|
||
|
||
- 记录所有操作:时间戳、操作类型、坐标/文本、成功/失败
|
||
- 通过 MCP Resource `gui://audit-log` 可查询
|
||
- 环形缓冲(默认 1000 条)
|
||
|
||
#### S3: 最大轮次限制
|
||
|
||
- `gui_execute_task` 的 Agent 循环有 `maxSteps` 上限(默认 50)
|
||
- 超限后自动终止并返回当前状态和已完成的步骤
|
||
|
||
#### S4: 输入校验
|
||
|
||
- 坐标范围校验
|
||
- 文本长度限制(≤10000 字符)
|
||
- 请求体大小限制
|
||
|
||
---
|
||
|
||
### 3.8 平台需求
|
||
|
||
| 能力 | macOS | Windows | Linux (X11) | Linux (Wayland) |
|
||
|------|:-----:|:-------:|:-----------:|:---------------:|
|
||
| 截图 | 需 Screen Recording 权限 | 无需 | 支持 | 受限 |
|
||
| 键鼠控制 | 需 Accessibility 权限 | 无需 | 需 xdotool | 受限 |
|
||
| 图像查找 | 支持 | 支持 | 支持 | 受限 |
|
||
|
||
---
|
||
|
||
### 3.9 运行模式
|
||
|
||
| 模式 | 描述 | 入口 |
|
||
|------|------|------|
|
||
| **MCP Server 模式**(主要) | 作为持久化本地 HTTP 服务启动,多 Agent 通过 URL 连接 | `gui-agent --port 60008`(Streamable HTTP)或 `gui-agent --transport stdio`(stdio 备选) |
|
||
| **SDK 嵌入模式** | 被 QimingClaw Electron 客户端等应用集成 | `import { createGuiAgentMcpServer }` |
|
||
|
||
> 注意:**没有 CLI 交互模式**。用户不直接与 GUI Agent 对话。
|
||
|
||
---
|
||
|
||
## 4. 技术选型
|
||
|
||
| 组件 | 选型 | 理由 |
|
||
|------|------|------|
|
||
| **Agent 框架** | pi-mono (`@mariozechner/pi-ai` + `@mariozechner/pi-agent-core`) | 极简、多 Provider(Anthropic/OpenAI/Gemini)、tool calling 成熟、context compaction |
|
||
| **桌面自动化** | nut.js (`@nut-tree/nut-js`) | 成熟的 Node.js 桌面自动化库,跨平台,支持截图/键鼠/图像查找 |
|
||
| **MCP SDK** | `@modelcontextprotocol/sdk` | MCP 官方 SDK,支持 stdio / Streamable HTTP |
|
||
| **语言** | TypeScript | 与 pi-mono 和 nut.js 生态一致 |
|
||
| **运行时** | Node.js 22+ | nut.js 要求 |
|
||
|
||
---
|
||
|
||
## 5. 架构概览
|
||
|
||
### 5.1 进程模型
|
||
|
||
```
|
||
文本 Agent 进程 (claude-code / qimingcode)
|
||
│
|
||
│ spawn 子进程 (stdio) 或 HTTP 连接
|
||
▼
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ GUI Agent 进程 │
|
||
│ │
|
||
│ ┌─────────────────────────────────────────────┐ │
|
||
│ │ MCP Server 层 │ │
|
||
│ │ 接收 MCP tool call → 分发到对应 handler │ │
|
||
│ └──────┬──────────────────────┬───────────────┘ │
|
||
│ │ │ │
|
||
│ 原子操作 完整任务 │
|
||
│ (直接执行) (启动 Agent 循环) │
|
||
│ │ │ │
|
||
│ ▼ ▼ │
|
||
│ ┌─────────────┐ ┌──────────────────┐ │
|
||
│ │ 桌面工具层 │ │ pi-mono Agent │ │
|
||
│ │ (nut.js) │◄────│ 循环引擎 │ │
|
||
│ │ 截图/键鼠 │ │ LLM + Tools │ │
|
||
│ └─────────────┘ └──────────────────┘ │
|
||
│ │ │
|
||
│ ┌─────────────┐ │
|
||
│ │ 安全层 │ │
|
||
│ │ 热键审计 │ │
|
||
│ └─────────────┘ │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 5.2 两种调用路径对比
|
||
|
||
| | 原子操作 (`gui_click` 等) | 完整任务 (`gui_execute_task`) |
|
||
|--|--------------------------|------------------------------|
|
||
| **决策者** | 文本 Agent | GUI Agent 内部 LLM |
|
||
| **步骤编排** | 文本 Agent 自行规划 | GUI Agent 自主循环 |
|
||
| **LLM 调用** | 不涉及(纯工具执行) | GUI Agent 调用 pi-ai |
|
||
| **适用场景** | 简单操作、文本 Agent 有视觉能力时 | 复杂多步 GUI 任务 |
|
||
| **延迟** | 低(单次操作) | 高(多轮 LLM + 多次截图) |
|
||
|
||
### 5.3 与现有 Electron 内嵌版的关系
|
||
|
||
```
|
||
QimingClaw Electron 客户端
|
||
│
|
||
├── 现有内嵌版 (HTTP API, src/main/services/gui/)
|
||
│ 特点: 依赖 Electron API, 工具层, 无自主决策
|
||
│ 适用: 引擎自己能看图决策时的简单 GUI 操作
|
||
│
|
||
└── 本项目 (MCP Server, 独立进程)
|
||
特点: 独立于 Electron, 自带 LLM, 自主决策
|
||
适用: 复杂 GUI 任务, 标准 MCP 集成, 任何 Agent 可调用
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Electron 客户端集成需求(crates/agent-electron-client 改造)
|
||
|
||
GUI Agent 作为独立 MCP 服务运行,但需要 QimingClaw Electron 客户端提供两项配套能力:**显示器选择** 和 **视觉模型配置接口**。
|
||
|
||
### 6.1 显示器选择
|
||
|
||
#### 6.1.1 需求背景
|
||
|
||
- 用户可能有多个显示器(如笔记本 + 外接屏)
|
||
- GUI Agent 截图和操作需要明确针对**哪一个显示器**
|
||
- 用户应在使用 GUI 操作前选择目标显示器,未选择则默认主显示器
|
||
|
||
#### 6.1.2 功能描述
|
||
|
||
| 功能点 | 说明 |
|
||
|--------|------|
|
||
| **显示器列表获取** | 调用 `screen.getAllDisplays()` 获取所有显示器信息(名称、分辨率、scaleFactor、是否主屏) |
|
||
| **默认选择** | 未设置时默认使用主显示器(`screen.getPrimaryDisplay()`) |
|
||
| **用户可选** | 在 GUIAgentSettings.tsx 中提供下拉选择器,显示所有显示器及其分辨率 |
|
||
| **持久化** | 选择结果存入 SQLite(`gui_agent_config` 中新增 `displayIndex` 字段) |
|
||
| **热更新** | 显示器插拔时自动刷新列表;如果已选显示器被拔掉,回退到主显示器 |
|
||
|
||
#### 6.1.3 配置扩展
|
||
|
||
`GuiAgentConfig` 新增字段:
|
||
|
||
```typescript
|
||
interface GuiAgentConfig {
|
||
// ... 现有字段 ...
|
||
/** 目标显示器索引,默认 0(主显示器) */
|
||
displayIndex: number;
|
||
}
|
||
```
|
||
|
||
#### 6.1.4 UI 设计
|
||
|
||
在 GUIAgentSettings.tsx 的配置区新增"目标显示器"选择器:
|
||
|
||
```
|
||
目标显示器: [Display 0 (Primary) - 1440×900 @2x ▼]
|
||
├── Display 0 (Primary) - 1440×900 @2x
|
||
├── Display 1 - 2560×1440 @1x
|
||
└── Display 2 - 1920×1080 @1.5x
|
||
```
|
||
|
||
- 显示格式:`Display {index}{primary标记} - {width}×{height} @{scaleFactor}x`
|
||
- 选中后实时预览(可选:在目标显示器上短暂闪烁边框确认)
|
||
|
||
#### 6.1.5 IPC 接口
|
||
|
||
| IPC 方法 | 说明 |
|
||
|---------|------|
|
||
| `guiAgent:getDisplays` | 返回当前所有显示器列表 |
|
||
| `guiAgent:setConfig({ displayIndex })` | 持久化选择(复用现有 setConfig) |
|
||
|
||
#### 6.1.6 传递给 GUI Agent
|
||
|
||
选中的 `displayIndex` 通过以下方式传递给独立 GUI Agent 进程:
|
||
- MCP 模式:作为 MCP Server 启动参数或环境变量 `GUI_AGENT_DISPLAY_INDEX`
|
||
- SDK 模式:直接传入配置对象
|
||
|
||
---
|
||
|
||
### 6.2 视觉模型配置 HTTP 接口
|
||
|
||
#### 6.2.1 需求背景
|
||
|
||
- GUI Agent 的 `gui_execute_task` 内部需要调用视觉模型(LLM)来分析截图并决策
|
||
- 视觉模型的配置(provider、model、api_key、base_url 等)需要一种方式传入
|
||
- 复用现有 computerServer 的 agent 端口,新增一个 HTTP 路径来接收和管理视觉模型配置
|
||
- 参考现有 `/computer/chat` 接口的 `ModelProviderConfig` 结构
|
||
|
||
#### 6.2.2 接口设计
|
||
|
||
**复用现有 computerServer**(`src/main/services/computerServer.ts`),在 `handleRequest` 中新增路由:
|
||
|
||
| 接口 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| 设置视觉模型配置 | POST | `/computer/gui-agent/vision-model` | 设置/更新视觉模型配置 |
|
||
| 获取视觉模型配置 | GET | `/computer/gui-agent/vision-model` | 读取当前视觉模型配置 |
|
||
| 获取显示器列表 | GET | `/computer/gui-agent/displays` | 返回所有显示器信息 |
|
||
| 设置目标显示器 | POST | `/computer/gui-agent/display` | 设置目标显示器 |
|
||
|
||
#### 6.2.3 视觉模型配置结构
|
||
|
||
参考现有 `ModelProviderConfig`(`/computer/chat` 入参),定义视觉模型配置:
|
||
|
||
```typescript
|
||
/** 视觉模型配置 — 用于 GUI Agent 内部的 gui_execute_task 决策 */
|
||
interface GuiVisionModelConfig {
|
||
/** LLM Provider: "anthropic" | "openai" | "qwen" | "gemini" 等 */
|
||
provider: string;
|
||
/** API Key */
|
||
api_key?: string;
|
||
/** API Base URL(OpenAI 兼容端点) */
|
||
base_url?: string;
|
||
/** 模型名称(如 "claude-sonnet-4-20250514", "gpt-4o", "qwen2.5-vl-72b") */
|
||
model: string;
|
||
/** API 协议: "anthropic" | "openai"(默认 "openai") */
|
||
api_protocol?: string;
|
||
/** 坐标模式覆盖(不传则按模型名自动匹配内置配置表) */
|
||
coordinate_mode?: 'image-absolute' | 'normalized-1000' | 'normalized-999' | 'normalized-0-1';
|
||
}
|
||
```
|
||
|
||
> **设计决策**:`jpeg_quality` 不暴露给外部接口。
|
||
> - **截图分辨率**:统一分级缩放(逻辑分辨率 + 最长边 ≤1920),不按模型区分
|
||
> - **JPEG 质量**:内部默认 75(清晰度与文件大小的最佳平衡),无需外部配置
|
||
|
||
与现有 `/computer/chat` 的 `ModelProviderConfig` 对比:
|
||
|
||
| 字段 | /computer/chat | /computer/gui-agent/vision-model | 说明 |
|
||
|------|---------------|----------------------------------|------|
|
||
| `provider` | ✅ | ✅ | 相同 |
|
||
| `api_key` | ✅ | ✅ | 相同 |
|
||
| `base_url` | ✅ | ✅ | 相同 |
|
||
| `model` | ✅ | ✅ | 相同 |
|
||
| `api_protocol` | ✅ | ✅ | 相同 |
|
||
| `coordinate_mode` | ❌ | ✅ 新增(可选) | GUI 专用:视觉模型坐标格式,不传则按模型名自动匹配 |
|
||
|
||
#### 6.2.4 请求/响应示例
|
||
|
||
**POST `/computer/gui-agent/vision-model`** — 设置视觉模型配置:
|
||
|
||
```json
|
||
// Request
|
||
{
|
||
"provider": "anthropic",
|
||
"model": "claude-sonnet-4-20250514",
|
||
"api_key": "sk-ant-xxx",
|
||
"api_protocol": "anthropic"
|
||
}
|
||
|
||
// Response
|
||
{
|
||
"code": 200,
|
||
"success": true,
|
||
"message": "Vision model config updated",
|
||
"data": { "provider": "anthropic", "model": "claude-sonnet-4-20250514" }
|
||
}
|
||
```
|
||
|
||
**GET `/computer/gui-agent/vision-model`** — 获取当前配置(含内部自动推断的参数):
|
||
|
||
```json
|
||
// Response
|
||
{
|
||
"code": 200,
|
||
"success": true,
|
||
"data": {
|
||
"provider": "anthropic",
|
||
"model": "claude-sonnet-4-20250514",
|
||
"api_protocol": "anthropic",
|
||
"coordinate_mode": "image-absolute",
|
||
"screenshot_strategy": "logical_resolution, max_longest_edge=1920",
|
||
"jpeg_quality": 75
|
||
}
|
||
}
|
||
```
|
||
|
||
> GET 响应中的 `screenshot_strategy` 和 `jpeg_quality` 仅供查看,不可通过 POST 设置。
|
||
|
||
**GET `/computer/gui-agent/displays`** — 获取显示器列表:
|
||
|
||
```json
|
||
// Response
|
||
{
|
||
"code": 200,
|
||
"success": true,
|
||
"data": {
|
||
"displays": [
|
||
{ "index": 0, "label": "Display 0 (Primary)", "width": 1440, "height": 900, "scaleFactor": 2, "isPrimary": true },
|
||
{ "index": 1, "label": "Display 1", "width": 2560, "height": 1440, "scaleFactor": 1, "isPrimary": false }
|
||
],
|
||
"selectedIndex": 0
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 6.2.5 持久化
|
||
|
||
- 视觉模型配置存入 SQLite,key = `gui_vision_model_config`
|
||
- 显示器选择存入 SQLite,在 `gui_agent_config` 中(同 6.1)
|
||
- 配置更新后通过 engineHooks 环境变量注入给 GUI Agent 进程
|
||
|
||
#### 6.2.6 配置传递给 GUI Agent
|
||
|
||
```
|
||
Electron 客户端
|
||
│
|
||
├── 用户在 Settings UI 配置视觉模型 + 选择显示器
|
||
│ ↓ 持久化到 SQLite
|
||
│
|
||
├── 外部调用 POST /computer/gui-agent/vision-model 配置
|
||
│ ↓ 持久化到 SQLite
|
||
│
|
||
└── 启动 GUI Agent MCP Server 时
|
||
↓ 注入环境变量
|
||
GUI_AGENT_PROVIDER=anthropic
|
||
GUI_AGENT_MODEL=claude-sonnet-4-20250514
|
||
GUI_AGENT_API_KEY=sk-xxx
|
||
GUI_AGENT_BASE_URL=...
|
||
GUI_AGENT_COORDINATE_MODE=image-absolute
|
||
GUI_AGENT_DISPLAY_INDEX=0
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 非目标(v1 不做什么)
|
||
|
||
| 不做 | 理由 |
|
||
|------|------|
|
||
| 不做 CLI 交互模式 | 用户不直接使用,仅被文本 Agent 调用 |
|
||
| 不做 TUI / Web UI | 同上,无需用户界面 |
|
||
| 不做浏览器专用自动化 | 不替代 Playwright/Puppeteer,专注通用桌面 GUI |
|
||
| 不做 RPA 流程编排 | Agent 是 LLM 驱动的,不做可视化流程编辑 |
|
||
| 不做移动端 | 仅桌面(macOS/Windows/Linux) |
|
||
| 不做远程桌面控制 | 仅控制本机桌面 |
|
||
| 不做实时视频流分析 | 逐帧截图方式 |
|
||
| v1 不做 Accessibility Tree | 先依赖纯视觉方案保持简单通用;v2 可参考 TuriX-CUA 的 Accessibility Tree 标注辅助方案(截图上叠加元素编号,提升点击准确率) |
|
||
|
||
---
|
||
|
||
## 8. 配置
|
||
|
||
### 8.1 环境变量
|
||
|
||
| 变量 | 说明 | 默认值 |
|
||
|------|------|--------|
|
||
| `GUI_AGENT_PROVIDER` | LLM Provider (anthropic/openai/...) | `anthropic` |
|
||
| `GUI_AGENT_MODEL` | 模型名 | `claude-sonnet-4-20250514` |
|
||
| `GUI_AGENT_API_KEY` | API Key | 必填 |
|
||
| `GUI_AGENT_BASE_URL` | API Base URL(OpenAI 兼容端点) | Provider 默认 |
|
||
| `GUI_AGENT_MAX_STEPS` | Agent 循环最大轮次 | `50` |
|
||
| `GUI_AGENT_STEP_DELAY_MS` | 步骤间等待时间 (ms),操作完成到截图验证之间 | `1500` |
|
||
| `GUI_AGENT_STUCK_THRESHOLD` | 连续无变化步数阈值,超过则判定卡死 | `3` |
|
||
| `GUI_AGENT_TRANSPORT` | MCP 传输方式 (http/stdio) | `http` |
|
||
| `GUI_AGENT_PORT` | HTTP 传输端口 | `60008` |
|
||
| `GUI_AGENT_COORDINATE_MODE` | 坐标模式覆盖 (image-absolute/normalized-1000/normalized-999/normalized-0-1) | 自动按模型匹配 |
|
||
| `GUI_AGENT_JPEG_QUALITY` | JPEG 编码质量 (1-100),仅内部调试用 | `75` |
|
||
| `GUI_AGENT_DISPLAY_INDEX` | 目标显示器索引 | `0`(主显示器) |
|
||
| `GUI_AGENT_MEMORY_MODEL` | 记忆摘要用的模型(可选,可用更便宜的模型降低成本) | 复用 `GUI_AGENT_MODEL` |
|
||
| `GUI_AGENT_MEMORY_PROVIDER` | 记忆模型 Provider(可选) | 复用 `GUI_AGENT_PROVIDER` |
|
||
|
||
### 8.2 文本 Agent 的 MCP 配置示例
|
||
|
||
**Streamable HTTP 模式**(推荐,多 Agent 共享):
|
||
|
||
先启动 GUI Agent 服务:
|
||
```bash
|
||
GUI_AGENT_API_KEY=sk-xxx gui-agent --port 60008
|
||
```
|
||
|
||
文本 Agent 通过 URL 连接:
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"gui-agent": {
|
||
"url": "http://127.0.0.1:60008/mcp"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**stdio 模式**(单 Agent 专用):
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"gui-agent": {
|
||
"command": "npx",
|
||
"args": ["-y", "@qiming-ai/gui-agent", "--transport", "stdio"],
|
||
"env": {
|
||
"GUI_AGENT_PROVIDER": "anthropic",
|
||
"GUI_AGENT_MODEL": "claude-sonnet-4-20250514",
|
||
"GUI_AGENT_API_KEY": "sk-xxx"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 验收标准
|
||
|
||
### 9.1 MCP 服务
|
||
|
||
- [ ] 可作为 MCP Server 启动(stdio 模式)
|
||
- [ ] 文本 Agent 可通过 MCP 调用所有原子操作工具(13 个)
|
||
- [ ] 文本 Agent 可通过 MCP 调用 `gui_execute_task` 执行完整 GUI 任务
|
||
- [ ] `gui_execute_task` 返回结果包含步骤日志和最终截图
|
||
- [ ] `gui_execute_task` 通过 MCP progress notification 推送每步进度
|
||
- [ ] `gui_execute_task` 支持通过 MCP AbortSignal 取消执行
|
||
- [ ] 互斥锁生效:同时只有一个 `gui_execute_task` 在执行
|
||
|
||
### 9.2 Agent 循环
|
||
|
||
- [ ] `gui_execute_task` 内部可调用 Anthropic (Claude) 模型分析截图并决策
|
||
- [ ] `gui_execute_task` 内部可调用 OpenAI 协议模型分析截图并决策
|
||
- [ ] 可执行完整的"截图→分析→操作→验证"循环
|
||
- [ ] maxSteps 限制生效
|
||
|
||
### 9.3 截图与坐标
|
||
|
||
- [ ] 全屏截图正确返回 imageWidth/imageHeight/logicalWidth/logicalHeight/scaleFactor
|
||
- [ ] 归一化千分比坐标 (0-1000) 正确转换为逻辑坐标并点击准确
|
||
- [ ] 图片绝对坐标(Qwen2.5-VL 格式)正确转换为逻辑坐标并点击准确
|
||
- [ ] Retina/高 DPI 屏幕下坐标转换正确(scaleFactor > 1)
|
||
- [ ] 截图缩放 (scale < 1.0) 不影响坐标转换的准确性
|
||
- [ ] 模型类型自动匹配坐标模式,也可通过环境变量手动覆盖
|
||
- [ ] 截图转 JPEG 后文件大小在 API 限制内(Claude ≤~1.15MP,GPT ≤20MB)
|
||
- [ ] JPEG quality 可配置,默认 75
|
||
|
||
### 9.4 操作时序与可靠性
|
||
|
||
- [ ] 操作后有步骤间延迟(默认 1.5s),等待 UI 渲染完成后再截图
|
||
- [ ] 中文/CJK 文本输入通过剪贴板粘贴正确工作
|
||
- [ ] 长文本(>50 字符)自动切换为剪贴板粘贴模式
|
||
- [ ] 剪贴板粘贴后恢复用户原有的剪贴板内容
|
||
- [ ] 多显示器场景下,副屏坐标正确加上全局偏移量
|
||
- [ ] 连续 N 步截图无变化时触发 stuck 检测并终止
|
||
- [ ] Agent 循环 system prompt 包含意外弹窗处理指导
|
||
|
||
### 9.5 安全与稳定
|
||
|
||
- [ ] 危险热键被阻断
|
||
- [ ] 审计日志完整记录
|
||
|
||
### 9.6 平台兼容
|
||
|
||
- [ ] macOS 上截图和键鼠均正常(授权后)
|
||
- [ ] Windows 上截图和键鼠均正常
|
||
- [ ] Linux X11 上截图和键鼠均正常
|
||
|
||
---
|
||
|
||
## 10. 开放问题
|
||
|
||
| # | 问题 | 影响 | 建议 |
|
||
|---|------|------|------|
|
||
| Q1 | pi-mono 不支持 MCP,MCP Server 与 Agent 循环如何协同? | 架构核心 | MCP Server 是外壳,收到 `gui_execute_task` 后创建 pi-mono AgentSession 执行,完成后返回 MCP 结果。原子操作不经过 pi-mono |
|
||
| Q2 | ~~`gui_execute_task` 执行期间是阻塞还是异步?~~ | ~~调用体验~~ | **已解决**,同步阻塞。MCP SDK 原生支持长时间 tool call + progress notification + AbortSignal 取消。无需自定义 TaskQueue/gui_task_status/gui_abort_task |
|
||
| Q3 | ~~长任务的上下文管理?~~ | ~~token 溢出~~ | **已解决**,三层记忆管理(Summary/Recent/Pending)+ LLM 摘要压缩 + pi-mono transformContext hook。见 3.2 及 Plan 3.7 |
|
||
| Q4 | nut.js prebuilt binary 分发? | 安装体验 | 跟随 npm 安装自动下载,需评估离线场景 |
|
||
| Q5 | `gui_execute_task` 中 Agent 循环的模型,是否可以与文本 Agent 的模型不同? | 灵活性 | 是,通过 `model` 参数覆盖,允许用更便宜/更快的模型做 GUI 决策 |
|
||
| Q6 | 新增视觉模型的坐标格式如何扩展? | 可维护性 | 内置模型映射表 + 环境变量覆盖。新增模型只需在映射表中加一行 |
|
||
| Q7 | ~~多显示器场景下的坐标如何处理?~~ | ~~多屏用户~~ | **已解决**,见 3.6.5 多显示器坐标偏移 |
|
||
| Q8 | ~~Agent 循环中连续失败如何处理?~~ | ~~稳定性~~ | **已解决**,见 3.6.4 stuck 检测 + 3.7 S4 最大轮次限制。参考 TuriX-CUA 连续失败计数器 |
|
||
| Q9 | ~~截图中的历史图片如何管理 token?~~ | ~~token 爆炸~~ | **已解决**,pruneScreenshots 策略:保留最近 3 步完整截图,更早步骤移除 base64 替换为文字描述。见 Plan 3.7.5 |
|
||
|
||
---
|
||
|
||
## 11. 项目结构(初步)
|
||
|
||
模块位于 `crates/agent-gui-server/`,与现有模块平级:
|
||
|
||
```
|
||
crates/agent-gui-server/
|
||
├── src/
|
||
│ ├── index.ts # CLI 入口: 参数解析 + 启动 MCP Server
|
||
│ ├── lib.ts # SDK 入口: 导出 createGuiAgentMcpServer()
|
||
│ ├── config.ts # 统一配置: 环境变量解析、校验、Fail Fast
|
||
│ │
|
||
│ ├── mcp/ # MCP 协议层(外部接口)
|
||
│ │ ├── server.ts # MCP Server 实例 (stdio + HTTP 双模式)
|
||
│ │ ├── atomicTools.ts # 13 个原子操作 tool handler
|
||
│ │ ├── taskTools.ts # gui_execute_task 互斥执行 + 进度通知
|
||
│ │ └── resources.ts # MCP Resources (status/permissions/audit)
|
||
│ │
|
||
│ ├── agent/ # Agent 循环引擎(gui_execute_task 内部)
|
||
│ │ ├── taskRunner.ts # 循环核心: pi-mono Agent + 截图→LLM→操作
|
||
│ │ ├── systemPrompt.ts # GUI Agent 专用 system prompt
|
||
│ │ ├── memoryManager.ts # 三层记忆管理 (Summary/Recent/Pending) + LLM 摘要压缩
|
||
│ │ └── stuckDetector.ts # 卡死检测: 连续截图相似度比对
|
||
│ │
|
||
│ ├── desktop/ # 桌面操作层(底层能力封装,不依赖 MCP)
|
||
│ │ ├── screenshot.ts # 截图管线: capture → scale → JPEG → base64
|
||
│ │ ├── mouse.ts # 鼠标操作 (nut.js mouse)
|
||
│ │ ├── keyboard.ts # 键盘操作 (nut.js keyboard)
|
||
│ │ ├── clipboard.ts # 剪贴板操作(CJK/长文本粘贴、剪贴板备份恢复)
|
||
│ │ ├── display.ts # 显示器信息
|
||
│ │ └── imageSearch.ts # 图像查找 (nut.js template matcher)
|
||
│ │
|
||
│ ├── coordinates/ # 坐标系统(核心难点,独立目录)
|
||
│ │ ├── resolver.ts # CoordinateResolver: 模型坐标 → 逻辑坐标 → 全局坐标
|
||
│ │ └── modelProfiles.ts # 模型配置表: 坐标模式、坐标顺序、缩放策略
|
||
│ │
|
||
│ ├── safety/ # 安全层
|
||
│ │ ├── hotkeys.ts # 危险热键黑名单拦截
|
||
│ │ └── auditLog.ts # 环形缓冲审计日志
|
||
│ │
|
||
│ └── utils/
|
||
│ ├── logger.ts # 日志: stderr + 可选文件
|
||
│ ├── platform.ts # 平台检测与权限检查
|
||
│ └── errors.ts # 结构化错误类型
|
||
│
|
||
├── tests/ # Vitest 测试
|
||
├── package.json
|
||
├── tsconfig.json
|
||
└── vitest.config.ts
|
||
```
|
||
|
||
---
|
||
|
||
## 12. 参考项目与借鉴
|
||
|
||
### 12.1 TuriX-CUA(Python GUI Agent 参考实现)
|
||
|
||
**项目**: `/Volumes/soddygo/git_work/TuriX-CUA` — Python 实现的桌面 GUI 自动化 Agent。
|
||
|
||
**架构亮点**:
|
||
|
||
| 设计 | 说明 | 对我们的启发 |
|
||
|------|------|------------|
|
||
| **多角色 LLM 架构** | Brain(分析状态)+ Actor(生成操作)+ Memory(压缩历史),各用不同 model | 可考虑 `gui_execute_task` 内部用更便宜的模型做决策,视觉分析用强模型 |
|
||
| **三层记忆管理** | Recent(近期详细)→ Summary(历史摘要)→ High-level(全局总结),token 接近上限时自动压缩 | 必须实现,否则每步带截图 base64 会迅速撑爆 context |
|
||
| **坐标自动检测** | `if position > 1` 则为 0-1000 格式,否则为 0-1 格式,运行时自动适配 | 简洁实用,我们的 CoordinateResolver 可以借鉴这种运行时检测 |
|
||
| **截图分级缩放** | ≤1080p 不缩放,2K-4K 缩 50%,8K 缩 25%,LANCZOS 重采样 | 比固定 scale 更合理,按实际分辨率自适应 |
|
||
| **Accessibility Tree 标注** | 截图上叠加编号框标注可交互元素(红/蓝/绿/黄/紫循环) | v2 可引入,显著提升点击准确率 |
|
||
| **连续失败熔断** | `consecutive_failures` 计数,超过 `max_failures`(默认 5)自动终止 | 必须实现,防止 Agent 卡在无效循环 |
|
||
| **强制停止热键** | `pynput.keyboard.GlobalHotKeys` 监听,用户可随时中断 | MCP 模式下通过 AbortSignal(客户端取消请求)实现等价功能 |
|
||
| **隐形点击** | 鼠标事件通过 Quartz `kCGHIDEventTap` 直发,不移动光标 | 减少视觉干扰,但可能影响某些应用的响应 |
|
||
| **操作后等待** | 每步操作后固定等待 2s 再截图验证 | 等待是必要的(UI 动画/渲染需要时间),但应可配置 |
|
||
|
||
**TuriX-CUA 的局限(我们需要改进的)**:
|
||
|
||
| 局限 | 我们的方案 |
|
||
|------|-----------|
|
||
| 仅 macOS(依赖 Quartz + Cocoa Accessibility API) | 跨平台(nut.js 抽象层) |
|
||
| 无 MCP 暴露 | MCP Server 为唯一入口 |
|
||
| 坐标无显式 DPI 处理(依赖 pyautogui 隐式处理) | 显式三层坐标空间 + CoordinateResolver |
|
||
| 不支持 Claude computer_use 的图片绝对坐标格式 | 内置模型配置表,支持所有三大坐标家族 |
|
||
| 无多显示器支持 | 支持 displayIndex |
|
||
|
||
### 12.2 社区 MCP 桌面自动化项目
|
||
|
||
**调研结论**:没有现成项目同时解决"多模型坐标适配 + 跨平台 DPI + MCP 暴露"。以下项目各有值得借鉴之处。
|
||
|
||
#### 跨平台
|
||
|
||
| 项目 | 地址 | 亮点 | 局限 |
|
||
|------|------|------|------|
|
||
| **computer-use-mcp** | github.com/domdomegg/computer-use-mcp | 最接近 Anthropic 官方方案,MIT 协议 | 仅适配 Claude,不处理多模型坐标差异 |
|
||
| **mcp-desktop-automation** | github.com/tanob/mcp-desktop-automation | 基于 RobotJS 的通用桌面自动化 | 无 DPI 处理 |
|
||
| **mcp-pyautogui-server** | github.com/hetaoBackend/mcp-pyautogui-server | PyAutoGUI 封装为 MCP | Python,Wayland 支持差 |
|
||
|
||
#### Windows 专项
|
||
|
||
| 项目 | 地址 | 亮点 |
|
||
|------|------|------|
|
||
| **precision-desktop** | github.com/ikoskela/precision-desktop | **DPI 校准方案**:用 landmark 检测实际 scale factor,不盲信 OS 报告值。影响 47% 高分屏用户 |
|
||
| **mcp-windows** | github.com/sbroenne/mcp-windows | Win11 专用,正确处理多显示器 + DPI + 虚拟桌面 |
|
||
|
||
#### macOS 专项
|
||
|
||
| 项目 | 地址 | 亮点 |
|
||
|------|------|------|
|
||
| **mcp-desktop-pro** | github.com/lksrz/mcp-desktop-pro | **显式 Retina 2x 支持**,窗口相对坐标,AI 优化截图(WebP 压缩) |
|
||
| **mcp-remote-macos-use** | github.com/baryhuang/mcp-remote-macos-use | 自动坐标缩放,支持远程 Mac 控制 |
|
||
|
||
#### Linux 专项
|
||
|
||
| 项目 | 地址 | 亮点 |
|
||
|------|------|------|
|
||
| **kwin-mcp** | github.com/isac322/kwin-mcp | **解决 Wayland 问题**,29 个工具,隔离虚拟 KWin 会话,支持无头环境(CI) |
|
||
| **ubuntu-desktop-control** | github.com/charettep/ubuntu-desktop-control-mcp | HiDPI 自动缩放 + 网格调试 overlay |
|
||
|
||
#### 标准参考
|
||
|
||
| 项目 | 地址 | 亮点 |
|
||
|------|------|------|
|
||
| **Anthropic quickstarts** | github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo | 坐标缩放回映的**标准参考实现**(Python) |
|
||
| **GUI-Actor** (微软研究) | microsoft.github.io/GUI-Actor | **无坐标方案**(token-based grounding),未来演进方向 |
|
||
|
||
### 12.3 关键借鉴总结
|
||
|
||
| 来源 | 借鉴点 | 落地位置 |
|
||
|------|--------|---------|
|
||
| TuriX-CUA | 三层记忆管理(Recent/Summary/High-level) | 3.2 Agent 循环 |
|
||
| TuriX-CUA | 连续失败熔断机制 | 3.7 安全需求 |
|
||
| TuriX-CUA | 截图分级缩放(按分辨率自适应) | 3.4.4 截图策略 |
|
||
| TuriX-CUA | 分层操作延迟(微操作/操作间/步骤间) | 3.6.1 操作后等待策略 |
|
||
| TuriX-CUA | 依赖 LLM 截图分析处理弹窗 | 3.6.4 意外弹窗处理 |
|
||
| TuriX-CUA | Accessibility Tree 标注辅助(v2) | 7. 非目标 |
|
||
| precision-desktop | DPI 校准(不盲信 OS 报告值) | 3.4 坐标系统 |
|
||
| mcp-desktop-pro | 窗口相对坐标 | 未来优化 |
|
||
| Anthropic quickstarts | 截图缩放 + 坐标回映公式 | 3.4.5 坐标转换链路 |
|
||
| GUI-Actor | 无坐标 token-based 方案 | 未来研究方向 |
|
||
|
||
---
|
||
|
||
*下一步: 基于本 Spec 编写 Plan(技术方案)和 Task(执行任务)文档*
|