Open-AutoGLM 深度解析
让 AI 帮你完成手机上的任务
本文深入剖析 Open-AutoGLM 的架构设计与实现原理,带你理解 AI 如何「看懂」手机屏幕并自动完成任务。
🎯 这是什么?
Open-AutoGLM 是智谱 AI 开源的手机 Agent 框架。你只需要用自然语言描述任务:
"打开微信发消息给文件传输助手:测试成功"
AI 就能帮你在安卓手机上一步步完成这个任务。
核心能力:给 AI 一双眼睛(截图)+ 一双手(ADB)+ 一个大脑(视觉语言模型)= 自动化操作手机。
🏗️ 整体架构
┌─────────────────────────────────────────────────────────────────┐
│ 主控制循环 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 截屏 │ ──► │ AI 理解 │ ──► │ 执行 │ │
│ │ (ADB) │ │ (VLM) │ │ (ADB) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ▲ │ │
│ └───────────────────────────────────┘ │
│ 循环直到任务完成 │
└─────────────────────────────────────────────────────────────────┘
整个系统分为三个核心部分:
| 模块 | 职责 | 技术实现 |
|---|---|---|
| 截屏模块 | 获取手机当前画面 | ADB + Pillow |
| AI 决策模块 | 理解画面,决定下一步 | 视觉语言模型 (VLM) |
| 动作执行模块 | 执行点击、滑动、输入等 | ADB Shell |
🔄 核心执行流程
每一步都是「看 → 想 → 做」的循环:
def _execute_step(self):
# 1️⃣ 截取当前屏幕
screenshot = get_screenshot()
# 2️⃣ 发送给 AI 模型
response = self.model_client.request([
{"role": "system", "content": system_prompt},
{"role": "user", "content": [截图, 任务描述]}
])
# 3️⃣ 解析 AI 返回的动作
action = parse_action(response.action)
# 例如: {"action": "Tap", "element": [500, 300]}
# 4️⃣ 执行动作
self.action_handler.execute(action)
# 5️⃣ 检查是否完成,否则继续循环
一个真实任务的执行过程
任务:「打开微信发消息给文件传输助手:测试成功」
Step 1:
📸 截图 → 看到桌面
🧠 AI: "我看到桌面有微信图标,应该先启动微信"
🎯 执行: Launch("微信")
Step 2:
📸 截图 → 微信首页
🧠 AI: "微信打开了,我需要点击搜索按钮"
🎯 执行: Tap([826, 86])
Step 3:
📸 截图 → 搜索页面
🧠 AI: "搜索框已激活,输入文件传输助手"
🎯 执行: Type("文件传输助手")
... 继续执行直到完成 ...
Step N:
📸 截图 → 消息已发送
🧠 AI: "消息发送成功,任务完成"
🎯 执行: finish(message="任务完成!")
🧠 AI 是如何「看懂」屏幕的?
视觉语言模型 (VLM)
Open-AutoGLM 使用的是 AutoGLM-Phone-9B,一个专门为手机操控训练的视觉语言模型。
┌─────────────────────────────────────────────────────────────────┐
│ VLM 的工作方式 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 输入: │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 📷 手机截图 (Base64 编码) │ │
│ │ 📝 任务描述: "打开微信发消息..." │ │
│ │ 📋 System Prompt: 角色定义 + 动作说明 + 规则 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ AutoGLM-Phone-9B │ │
│ │ │ │
│ │ • 识别屏幕上的 UI 元素(按钮、文字、图标) │ │
│ │ • 理解元素的位置坐标 │ │
│ │ • 根据任务目标决定下一步动作 │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 输出: │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 💭 思考: "我看到搜索按钮在右上角,应该点击它" │ │
│ │ 🎯 动作: do(action="Tap", element=[826, 86]) │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
System Prompt 的关键作用
System Prompt 告诉 AI:
- 你是谁:智能体分析专家
- 能做什么:15 种动作类型
- 怎么做:18 条行为规则
SYSTEM_PROMPT = """
你是一个智能体分析专家,可以根据操作历史和当前状态图执行一系列操作来完成任务。
操作指令及其作用如下:
- do(action="Launch", app="xxx") # 启动 APP
- do(action="Tap", element=[x,y]) # 点击坐标
- do(action="Type", text="xxx") # 输入文字
- do(action="Swipe", start=[...], end=[...]) # 滑动
- do(action="Back") # 返回键
- do(action="Home") # 主屏幕键
- do(action="Wait", duration="x seconds") # 等待
- do(action="Take_over", message="xxx") # 人工接管
- finish(message="xxx") # 完成任务
...
必须遵循的规则:
1. 在执行任何操作前,先检查当前app是否是目标app
2. 如果进入到了无关页面,先执行 Back
3. 如果页面未加载出内容,最多连续 Wait 三次
...
"""
📐 坐标系统设计
AI 输出的是 0-999 的相对坐标,而不是实际像素:
┌─────────────────────────────────────────────────────────────────┐
│ 坐标归一化设计 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ AI 使用的相对坐标 转换后的实际像素 │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ (0,0) (999,0)│ │ (0,0) (1080,0) │ │
│ │ │ → │ │ │
│ │ (500,500) │ │ (540,1200) │ │
│ │ │ │ │ │
│ │ (0,999) (999,999│ │ (0,2400)(1080,..) │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 转换公式: │
│ pixel_x = (ai_x / 1000) * screen_width │
│ pixel_y = (ai_y / 1000) * screen_height │
│ │
│ 优点: │
│ • AI 不需要知道具体分辨率 │
│ • 适配所有手机 (720p, 1080p, 2K, 4K...) │
│ • 简化模型训练 │
│ │
└─────────────────────────────────────────────────────────────────┘
⌨️ 中文输入的巧妙解决方案
ADB 原生的 input text 命令不支持中文:
adb shell input text "hello" # ✅ 可以
adb shell input text "你好" # ❌ 乱码
Open-AutoGLM 的解决方案是使用 ADB Keyboard:
┌─────────────────────────────────────────────────────────────────┐
│ 中文输入流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "测试成功" │
│ │ │
│ ▼ Base64 编码 │
│ "5rWL6K+V5oiQ5Yqf" │
│ │ │
│ ▼ 通过广播发送 │
│ adb shell am broadcast -a ADB_INPUT_B64 --es msg "5rWL..." │
│ │ │
│ ▼ ADB Keyboard 接收 │
│ 手机上的 ADB Keyboard APP 解码并输入 │
│ │ │
│ ▼ │
│ 输入框显示: "测试成功" ✅ │
│ │
└─────────────────────────────────────────────────────────────────┘
🎮 支持的 15 种动作
| 动作 | 说明 | 示例 |
|---|---|---|
Launch | 启动 APP | do(action="Launch", app="微信") |
Tap | 点击 | do(action="Tap", element=[500, 300]) |
Type | 输入文字 | do(action="Type", text="你好") |
Swipe | 滑动 | do(action="Swipe", start=[500,800], end=[500,200]) |
Long Press | 长按 | do(action="Long Press", element=[500,500]) |
Double Tap | 双击 | do(action="Double Tap", element=[500,500]) |
Back | 返回键 | do(action="Back") |
Home | 主屏幕 | do(action="Home") |
Wait | 等待 | do(action="Wait", duration="2 seconds") |
Take_over | 人工接管 | do(action="Take_over", message="请完成验证码") |
Note | 记录内容 | do(action="Note", message="...") |
Call_API | 调用总结 | do(action="Call_API", instruction="...") |
Interact | 用户选择 | do(action="Interact") |
Type_Name | 输入人名 | do(action="Type_Name", text="张三") |
finish | 完成任务 | finish(message="任务完成") |
🤖 反应式设计:边看边做
Open-AutoGLM 采用 ReAct (Reasoning + Acting) 模式,而不是预先规划:
┌─────────────────────────────────────────────────────────────────┐
│ 规划式 vs 反应式 │
├───────────────────────────────┬─────────────────────────────────┤
│ ❌ 规划式 │ ✅ 反应式 (Open-AutoGLM) │
├───────────────────────────────┼─────────────────────────────────┤
│ │ │
│ 先制定完整计划: │ 每步根据当前状态决策: │
│ 1. 打开微信 │ │
│ 2. 点击搜索 │ 截图 → "我看到桌面" │
│ 3. 输入联系人 │ → Launch 微信 │
│ 4. 点击联系人 │ │
│ 5. 输入消息 │ 截图 → "微信打开了" │
│ 6. 点击发送 │ → Tap 搜索 │
│ │ │
│ 然后按计划执行 │ 截图 → "搜索框激活" │
│ ❌ 界面变化会导致失败 │ → Type 联系人 │
│ │ │
│ │ ... 继续循环 ... │
│ │ ✅ 能处理弹窗、广告等意外 │
│ │ │
└───────────────────────────────┴─────────────────────────────────┘
优点:
- 适应性强:界面变化、弹窗、广告都能处理
- 容错性好:一步失败可以重试
- 通用性高:同一套代码处理所有任务
🧠 上下文记忆管理
AI Agent 需要「记住」之前做过什么,才能做出连贯的决策。Open-AutoGLM 通过维护一个 对话上下文列表 来实现:
class PhoneAgent:
def __init__(self, ...):
self._context = [] # 存储对话历史
self._step_count = 0
def run(self, task: str):
self._context = [] # 新任务清空历史
self._step_count = 0
# ... 执行任务 ...
上下文结构
每一步都会往 _context 中追加消息:
┌─────────────────────────────────────────────────────────────────┐
│ 对话上下文示例 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [0] System: "你是智能体分析专家..." ← 系统提示(固定) │
│ │
│ [1] User: [截图1] + "打开微信发消息..." ← 第一步:任务+截图 │
│ [2] Assistant: "<think>看到桌面</think> │
│ <answer>Launch(微信)</answer>" │
│ │
│ [3] User: [截图2] + "继续执行任务" ← 第二步:新截图 │
│ [4] Assistant: "<think>微信打开了</think> │
│ <answer>Tap([826,86])</answer>" │
│ │
│ [5] User: [截图3] + "继续执行任务" ← 第三步... │
│ ... │
│ │
└─────────────────────────────────────────────────────────────────┘
内存优化:清除历史截图
截图数据很大(Base64 编码后约 1-2MB),如果保留所有历史截图会导致:
- 内存占用飙升
- API 请求变慢
- Token 超限
Open-AutoGLM 的解决方案:只保留最新截图,历史截图从消息中移除
def _execute_step(self, ...):
# 1. 截图并构建消息
screenshot = get_screenshot()
self._context.append(user_message_with_image)
# 2. 调用 AI
response = self.model_client.request(self._context)
# 3. 立即移除刚才的截图,只保留文字
self._context[-1] = MessageBuilder.remove_images_from_message(self._context[-1])
# 4. 追加 AI 的回复
self._context.append(assistant_message)
这样 AI 仍然能看到:
- ✅ 当前屏幕截图(最新的)
- ✅ 历史操作记录(文字)
- ✅ 历史思考过程(文字)
但不会:
- ❌ 保留历史截图(节省内存)
- ❌ 让 context 无限增长
为什么需要历史记录?
虽然是「反应式」设计,但历史记录仍然重要:
| 场景 | 作用 |
|---|---|
| 重复操作检测 | AI 看到"我刚点过这里",避免死循环 |
| 错误恢复 | AI 知道"刚才点错了",可以尝试其他方案 |
| 任务连贯性 | AI 记得"我在找文件传输助手",不会迷失方向 |
| 步骤计数 | 达到 max_steps 时强制结束,防止无限执行 |
🔒 特殊场景处理
敏感页面(支付、银行)
当截图失败时(系统禁止截图),返回黑屏占位图:
def _create_fallback_screenshot(is_sensitive: bool) -> Screenshot:
"""Create a black fallback image when screenshot fails."""
black_img = Image.new("RGB", (1080, 2400), color="black")
return Screenshot(
base64_data=encode(black_img),
is_sensitive=is_sensitive # 标记为敏感页面
)
AI 看到黑屏后会尝试等待或返回。
人工接管(验证码、登录)
当 AI 遇到无法自动完成的操作时:
def _handle_takeover(self, action, ...):
message = action.get("message", "需要人工操作")
# 阻塞等待用户完成
input(f"{message}\n完成后按 Enter 继续...")
return ActionResult(True, should_finish=False) # 继续执行
用户完成操作后按 Enter,Agent 继续执行。
📊 与传统自动化的对比
| 特性 | Appium/UIAutomator | Open-AutoGLM |
|---|---|---|
| 需要 APP 源码 | ❌ 需要了解结构 | ✅ 不需要 |
| 需要写脚本 | ❌ 需要编程 | ✅ 自然语言 |
| 界面变化适应 | ❌ 脚本失效 | ✅ AI 自动适应 |
| 跨 APP 操作 | ❌ 困难 | ✅ 轻松 |
| 理解语义 | ❌ 只能按规则 | ✅ 理解意图 |
| 处理异常 | ❌ 需预设 | ✅ 智能应对 |
🎯 总结
Open-AutoGLM 的核心创新:
- 视觉理解:用 VLM 直接「看图」理解界面,不依赖 Accessibility API
- 自然语言:用户说人话,AI 自己规划和执行
- 反应式架构:每步都根据当前状态决策,适应动态环境
- ADB Keyboard:巧妙解决中文输入问题
- 坐标归一化:0-999 相对坐标适配所有分辨率
一句话总结:
给 AI 一双眼睛(截图)+ 一双手(ADB)+ 一个大脑(VLM)= 自动化操作手机
📚 扩展阅读
本文基于 Open-AutoGLM v0.1.0 源码分析