去年12月9日,智谱开源了AutoGLM,那天我特别兴奋。在我的想象中,Agent 应该是极其轻便且无处不在的:无论我身处何地,只要拿起手机输入指令,它就能在云端帮我精准执行任务,这种"指点江山"的感觉真的很酷。
但当我深入研究项目后,发现AutoGLM的底层是ADB,这在局域网或USB连接下表现完美,但对我这种没有公网IP、没有稳定服务器的学生党来说,远程部署成了难题。我思考能不能用内网穿透,如果换成对NAT穿透更友好的UDP,稳定性会不会好一点?
那阵子正赶上期末周,复习间隙,这些技术细节一直在脑子里转。或许是觉得纯粹端到端的架构显得有些"单薄",更复杂的架构显得更酷,某个下午,我突然进入了一种奇妙的"心流"状态:
我开始反思:每一帧页面都交给大模型做视觉分析和决策,真的最优吗?在那些逻辑确定的路径上,我们是否可以引入一些确定性的程序来分担大模型的压力,从而换取更高的精度和更低的开销?如果真的要换UDP,我这半吊子的技术储备又该如何去实现?
想得越多,坑就显得越深。最后我冒出了一个现在看来可能有些"自以为是"的念头:与其在别人的地基上修修补补,不如尝试从零开始,按自己的理解去搭一套架构试试?于是我寒假就尝试按自己的想法 Vibe Coding 了一下。
从一个问题出发——为什么要现场发挥?
整个项目其实源于一个很朴素的观察:
Agent执行某个任务的过程实际上就是先页面跳转,再执行任务。 纯 VLM 方案在每一步都截图、发请求、等响应,每一跳都要 2-5 秒,还可能在中间某步判断失误,或者点歪了,整条链路就断了。
如果导航路径是可以预先知道的,为什么每次都要让大模型"现场发挥"?就像人一样,我们对熟悉的软件,其实并不用每次都观察 “我要点朋友圈,要怎么点”,基本都是肌肉记忆。那么有没有办法也让架构拥有“肌肉记忆”?
这个问题促使我设计了 LXB 的核心思路:Route-Then-Act(先规划路线,再执行任务)。
LXB 是什么
LXB 是一个跨设备手机自动化框架,核心思想是把手机自动化拆成两个阶段:
- 路由阶段:用预先建好的导航图,通过 BFS 找到目标页面的路径,然后确定性地一步步点过去——全程零大模型调用
- 执行阶段:到了目标页面,再召唤 VLM 来分析页面内容、执行具体任务
三个技术模块
1. 自动建图:Node-Driven Exploration
在路由之前,得先有图。建图这件事听起来简单,但有个很实际的问题:
淘宝这类 App,许多页面其实共用一个 Android Activity。 传统的"页面指纹"方案(用页面截图或 Activity 名称做唯一标识)在这里会失效,因为动态内容太多了,且导航栏又是一样的,找不出合适的页面指纹构建方法(或许有办法,只是我不知道)。
LXB 的解法是换一个"驱动实体":不以页面状态为 BFS 队列的驱动单元,而以 UI 节点(导航元素本身) 为单元。每次从队列里取出一个未访问的导航节点,从主页回放路径点击它,记录到达的新页面,再把新页面上发现的导航元素入队。页面是点击节点的副产品,产出UI背后页面的语义描述和拥有功能,而不是探索的目标。我们不关注 “我现在在哪个页面” ,而是关注 “我要到这个页面要点什么节点”
这样就彻底避开了"页面指纹是否可靠"的问题:只要能够见节点全局唯一的locator,就可以用节点的唯一性替代页面指纹。
建图过程演示:
图可视化演示:
2. 可靠定位:VLM-XML Fusion
建图过程中,如何可靠地识别"这个按钮是什么"并在未来重新找到它?
单纯依赖 VLM 的坐标输出不靠谱——换个设备分辨率就失效了。单纯依赖 XML 可访问性树也不够——单靠属性过滤经常不唯一。
LXB 的方案是把两者融合:
- VLM 负责语义理解:截图发给 VLM,让它用思维链过滤掉商品卡片、推荐流等动态内容,只保留"导航锚点"(Tab、设置按钮、跳转入口等),输出每个元素的中心坐标
- XML 负责精确定位:用 VLM 给出的坐标,在
dump_actions返回的可点击节点列表里做点-包含匹配,找到包含该坐标的最小面积节点;如果精确命中失败,把边界扩大 20px 再试一次 - 构建全局唯一 Locator:命中节点后,用其
resource_id、text、content_desc、class_name组合判断唯一性。不唯一就加上父节点的resource_id缩小范围;还不唯一(2-3个同类节点)就记录空间排序下的相对位置索引;超过 3 个同类节点则放弃这个元素
这个 Locator 完全由开发者分配的属性构成,跟屏幕分辨率无关,所以在 A 手机上建的图可以直接在 B 手机上回放——这就是"跨设备"的底层来源。
3. 执行引擎:Route-Then-Act
有了图,有了可靠的 Locator,执行阶段就分三步走:
Phase 1 · 规划:用户输入自然语言任务(比如"帮我查一下我的订单"),LLM 对照导航图里每个页面的语义描述(页面名称、功能说明、关键词),用纯文本查询找到目标页面——不需要截图,不需要 VLM
Phase 2 · 路由:BFS 计算从当前页面到目标页面的最短路径,然后按照路径依次用 Locator 强匹配点击每个导航元素——全程零大模型调用
Phase 3 · 执行:到达目标页面后,才调用 VLM 分析当前页面内容,执行具体的任务操作
下面的动图是架构根据需求 “帮我打开哔哩哔哩,发送一条动态,内容为test,标题为test” 执行的过程
执行过程中还有一套三级恢复机制——循环检测、弹窗处理规则、应用重启——尽可能在不中断任务的情况下自愈。
效果怎么样
在淘宝和 Bilibili 共 8 个真实任务上做了测试(每个任务重复 3 次,共 96 次运行),和几个纯 VLM 方案对比:
测试任务(都使用模型qwen3-vl-30b-a3b-instruct)
| 编号 | App | 指令 | 难度 |
|---|---|---|---|
| tb_s1 | 淘宝 | 查看消息页面 | 浅层 |
| tb_s3 | 淘宝 | 进入关注页面 | 浅层 |
| bili_s1 | B站 | 查看消息页面 | 浅层 |
| bili_m1 | B站 | 查看我收到的点赞 | 中层 |
| bili_m3 | B站 | 查看每周必看视频列表 | 中层 |
| tb_d1 | 淘宝 | 查看我的订单列表 | 深层 |
| tb_d2 | 淘宝 | 进入淘宝 APP 设置页面 | 深层 |
| bili_d2 | B站 | 进入鬼畜分区 | 深层 |
浅层 = 从首页一跳直达;中/深层 = 需要经过 2 跳及以上的导航路径。
| 方法 | 成功率 | 平均模型调用次数 | 平均 Token 消耗 |
|---|---|---|---|
| LXB(本框架) | 100% | 1.0 次 | 4,795 |
| VLM-ReAct | 87.5% | 2.79 次 | 8,618 |
| Text-ReAct | 75.0% | 2.17 次 | 4,029 |
| VLM+语义地图 | 91.7% | 2.38 次 | 20,360 |
对于需要 2 跳以上导航的深层任务,LXB 成功率 100%,而 VLM-ReAct 是 80%、Text-ReAct 是 60%。模型调用次数减少了 74%(从 3.87 次降到 1.0 次),Token 消耗减少了 58.6%。
需要说明的是:这套测试设计上本就对 LXB 的优势场景更友好(导航任务,且都是map覆盖到的),泛化能力还有待更大规模的验证。数据只能说明在这个场景下思路是有效的,距离一个成熟的系统还有很长的路要走。
踩过的坑(部分)
随手列几个印象比较深的:
-
UDP 的选择:最初选 UDP 是因为想支持 NAT 穿透场景,后来测试发现局域网下 TCP 反而更稳。现在的自定义 ARQ 协议加了 Stop-and-Wait + CRC32,可靠性有保证,但如果重来一次,可能会在 TCP 和 UDP 之间再斟酌一下
-
页面指纹 的坑:我做了半个月的页面指纹,不过技术有限,一直找不到合适的方法,阈值高了又容易多个页面都认为是一个页面,阈值低了又太敏感
-
VLM 坐标空间的差异:不同 VLM 的坐标输出方式不一样——有的是相对于原始图片像素,有的是固定在 0-1000 的归一化空间。用探针图像做一次运行时标定之后,才能正确映射到屏幕坐标。不过这种方式的可靠性我认为还有待商榷
现在的状态
目前框架的核心功能已经跑通。但坦白说,还有很多地方不完善:
- WebView 渲染的内容(很多 H5 页面)无法获取 XML 节点,Fusion 会直接失败
- 导航图是静态的,App 更新后,如果重要导航节点发生了属性的改变,需要重新建图
- 测试任务数量偏少,泛化能力未经充分验证
- 现在locator还没发做到覆盖所有的重要导航节点,一些特征不太鲜明的依然覆盖不到。
代码已经开源,感兴趣的朋友可以看看(顺便点个star):github.com/wuwei-crg/L…
如果你也在研究手机自动化相关的工作,或者有任何问题和建议,欢迎交流。作为第一次独立做系统性的工程项目,有很多地方一定考虑不周,欢迎指出 :)