【开源】LXB-Framework:支持定时任务的端侧安卓自动化框架,用预置地图做页面路由,让大模型专注任务执行
- 内容介绍
- 文章标签
- 相关推荐
本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容:
- 我的帖子已经打上 开源推广 标签: 是
- 我的开源项目完整开源,无未开源部分: 是
- 我的开源项目已链接认可 LINUX DO 社区: 是
- 我帖子内的项目介绍,AI生成、润色内容部分已截图发出: 是
- 以上选择我承诺是永久有效的,接受社区和佬友监督: 是
以下为项目介绍正文内容,AI生成、润色内容已使用截图方式发出
项目地址:https://github.com/wuwei-crg/LXB-Framework
省流:这个项目装进你的手机里,能做什么?
我做 LXB-Framework 的目标其实很简单,不是为了做一个“无所不能”的手机 Agent。
它主要解决的是我每天都会遇到的那类任务:流程固定、步骤重复、但非常耗时间和耐心。
现在这套框架依赖两件事在工作:
- 用
app_process守护进程做后台常驻,保证任务能按时拉起。 - 用建好的地图做路由,在固定路径上稳定执行操作,减少大模型幻觉对任务的影响。
我自己用它长期在跑的任务主要有三个:
-
定时点咖啡
到点后自动唤醒,打开瑞幸,按固定流程帮我点一杯超大杯生椰拿铁。 -
“学习强国”每日答题
在我半夜睡觉的时候,自动启动并跳转到答题页,再用视觉模型识别题目并完成答题。不过现在学习强国做了“答题界面禁止截图,这个任务现在完成不了了。 -
学校课程测验巡检与执行
定时检查是否有新测验;如果有,就继续完成识别、作答和提交。
这套东西的出发点很直接:不追求“看起来聪明”,先把真实、重复、耗时的事情稳定做完。
如果你也有定时任务的需求,欢迎下载体验我的项目;如果你对安卓自动化感兴趣,对大模型执行任务时经常点错跳转到错误页面的问题感到烦恼,可以看看我的实现思路,希望对你有一定帮助。
一、引言:关于大模型操控手机——Agent,还是Pipeline?
最近一段时间,我一直在关注大模型在端侧设备上的应用,了解了不少项目后,我产生了一些个人的思考。
我们现在谈论大模型操控设备,言必称“Agent”。在电脑端,这套逻辑是完全成立的。电脑端的任务往往更加复杂、灵活且充满变数。我对 Agent 的定义是:模型是高度自由的,人类为它提供各种 Skills 和 Tools 作为它执行任务的辅助手段,系统不会去过度限制模型的自由发挥。 面对复杂的办公或跨软件协作,这种自由度是必须的。
但手机不一样,手机更多的是“生活用品”,而非纯粹的“办公用品”。
我们在手机上经常做的事情,比如刷视频、看帖子,本质上是为了取悦自己,带有很强的“探索乐趣”。如果把这种充满个人乐趣的主观行为交给 AI 去替我们滑动、替我们看,我会觉得有一种强烈的违和感。
在我看来,真正应该交给手机端 AI 去完成的,是那些重复的、线性的任务。
打个比方,同样是“点外卖”:
如果我的初衷是“看看今天有什么好吃的”,那么这个任务是高度灵活、探索性的,与我个人的主观意愿强相关。我会自己去划屏幕,看图片有没有食欲,算算价格满减。如果把这种任务交给 AI,让它慢吞吞地替我找饭吃,我觉得是很诡异的。
但如果我的初衷是“为了填饱肚子,我每天中午 12 点都要点固定那家店的那个单品”,这时候任务就变了。它变成了一个极其线性的流程:“打开软件 → 到那家店铺 → 点那个单品 → 结算”,整条路径非常明确。
我认为这才是手机端 AI 最应该聚焦去解决的任务。
这就引出了我的另一个核心思考:
面对这类非常线性的任务,且大模型在底层能做的动作其实非常简单(无非就是 Swipe 滑动、Tap 点击、Input 输入等),我们到底需要一个自由发挥的 Agent,还是一个用 Pipeline(流水线)来约束大模型的系统?
如果是纯粹的 Agent 模式,AI 会在每一个页面停留,漫无目的地去“看”当前页面有什么,猜测下一步该点哪,极其容易迷路。
而如果采用 Pipeline 模式,我们可以在不同的阶段明确规定大模型应该怎么做、关注什么,用流水线的方式去约束它的行为,一步步稳妥地达到最终的任务结果。
我认为对于手机端这种明确的线性任务,后者(Pipeline)显然是更合适、更高效的解法。
然而,目前我接触到的一些安卓端自动化开源项目,都有点像电脑端 Agent 的模式,赋予了模型过高的自由度,打开软件后便交给模型自由完成后续动作,却忽视了手机端任务”线性与重复”的本质。
正是基于对“手机不该用纯 Agent,而更适合 Pipeline 约束”的理解,我动手做了这个项目——LXB-Framework。
它的架构设计,完全是为了响应上面提到的两个核心痛点:
1. 出于“线性任务”的需求,我设计了 Route-Then-Act 架构
既然任务是线性的,我们就不该让 AI 去漫无目的地找路。我将任务硬性拆分为“路由导航(Route)”和“需求执行(Act)”两个阶段。用确定性的地图把系统精准送达目标页面,到了地方再让大模型接管并发挥它的理解能力去“干活”。用 Pipeline 约束住了模型的不确定性。
2. 出于“重复任务”的需求,我引入了底层守护进程机制
既然是重复任务,如果每次都要人手动打开 App 去触发执行,那就失去了自动化的意义。为了实现真正的“定时任务”,我放弃了传统 Android 极度不可靠的 Service 保活方案,而是参考了 Shizuku 的原理:直接利用 app_process 启动了一个独立于 Android 应用生命周期的系统级守护进程(Daemon)。
它不受普通应用生命周期约束,能够在后台稳定驻留并显著降低被系统回收的概率。只有时间一到,它才会准时唤醒并执行流水线,真正做到了“设置一次,彻底忘掉”。
后续我会详细介绍整个项目的技术细节。
二、演示:一些实际场景的操作过程
俗话说眼见为实,在深入聊枯燥的架构之前,大家可以先看看它跑起来是什么样子:
演示视频(B站链接)
视频里我主要演示了两个场景。乍一看,大家可能会觉得:这似乎跟其他Agent在表现上也没什么不同。别急,待我细细说来。
场景一:基于 Map 的“学习强国”每日答题(对话交互触发)
在这个任务中,大模型并没有参与中间的页面跳转。
它的真实工作流是:大模型根据用户的自然语言需求,结合我预先为应用构建的 Map(包含各个页面的语义描述和功能),推理出**“目标页面”**是【答题页】。一旦目标确定,剩下的跳转过程(点哪个Tab、进哪个入口)完全是程序化、确定性的直达。
注:因为视频做了倍速,所以这种“程序化跳转”的极速感可能不太明显,但在真机上它是速度很快的,没有大模型推理的停顿。
对于这个任务,我尝试过纯视觉页面跳转,结果是模型其实没办法第一次就点到正确的路径。因为仅仅通过页面截图分析,很难知道“每日答题”其实是在个人主页中的,需要点击 “我的”进入个人主页,才能看到“我要答题”。
一个小插曲:
就在我发布视频两天后,“学习强国”App 突然更新,把答题页面设置成了“禁止截图”。由于目前的“Act(操作)”阶段高度依赖视觉模型去“看”题目和选项,无法截图直接导致了该任务的失效。
这是一个非常有意思的现实挑战,我有一些初步的 Future Work 构想,但在这里就不展开扯太远了。
场景二:无 Map 兜底方案下的“瑞幸咖啡”自动下单(定时任务触发)
我给它下达的指令是:“帮我在瑞幸点一杯超大杯生椰拿铁”,并利用定时任务功能,把时间定在了一分钟之后(因为我懒得等)。
这个演示主要体现了两个能力:
-
真正的定时后台执行: 倒计时结束时,得益于底层
app_process的守护进程,它在无人干预的情况下准时被唤醒并开始干活。 -
Playbook(操作指南)兜底机制: 瑞幸这个 App 我暂时还没有为它构建 Map。作为没有 Map 时的兜底方案,在新增任务时你可以构建一份playbook(操作指南),直接告诉模型“第一步点哪里、第二步选什么”。这保证了即使在没有结构化地图的陌生 App 里,只要有线性说明书,流水线(Pipeline)依然能稳定运转。
三、思考:任务的“页面跳转”与“需求完成”
我个人的一个体会是,手机上的任务,尤其是比较线性的任务,它在底层其实都可以被清晰地拆分为两个阶段:
- 阶段一:页面跳转(Route)
这是前往目的地的过程。比如我想关闭某个权限,我需要依次点击:首页 → 我的 → 设置 → 隐私 → 权限管理。在这个阶段,我们的目标非常明确,就是“到达特定页面”。
- 阶段二:需求完成(Act)
这是真正“干活”的过程。当终于到达“权限管理”页面后,我们需要在复杂的列表中找到特定的开关,识别它的当前状态,并执行最终的点击。
目前业界有很多优秀的探索,比如 AppAgent,它尝试通过自动生成操作文档,并利用 RAG(检索增强生成)来辅助大模型决策,相当于给 AI 配了一本“操作手册”。我的playbook兜底思路其实也是参照了这个方式,不过AppAgent是模型总结,而我比较繁琐一点,需要用户提供。不过对单一线性任务来说,写这么一份playbook其实也不是很难。
但在我看来,即便是拿着“操作手册”,只要依然依赖 VLM(视觉大模型)去逐帧推理下一步该点哪里,本质上就还是一种 “摸索”。
这就像是我们第一次使用一个全新的软件:我们会一边盯着屏幕,一边查阅说明书(RAG),按自己的理解去猜测、点击各种按键,尝试摸索出一条通往目标页面的路。这种模式下,AI 每次执行任务,都像是一个刚接触软件的新手,对着每一帧截图冥思苦想“根据文档,这个按钮看起来是不是我要找的跳转点”。
但仔细想想,当我们把一个 App 用熟了之后,是怎样的状态?我们每次打开朋友圈,需要打开微信后先看看朋友圈的图标在哪,然后判断“噢,朋友圈在这里”,再点击吗?
我们根本不会再浪费脑力去分析页面结构,更不需要查阅说明书。我们会先想好“我要去朋友圈”,然后直接凭借 “肌肉记忆”,甚至不怎么看屏幕,就能形成条件反射,快速点到那个隐藏得很深的页面。
对于一个旨在完成“线性重复性任务”的自动化工具来说,它需要的正是这种“肌肉记忆”,而不是每次都学习一遍操作文档。所以我在这个架构中尝试构建一种 Map(地图),能够实现根据需求确定目标页面,然后无需大模型介入实现程序化的自动化的页面跳转,来尝试为系统构建这样一种肌肉记忆。
因此,LXB-Framework 采用了 Route-Then-Act(先路由,后动作) 的核心逻辑:
-
Route(寻找页面): 既然是肌肉记忆,就不需要 AI 的高级认知参与。通过预构建的 Map 和状态机,系统明确知道目标在哪,直接按既定路线高效、确定地跳转。这不仅速度极快,还从根本上杜绝了大模型在层层跳转中容易发生的坐标漂移和迷路问题。
-
Act(完成需求): 只有当系统凭借“肌肉记忆”精准到达了目标页面,我才唤醒 AI 介入。此时目标页面的 UI 可能是动态的,文本和布局可能会变,这时候让视觉模型去识别开关、提取信息并完成操作,才是真正把 AI 的“语义理解能力”用在了刀刃上。
这种“用确定性的地图找路,用非确定性的 AI 干活”的拆分,是我构建整个自动化框架的核心出发点。
既然一切的基础是“地图”,那么接下来的问题就是:面对手机 App 里复杂多变、层层叠叠的 UI 页面,我们要如何为系统构建出这种可靠的“肌肉记忆”?
四、拆解一:如何给 App 画地图——VLM-XML Fusion
既然要构建地图,这张图最终的形态应该是一个能体现跳转关系的有向图(Directed Graph):其中“节点”是页面,“边”是触发跳转的 UI 元素。
一开始,我的直觉是:既然要根据地图推导下一步干什么,那系统首先得回答两个经典哲学问题——“我在哪个页面?”以及“我要去哪个页面?”。
为了解决“我在哪”这个问题,我踩了非常多的坑:
-
尝试用 Activity 标识: 很快宣告失败。因为现代 App 普遍采用”单 Activity 多 Fragment”的架构。比如淘宝,从首页到各种深层子页面,底层的 Activity 名字根本没变过(都是 MainActivity)。
-
尝试构建“页面指纹”(Activity + XML 特征): 既然名字一样,那我看 UI 结构总行了吧?我花了一个月的时间,试图通过提取页面的 XML 结构特征来做指纹比对。结果发现,像淘宝这类软件,它的页面是 “相似与不相似共存” 的:各个子页面的顶部和底部导航栏完全一样,但中间的 Feed 流内容(商品卡片)是高度动态的。
这就导致了一个无解的死局:如果你把判断阈值调高,稍微有点相似的子页面就会被错误识别成同一个页面;如果你把阈值调低,同一个子页面只要刷新了一下商品,就会被系统当成新页面。调参调到最后变成了彻头彻尾的玄学,甚至还不如让大模型直接盲猜准。
在死磕了一个月页面指纹无果后,我干脆停下来一段时间,重新整理思路。
等我重拾这个项目,代码写到哪都已经忘得差不多了。但就在某天下午,我突然灵光一闪,想通了一个极其关键的逻辑:
我们假设要到达目标页面 B,必须得通过点击当前页面 A 上的一个特定 UI 元素(事实上绝大多数 App 的逻辑都是如此)。那么,我能否直接默认:只要我成功点击了这个特定的 UI,我就一定会到达那个页面?
在这个假设下,我们完全可以用“UI 的唯一性”来替代“页面的唯一性”!
我的思路豁然开朗:我们不再需要死磕“路径上的每一个页面长什么样”,也不再需要每走一步都费劲地去验证“我到底有没有到达中转页”。我们只关注 UI 链路。
什么意思呢?
比如我要去淘宝的隐私设置页。
-
以前的思路是状态确认: 我要先确认“我到了用户页” → 再确认“我到了设置页” → 最后确认“我到了隐私页”。
-
现在的思路是动作执行: 我只要依次点击“我的”按钮 → 点击“设置”按钮 → 点击“隐私设置”按钮。
相比于为一个包含动态商品流的复杂“页面”提取指纹,去为一个静态的“按钮(UI)”构建唯一特征,简直要容易太多了!
顺着这个思路,构建地图的任务就变成了:找出那些具有重要跳转功能的 UI,把它们记录下来。
至于那些不具备导航功能的普通 UI(比如某个具体的商品图、某条评论),我们直接忽略。
但问题又来了:纯靠底层 XML 结构分析,程序是不知道哪个按钮是用来导航的,哪个按钮只是个普通卡片的。这时候,视觉大模型(VLM)强大的视觉语义理解能力就终于派上用场了。
于是,我最终梳理出的建图(Exploration)流程图是这样的:
Exploration1920×860 214 KB
这张流程图看起来可能有点复杂,但其实它背后的核心理念非常朴素。
我的做法是把 VLM(视觉大模型)放在它最擅长的位置上——让它做 “语义判断”;而把诸如坐标落点、元素去重、唯一性校验以及图结构生成这些极易失控、容易产生幻觉的环节,死死地交由传统程序来做 “结构化约束”。
这就是我所谓的 VLM-XML Fusion(视觉与结构的融合)。
整个建图(Exploration)过程,其实就是一个自动化的持续循环,我们可以把它拆解为五个核心步骤:
1. VLM 提取”灵魂”(一次综合分析)
每一轮探索开始,系统截取当前屏幕,向 VLM 发起一次综合分析请求。这个请求的 Prompt 极为克制,它要求模型输出一个结构化的 <最终输出> 块,里面只允许出现四种结果行:
-
PAGE:当前页面是什么?输出页面的语义 ID、名称、功能描述。 -
NAV:页面里有哪些具备”页面跳转”价值的导航入口?输出每个入口的中心坐标和语义信息(名称、类型、预期目标页)。核心要求是严格区分”固定功能结构”和”动态内容实例”——商品卡片、帖子条目一律剔除,绝不允许硬凑。 -
POPUP:是否有阻断型大弹窗(开屏广告、强制更新、权限申请等)?输出关闭按钮的坐标。 -
BLOCK:页面是否是异常页面(人机验证、风控拦截、强制登录)?这类页面无法正常探索,需要特殊处理。
四种结果互斥且有优先级:BLOCK > POPUP > PAGE + NAV。一旦检测到 BLOCK,本轮直接放弃继续,回退重试;检测到 POPUP,则优先处理弹窗,PAGE/NAV 结果丢弃;只有两者都没有,才记录页面和导航节点。
以 Bilibili 首页为例,这一轮 VLM 输出的”灵魂”可能是:
PAGE|bilibili_home|Bilibili首页|视频推荐流首页|底部导航, 顶部搜索
NAV|540|2310|首页Tab|tab|bilibili_home
NAV|120|150|搜索|jump|bilibili_search
2. XML 提取“肉体”(dump_actions)
光有 VLM 给的“灵魂”是不够的,系统需要“可执行的真实物理按键”。
因此,每一轮在截图的同时,系统会通过底层执行一次 dump_actions,把当前界面的 XML 控件树扁平化,提取出所有真正可点击、可交互的底层节点。
继续以 Bilibili 为例,程序提取到的“肉体”可能长这样:
一个矩形包围框 [870,2260][1020,2360],文本是 发布,类名是 android.widget.TextView,资源 ID 是 com.bilibili.bili:id/btn_publish,且 clickable=true。
3. 核心工序:双向融合与 Locator 构建(赋予唯一标识)
这一步是整个框架最精髓的地方:
系统将 VLM 输出的每个元素中心坐标,在 XML 控件树中查找包含该坐标的最小 clickable 节点(叶级节点优先)。若坐标恰好落在节点边缘存在偏差,还会向外各扩展 20px 再试一次。只有当 VLM 指出的位置在物理层面上确实对应一个可交互节点时,融合才算成功;若两次均无命中,则丢弃该 VLM 检测结果,确保地图中每个节点都有可执行的物理落点。
一旦映射成功,系统会立即为该节点构建一个坐标无关的定位器(Locator)。设计上刻意把 text 排在第二优先级之后,因为 text 容易随语言、状态、版本变化,而 resource_id/content_desc/class 相对稳定。Locator 采用四级降级策略,超出上限则直接拒绝收录:
-
第一级(稳定属性): 用
resource_id、content_desc、class三个字段查找候选节点。text在本级刻意跳过,避免文本随本地化或状态改变导致定位失效。 -
第二级(文本收敛): 若第一级候选仍有多个,追加
text进一步缩小范围。 -
第三级(父容器约束): 若仍有多个候选,追加最近父节点的
resource_id进行范围限定。 -
第四级(同级索引): 若冲突候选节点仅剩 2–3 个,按坐标(从上到下、从左到右)排序后赋予
index加以区分。 -
超出上限则拒绝: 若候选副本超过 3 个,判定该节点无法可靠唯一定位,直接丢弃、不写入地图。
经过这套策略构建的 Locator,最核心的特性是坐标无关——定位依赖属性与层级关系,完全不依赖像素坐标,因此跨设备通用:你在 1080P 手机上建的 Map,可以直接用于 2K 屏的平板,只要页面布局未做平台专属适配即可。
4. 弹窗与异常的隔离处理
在安卓上做自动化,弹窗和风控是两大拦路虎。
POPUP(阻断型弹窗):开屏广告、强制更新、青少年模式弹窗等。一旦 VLM 在第一步识别到 POPUP,本轮 PAGE/NAV 结果全部丢弃,系统转入”清扫路障”模式:根据 VLM 给出的关闭按钮坐标,在 XML 树中按包含关系+评分匹配找到最佳节点,构建关闭 Locator,点击后重试本轮探索。弹窗的 Locator 会被单独记录进地图的 popups 表,用于运行时提前规避,但永远不写进跳转图。
BLOCK(异常页面):验证码、风控拦截、强制登录等。一旦检测到 BLOCK,系统重启 App 并将该节点重新入队稍后重试,同时把异常信息记入地图的 blocks 表。
即便如此,弹窗依然是建图流程中最难处理的问题——这也是为什么目前仓库里的地图大多是弹窗较少的 App。
5. 循环探索,沉淀为地图资产
每次点击目标节点、完成一轮分析后,系统并不会”停在原地继续往深处走”,而是先回到首页,然后按照记录的路径逐步重放到目标节点所在的父页面,再尝试点击下一个待探索节点。这种”每次从家出发”的策略保证了探索路径的可预测性,避免在多级嵌套页面中迷失状态。
探索终止条件:所有节点均已探索完毕,或达到设定的最大动作次数/最大耗时。
最终,这个循环输出一份标准化的 JSON 格式地图,包含四个核心字段:
-
pages:每个遇到过的页面,记录其语义 ID、名称、功能描述。页面 ID 由 VLM 给出的语义名加上一段来源哈希后缀组成(如bilibili_home__n_a3f9b2c4),确保每条导航边的目标都是全局唯一的节点。 -
transitions:所有成功构建了 Locator 的跳转边,记录 from → to 以及完整的 Locator 对象。 -
popups:探索中遇到的弹窗及其关闭方式,供运行时提前规避。 -
blocks:探索中遇到的异常页面记录,供调试和后续处理参考。
换句话说,这条建图链路真正做到的事情是:用 VLM 负责“感性理解”,用 XML 负责“物理落地”,最后用程序逻辑把两者压铸成一份极其稳定、可重复执行的工程地图资产。
在实际应用中,我们就是通过这张map,进行需求分析、页面确定、路径规划(BFS)和节点定位,完成页面跳转。在当前这组实验任务与模型设置下,这种方式在路由准确度和速度上都优于纯视觉方案。下面是具体对比,用的任务是 “打开bilibili,帮我发一条动态,内容为test,标题为test”。动图中只展示了路由过程,用的是同一渠道的gemini-3-flash模型。
能够看到,虽然使用地图准备得比较慢(因为需要调用一次模型来确定目标页面),但是后面跳转速度是很快的。这还是在视觉模型没有失误的情况下。如果视觉模型并不理解用户指令目标页面需要如何跳转,到了一条错误的路径上,那么这个差距会更大。
五、拆解二:有了地图怎么走?—— 基于 FSM 的任务流编排
要让程序真正根据用户的自然语言指令,在手机里稳妥地完成一连串操作,我们需要一个清晰的执行引擎。完全放权给大模型让它自由发挥很容易失控,所以我选择引入 FSM(有限状态机) 架构。
这就回到了我一开篇提到的理念:对于明确的任务,用流水线(Pipeline)去约束执行的边界。
下面是整个 FSM 的流转全貌:
FSM780×1701 91.5 KB
如果我们把这个状态机代入到实际的任务流中,它的职责划分其实非常明确。整个任务生命周期在结构上分为两层:全局初始化与任务拆解(一次性运行),以及子任务循环(每个子任务独立执行一遍流水线)。
阶段零:摸清底细(INIT 初始化)
INIT: 在真正开始动脑子或跑动之前,系统会先把一些全局环境信息采集齐全:获取当前设备的屏幕分辨率(确保后续坐标计算不会越界)、获取当前前台的 Activity 信息、扫描已安装的 App 列表(用于后续 App 选择)。
阶段一:任务拆解(跨页/跨 App 的关键)
TASK_DECOMPOSE: 这一步对每个任务都会触发,而不仅仅是复杂任务。系统调用 LLM,把用户的自然语言需求连同已安装 App 列表一并传入,让模型将整个任务拆解为若干个结构化的子任务(SubTask),并判断每个子任务的模式:single(一次性完成)或loop(需要在一批对象上重复执行)。
比如一个略微没什么现实意义的例子:”帮我看看 Bilibili 的每周热门,把第一个视频的标题发给我的微信好友”。
LLM 会将其拆解为两个 single 模式的子任务,并通过 outputs/inputs 字段定义它们之间的数据传递契约:
-
子任务 1:到 Bilibili 查看每周热门第一条视频,outputs 为”视频标题”
-
子任务 2:到微信找到指定好友,将”视频标题”(来自上一任务的 inputs)作为消息发送
若拆解失败(LLM 返回无效 JSON 等情况),系统会自动兜底为单个合成子任务,保证流程不中断。拆解完成后,进入子任务循环,每个子任务独立跑完一套完整的流水线(APP_RESOLVE → ROUTE_PLAN → ROUTING → VISION_ACT),直到全部完成。
阶段二:目标与路线规划(进入单个子任务)
进入子任务循环后,针对当前的子任务(比如先处理 Bilibili):
-
APP_RESOLVE: 确认本次子任务要打开哪个 App。若调用方已在请求中明确指定了包名,则直接采用;否则调用 LLM,结合当前子任务描述和设备上已安装的 App 列表进行推断,并验证推断结果是否在已知候选列表中(不在则启用启发式兜底)。 -
ROUTE_PLAN: 加载目标 App 对应的地图(如果有的话),然后调用 LLM,给它看地图里所有页面的语义描述,让它从中选出”目标页面”是哪一个。如果地图不存在,或者用户/配置主动关闭了地图功能,则记录为无图模式,直接进入ROUTING。
阶段三:页面路由(Route 物理寻路)
-
ROUTING: 进入本阶段时,系统先以CLEAR_TASK标志拉起目标 App(确保每次路由都从干净的任务栈出发)。如果配置了自动解锁且当前屏幕处于锁定状态,会先执行解锁流程。 -
有图模式: 在地图的有向图中,从首页出发对目标页面运行 BFS 寻找最短路径(最多 64 步),然后完全不依赖视觉推理,按路径顺序逐步读取每条边的 Locator,在 XML 控件树中定位对应节点并执行点击。
-
无图模式: 只执行 App 启动,不做任何路由跳转,直接流转到
VISION_ACT,让视觉模型全程接管。
阶段四:睁眼干活(Act 视觉操作)
VISION_ACT: 系统进入”观察 → 思考 → 动作”的多轮循环。每一轮截取当前屏幕,调用视觉大模型(VLM)理解页面内容并输出下一步指令——支持TAP(点击)、SWIPE(滑动)、INPUT(输入文字)、WAIT(等待)、BACK(返回)、DONE(子任务完成)、FAIL(本子任务失败)七种操作。直到模型输出DONE,当前子任务才算完成,系统返回首页并停止当前 App,随后流转到下一个子任务。
运行时的基础护栏(Runtime Guards)
除了正常的流转,FSM 还在底层埋了一些基础的”护栏”机制,用来处理任务中的常见异常:
-
路由恢复尝试(Routing Recovery): 在
ROUTING阶段,若某个 Locator 定位失败(例如被弹窗遮挡),状态机最多会进行ROUTING_RECOVERY_MAX = 2次视觉模型接管恢复:调用 VLM 尝试关掉当前障碍物后重试。若恢复失败,不是直接 FAIL,而是降级进入VISION_ACT,让视觉模型全程处理剩余操作。 -
优雅退出(Graceful Exit): 每个子任务完成后(无论成功还是失败),系统会按压 Home 键返回桌面并停止 App 进程,确保不在用户屏幕上留下烂摊子。任务全部结束后,若本次任务是 FSM 主动解锁了屏幕(
autoLockAfterTask=true且unlockedByFsm=true),则按下电源键重新锁屏,恢复手机到执行前的状态。
六、全局架构设计:三仓库协同的完整拼图
前几节我们分别聊了"怎么建图"和"有了图怎么跑"。现在退后一步,把整个系统当作一幅完整的拼图来看。
整个 LXB 生态由三个独立的仓库组成,各司其职:
LXB-MapBuilder(地图工厂) 是运行在 PC 端的 Python 项目,自带 Web Console,通过 ADB 连接真机,驱动设备执行第四节所述的 VLM-XML Fusion 建图流程,最终产出标准化的 JSON 格式导航地图。
LXB-MapRepo(地图仓库) 是一个纯数据的分发仓库。仓库对外暴露两条渠道:stable(经过验证的稳定地图)和 candidates(正在测试中的候选地图)。MapBuilder 产出的地图按 App 包名和 map_id 组织,发布到对应渠道,供 Framework 按需拉取。
LXB-Framework(运行时框架) 就是本文的主角,它是用户最终安装和交互的部分。在本地,Framework 维护三个地图槽位:stable、candidate(从 MapRepo 对应渠道拉取)和 burn(纯本地调试用途,不对应任何远程渠道)。用户可以在 Config 中切换当前生效的槽位。
整体生态架构如下:
architecture_overall2553×1302 253 KB
Framework 内部架构如下:
architecture_LXB-Framework905×590 20.9 KB
整个架构可以分为两个主要部分来看:
前端(LXB-Ignition APK) 用 Kotlin + Jetpack Compose 构建,是用户与框架交互的唯一入口。负责以下职责:
-
对话任务的输入与结果展示
-
定时任务的创建与管理
-
LLM/VLM 模型接口配置
-
地图同步:通过
MapSyncManager从 MapRepo 拉取地图,做 SHA-256 完整性校验(同时支持 gzip 压缩格式),写入本地_lane目录 -
ADB 配对与守护进程部署:通过
WirelessAdbBootstrapService建立 Wireless ADB 连接,将打包在 APK assets 中的 lxb-core DEX 和原生 Starter 二进制推送到设备数据目录,然后通过app_process拉起守护进程
后端守护进程(lxb-core) 用 Java 编写,以 DEX 格式部署到设备上,通过 app_process 独立于 Android 应用生命周期运行。它是整个框架的"大脑":
-
CortexTaskManager:任务队列与调度,支持取消正在运行的任务 -
CortexFsmEngine:第五节所述的 FSM 状态机引擎 -
MapManager:从本地私有目录加载地图文件,供 FSM 在ROUTE_PLAN/ROUTING阶段使用 -
ExecutionEngine:通过 UiAutomation 执行点击、滑动、文字输入、按键等操作 -
PerceptionEngine:截图、XML 控件树转储、Activity 信息获取、屏幕状态感知 -
LlmClient:向配置的 LLM/VLM 接口发起 HTTP 请求(兼容 OpenAI/v1/chat/completions格式) -
TraceLogger:记录 FSM 每次状态跳转和关键事件,通过 UDP 实时推送给前端
前后端通信:两端运行在不同的进程空间,通过两条独立通道交互。前端通过 TCP 命令通道向守护进程下发任务指令(使用 FrameCodec 自定义帧格式编码);守护进程则通过 UDP trace 事件将 FSM 状态变化实时推回前端,让界面能即时显示当前处于 ROUTING 还是 VISION_ACT 等状态。
下面聊几个值得展开说的设计决策:
关于"为什么后端是 Java 而不是 Python":早期版本其实是 Python 实现的,需要连接电脑。后来我把它整个重写成了 Java。原因是后端代码最终需要以 DEX 格式部署到 Android 设备上,通过 app_process 直接在设备侧执行。Java 天然可以编译为 DEX,整个工具链非常成熟。而 Python 在 Android 上跑需要额外的运行时,链路更长、更不可控。
具体来说,构建与部署流程是这样的:Java 源码 → Gradle 编译为 JAR → 通过 Android SDK 的 d8 工具转换为 DEX → 连同 NDK 编译的原生 Starter 二进制一起打包进 APK 的 assets 目录。运行时由 WirelessAdbBootstrapService 接管后续的推送与启动流程。
这套"通过 ADB 启动独立于 App 生命周期的 shell 级守护进程"的思路,参考了 Shizuku 的设计,在此表示感谢。不同的是,LXB-Framework 不依赖 Shizuku 作为运行时依赖,而是自行实现了 Wireless ADB 配对与连接,将整个链路收归框架内部。
关于"地图的生命周期":一次完整的地图生命周期是这样的——MapBuilder 在 PC 端驱动真机探索,产出地图 JSON → 发布到 LXB-MapRepo 的 stable 或 candidates 渠道 → Framework 前端拉取并写入本地 _lane 目录(附带 SHA-256 校验) → FSM 在 ROUTE_PLAN 阶段由 LLM 根据地图页面语义描述确定目标页面,在 ROUTING 阶段执行 BFS 寻路并完成程序化跳转。如果某个 App 暂时没有地图,系统会自动降级到纯视觉的 VISION_ACT 模式,或者使用用户手写的 Playbook 兜底。三层降级策略保证了框架在任何情况下都能"有办法干活"。
七、安装与使用
使用框架只需要满足三个前提:Android 11+ 真机(开启开发者选项和无线调试,无需 Root)、一个兼容 OpenAI 格式的 LLM/VLM 接口,以及从 Releases 安装 lxb-ignition APK。
安装完成后,App 内置引导会带你完成 Wireless ADB 配对——手机屏幕上显示六位码,在 App 弹出的通知栏中输入即可。配对完成后 App 会自动部署后端守护进程并启动,之后在首页输入自然语言需求或在 Tasks 创建定时任务即可开始使用。如需使用导航地图,在 Config 中配置 MapRepo 地址后一键同步。
完整的环境要求、配对步骤、定时任务配置、以及为新 App 构建地图的详细说明,请参阅各仓库 README:
-
LXB-Framework README — 安装、配置、任务执行完整指南
-
LXB-MapBuilder README — 建图环境搭建与探索流程
八、结语
写到这里,LXB-Framework 的核心路线基本讲完了:用 Route-Then-Act 把“找路”和“干活”拆开,用 FSM 保证流程稳定,用地图沉淀“肌肉记忆”。
但这个项目目前仍有几个很现实的短板:
- 弹窗处理逻辑还不够强。 现在对阻塞弹窗已经有策略,但在复杂场景下仍然会出现误判或漏判,影响路由和建图稳定性。这也是现在构建的地图大部分是弹窗比较少的软件的原因。
- 中文输入兼容性还不够好。 部分应用在当前中文输入模式下表现不稳定,输入体验和成功率都还有提升空间。
- 地图覆盖率还不高。 目前受个人精力和测试资源限制,地图仓库还处在持续建设阶段,离“常用 App 开箱即用”还有明显距离。而且对于不对外暴露xml结构的应用,比如微信,暂时无法构建map。后续可能会做一版类似AppAgent那种方式的map出来兜底。
截至当前版本,已提供的稳定地图主要覆盖以下应用:- Bilibili(tv.danmaku.bili)
- 知乎(com.zhihu.android)
- 小红书(com.xingin.xhs)
- 小黑盒(com.max.xiaoheihe)
- 铁路12306(com.MobileTicket)
- 学习强国(cn.xuexi.android)
- 实验机型数量不足。 现阶段测试设备覆盖有限,无法保证在所有品牌和系统版本上都稳定运行。
- locator机制不够完善。 目前locator有时候对一些UI可能匹配得不是很好,有时候又容易把某些节点误判为弹窗关闭节点,导致弹窗处理有问题,以及对text和content_description的匹配也存在问题,比如bilibili的“消息”按钮,如果有未读消息,可能会变成“消息,n条未读”,导致匹配失败。这个或许可以把匹配逻辑改成文本相似度的匹配?
后续有空的话会持续迭代,把可用性和稳定性再往前推一轮。
最后补充一个命名上的想法:我一直没有把它叫做 Agent,而是叫 Framework。
原因很简单,这套方案并不是一个端到端、完全放权给模型自由发挥的系统。它带着明显的传统自动化工程色彩,模型被放在 FSM 设定的边界内工作,重点是“可控、可复现、可维护”。从这个角度看,“自动化框架”比“Agent”更准确。
目前还是比较实验性的一个版本,很多功能还需要后续完善,项目一定还有很多问题。欢迎大家到 GitHub 提 Issue,有好的建图流程优化方法或地图管理方法也欢迎提出,十分感谢大家能够看到这里。
最后再次放一下项目链接:
LXB-Framework:主要应用
LXB-MapBuilder:建图工具
LXB-MapRepo:地图仓库
--【壹】--:
老友太强了
--【贰】--:
不明觉厉
--【叁】--:
佬,如果端侧小模型针对手机场景做特化训练,会否更能够匹配使用场景
--【肆】--:
感谢大佬,分享思路经验
--【伍】--:
佬,是需要APP支持截屏吗?对于飞书等限制禁止截屏还可以操作吗
--【陆】--:
多年前写过学习强国的自动化学习,8 万多积分的号永封了
--【柒】--:
可能会吧,不过我对端侧模型也不了解
之前智谱开源了个autoglm-phone-9b,算是特异化训练过的,不过似乎有人反馈说老是点不准?
感觉ui识别效果应该还行,坐标精度是个大问题,现在有些大参数量的模型坐标进度都一言难尽,比如gpt系列
--【捌】--:
暂时还不行。以后可能会引入xml结构兜底吧,不过效果可能不是很好
--【玖】--: wuwei01:
四、拆解一:如何给 App 画地图 ——VLM-XML Fusion
这块,基于xml 的话,如果遇到flutter、RN 等自绘制的跨平台框架,不会有xml 给你操作,目前国内大多数大型APP 基本上都是混合框架(原生+跨平台)的,这个可能会是一个比较大的问题
wuwei01:我能否直接默认:只要我成功点击了这个特定的 UI,我就一定会到达那个页面?
有些APP 会上节日主题、动态下发的工作台入口
另外需要考虑下不同人的页面可能是长得完全不一样的,有的APP 功能是可以手动编辑开关的、甚至不同地区、不同时间也会有差异
因此,每一轮在截图的同时,系统会通过底层执行一次
dump_actions,把当前界面的 XML 控件树扁平化,提取出所有真正可点击、可交互的底层节点。
有些系统弹窗、组件,是不可点击的,直接对无障碍隐身;还有一部分是无法直接触发点击,但是可以通过计算坐标、模拟操作。既然你已经有adb shell 了,直接通过类似shizuku 的方案可能会比无障碍的方案更加稳妥
粗略看了一下,暂时发现这些可能会是问题,如果我有错误的地方还请指出,一同进步。
这个思路很有意思,像是在autoJS (录制脚本)的思路基础上插上了AI 的翅膀,也给AI 画了个圈
--【拾】--:
这么恐怖
--【拾壹】--:
感谢大佬!
本帖使用社区开源推广,符合推广要求。我申明并遵循社区要求的以下内容:
- 我的帖子已经打上 开源推广 标签: 是
- 我的开源项目完整开源,无未开源部分: 是
- 我的开源项目已链接认可 LINUX DO 社区: 是
- 我帖子内的项目介绍,AI生成、润色内容部分已截图发出: 是
- 以上选择我承诺是永久有效的,接受社区和佬友监督: 是
以下为项目介绍正文内容,AI生成、润色内容已使用截图方式发出
项目地址:https://github.com/wuwei-crg/LXB-Framework
省流:这个项目装进你的手机里,能做什么?
我做 LXB-Framework 的目标其实很简单,不是为了做一个“无所不能”的手机 Agent。
它主要解决的是我每天都会遇到的那类任务:流程固定、步骤重复、但非常耗时间和耐心。
现在这套框架依赖两件事在工作:
- 用
app_process守护进程做后台常驻,保证任务能按时拉起。 - 用建好的地图做路由,在固定路径上稳定执行操作,减少大模型幻觉对任务的影响。
我自己用它长期在跑的任务主要有三个:
-
定时点咖啡
到点后自动唤醒,打开瑞幸,按固定流程帮我点一杯超大杯生椰拿铁。 -
“学习强国”每日答题
在我半夜睡觉的时候,自动启动并跳转到答题页,再用视觉模型识别题目并完成答题。不过现在学习强国做了“答题界面禁止截图,这个任务现在完成不了了。 -
学校课程测验巡检与执行
定时检查是否有新测验;如果有,就继续完成识别、作答和提交。
这套东西的出发点很直接:不追求“看起来聪明”,先把真实、重复、耗时的事情稳定做完。
如果你也有定时任务的需求,欢迎下载体验我的项目;如果你对安卓自动化感兴趣,对大模型执行任务时经常点错跳转到错误页面的问题感到烦恼,可以看看我的实现思路,希望对你有一定帮助。
一、引言:关于大模型操控手机——Agent,还是Pipeline?
最近一段时间,我一直在关注大模型在端侧设备上的应用,了解了不少项目后,我产生了一些个人的思考。
我们现在谈论大模型操控设备,言必称“Agent”。在电脑端,这套逻辑是完全成立的。电脑端的任务往往更加复杂、灵活且充满变数。我对 Agent 的定义是:模型是高度自由的,人类为它提供各种 Skills 和 Tools 作为它执行任务的辅助手段,系统不会去过度限制模型的自由发挥。 面对复杂的办公或跨软件协作,这种自由度是必须的。
但手机不一样,手机更多的是“生活用品”,而非纯粹的“办公用品”。
我们在手机上经常做的事情,比如刷视频、看帖子,本质上是为了取悦自己,带有很强的“探索乐趣”。如果把这种充满个人乐趣的主观行为交给 AI 去替我们滑动、替我们看,我会觉得有一种强烈的违和感。
在我看来,真正应该交给手机端 AI 去完成的,是那些重复的、线性的任务。
打个比方,同样是“点外卖”:
如果我的初衷是“看看今天有什么好吃的”,那么这个任务是高度灵活、探索性的,与我个人的主观意愿强相关。我会自己去划屏幕,看图片有没有食欲,算算价格满减。如果把这种任务交给 AI,让它慢吞吞地替我找饭吃,我觉得是很诡异的。
但如果我的初衷是“为了填饱肚子,我每天中午 12 点都要点固定那家店的那个单品”,这时候任务就变了。它变成了一个极其线性的流程:“打开软件 → 到那家店铺 → 点那个单品 → 结算”,整条路径非常明确。
我认为这才是手机端 AI 最应该聚焦去解决的任务。
这就引出了我的另一个核心思考:
面对这类非常线性的任务,且大模型在底层能做的动作其实非常简单(无非就是 Swipe 滑动、Tap 点击、Input 输入等),我们到底需要一个自由发挥的 Agent,还是一个用 Pipeline(流水线)来约束大模型的系统?
如果是纯粹的 Agent 模式,AI 会在每一个页面停留,漫无目的地去“看”当前页面有什么,猜测下一步该点哪,极其容易迷路。
而如果采用 Pipeline 模式,我们可以在不同的阶段明确规定大模型应该怎么做、关注什么,用流水线的方式去约束它的行为,一步步稳妥地达到最终的任务结果。
我认为对于手机端这种明确的线性任务,后者(Pipeline)显然是更合适、更高效的解法。
然而,目前我接触到的一些安卓端自动化开源项目,都有点像电脑端 Agent 的模式,赋予了模型过高的自由度,打开软件后便交给模型自由完成后续动作,却忽视了手机端任务”线性与重复”的本质。
正是基于对“手机不该用纯 Agent,而更适合 Pipeline 约束”的理解,我动手做了这个项目——LXB-Framework。
它的架构设计,完全是为了响应上面提到的两个核心痛点:
1. 出于“线性任务”的需求,我设计了 Route-Then-Act 架构
既然任务是线性的,我们就不该让 AI 去漫无目的地找路。我将任务硬性拆分为“路由导航(Route)”和“需求执行(Act)”两个阶段。用确定性的地图把系统精准送达目标页面,到了地方再让大模型接管并发挥它的理解能力去“干活”。用 Pipeline 约束住了模型的不确定性。
2. 出于“重复任务”的需求,我引入了底层守护进程机制
既然是重复任务,如果每次都要人手动打开 App 去触发执行,那就失去了自动化的意义。为了实现真正的“定时任务”,我放弃了传统 Android 极度不可靠的 Service 保活方案,而是参考了 Shizuku 的原理:直接利用 app_process 启动了一个独立于 Android 应用生命周期的系统级守护进程(Daemon)。
它不受普通应用生命周期约束,能够在后台稳定驻留并显著降低被系统回收的概率。只有时间一到,它才会准时唤醒并执行流水线,真正做到了“设置一次,彻底忘掉”。
后续我会详细介绍整个项目的技术细节。
二、演示:一些实际场景的操作过程
俗话说眼见为实,在深入聊枯燥的架构之前,大家可以先看看它跑起来是什么样子:
演示视频(B站链接)
视频里我主要演示了两个场景。乍一看,大家可能会觉得:这似乎跟其他Agent在表现上也没什么不同。别急,待我细细说来。
场景一:基于 Map 的“学习强国”每日答题(对话交互触发)
在这个任务中,大模型并没有参与中间的页面跳转。
它的真实工作流是:大模型根据用户的自然语言需求,结合我预先为应用构建的 Map(包含各个页面的语义描述和功能),推理出**“目标页面”**是【答题页】。一旦目标确定,剩下的跳转过程(点哪个Tab、进哪个入口)完全是程序化、确定性的直达。
注:因为视频做了倍速,所以这种“程序化跳转”的极速感可能不太明显,但在真机上它是速度很快的,没有大模型推理的停顿。
对于这个任务,我尝试过纯视觉页面跳转,结果是模型其实没办法第一次就点到正确的路径。因为仅仅通过页面截图分析,很难知道“每日答题”其实是在个人主页中的,需要点击 “我的”进入个人主页,才能看到“我要答题”。
一个小插曲:
就在我发布视频两天后,“学习强国”App 突然更新,把答题页面设置成了“禁止截图”。由于目前的“Act(操作)”阶段高度依赖视觉模型去“看”题目和选项,无法截图直接导致了该任务的失效。
这是一个非常有意思的现实挑战,我有一些初步的 Future Work 构想,但在这里就不展开扯太远了。
场景二:无 Map 兜底方案下的“瑞幸咖啡”自动下单(定时任务触发)
我给它下达的指令是:“帮我在瑞幸点一杯超大杯生椰拿铁”,并利用定时任务功能,把时间定在了一分钟之后(因为我懒得等)。
这个演示主要体现了两个能力:
-
真正的定时后台执行: 倒计时结束时,得益于底层
app_process的守护进程,它在无人干预的情况下准时被唤醒并开始干活。 -
Playbook(操作指南)兜底机制: 瑞幸这个 App 我暂时还没有为它构建 Map。作为没有 Map 时的兜底方案,在新增任务时你可以构建一份playbook(操作指南),直接告诉模型“第一步点哪里、第二步选什么”。这保证了即使在没有结构化地图的陌生 App 里,只要有线性说明书,流水线(Pipeline)依然能稳定运转。
三、思考:任务的“页面跳转”与“需求完成”
我个人的一个体会是,手机上的任务,尤其是比较线性的任务,它在底层其实都可以被清晰地拆分为两个阶段:
- 阶段一:页面跳转(Route)
这是前往目的地的过程。比如我想关闭某个权限,我需要依次点击:首页 → 我的 → 设置 → 隐私 → 权限管理。在这个阶段,我们的目标非常明确,就是“到达特定页面”。
- 阶段二:需求完成(Act)
这是真正“干活”的过程。当终于到达“权限管理”页面后,我们需要在复杂的列表中找到特定的开关,识别它的当前状态,并执行最终的点击。
目前业界有很多优秀的探索,比如 AppAgent,它尝试通过自动生成操作文档,并利用 RAG(检索增强生成)来辅助大模型决策,相当于给 AI 配了一本“操作手册”。我的playbook兜底思路其实也是参照了这个方式,不过AppAgent是模型总结,而我比较繁琐一点,需要用户提供。不过对单一线性任务来说,写这么一份playbook其实也不是很难。
但在我看来,即便是拿着“操作手册”,只要依然依赖 VLM(视觉大模型)去逐帧推理下一步该点哪里,本质上就还是一种 “摸索”。
这就像是我们第一次使用一个全新的软件:我们会一边盯着屏幕,一边查阅说明书(RAG),按自己的理解去猜测、点击各种按键,尝试摸索出一条通往目标页面的路。这种模式下,AI 每次执行任务,都像是一个刚接触软件的新手,对着每一帧截图冥思苦想“根据文档,这个按钮看起来是不是我要找的跳转点”。
但仔细想想,当我们把一个 App 用熟了之后,是怎样的状态?我们每次打开朋友圈,需要打开微信后先看看朋友圈的图标在哪,然后判断“噢,朋友圈在这里”,再点击吗?
我们根本不会再浪费脑力去分析页面结构,更不需要查阅说明书。我们会先想好“我要去朋友圈”,然后直接凭借 “肌肉记忆”,甚至不怎么看屏幕,就能形成条件反射,快速点到那个隐藏得很深的页面。
对于一个旨在完成“线性重复性任务”的自动化工具来说,它需要的正是这种“肌肉记忆”,而不是每次都学习一遍操作文档。所以我在这个架构中尝试构建一种 Map(地图),能够实现根据需求确定目标页面,然后无需大模型介入实现程序化的自动化的页面跳转,来尝试为系统构建这样一种肌肉记忆。
因此,LXB-Framework 采用了 Route-Then-Act(先路由,后动作) 的核心逻辑:
-
Route(寻找页面): 既然是肌肉记忆,就不需要 AI 的高级认知参与。通过预构建的 Map 和状态机,系统明确知道目标在哪,直接按既定路线高效、确定地跳转。这不仅速度极快,还从根本上杜绝了大模型在层层跳转中容易发生的坐标漂移和迷路问题。
-
Act(完成需求): 只有当系统凭借“肌肉记忆”精准到达了目标页面,我才唤醒 AI 介入。此时目标页面的 UI 可能是动态的,文本和布局可能会变,这时候让视觉模型去识别开关、提取信息并完成操作,才是真正把 AI 的“语义理解能力”用在了刀刃上。
这种“用确定性的地图找路,用非确定性的 AI 干活”的拆分,是我构建整个自动化框架的核心出发点。
既然一切的基础是“地图”,那么接下来的问题就是:面对手机 App 里复杂多变、层层叠叠的 UI 页面,我们要如何为系统构建出这种可靠的“肌肉记忆”?
四、拆解一:如何给 App 画地图——VLM-XML Fusion
既然要构建地图,这张图最终的形态应该是一个能体现跳转关系的有向图(Directed Graph):其中“节点”是页面,“边”是触发跳转的 UI 元素。
一开始,我的直觉是:既然要根据地图推导下一步干什么,那系统首先得回答两个经典哲学问题——“我在哪个页面?”以及“我要去哪个页面?”。
为了解决“我在哪”这个问题,我踩了非常多的坑:
-
尝试用 Activity 标识: 很快宣告失败。因为现代 App 普遍采用”单 Activity 多 Fragment”的架构。比如淘宝,从首页到各种深层子页面,底层的 Activity 名字根本没变过(都是 MainActivity)。
-
尝试构建“页面指纹”(Activity + XML 特征): 既然名字一样,那我看 UI 结构总行了吧?我花了一个月的时间,试图通过提取页面的 XML 结构特征来做指纹比对。结果发现,像淘宝这类软件,它的页面是 “相似与不相似共存” 的:各个子页面的顶部和底部导航栏完全一样,但中间的 Feed 流内容(商品卡片)是高度动态的。
这就导致了一个无解的死局:如果你把判断阈值调高,稍微有点相似的子页面就会被错误识别成同一个页面;如果你把阈值调低,同一个子页面只要刷新了一下商品,就会被系统当成新页面。调参调到最后变成了彻头彻尾的玄学,甚至还不如让大模型直接盲猜准。
在死磕了一个月页面指纹无果后,我干脆停下来一段时间,重新整理思路。
等我重拾这个项目,代码写到哪都已经忘得差不多了。但就在某天下午,我突然灵光一闪,想通了一个极其关键的逻辑:
我们假设要到达目标页面 B,必须得通过点击当前页面 A 上的一个特定 UI 元素(事实上绝大多数 App 的逻辑都是如此)。那么,我能否直接默认:只要我成功点击了这个特定的 UI,我就一定会到达那个页面?
在这个假设下,我们完全可以用“UI 的唯一性”来替代“页面的唯一性”!
我的思路豁然开朗:我们不再需要死磕“路径上的每一个页面长什么样”,也不再需要每走一步都费劲地去验证“我到底有没有到达中转页”。我们只关注 UI 链路。
什么意思呢?
比如我要去淘宝的隐私设置页。
-
以前的思路是状态确认: 我要先确认“我到了用户页” → 再确认“我到了设置页” → 最后确认“我到了隐私页”。
-
现在的思路是动作执行: 我只要依次点击“我的”按钮 → 点击“设置”按钮 → 点击“隐私设置”按钮。
相比于为一个包含动态商品流的复杂“页面”提取指纹,去为一个静态的“按钮(UI)”构建唯一特征,简直要容易太多了!
顺着这个思路,构建地图的任务就变成了:找出那些具有重要跳转功能的 UI,把它们记录下来。
至于那些不具备导航功能的普通 UI(比如某个具体的商品图、某条评论),我们直接忽略。
但问题又来了:纯靠底层 XML 结构分析,程序是不知道哪个按钮是用来导航的,哪个按钮只是个普通卡片的。这时候,视觉大模型(VLM)强大的视觉语义理解能力就终于派上用场了。
于是,我最终梳理出的建图(Exploration)流程图是这样的:
Exploration1920×860 214 KB
这张流程图看起来可能有点复杂,但其实它背后的核心理念非常朴素。
我的做法是把 VLM(视觉大模型)放在它最擅长的位置上——让它做 “语义判断”;而把诸如坐标落点、元素去重、唯一性校验以及图结构生成这些极易失控、容易产生幻觉的环节,死死地交由传统程序来做 “结构化约束”。
这就是我所谓的 VLM-XML Fusion(视觉与结构的融合)。
整个建图(Exploration)过程,其实就是一个自动化的持续循环,我们可以把它拆解为五个核心步骤:
1. VLM 提取”灵魂”(一次综合分析)
每一轮探索开始,系统截取当前屏幕,向 VLM 发起一次综合分析请求。这个请求的 Prompt 极为克制,它要求模型输出一个结构化的 <最终输出> 块,里面只允许出现四种结果行:
-
PAGE:当前页面是什么?输出页面的语义 ID、名称、功能描述。 -
NAV:页面里有哪些具备”页面跳转”价值的导航入口?输出每个入口的中心坐标和语义信息(名称、类型、预期目标页)。核心要求是严格区分”固定功能结构”和”动态内容实例”——商品卡片、帖子条目一律剔除,绝不允许硬凑。 -
POPUP:是否有阻断型大弹窗(开屏广告、强制更新、权限申请等)?输出关闭按钮的坐标。 -
BLOCK:页面是否是异常页面(人机验证、风控拦截、强制登录)?这类页面无法正常探索,需要特殊处理。
四种结果互斥且有优先级:BLOCK > POPUP > PAGE + NAV。一旦检测到 BLOCK,本轮直接放弃继续,回退重试;检测到 POPUP,则优先处理弹窗,PAGE/NAV 结果丢弃;只有两者都没有,才记录页面和导航节点。
以 Bilibili 首页为例,这一轮 VLM 输出的”灵魂”可能是:
PAGE|bilibili_home|Bilibili首页|视频推荐流首页|底部导航, 顶部搜索
NAV|540|2310|首页Tab|tab|bilibili_home
NAV|120|150|搜索|jump|bilibili_search
2. XML 提取“肉体”(dump_actions)
光有 VLM 给的“灵魂”是不够的,系统需要“可执行的真实物理按键”。
因此,每一轮在截图的同时,系统会通过底层执行一次 dump_actions,把当前界面的 XML 控件树扁平化,提取出所有真正可点击、可交互的底层节点。
继续以 Bilibili 为例,程序提取到的“肉体”可能长这样:
一个矩形包围框 [870,2260][1020,2360],文本是 发布,类名是 android.widget.TextView,资源 ID 是 com.bilibili.bili:id/btn_publish,且 clickable=true。
3. 核心工序:双向融合与 Locator 构建(赋予唯一标识)
这一步是整个框架最精髓的地方:
系统将 VLM 输出的每个元素中心坐标,在 XML 控件树中查找包含该坐标的最小 clickable 节点(叶级节点优先)。若坐标恰好落在节点边缘存在偏差,还会向外各扩展 20px 再试一次。只有当 VLM 指出的位置在物理层面上确实对应一个可交互节点时,融合才算成功;若两次均无命中,则丢弃该 VLM 检测结果,确保地图中每个节点都有可执行的物理落点。
一旦映射成功,系统会立即为该节点构建一个坐标无关的定位器(Locator)。设计上刻意把 text 排在第二优先级之后,因为 text 容易随语言、状态、版本变化,而 resource_id/content_desc/class 相对稳定。Locator 采用四级降级策略,超出上限则直接拒绝收录:
-
第一级(稳定属性): 用
resource_id、content_desc、class三个字段查找候选节点。text在本级刻意跳过,避免文本随本地化或状态改变导致定位失效。 -
第二级(文本收敛): 若第一级候选仍有多个,追加
text进一步缩小范围。 -
第三级(父容器约束): 若仍有多个候选,追加最近父节点的
resource_id进行范围限定。 -
第四级(同级索引): 若冲突候选节点仅剩 2–3 个,按坐标(从上到下、从左到右)排序后赋予
index加以区分。 -
超出上限则拒绝: 若候选副本超过 3 个,判定该节点无法可靠唯一定位,直接丢弃、不写入地图。
经过这套策略构建的 Locator,最核心的特性是坐标无关——定位依赖属性与层级关系,完全不依赖像素坐标,因此跨设备通用:你在 1080P 手机上建的 Map,可以直接用于 2K 屏的平板,只要页面布局未做平台专属适配即可。
4. 弹窗与异常的隔离处理
在安卓上做自动化,弹窗和风控是两大拦路虎。
POPUP(阻断型弹窗):开屏广告、强制更新、青少年模式弹窗等。一旦 VLM 在第一步识别到 POPUP,本轮 PAGE/NAV 结果全部丢弃,系统转入”清扫路障”模式:根据 VLM 给出的关闭按钮坐标,在 XML 树中按包含关系+评分匹配找到最佳节点,构建关闭 Locator,点击后重试本轮探索。弹窗的 Locator 会被单独记录进地图的 popups 表,用于运行时提前规避,但永远不写进跳转图。
BLOCK(异常页面):验证码、风控拦截、强制登录等。一旦检测到 BLOCK,系统重启 App 并将该节点重新入队稍后重试,同时把异常信息记入地图的 blocks 表。
即便如此,弹窗依然是建图流程中最难处理的问题——这也是为什么目前仓库里的地图大多是弹窗较少的 App。
5. 循环探索,沉淀为地图资产
每次点击目标节点、完成一轮分析后,系统并不会”停在原地继续往深处走”,而是先回到首页,然后按照记录的路径逐步重放到目标节点所在的父页面,再尝试点击下一个待探索节点。这种”每次从家出发”的策略保证了探索路径的可预测性,避免在多级嵌套页面中迷失状态。
探索终止条件:所有节点均已探索完毕,或达到设定的最大动作次数/最大耗时。
最终,这个循环输出一份标准化的 JSON 格式地图,包含四个核心字段:
-
pages:每个遇到过的页面,记录其语义 ID、名称、功能描述。页面 ID 由 VLM 给出的语义名加上一段来源哈希后缀组成(如bilibili_home__n_a3f9b2c4),确保每条导航边的目标都是全局唯一的节点。 -
transitions:所有成功构建了 Locator 的跳转边,记录 from → to 以及完整的 Locator 对象。 -
popups:探索中遇到的弹窗及其关闭方式,供运行时提前规避。 -
blocks:探索中遇到的异常页面记录,供调试和后续处理参考。
换句话说,这条建图链路真正做到的事情是:用 VLM 负责“感性理解”,用 XML 负责“物理落地”,最后用程序逻辑把两者压铸成一份极其稳定、可重复执行的工程地图资产。
在实际应用中,我们就是通过这张map,进行需求分析、页面确定、路径规划(BFS)和节点定位,完成页面跳转。在当前这组实验任务与模型设置下,这种方式在路由准确度和速度上都优于纯视觉方案。下面是具体对比,用的任务是 “打开bilibili,帮我发一条动态,内容为test,标题为test”。动图中只展示了路由过程,用的是同一渠道的gemini-3-flash模型。
能够看到,虽然使用地图准备得比较慢(因为需要调用一次模型来确定目标页面),但是后面跳转速度是很快的。这还是在视觉模型没有失误的情况下。如果视觉模型并不理解用户指令目标页面需要如何跳转,到了一条错误的路径上,那么这个差距会更大。
五、拆解二:有了地图怎么走?—— 基于 FSM 的任务流编排
要让程序真正根据用户的自然语言指令,在手机里稳妥地完成一连串操作,我们需要一个清晰的执行引擎。完全放权给大模型让它自由发挥很容易失控,所以我选择引入 FSM(有限状态机) 架构。
这就回到了我一开篇提到的理念:对于明确的任务,用流水线(Pipeline)去约束执行的边界。
下面是整个 FSM 的流转全貌:
FSM780×1701 91.5 KB
如果我们把这个状态机代入到实际的任务流中,它的职责划分其实非常明确。整个任务生命周期在结构上分为两层:全局初始化与任务拆解(一次性运行),以及子任务循环(每个子任务独立执行一遍流水线)。
阶段零:摸清底细(INIT 初始化)
INIT: 在真正开始动脑子或跑动之前,系统会先把一些全局环境信息采集齐全:获取当前设备的屏幕分辨率(确保后续坐标计算不会越界)、获取当前前台的 Activity 信息、扫描已安装的 App 列表(用于后续 App 选择)。
阶段一:任务拆解(跨页/跨 App 的关键)
TASK_DECOMPOSE: 这一步对每个任务都会触发,而不仅仅是复杂任务。系统调用 LLM,把用户的自然语言需求连同已安装 App 列表一并传入,让模型将整个任务拆解为若干个结构化的子任务(SubTask),并判断每个子任务的模式:single(一次性完成)或loop(需要在一批对象上重复执行)。
比如一个略微没什么现实意义的例子:”帮我看看 Bilibili 的每周热门,把第一个视频的标题发给我的微信好友”。
LLM 会将其拆解为两个 single 模式的子任务,并通过 outputs/inputs 字段定义它们之间的数据传递契约:
-
子任务 1:到 Bilibili 查看每周热门第一条视频,outputs 为”视频标题”
-
子任务 2:到微信找到指定好友,将”视频标题”(来自上一任务的 inputs)作为消息发送
若拆解失败(LLM 返回无效 JSON 等情况),系统会自动兜底为单个合成子任务,保证流程不中断。拆解完成后,进入子任务循环,每个子任务独立跑完一套完整的流水线(APP_RESOLVE → ROUTE_PLAN → ROUTING → VISION_ACT),直到全部完成。
阶段二:目标与路线规划(进入单个子任务)
进入子任务循环后,针对当前的子任务(比如先处理 Bilibili):
-
APP_RESOLVE: 确认本次子任务要打开哪个 App。若调用方已在请求中明确指定了包名,则直接采用;否则调用 LLM,结合当前子任务描述和设备上已安装的 App 列表进行推断,并验证推断结果是否在已知候选列表中(不在则启用启发式兜底)。 -
ROUTE_PLAN: 加载目标 App 对应的地图(如果有的话),然后调用 LLM,给它看地图里所有页面的语义描述,让它从中选出”目标页面”是哪一个。如果地图不存在,或者用户/配置主动关闭了地图功能,则记录为无图模式,直接进入ROUTING。
阶段三:页面路由(Route 物理寻路)
-
ROUTING: 进入本阶段时,系统先以CLEAR_TASK标志拉起目标 App(确保每次路由都从干净的任务栈出发)。如果配置了自动解锁且当前屏幕处于锁定状态,会先执行解锁流程。 -
有图模式: 在地图的有向图中,从首页出发对目标页面运行 BFS 寻找最短路径(最多 64 步),然后完全不依赖视觉推理,按路径顺序逐步读取每条边的 Locator,在 XML 控件树中定位对应节点并执行点击。
-
无图模式: 只执行 App 启动,不做任何路由跳转,直接流转到
VISION_ACT,让视觉模型全程接管。
阶段四:睁眼干活(Act 视觉操作)
VISION_ACT: 系统进入”观察 → 思考 → 动作”的多轮循环。每一轮截取当前屏幕,调用视觉大模型(VLM)理解页面内容并输出下一步指令——支持TAP(点击)、SWIPE(滑动)、INPUT(输入文字)、WAIT(等待)、BACK(返回)、DONE(子任务完成)、FAIL(本子任务失败)七种操作。直到模型输出DONE,当前子任务才算完成,系统返回首页并停止当前 App,随后流转到下一个子任务。
运行时的基础护栏(Runtime Guards)
除了正常的流转,FSM 还在底层埋了一些基础的”护栏”机制,用来处理任务中的常见异常:
-
路由恢复尝试(Routing Recovery): 在
ROUTING阶段,若某个 Locator 定位失败(例如被弹窗遮挡),状态机最多会进行ROUTING_RECOVERY_MAX = 2次视觉模型接管恢复:调用 VLM 尝试关掉当前障碍物后重试。若恢复失败,不是直接 FAIL,而是降级进入VISION_ACT,让视觉模型全程处理剩余操作。 -
优雅退出(Graceful Exit): 每个子任务完成后(无论成功还是失败),系统会按压 Home 键返回桌面并停止 App 进程,确保不在用户屏幕上留下烂摊子。任务全部结束后,若本次任务是 FSM 主动解锁了屏幕(
autoLockAfterTask=true且unlockedByFsm=true),则按下电源键重新锁屏,恢复手机到执行前的状态。
六、全局架构设计:三仓库协同的完整拼图
前几节我们分别聊了"怎么建图"和"有了图怎么跑"。现在退后一步,把整个系统当作一幅完整的拼图来看。
整个 LXB 生态由三个独立的仓库组成,各司其职:
LXB-MapBuilder(地图工厂) 是运行在 PC 端的 Python 项目,自带 Web Console,通过 ADB 连接真机,驱动设备执行第四节所述的 VLM-XML Fusion 建图流程,最终产出标准化的 JSON 格式导航地图。
LXB-MapRepo(地图仓库) 是一个纯数据的分发仓库。仓库对外暴露两条渠道:stable(经过验证的稳定地图)和 candidates(正在测试中的候选地图)。MapBuilder 产出的地图按 App 包名和 map_id 组织,发布到对应渠道,供 Framework 按需拉取。
LXB-Framework(运行时框架) 就是本文的主角,它是用户最终安装和交互的部分。在本地,Framework 维护三个地图槽位:stable、candidate(从 MapRepo 对应渠道拉取)和 burn(纯本地调试用途,不对应任何远程渠道)。用户可以在 Config 中切换当前生效的槽位。
整体生态架构如下:
architecture_overall2553×1302 253 KB
Framework 内部架构如下:
architecture_LXB-Framework905×590 20.9 KB
整个架构可以分为两个主要部分来看:
前端(LXB-Ignition APK) 用 Kotlin + Jetpack Compose 构建,是用户与框架交互的唯一入口。负责以下职责:
-
对话任务的输入与结果展示
-
定时任务的创建与管理
-
LLM/VLM 模型接口配置
-
地图同步:通过
MapSyncManager从 MapRepo 拉取地图,做 SHA-256 完整性校验(同时支持 gzip 压缩格式),写入本地_lane目录 -
ADB 配对与守护进程部署:通过
WirelessAdbBootstrapService建立 Wireless ADB 连接,将打包在 APK assets 中的 lxb-core DEX 和原生 Starter 二进制推送到设备数据目录,然后通过app_process拉起守护进程
后端守护进程(lxb-core) 用 Java 编写,以 DEX 格式部署到设备上,通过 app_process 独立于 Android 应用生命周期运行。它是整个框架的"大脑":
-
CortexTaskManager:任务队列与调度,支持取消正在运行的任务 -
CortexFsmEngine:第五节所述的 FSM 状态机引擎 -
MapManager:从本地私有目录加载地图文件,供 FSM 在ROUTE_PLAN/ROUTING阶段使用 -
ExecutionEngine:通过 UiAutomation 执行点击、滑动、文字输入、按键等操作 -
PerceptionEngine:截图、XML 控件树转储、Activity 信息获取、屏幕状态感知 -
LlmClient:向配置的 LLM/VLM 接口发起 HTTP 请求(兼容 OpenAI/v1/chat/completions格式) -
TraceLogger:记录 FSM 每次状态跳转和关键事件,通过 UDP 实时推送给前端
前后端通信:两端运行在不同的进程空间,通过两条独立通道交互。前端通过 TCP 命令通道向守护进程下发任务指令(使用 FrameCodec 自定义帧格式编码);守护进程则通过 UDP trace 事件将 FSM 状态变化实时推回前端,让界面能即时显示当前处于 ROUTING 还是 VISION_ACT 等状态。
下面聊几个值得展开说的设计决策:
关于"为什么后端是 Java 而不是 Python":早期版本其实是 Python 实现的,需要连接电脑。后来我把它整个重写成了 Java。原因是后端代码最终需要以 DEX 格式部署到 Android 设备上,通过 app_process 直接在设备侧执行。Java 天然可以编译为 DEX,整个工具链非常成熟。而 Python 在 Android 上跑需要额外的运行时,链路更长、更不可控。
具体来说,构建与部署流程是这样的:Java 源码 → Gradle 编译为 JAR → 通过 Android SDK 的 d8 工具转换为 DEX → 连同 NDK 编译的原生 Starter 二进制一起打包进 APK 的 assets 目录。运行时由 WirelessAdbBootstrapService 接管后续的推送与启动流程。
这套"通过 ADB 启动独立于 App 生命周期的 shell 级守护进程"的思路,参考了 Shizuku 的设计,在此表示感谢。不同的是,LXB-Framework 不依赖 Shizuku 作为运行时依赖,而是自行实现了 Wireless ADB 配对与连接,将整个链路收归框架内部。
关于"地图的生命周期":一次完整的地图生命周期是这样的——MapBuilder 在 PC 端驱动真机探索,产出地图 JSON → 发布到 LXB-MapRepo 的 stable 或 candidates 渠道 → Framework 前端拉取并写入本地 _lane 目录(附带 SHA-256 校验) → FSM 在 ROUTE_PLAN 阶段由 LLM 根据地图页面语义描述确定目标页面,在 ROUTING 阶段执行 BFS 寻路并完成程序化跳转。如果某个 App 暂时没有地图,系统会自动降级到纯视觉的 VISION_ACT 模式,或者使用用户手写的 Playbook 兜底。三层降级策略保证了框架在任何情况下都能"有办法干活"。
七、安装与使用
使用框架只需要满足三个前提:Android 11+ 真机(开启开发者选项和无线调试,无需 Root)、一个兼容 OpenAI 格式的 LLM/VLM 接口,以及从 Releases 安装 lxb-ignition APK。
安装完成后,App 内置引导会带你完成 Wireless ADB 配对——手机屏幕上显示六位码,在 App 弹出的通知栏中输入即可。配对完成后 App 会自动部署后端守护进程并启动,之后在首页输入自然语言需求或在 Tasks 创建定时任务即可开始使用。如需使用导航地图,在 Config 中配置 MapRepo 地址后一键同步。
完整的环境要求、配对步骤、定时任务配置、以及为新 App 构建地图的详细说明,请参阅各仓库 README:
-
LXB-Framework README — 安装、配置、任务执行完整指南
-
LXB-MapBuilder README — 建图环境搭建与探索流程
八、结语
写到这里,LXB-Framework 的核心路线基本讲完了:用 Route-Then-Act 把“找路”和“干活”拆开,用 FSM 保证流程稳定,用地图沉淀“肌肉记忆”。
但这个项目目前仍有几个很现实的短板:
- 弹窗处理逻辑还不够强。 现在对阻塞弹窗已经有策略,但在复杂场景下仍然会出现误判或漏判,影响路由和建图稳定性。这也是现在构建的地图大部分是弹窗比较少的软件的原因。
- 中文输入兼容性还不够好。 部分应用在当前中文输入模式下表现不稳定,输入体验和成功率都还有提升空间。
- 地图覆盖率还不高。 目前受个人精力和测试资源限制,地图仓库还处在持续建设阶段,离“常用 App 开箱即用”还有明显距离。而且对于不对外暴露xml结构的应用,比如微信,暂时无法构建map。后续可能会做一版类似AppAgent那种方式的map出来兜底。
截至当前版本,已提供的稳定地图主要覆盖以下应用:- Bilibili(tv.danmaku.bili)
- 知乎(com.zhihu.android)
- 小红书(com.xingin.xhs)
- 小黑盒(com.max.xiaoheihe)
- 铁路12306(com.MobileTicket)
- 学习强国(cn.xuexi.android)
- 实验机型数量不足。 现阶段测试设备覆盖有限,无法保证在所有品牌和系统版本上都稳定运行。
- locator机制不够完善。 目前locator有时候对一些UI可能匹配得不是很好,有时候又容易把某些节点误判为弹窗关闭节点,导致弹窗处理有问题,以及对text和content_description的匹配也存在问题,比如bilibili的“消息”按钮,如果有未读消息,可能会变成“消息,n条未读”,导致匹配失败。这个或许可以把匹配逻辑改成文本相似度的匹配?
后续有空的话会持续迭代,把可用性和稳定性再往前推一轮。
最后补充一个命名上的想法:我一直没有把它叫做 Agent,而是叫 Framework。
原因很简单,这套方案并不是一个端到端、完全放权给模型自由发挥的系统。它带着明显的传统自动化工程色彩,模型被放在 FSM 设定的边界内工作,重点是“可控、可复现、可维护”。从这个角度看,“自动化框架”比“Agent”更准确。
目前还是比较实验性的一个版本,很多功能还需要后续完善,项目一定还有很多问题。欢迎大家到 GitHub 提 Issue,有好的建图流程优化方法或地图管理方法也欢迎提出,十分感谢大家能够看到这里。
最后再次放一下项目链接:
LXB-Framework:主要应用
LXB-MapBuilder:建图工具
LXB-MapRepo:地图仓库
--【壹】--:
老友太强了
--【贰】--:
不明觉厉
--【叁】--:
佬,如果端侧小模型针对手机场景做特化训练,会否更能够匹配使用场景
--【肆】--:
感谢大佬,分享思路经验
--【伍】--:
佬,是需要APP支持截屏吗?对于飞书等限制禁止截屏还可以操作吗
--【陆】--:
多年前写过学习强国的自动化学习,8 万多积分的号永封了
--【柒】--:
可能会吧,不过我对端侧模型也不了解
之前智谱开源了个autoglm-phone-9b,算是特异化训练过的,不过似乎有人反馈说老是点不准?
感觉ui识别效果应该还行,坐标精度是个大问题,现在有些大参数量的模型坐标进度都一言难尽,比如gpt系列
--【捌】--:
暂时还不行。以后可能会引入xml结构兜底吧,不过效果可能不是很好
--【玖】--: wuwei01:
四、拆解一:如何给 App 画地图 ——VLM-XML Fusion
这块,基于xml 的话,如果遇到flutter、RN 等自绘制的跨平台框架,不会有xml 给你操作,目前国内大多数大型APP 基本上都是混合框架(原生+跨平台)的,这个可能会是一个比较大的问题
wuwei01:我能否直接默认:只要我成功点击了这个特定的 UI,我就一定会到达那个页面?
有些APP 会上节日主题、动态下发的工作台入口
另外需要考虑下不同人的页面可能是长得完全不一样的,有的APP 功能是可以手动编辑开关的、甚至不同地区、不同时间也会有差异
因此,每一轮在截图的同时,系统会通过底层执行一次
dump_actions,把当前界面的 XML 控件树扁平化,提取出所有真正可点击、可交互的底层节点。
有些系统弹窗、组件,是不可点击的,直接对无障碍隐身;还有一部分是无法直接触发点击,但是可以通过计算坐标、模拟操作。既然你已经有adb shell 了,直接通过类似shizuku 的方案可能会比无障碍的方案更加稳妥
粗略看了一下,暂时发现这些可能会是问题,如果我有错误的地方还请指出,一同进步。
这个思路很有意思,像是在autoJS (录制脚本)的思路基础上插上了AI 的翅膀,也给AI 画了个圈
--【拾】--:
这么恐怖
--【拾壹】--:
感谢大佬!

