Android Agent 用任务游标执行跨应用操作
文章讨论 Android Agent 如何按顺序执行跨应用任务,并在失败后保持进度。实现采用扁平任务列表与游标,避免为持续变化的应用和用户流程预先枚举全部状态。
AI 深度解读
跨 App 的 AI Agent,难点不只是把一句话拆成几步,还要知道执行到哪一步、什么时候等用户确认、失败后能不能继续。作者给出的方案很克制:让 LLM 提前生成步骤,用客户端的一张任务列表和一个游标推动执行。
这套设计适合顺序明确的任务,也能减少执行期间对后端的依赖。但文章展示的代码存在两个失败处理缺口。读它最有用的方式,是借走任务建模思路,同时看清「能暂停」离「可靠恢复」还有多远。
不画完整状态图,任务怎么往前走
系列 Part 8 已经加入按场景调整的确认机制:Agent 能区分银行 App 和笔记 App,采用不同的确认门槛。Part 9 要解决的是,一条指令涉及多个动作、多个 App 时,如何维持进度。
原文用「把发票发给刚联系的客户,再把任务 App 中的项目标记为完成」举例,称其包含 3 个步骤、2 个 App。不过,这句话没有完整展开步骤及应用归属,具体拆分仍不清楚。
作者没有为每种跨应用流程设计状态机,而是使用 AgentTask 保存完整任务,通过 steps: List<AgentStep> 存步骤,用 currentStepIndex 标记执行位置。LLM 先生成列表,执行器再按顺序遍历。
每个 AgentStep 包含 instruction、可空的 targetPackage、status 和可空的 StepResult。任务状态为 PENDING、IN_PROGRESS、COMPLETED、FAILED、AWAITING_CONFIRMATION;步骤状态为 PENDING、IN_PROGRESS、COMPLETED、FAILED、SKIPPED。
它省掉的是复杂的流程转移图,没有省掉执行状态。
作者的理由是,任意设备上的 App 和用户流程都可能变化,预先穷举状态容易成为维护负担。这个判断适用于界面和操作组合不断变化的场景,但不能推出状态机普遍不合适。固定业务流程里的状态机,仍能清楚表达分支、等待和异常路径。
确认能暂停,但恢复代码有两个缺口
执行器 TaskExecutor 通过 ConfirmationGuard.shouldConfirm 检查每一步,把 step.instruction 和 step.targetPackage 传入检查,另外两个参数传 null。原文没有解释这两个空参数对应什么上下文。
如果需要确认,执行器保留当前索引,将任务设为 AWAITING_CONFIRMATION 并返回。比如 4 步任务走到第 2 步,UI 弹出确认后,应当从第 2 步接着执行,避免重放前面的动作。
下面是原文的恢复函数,它负责直接执行刚获批准的步骤,再回到常规执行循环:
suspend fun resume(task: AgentTask, approved: Boolean): AgentTask {
if (!approved) {
return task.copy(status = TaskStatus.FAILED)
}
// Skip the confirmation check for the step we just approved
val step = task.steps[task.currentStepIndex]
val result = executeStep(step)
val updatedStep = step.copy(status = result.status, result = result)
val updatedSteps = task.steps.toMutableList()
updatedSteps[task.currentStepIndex] = updatedStep
val resumed = task.copy(
steps = updatedSteps,
currentStepIndex = task.currentStepIndex + 1,
status = TaskStatus.IN_PROGRESS
)
return execute(resumed)
}
要盯住最后的 return execute(resumed):前面没有检查 result.status 是否为 FAILED,所以获批步骤即使执行失败,后续步骤仍会继续。这与作者宣称的「失败立即停止整条链」不一致。
另一个问题出现在常规 execute 中。它先把 currentStepIndex 更新为 i + 1,再判断本步是否失败并返回 FAILED。因此,失败任务的游标已经指向下一步。如果 UI 的重试只是再次调用 execute(task),失败步骤就会被越过。原文没有提供单独的重试实现,不能认定这个问题已被处理。
作者明确提出不静默跳过失败,并让 UI 展示失败步骤及原因。这是合理目标;展示出来的实现,还没有完整兑现它。
为什么执行进度放在客户端
后端负责生成步骤,客户端负责保存执行状态,这是文章的另一项关键取舍。
作者认为,如果后端持有进度,每一步都需要通过网络往返保存结果。移动端连接不稳定,会让任务推进受网络影响。客户端已经拥有无障碍服务提供的界面上下文、当前 App 状态和用户确认结果,把进度放在本地更直接。
具体动作通过 StareBrainService.performAction(step) 执行。只有步骤需要新的 LLM 判断时,才再次调用后端,例如执行中遇到了意料之外的页面。
这里的边界要读准:客户端可以在后端短暂不可达时继续执行已有计划,不代表整个 Agent 能离线工作。遇到新页面后如何处理,作者留给 Part 10。
文章也只展示了 Kotlin 数据类和通过 copy 更新任务的方式,没有交代本地持久化、进程被系统回收后的恢复。因此,「客户端持有状态」与「重启后进度仍在」是两件需要分别实现的事。
我的判断
我认可这种先用列表承载顺序任务的做法。对于「步骤提前可知、主要按顺序执行、只在少数节点等待确认」的 Agent,它容易理解,也便于 UI 展示进度和失败位置。
但列表降低的是流程建模成本,不会自动解决执行可靠性。发送发票已经成功、结果却没能记录下来时,重试可能重复发送;索引向前移动,也无法证明业务动作确实成功。这些问题需要动作结果验证和明确的重试规则。
如果流程开始出现条件分支、循环、补偿操作,列表也可能逐渐长出复杂的调度逻辑。届时使用显式状态机或工作流工具,未必比继续扩展游标方案更难维护。
原文提供的是实现思路和代码片段,没有给出成功率、延迟或维护成本的对照数据。 关于可靠性和维护性的结论,应当视为工程经验,而非已验证的性能优势。
能拿来做什么
- 给日常 AI 编程工具增加多步任务时,可以先采用「步骤列表+当前索引+逐步结果」的结构,让前端明确展示正在执行、等待确认和失败的位置。
- 实现暂停与继续时,应让普通执行和确认后恢复共用失败判断,确保获批步骤失败后不会继续触发后续动作。
- 设计重试入口时,要明确重试的是哪一步,并检查动作是否已经产生效果。发送消息、提交表单等操作,尤其不能只靠游标决定是否重放。
- 如果要在 Android 客户端支持任务恢复,应补上持久化和进程重启后的校验;如果执行需要新的 LLM 决策,也应明确展示等待后端的状态。