打造你的AI手机助手:Open-AutoGLM 项目深度解析与视觉策略优化

104 阅读5分钟

本文将深入介绍一个开源的 Android AI 助手项目——Open-AutoGLM,并重点讲解我们如何通过 Set-of-MarksGrid Overlay 两种视觉策略,让 AI 更精准地操控手机界面。

📱 项目简介

Open-AutoGLM 是一个基于多模态大语言模型(VLM)的 Android 智能助手应用。用户只需用自然语言描述任务,AI 就能自动分析屏幕内容并执行相应操作。

核心能力:

  • 🎯 自然语言理解 → 自动执行多步操作
  • 📸 实时屏幕截图 → AI 视觉分析
  • 🖱️ 精准 UI 交互 → 点击、滑动、输入
  • 🔄 多模型支持 → OpenAI、Claude、Gemini、通义千问等

应用场景:

  • "帮我在微信上给张三发送'明天见'"
  • "打开B站搜索最新动漫并收藏"
  • "自动签到所有常用APP"

🏗️ 技术架构

项目采用 Kotlin + Vue 3 + TypeScript 混合架构:

Open-AutoGLM-App/
├── app/                          # Android 原生层
│   └── src/main/java/com/autoglm/app/
│       ├── core/                 # 核心业务逻辑
│       │   ├── AIClient.kt       # AI 模型通信
│       │   ├── TaskExecutor.kt   # 无障碍模式执行器
│       │   ├── ShizukuTaskExecutor.kt  # Shizuku模式执行器
│       │   ├── SetOfMarks.kt     # SoM 标记技术
│       │   └── GridOverlay.kt    # 网格叠加技术
│       ├── service/              # 系统服务
│       │   ├── FloatingWindowService.kt  # 悬浮窗
│       │   └── AutoGLMAccessibilityService.kt  # 无障碍服务
│       └── data/
│           └── PreferencesManager.kt  # 偏好设置
├── frontend/                     # Vue 前端
│   └── src/
│       ├── App.vue              # 主界面
│       └── Bridge.ts            # 原生桥接
└── shizuku/                      # Shizuku 集成

两种执行模式

模式原理优点缺点
无障碍服务Android AccessibilityService API免 ROOT,易部署部分系统限制
ShizukuADB 命令注入权限更高,更稳定需要 Shizuku 环境

🎯 核心难题:AI 如何精准点击?

多模态 AI 虽然能"看懂"屏幕,但让它精准输出坐标是个大问题:

  1. 坐标精度低:直接让 AI 输出像素坐标,误差可达几十像素
  2. 元素识别困难:纯视觉难以区分相似按钮
  3. 游戏场景失效:游戏画面无法提取 UI 层级

解决方案:视觉策略系统

我们设计了一套自适应视觉策略系统,包含三种模式:

enum class VisualStrategy {
    AUTO,  // 自动选择最佳策略
    SOM,   // Set-of-Marks 标记模式
    GRID,  // 网格叠加模式
    NONE   // 纯视觉模式
}

🔢 Set-of-Marks (SoM) 标记技术

原理

通过 uiautomator dump 获取 UI 层级树,提取所有可点击元素的坐标,在截图上绘制粉色数字标记

SoM 示例转存失败,建议直接上传图片文件

核心代码

object SetOfMarks {
    fun drawMarks(screenshot: Bitmap, elements: List<UIElement>): Bitmap {
        val canvas = Canvas(screenshot.copy(Bitmap.Config.ARGB_8888, true))
        val paint = Paint().apply {
            color = Color.parseColor("#E91E63")  // 粉色
            textSize = 24f
            style = Paint.Style.FILL
        }
        
        elements.forEachIndexed { index, element ->
            // 绘制圆形背景
            canvas.drawCircle(
                element.centerX.toFloat(),
                element.centerY.toFloat(),
                20f, paint
            )
            // 绘制数字
            canvas.drawText(
                "${index + 1}",
                element.centerX.toFloat(),
                element.centerY.toFloat(),
                textPaint
            )
        }
        return canvas.bitmap
    }
}

AI 指令格式

do(action="Tap", mark=5)  // 点击5号标记

优势与局限

✅ 精准定位,无坐标误差
✅ AI 只需输出数字,简化推理
❌ 元素过多时画面杂乱
❌ 游戏等自绘UI无法识别


🔲 Grid Overlay 网格叠加技术

为解决 SoM 的局限性,我们开发了 Grid Overlay 方案。

原理

在截图上叠加 10×10 网格,用 A1-J10 坐标标注(类似 Excel):

   A    B    C    D    E    F    G    H    I    J
  ┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
1 │ A1 │ B1 │ C1 │ D1 │ E1 │ F1 │ G1 │ H1 │ I1 │ J1 │
  ├────┼────┼────┼────┼────┼────┼────┼────┼────┼────┤
2 │ A2 │ B2 │ ...                                   │
  ...
10│ A10│ B10│ ...                            │ J10 │
  └────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘

核心代码

