ApkClaw:用自然语言远程操控 Android 手机的 AI Agent 实践
图片来源:Unsplash
本文要回答的核心问题:如何让一台 Android 手机像有了自己的”大脑”一样,仅凭你发去的一句自然语言消息,就能自主完成打开 App、点击按钮、滑动页面等一系列复杂操作?
ApkClaw 是一款 AI 驱动的 Android 自动化应用,它的核心理念非常直接——通过自然语言让 LLM Agent 操控 Android 设备。用户不需要写脚本、不需要连数据线、不需要懂任何编程,只需在钉钉、飞书、QQ、Discord 或 Telegram 中发一条消息,AI Agent 就会理解你的意图,然后自主在手机上执行操作。
这款产品尤其适合那些闲置的旧 Android 手机。一台 Android 9 及以上版本的备用机,装上 ApkClaw 之后,就变成了一个 24 小时待命的智能干活助手。
核心架构:一条消息如何变成手机上的实际操作?
本节要回答的核心问题:当你在一款聊天软件里发了一条”帮我打卡”,这条文字消息经过了哪些环节,最终变成了手机屏幕上的点击和滑动?
ApkClaw 的整体架构可以分为四个清晰的层次,从上到下依次是:消息渠道层、消息路由层、任务编排层和 Agent 执行层。
图片来源:Unsplash
┌───────────────────────────────────────────────────────────────┐
│ 消息渠道 │
│ 钉钉 │ 飞书 │ QQ │ Discord │ Telegram │ 微信 │
└──────────────────────┬────────────────────────────────────────┘
│ 收到消息
▼
┌─────────────────┐
│ ChannelManager │ 消息路由与分发
└────────┬────────┘
│
┌────────▼────────┐
│ TaskOrchestrator │ 任务锁、生命周期管理
└────────┬────────┘
│
┌────────▼────────┐
│ AgentService │ Agent 循环
│ │
│ ┌────────────┐ │
│ │ LLM 调用 │◄─┼── LangChain4j (OpenAI / Anthropic)
│ └─────┬──────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ 工具执行 │◄─┼── ToolRegistry → ClawAccessibilityService
│ └─────┬──────┘ │
│ │ │
│ 循环直到 │
│ 任务完成 │
└────────┬────────┘
│
▼
通过渠道回复用户
第一层:消息渠道。 这是用户接触 ApkClaw 的入口。目前支持钉钉、飞书、QQ、Discord、Telegram,以及微信。每个渠道对应一套独立的协议和凭证体系——比如钉钉使用 App Stream Client,需要 Client ID 和 Client Secret;Discord 使用 Gateway WebSocket 加 REST,只需要一个 Bot Token。多渠道的设计意味着你不需要为了用 ApkClaw 而去安装一个新的 App,直接用你已经在用的聊天工具就能发指令。
第二层:ChannelManager(消息路由与分发)。 当任意一个渠道收到消息后,ChannelManager 负责把这条消息统一接进来,进行初步校验(比如检查无障碍服务是否已开启),然后传递给下一层。
第三层:TaskOrchestrator(任务编排)。 这一层做两件关键的事:第一,获取任务锁——ApkClaw 采用单任务模型,同一时间只执行一个任务,避免多个指令在手机上打架;第二,按 Home 键重置设备状态,确保每次任务都从一个干净的桌面开始,而不是在某个 App 的深层页面里。
第四层:AgentService(Agent 循环)。 这是整个系统的大脑所在,也是后文会重点展开的部分。简单说,它调用 LLM 理解指令、决定调用哪个工具、执行工具、把结果反馈给 LLM,如此循环直到任务完成。
最终,执行结果会通过你发消息的那个渠道原路返回,你在聊天窗口里就能看到任务的执行过程和最终结果。
反思: 四层架构看似不复杂,但”单任务锁 + Home 键重置”这个设计细节很值得玩味。很多自动化工具出问题,不是因为”做不对”,而是因为”状态乱了”——上一个任务停在了某个弹窗,下一个任务就从错误的状态开始。ApkClaw 在编排层就强制重置状态,把状态管理的复杂度从 Agent 层移到了编排层,这是一个很务实的工程选择。
Agent 执行流程:从收到指令到任务完成的完整链路
本节要回答的核心问题:Agent 在接到一条自然语言指令后,具体是怎么一步步”看屏幕、想对策、做动作”的?
ApkClaw 的核心执行流程可以拆解为五个阶段:
-
用户发送消息:通过任意已连接的渠道发送自然语言消息,比如”打开飞书并进行打卡”。 -
渠道校验:ChannelSetup 校验无障碍服务是否已经开启。如果没有开启,任务不会继续,因为后续所有操作都依赖无障碍服务。 -
任务编排:TaskOrchestrator 获取任务锁,按 Home 键把手机重置到桌面状态。 -
Agent 循环:DefaultAgentService 进入核心的 Agent 循环,这是整个系统最关键的部分。 -
结果回复:任务完成后,结果通过同一渠道回复给用户。
Agent 循环内部遵循 观察 → 思考 → 行动 → 验证 的协议,每一轮循环做的事情如下:
构建上下文。 系统提示词会注入设备上下文信息,包括手机品牌、型号、Android 版本、屏幕分辨率,以及当前已注册的所有工具列表和安全约束。这些信息让 LLM “知道”自己正在操作一台什么样的设备、能用到哪些能力。
调用 LLM。 通过 LangChain4j 桥接层,把系统提示词、用户消息和工具定义一起发给 LLM。LLM 返回的不是一个纯文本回答,而是一个工具调用指令——比如”调用 tap 工具,坐标 (540, 1200)”。
执行工具。 从 LLM 响应中提取工具调用,通过 ToolRegistry 找到对应的工具实现,最终由 ClawAccessibilityService(无障碍服务)在设备上真正执行操作。
反馈与循环。 工具执行的结果(成功/失败、屏幕变化等)被反馈给 LLM,LLM 根据新的信息决定下一步操作。这个循环会一直持续,直到 LLM 主动调用 finish 工具表示任务完成,或者达到最大迭代次数(40 轮)。
图片来源:Unsplash
Agent 循环中的四个关键机制
LLM 调用重试。 网络请求不可能永远成功。ApkClaw 对 LLM 调用设置了最多 3 次重试,采用指数退避策略(1 秒 → 2 秒 → 4 秒)。但如果遇到 401(未授权)或 403(禁止访问)错误,则不进行重试——因为这两种错误通常是 API Key 配置问题,重试多少次都不会变。
死循环检测。 AI 在执行任务时可能会陷入重复操作——比如反复点击同一个位置。ApkClaw 维护了一个 4 轮的滑动窗口,记录每一轮的 (screenHash, toolCall) 指纹。如果连续 4 轮的指纹完全相同,系统会注入一条系统消息强制 Agent 换一种方式操作,打破死循环。
Token 优化。 在多轮循环中,get_screen_info 工具会频繁返回 UI 层级树数据,这些数据非常消耗 token。ApkClaw 的策略是将历史中的 get_screen_info 结果替换为占位符,仅保留最近一次的完整结果。这在长任务场景下能显著节省 token 消耗。
系统弹窗处理。 当 getRootInActiveWindow() 返回 null 时,说明检测到了受保护的系统弹窗(比如权限确认对话框)。此时 Agent 无法读取界面也无法注入手势,于是会自动截取当前屏幕、发送给用户、并终止任务,让用户手动处理。
反思: 死循环检测和 Token 优化这两个机制,体现了”在真实设备上跑 Agent”和”在纯文本环境中跑 Agent”的本质区别。纯文本环境里 token 不够了就截断,死循环了大不了重来;但在真实设备上,死循环意味着手机一直在无意义地操作,token 暴涨意味着你的 API 账单在燃烧。这些机制不是锦上添花,而是从”能跑”到”能用在生产环境”的必要门槛。
工具系统:AI 能对手机做哪些操作?
本节要回答的核心问题:ApkClaw 的 AI Agent 到底有哪些”手和眼”,能够完成哪些类型的手机操作?
工具系统是 ApkClaw 的”手脚”。所有工具按设备类型在 ToolRegistry 中注册,每个工具继承 BaseTool,实现 execute(Map<String, Any>): ToolResult 方法,提供中英文双语描述和类型化参数声明。
工具分为两大类:通用工具和手机专属工具。
通用工具(所有设备可用)
| 工具 | 说明 | 场景举例 |
|---|---|---|
get_screen_info |
获取 UI 层级树,供 AI 分析当前界面 | 每轮循环开始时”看”一眼屏幕上有什么 |
find_node_info |
通过文本或资源 ID 查找元素 | 在当前页面找到”签到”按钮 |
take_screenshot |
截取当前屏幕为 PNG | 截图发给用户确认当前状态 |
input_text |
向焦点输入框输入文本 | 在搜索框中输入”小Lin说” |
open_app |
通过名称打开应用 | “打开抖音” |
get_installed_apps |
获取已安装应用列表 | 确认手机上是否安装了飞书 |
press_back |
返回上一页 | 从子页面返回 |
press_home |
回到桌面 | 重置设备状态 |
open_recent_apps |
打开最近任务列表 | 切换到另一个 App |
expand_notifications |
展开通知栏 | 查看是否有新消息 |
collapse_notifications |
收起通知栏 | 关闭通知栏 |
lock_screen |
锁屏 | 任务完成后锁屏省电 |
wait |
等待指定时长 | 等待页面加载完成 |
repeat_actions |
重复执行一组操作 | 批量点赞 |
send_file |
通过渠道发送文件给用户 | 把截图或日志发给用户 |
finish |
完成任务并返回总结 | 告诉用户”打卡已完成” |
手机专属工具
| 工具 | 说明 | 场景举例 |
|---|---|---|
tap |
点击指定坐标 (x, y) | 点击屏幕上的某个按钮 |
long_press |
长按指定坐标 | 长按消息进行删除 |
swipe |
从 A 点滑动到 B 点 | 上滑浏览抖音视频流 |
click_by_text |
通过可见文字点击元素 | 点击”发送”按钮 |
click_by_id |
通过资源 ID 点击元素 | 点击 com.example.app:id/submit |
search_app_in_store |
在应用商店中搜索应用 | 搜索并安装某个 App |
举个具体的例子:当你发送”打开抖音,搜索小Lin说的视频进行点赞和评论”这条消息时,Agent 的实际操作链路大致是——调用 open_app 打开抖音 → 调用 tap 点击搜索框 → 调用 input_text 输入”小Lin说” → 调用 tap 点击搜索结果 → 调用 get_screen_info 确认视频已打开 → 调用 tap 点赞 → 调用 tap 打开评论区 → 调用 input_text 输入评论 → 调用 tap 发送 → 调用 finish 结束任务。
反思: 工具设计的颗粒度选择是一件很有意思的事。ApkClaw 同时提供了
tap(坐标点击)和click_by_text(文本点击)两种方式。坐标点击更通用但更脆弱(换了分辨率就失效),文本点击更语义化但依赖 UI 节点的文本属性。两者并存,让 LLM 可以根据实际情况灵活选择,这种”给 AI 选择权而不是替它做决定”的思路,在 Agent 工具设计中值得借鉴。
LLM 集成:如何选择和配置大模型后端?
本节要回答的核心问题:ApkClaw 支持哪些大模型,如何配置它们,以及为什么 temperature 设得这么低?
ApkClaw 通过 LlmClientFactory 实现可插拔的 LLM 后端,目前支持两个提供商:
| 提供商 | 客户端类 | 模型构建器 |
|---|---|---|
| OpenAI 兼容 | OpenAiLlmClient |
OpenAiChatModel / OpenAiStreamingChatModel |
| Anthropic | AnthropicLlmClient |
AnthropicChatModel / AnthropicStreamingChatModel |
“OpenAI 兼容”意味着不只是 OpenAI 官方 API,任何兼容 OpenAI 接口格式的第三方服务都可以接入。这为使用国内 LLM 服务商(通常提供 OpenAI 兼容接口)留出了空间。
两个提供商均支持流式和非流式模式。在 HTTP 层,ApkClaw 使用基于 OkHttp 的自定义 OkHttpClientBuilderAdapter 替代了 LangChain4j 默认的 JDK HttpClient,原因是 JDK HttpClient 在 Android 上的兼容性不佳。
核心配置项
配置集中在 AgentConfig 中:
-
apiKey:你的 API Key,在本地设置中填写,不会上传到第三方服务器。 -
baseUrl:LLM 接口地址,默认为 https://api.openai.com/v1。如果你使用第三方兼容服务,需要修改为对应的端点地址。 -
modelName:模型名称,例如 gpt-4o或claude-sonnet-4-20250514,由用户自行选择。 -
provider:选择 OPENAI(默认)或ANTHROPIC。 -
temperature:默认 0.1。这是一个非常低的值,意味着模型输出高度确定性——对于”点击坐标 (540, 1200)”这种操作,你需要的是精确和稳定,而不是创意和发散。 -
maxIterations:最大循环轮数,默认 40。 -
streaming:是否启用流式输出,默认关闭。
LangChain4j 桥接层
ApkClaw 并没有直接使用 LangChain4j 的 @Tool 注解方式定义工具,而是自己定义了一套 BaseTool 抽象,然后通过 LangChain4jToolBridge 将其转换为 LangChain4j 的 ToolSpecification 格式。参数类型(string、integer、number、boolean)会被映射为 JSON Schema。这种桥接设计让工具定义完全掌控在 ApkClaw 自己手中,同时复用了 LangChain4j 的 Agent 编排能力。
反思: temperature 设为 0.1 这个细节,看似不起眼,实际上反映了”设备操控”和”内容生成”两种场景对 LLM 的根本不同要求。写文章时你希望 temperature 高一些让内容有灵气,但控制手机时你希望每次都点击同一个位置。很多开发者直接拿默认 temperature 0.7 去跑自动化任务,结果就是同一条指令每次执行路径都不一样,调试起来非常痛苦。
渠道系统:通过哪些平台发送指令?
本节要回答的核心问题:除了在手机上直接操作,你还能通过哪些聊天平台远程给手机下达指令?
ApkClaw 目前支持的渠道及其协议和凭证要求如下:
| 渠道 | 协议 | 所需凭证 |
|---|---|---|
| 钉钉 | App Stream Client | Client ID + Client Secret |
| 飞书 | OAPI SDK | App ID + App Secret |
| QQ Bot API | App ID + App Secret | |
| Discord | Gateway WebSocket + REST | Bot Token |
| Telegram | Bot HTTP API | Bot Token |
每个渠道的实现位于项目的 channel/ 目录下,各自是独立的处理器模块。
以远程打卡为例:你把 Android 手机放在公司充电,连接好 Wi-Fi。哪天起晚了,不需要慌张,直接在飞书里发一条”打开飞书并进行打卡”,手机端的 ApkClaw 通过飞书渠道收到消息,Agent 自主完成打开飞书、找到打卡入口、执行打卡的全过程,然后通过飞书把结果回复给你。
再以自媒体运营为例:你在 Discord 里发送”打开抖音,搜索小Lin说的视频进行点赞和评论”,手机端的 ApkClaw 通过 Discord 渠道收到指令,自动在手机上完成搜索、点赞、评论操作。这种基于真实手机物理操作的自动化,模拟的是正常用户的操作行为,与脚本注入有本质区别。
渠道凭证有两种配置方式:一是在手机 App 的设置页中手动填写;二是通过局域网 HTTP 服务器在 PC 浏览器上配置,后文会详细介绍。
反思: 多渠道设计看似只是”多接了几个 SDK”,但它解决的是一个真实的痛点——不同团队、不同场景下常用的沟通工具不同。产品团队用飞书,技术社区用 Discord,海外用户用 Telegram,个人用户可能用 QQ。ApkClaw 不强迫用户迁移到某个特定平台,而是”用户在哪,我就在哪”,这个产品思路在 ToB/ToC 边界模糊的工具类产品中非常重要。
无障碍服务:设备交互的底层引擎与已知限制
本节要回答的核心问题:ApkClaw 在 Android 系统层面到底是通过什么机制实现点击、滑动、读取界面的?有哪些做不了的事?
ClawAccessibilityService 是整个 ApkClaw 的物理执行层,用 Java 实现,承担四类核心操作:
手势操作。 通过 dispatchGesture() 实现点击、滑动、长按。这是手机专属工具(tap、swipe、long_press)的底层实现。所谓的”点击坐标 (x, y)”,最终就是通过这个方法在屏幕上对应位置触发一个触摸事件。
节点遍历。 通过 getRootInActiveWindow() 获取当前界面的 UI 层级树。这就是 get_screen_info 和 find_node_info 的数据来源。UI 层级树包含了界面上所有可访问性节点的信息——文本内容、资源 ID、坐标范围、是否可点击等。
按键注入。 通过 performGlobalAction() 实现 Home 键、返回键、打开最近任务等系统级按键。press_back、press_home、open_recent_apps 这几个工具的底层就是这个。
截屏。 通过 takeScreenshot() 实现屏幕截图,生成 PNG 图片。这个功能需要 Android 11 及以上版本的支持。
已知限制:系统保护窗口
Android 系统有一套安全机制叫做 filterTouchesWhenObscured。当出现某些系统保护窗口时(比如权限请求弹窗,典型的是 com.android.permissioncontroller 的权限确认对话框),这些窗口会同时阻止两件事:
-
节点树读取—— getRootInActiveWindow()返回 null,Agent “看”不到界面上有什么。 -
手势注入—— dispatchGesture()无法穿透保护窗口执行操作。
也就是说,Agent 在这种情况下既”看不清”也”摸不到”。ApkClaw 的处理策略是:检测到 getRootInActiveWindow() 返回 null 时,自动截取当前屏幕、通过渠道发送给用户、并终止当前任务,由用户手动处理这个系统弹窗。
图片来源:Unsplash
反思: 系统保护窗口的限制不是 ApkClaw 的 bug,而是 Android 的安全设计。任何基于无障碍服务的自动化方案都会遇到这个问题。ApkClaw 做得好的地方在于:它没有假装这个问题不存在,而是做了明确的检测和优雅的降级——截图通知用户,而不是卡死或者报一个让人摸不着头脑的错误。在真实使用中,这种”知道自己能做什么、不能做什么”的诚实,比”什么都想做但什么都做不好”要有价值得多。
实际应用场景:旧手机能变成什么?
本节要回答的核心问题:把一台闲置的 Android 手机装上 ApkClaw,在真实生活中到底能派上什么用场?
以下是经过实际验证或从产品能力中自然推导出的应用场景:
场景一:远程打卡
把 Android 手机放在公司,插上充电线。某天起晚了或者路上堵车,打开手机里的飞书或钉钉,发一条消息:”打开飞书并进行打卡”。ApkClaw 收到指令后,自动打开飞书 App、找到打卡入口、完成打卡操作,并把结果回复给你。
这个场景的价值在于:手机在公司、人在家里,实现了物理位置的”跨越”。传统的自动化脚本需要电脑在线才能跑,而 ApkClaw 只需要手机在线。
场景二:社交媒体自动操作
发送指令:”打开抖音,搜索小Lin说的视频进行点赞和评论”。ApkClaw 会在手机上自动打开抖音、进入搜索、找到目标视频、执行点赞和评论。
这个场景的特殊之处在于:因为操作发生在真实手机上,走的是正常的用户交互路径(点击、滑动、输入),而不是通过 API 或脚本注入,所以行为模式是合理的用户行为,不容易触发平台的风控策略。
场景三:App 抢票
很多票务平台只支持 App 端抢票,PC 端反而没有入口。ApkClaw 天然运行在手机上,直接在 App 内操作,完美适配这类场景。
场景四:App 测评自动化
如果你是产品体验官,需要对新 App 做全面的操作测试和截图记录。可以让 ApkClaw 自动打开 App、浏览各个页面、执行关键操作、截图保存,大幅减少重复性手工操作。
场景五:自媒体内容发布
自媒体运营者经常需要在抖音、小红书等多个平台发布内容。ApkClaw 可以模拟正常的手机点击行为,在各个 App 中完成打开、编辑、发布的流程。
场景六:机票酒店规划与预订
出差或出游时,经常需要在多个 App 之间查找最优路线、最合适的酒店、预估打车时间、设置闹钟提醒。这些跨 App 的重复性操作,可以一次性交给 ApkClaw 完成——它能在飞书中查行程、在酒店 App 中预订、在日历中设置提醒。
场景七:远程帮助父母操作手机
很多父母在使用智能手机办理社保、养老金等个人业务时遇到困难。子女可以通过聊天工具发送指令,远程操控父母的手机完成操作,而不需要电话里反复描述”点左边那个按钮”。
反思: 这七个场景看起来跨度很大,但有一个共同特征:它们都是”只能在手机上完成”或者”在手机上完成更自然”的操作。PC 端的自动化工具(比如传统的 RPA)在这些场景下要么完全无能为力(因为没有 App),要么需要绕很远的路(通过模拟器)。ApkClaw 直接运行在手机上这个事实,本身就是最大的竞争壁垒。手机龙虾和 PC 龙虾不是替代关系,而是互补关系。
安装配置完整指南
本节要回答的核心问题:从零开始,如何把 ApkClaw 跑起来并完成第一次任务?
环境要求
-
Java 17 及以上版本 -
Android Studio(建议 Ladybug 或更高版本) -
Android SDK 36(编译版本和目标版本),最低支持 SDK 28(Android 9)
从源码编译
# 克隆仓库
git clone https://github.com/apkclaw-team/ApkClaw.git
cd ApkClaw
# Debug 构建
./gradlew assembleDebug
# Release 构建
./gradlew assembleRelease
如果你不想自己编译,也可以直接下载预编译的 APK 安装到设备上。
安装与授权
将 APK 安装到 Android 9 及以上版本的设备后,打开 App,在首页依次开启以下权限:
-
无障碍服务:这是最核心的权限,所有设备交互都依赖它。 -
通知权限:用于接收渠道消息。 -
悬浮窗权限:用于显示悬浮球等 UI 组件。 -
电池白名单:防止系统因省电策略杀掉后台服务。 -
文件访问权限:用于截图保存和文件发送。
配置 LLM
进入 设置 > LLM Config,填写以下信息:
-
API Key:你的 OpenAI 或 Anthropic 的 API Key。 -
Base URL:LLM 接口地址。如果直接使用 OpenAI 官方,保持默认的 https://api.openai.com/v1即可;如果使用第三方兼容服务,修改为对应的端点地址。 -
Model Name:填写你想使用的模型名称,例如 gpt-4o或claude-sonnet-4-20250514。
配置消息渠道
进入设置页面,选择至少一个消息渠道,填写对应的机器人凭证。以 Telegram 为例,只需要填写一个 Bot Token 即可。
发送第一条指令
配置完成后,通过已配置的渠道发送一条简单的消息试试,比如”截一张图发给我”或者”打开设置”。如果一切配置正确,你应该能在聊天窗口中收到 Agent 的执行结果。
局域网配置服务器:用 PC 浏览器管理手机
本节要回答的核心问题:不想在手机的小屏幕上输入长长的 API Key 和 Token,有没有更方便的配置方式?
ApkClaw 内置了一个基于 NanoHTTPD 的 HTTP 服务器,运行在端口 9527。开启方法很简单:在设置中打开”LAN Config”开关,然后在 PC 浏览器中访问 http://<设备IP>:9527。
这个服务器提供了以下接口:
| 端点 | 方法 | 用途 |
|---|---|---|
/ |
GET | 配置页面(一个 Web UI) |
/api/channels |
GET | 读取渠道凭证(敏感信息脱敏,仅显示末尾 4 位) |
/api/channels |
POST | 更新渠道凭证 |
/api/llm |
GET | 读取 LLM 配置(敏感信息脱敏) |
/api/llm |
POST | 更新 LLM 配置 |
在 GET 请求返回数据时,所有敏感信息(如 API Key、Token)都会做脱敏处理,只显示末尾 4 位字符,防止在浏览器中意外泄露。
此外,Debug 构建版本还额外提供了一个 /debug.html 工具调试控制台,可以在浏览器中直接测试各个工具的执行效果,非常适合开发和调试阶段使用。
反思: 局域网配置服务器这个功能,典型地体现了”以用户为中心”的设计思维。从技术角度看,在手机 App 里加几个输入框是最简单的实现方式;但从用户角度看,在手机上复制粘贴几十个字符的 API Key 是一种糟糕的体验。一个轻量的 HTTP 服务器(NanoHTTPD 整个体积很小),换来的是配置效率的巨大提升。而且脱敏设计也说明开发者对安全性有基本的意识——不是”能跑就行”,而是”跑起来也要安全”。
项目结构与主要依赖
本节要回答的核心问题:ApkClaw 的代码是怎么组织的,背后依赖了哪些关键技术栈?
项目目录结构
app/src/main/java/com/apk/claw/android/
├── agent/ # Agent 循环、配置、回调
│ ├── langchain/ # LangChain4j 桥接层 & OkHttp 适配器
│ └── llm/ # LLM 客户端 (OpenAI, Anthropic)
├── base/ # BaseActivity(屏幕密度适配)
├── channel/ # 消息渠道处理器
│ ├── dingtalk/
│ ├── feishu/
│ ├── qqbot/
│ ├── discord/
│ └── telegram/
├── floating/ # 悬浮球 UI 管理
├── server/ # 局域网配置 & 调试 HTTP 服务器
├── service/ # 无障碍服务、前台服务、保活服务
├── tool/ # 工具抽象层 & 注册中心
│ └── impl/ # 工具实现 (通用/手机/电视)
├── ui/ # Activity(启动页、首页、引导页、设置)
├── utils/ # KVUtils, XLog, 格式化工具
└── widget/ # 自定义 UI 组件
结构清晰,模块边界明确。agent/ 负责 AI 大脑,channel/ 负责消息入口,tool/ 负责手脚,service/ 负责系统级能力,server/ 负责辅助配置。
主要依赖
AI / Agent 层
| 依赖 | 版本 | 用途 |
|---|---|---|
| LangChain4j | 1.12.2 | Agent 编排、工具定义、LLM 集成 |
消息渠道层
| 依赖 | 版本 | 用途 |
|---|---|---|
| DingTalk Stream Client | 1.3.12 | 钉钉渠道接入 |
| Feishu OAPI SDK | 2.5.3 | 飞书渠道接入 |
网络层
| 依赖 | 版本 | 用途 |
|---|---|---|
| OkHttp | 4.12.0 | HTTP 客户端,用于 LLM 调用 |
| Retrofit | 2.11.0 | REST API 客户端 |
| NanoHTTPD | 2.3.1 | 局域网配置与调试 HTTP 服务器 |
存储与工具层
| 依赖 | 版本 | 用途 |
|---|---|---|
| MMKV | 2.3.0 | 高性能本地键值存储 |
| Gson | 2.13.2 | JSON 序列化与反序列化 |
| ZXing | 3.5.3 | 二维码生成 |
| UtilCode | 1.31.1 | Android 通用工具函数库 |
UI 层
| 依赖 | 版本 | 用途 |
|---|---|---|
| Glide | 5.0.5 | 图片加载 |
| EasyFloat | 2.0.4 | 悬浮窗管理 |
| MultiType | 4.3.0 | RecyclerView 多类型适配器 |
整体技术栈的选择很务实:LangChain4j 做 Agent 编排省去了大量造轮子的工作,OkHttp 替代 JDK HttpClient 解决了 Android 兼容性问题,MMKV 替代 SharedPreferences 提升了存储性能,NanoHTTPD 用极小的代价实现了局域网配置服务器。
反思与总结
本节要回答的核心问题:ApkClaw 这类”手机端 AI Agent”产品的本质价值是什么,它的局限和发展方向在哪?
写完以上所有技术细节后,我想退一步谈谈对这类产品的整体认知。
ApkClaw 的本质不是”一个能控制手机的脚本”,而是把大语言模型的推理能力延伸到了物理设备上。LLM 本身只能输出文本,但通过 ApkClaw 的工具系统,LLM 的文本输出被翻译成了屏幕上的点击和滑动。这个”翻译层”看似简单(就是一个工具调用映射),但它打通的是数字世界和物理世界的边界。
从工程角度看,ApkClaw 在几个关键点上做了正确的选择:
-
单任务模型避免了多任务并发时设备状态混乱的问题。 -
Home 键重置把状态管理从 Agent 层提升到了编排层。 -
死循环检测和 Token 优化让系统在真实设备上长时间运行成为可能。 -
系统弹窗的优雅降级体现了对自身能力边界的诚实。 -
多渠道接入降低了用户的使用门槛。
当然,局限性也很明显:系统保护窗口无法自动处理、单任务模型限制了并发能力、40 轮迭代上限对特别复杂的任务可能不够、对 LLM 的推理能力有较高依赖(模型不够聪明时工具调用会出错)。
但从”让旧手机变成智能助手”这个定位来看,ApkClaw 找到了一个精准的切入点——不需要最新的旗舰机,不需要 root,不需要复杂的环境配置,一台 Android 9 的备用机加上一个 API Key 就能跑起来。这个低门槛,是它相比 PC 端自动化方案最核心的差异优势。
实用摘要 / 操作清单
从零开始使用 ApkClaw,按以下顺序操作:
-
准备一台 Android 9 及以上版本的手机 -
安装 ApkClaw APK(自行编译或下载预编译版本) -
在首页依次开启:无障碍服务、通知权限、悬浮窗权限、电池白名单、文件访问权限 -
进入设置 > LLM Config,填写 API Key、Base URL、Model Name -
进入设置,选择至少一个消息渠道,填写对应凭证 -
通过已配置的渠道发送第一条测试消息 -
(可选)在设置中开启 LAN Config,通过 PC 浏览器访问 http://<设备IP>:9527进行配置
一页速览
| 维度 | 内容 |
|---|---|
| 产品定位 | AI 驱动的 Android 自动化 Agent,通过自然语言远程操控手机 |
| 最低系统要求 | Android 9(SDK 28) |
| 支持 LLM | OpenAI 兼容接口、Anthropic |
| Agent 框架 | LangChain4j 1.12.2 |
| 支持渠道 | 钉钉、飞书、QQ、Discord、Telegram |
| 工具数量 | 通用工具 15 个 + 手机专属工具 6 个 |
| 最大迭代轮数 | 40 轮 |
| LLM 重试策略 | 3 次指数退避(1s→2s→4s),401/403 不重试 |
| 死循环检测 | 4 轮滑动窗口指纹比对 |
| 局域网配置 | 端口 9527,Web UI + REST API |
| 已知限制 | 系统保护窗口无法自动处理,单任务并发 |
| 开源协议 | Apache License 2.0 |
常见问答
ApkClaw 支持 iOS 吗?
目前不支持,仅支持 Android 9 及以上版本的设备。
必须使用 OpenAI 官方 API 吗?
不一定。ApkClaw 的 OpenAI 客户端兼容所有 OpenAI 接口格式的服务,你可以使用第三方兼容服务商的端点地址。
同时能执行多个任务吗?
不能。ApkClaw 采用单任务模型,同一时间只执行一个任务,新任务需要等待当前任务完成。
系统权限弹窗怎么办?
Agent 检测到系统保护窗口时会自动截图发送给你,并终止当前任务,由你手动处理弹窗后可以继续发送下一条指令。
局域网配置服务器安全吗?
GET 请求返回的敏感信息(API Key、Token)会做脱敏处理,仅显示末尾 4 位字符。建议仅在可信的局域网环境中使用。
不配置消息渠道能直接在手机上用吗?
ApkClaw 的设计是通过消息渠道发送指令,不配置渠道则无法接收和执行任务。
最多能循环多少轮?
默认最大迭代次数为 40 轮。如果 40 轮内 LLM 没有调用 finish 工具,任务会被强制终止。
temperature 为什么默认 0.1?
设备操控需要高度确定性的输出,低 temperature 能确保同样的屏幕状态和指令产生一致的工具调用,避免操作路径随机化。