object GridOverlay {
    fun drawGrid(bitmap: Bitmap, gridSize: Int = 10): Bitmap {
        val result = bitmap.copy(Bitmap.Config.ARGB_8888, true)
        val canvas = Canvas(result)
        val cellWidth = bitmap.width / gridSize
        val cellHeight = bitmap.height / gridSize
        
        // 绘制网格线
        val linePaint = Paint().apply {
            color = Color.argb(128, 255, 255, 255)  // 半透明白色
            strokeWidth = 2f
        }
        
        for (i in 1 until gridSize) {
            // 垂直线
            canvas.drawLine(i * cellWidth.toFloat(), 0f, 
                           i * cellWidth.toFloat(), bitmap.height.toFloat(), linePaint)
            // 水平线
            canvas.drawLine(0f, i * cellHeight.toFloat(), 
                           bitmap.width.toFloat(), i * cellHeight.toFloat(), linePaint)
        }
        
        // 绘制关键标签 (A1, E5, J10等)
        val keyLabels = listOf("A1", "A5", "A10", "E1", "E5", "E10", "J1", "J5", "J10")
        // ...
        
        return result
    }
    
    fun gridToCoordinates(gridRef: String, screenWidth: Int, screenHeight: Int): Pair<Int, Int>? {
        val col = gridRef[0].uppercaseChar() - 'A'  // A=0, B=1, ..., J=9
        val row = gridRef.substring(1).toIntOrNull()?.minus(1) ?: return null
        
        val cellWidth = screenWidth / 10
        val cellHeight = screenHeight / 10
        
        return Pair(
            col * cellWidth + cellWidth / 2,  // 单元格中心X
            row * cellHeight + cellHeight / 2  // 单元格中心Y
        )
    }
}

AI 指令格式

do(action="Tap", grid="E5")  // 点击E5网格

🤖 智能策略调度

系统根据 UI 元素数量自动选择最佳策略:

fun selectStrategy(uiElementCount: Int, manualStrategy: VisualStrategy): VisualStrategy {
    // 用户手动指定时,直接使用
    if (manualStrategy != VisualStrategy.AUTO) {
        return manualStrategy
    }
    
    return when {
        uiElementCount in 5..20 -> VisualStrategy.SOM   // 元素适中,用标记
        else -> VisualStrategy.GRID                      // 太多/太少/游戏,用网格
    }
}

策略选择流程

flowchart TD
    A[开始截图] --> B{用户手动设置?}
    B -->|是| C[使用指定策略]
    B -->|否| D[解析UI层级]
    D --> E{元素数量}
    E -->|5-20个| F[Set-of-Marks]
    E -->|0个/过多/过少| G[Grid Overlay]
    F --> H[发送给AI]
    G --> H

📝 AI Prompt 设计

为了让 AI 理解两种标记方式,我们设计了清晰的提示词:

## 核心规则
1. **识别界面标注类型**   - **粉色数字标记 (Set-of-Marks)**:使用 `mark=编号` 操作
   - **网格标签 (Grid)**:使用 `grid=标签` 操作(A1-J10)
   - **无标记**:使用归一化坐标 [0-1000]

## 动作指令表
| 意图 | 指令格式 |
|------|----------|
| 点击(标记) | `do(action="Tap", mark=5)` |
| 点击(网格) | `do(action="Tap", grid="E5")` |
| 点击(坐标) | `do(action="Tap", element=[500,500])` |
| 滑动 | `do(action="Swipe", start=[100,500], end=[900,500])` |

🎮 前端设置界面

用户可在设置中手动切换策略:

<!-- App.vue -->
<div class="flex items-center justify-between py-2">
    <div>
        <div class="text-sm">视觉策略</div>
        <div class="text-xs text-gray-500">控制 AI 如何识别屏幕元素</div>
    </div>
    <select v-model="visualStrategy" @change="saveVisualStrategy">
        <option value="auto">自动 (推荐)</option>
        <option value="som">标记模式</option>
        <option value="grid">网格模式</option>
        <option value="none">纯视觉</option>
    </select>
</div>

📊 性能对比

场景纯坐标SoMGrid
常规APP界面70%95%85%
复杂列表页面50%75%90%
游戏/Canvas30%80%
元素识别速度

🚀 快速开始

环境要求

  • Android Studio Arctic Fox+
  • JDK 17
  • Node.js 18+

编译运行

# 1. 克隆项目
git clone https://github.com/xietao778899-rgb/Open-AutoGLM-App.git

# 2. 构建前端
cd frontend && npm install && npm run build

# 3. 构建APK
cd .. && ./gradlew assembleDebug

# 4. 安装到设备
adb install app/build/outputs/apk/debug/app-debug.apk

配置 API

在应用设置中配置你的 AI API Key(支持 OpenAI、Claude、Gemini、通义千问等)。


🔮 未来规划

  • 支持更精细的 Grid 分区(20×20)
  • 添加 OCR 辅助定位
  • 支持语音指令输入
  • 云端任务脚本市场

📚 相关资源


觉得有帮助?欢迎 Star ⭐ 和 Fork 🍴!

如有问题欢迎评论区交流~