1. 项目概述一个运行在手机上的AI“操作员”最近在捣鼓一个挺有意思的东西我把它叫做“手机上的AI操作员”。简单来说就是让一个大语言模型LLM能直接看懂你的手机屏幕并且像真人一样去点击、滑动、输入文字帮你完成各种手机操作。这个项目的核心是把AI的“大脑”LLM的规划与理解能力和手机的“手”Android的无障碍服务Accessibility结合起来形成一个能自主执行任务的智能体。想象一下你不需要再一步步教Siri或者小爱同学“先点这里再点那里”而是直接告诉它“帮我把微信里张三昨天发的那个文件保存到‘工作资料’文件夹”它就能自己打开微信、找到聊天记录、长按文件、选择保存路径。这听起来有点像科幻电影里的场景但通过AndroidClaw这个项目我们已经在Android系统上搭建出了一个可运行的原型。这个项目特别适合两类朋友一类是对AI应用落地感兴趣的移动开发者你可以看到如何将最新的Agent智能体架构与成熟的Android开发技术栈融合另一类是希望自动化重复手机操作的效率追求者它提供了一个高度可定制的基础你可以基于它开发属于自己的手机自动化助手。整个项目的构建采用了一种被称为“vibe coding”的快速迭代模式即产品构思、架构决策、功能实现和调试文档都在与AI的协作中高速完成这本身也是一种非常现代的开发实践。2. 核心架构设计如何让AI“看见”并“操作”手机要让一个AI模型在手机上干活可不是简单地把ChatGPT装进去就行。它需要一套完整的“感知-思考-执行”闭环系统。AndroidClaw的架构就是围绕这个闭环设计的我们可以把它拆解成几个核心层次来理解。2.1 智能体核心层AI的“决策大脑”这是整个系统的指挥中心基于LangChain4j这类框架构建。它的核心是一个规划-执行循环。当用户下达一个指令比如“给妈妈发个生日祝福短信”智能体核心会进行以下工作意图理解与任务分解LLM首先将自然语言指令解析成结构化目标“这是一个涉及‘通讯录’和‘短信’应用的多步骤任务。”制定行动计划LLM规划出具体步骤序列例如步骤1: 打开通讯录应用 - 步骤2: 搜索“妈妈” - 步骤3: 进入联系人详情 - 步骤4: 点击“发信息” - 步骤5: 输入祝福语 - 步骤6: 点击发送。工具调用与执行规划中的每个步骤都会转化为对底层“工具”的调用。例如“打开通讯录应用”会调用phone_open_app工具并传入包名参数。观察与迭代执行完一个步骤后系统会通过UI理解层获取最新的屏幕状态反馈给LLM。LLM判断当前状态是否符合预期并决定下一步行动。比如打开通讯录后发现屏幕是搜索框那么下一步自然就是调用phone_input_node工具进行输入。这个循环中记忆Memory模块至关重要。它记录了完整的对话历史和已执行的操作确保LLM在长对话和多步骤任务中保持上下文连贯不会忘记自己刚才做了什么。实操心得规划循环的稳定性在实际编码中让LLM可靠地输出结构化的规划如JSON格式的动作列表是个挑战。我们采用了严格的输出格式Output Parser约束和重试机制。如果LLM第一次返回的格式无法解析系统会携带错误信息重新提问通常两到三次内就能获得有效响应。此外为每一步操作设置超时和失败回退策略比如点击失败后尝试滑动再点击是保证流程健壮性的关键。2.2 UI理解层AI的“眼睛”这是连接数字世界LLM和物理世界手机屏幕的桥梁。AndroidClaw采用了“无障碍快照优先截图OCR兜底”的双重策略。主渠道无障碍服务快照当启用无障碍服务后Android系统会提供一个当前屏幕的UI元素树AccessibilityNodeInfo。这棵树包含了每个控件的文本、描述、坐标、是否可点击等丰富信息。AndroidClaw通过phone_get_ui_snapshot_compact工具获取并格式化这份信息将其作为LLM“观察”屏幕的主要依据。这种方式速度快、信息结构化程度高LLM可以精确地知道“搜索按钮在屏幕右下角文本是‘搜索’”。备用渠道屏幕截图与OCR并非所有应用都完美支持无障碍服务有些自定义控件的信息可能缺失。此时系统会回退到截取屏幕截图。虽然项目当前版本未集成离线OCR模块但架构预留了接口。我们可以将截图转换成Data URL或Base64编码发送给支持视觉识别的多模态LLM如GPT-4V或者集成一个本地的OCR引擎如Tesseract来提取文字信息。这种设计保证了在不同应用环境下的可用性。例如在标准的系统设置界面使用无障碍快照效率极高而在一些游戏或特殊应用中则可以依靠视觉模型来理解屏幕。2.3 工具层AI的“双手”工具层封装了所有AI可以执行的具体操作。每个工具都是一个独立的函数有明确的输入参数和输出结果。AndroidClaw内置的工具主要分为几类手机操作工具这是核心工具集。phone_click_by_text在屏幕上寻找包含特定文字的控件并点击。这是最常用、最直观的工具。phone_click_coordinates/phone_click_coordinates_from_screenshot通过坐标点击。后者特别有用当LLM通过分析截图识别出某个图标位置时可以使用这个工具。phone_input_node向指定的输入框控件输入文本。phone_swipe,phone_scroll滑动和滚动操作用于浏览长列表或页面。phone_global_back/home/recents模拟物理按键。文件与系统工具file_read,file_write,shell_execute等。这些工具让AI能够读写应用内的文件甚至执行一些ADB Shell命令需权限极大地扩展了能力边界。集成工具如通过模型上下文协议MCP桥接的外部工具或自定义的“技能”工具包。工具的设计遵循单一职责原则并且输出尽可能结构化方便LLM理解执行结果。例如phone_get_ui_snapshot_compact返回的是一个JSON数组清晰列出了所有可交互节点。2.4 通道与持久化层系统的“神经网络”与“记忆体”通道层负责与用户或外部系统通信。目前支持本地App内交互和集成飞书机器人进行远程控制。这意味着你可以在电脑前通过飞书给家里的手机发消息“帮我看看外卖到哪了”手机上的AndroidClaw就能自动打开外卖App并查询进度将结果发回飞书。通道层抽象化了消息来源让核心Agent无需关心指令是从哪里来的。持久化层使用Android Jetpack的Room数据库来保存所有的对话记录、消息和执行日志。这不仅是简单的历史记录更是实现长时记忆和断点续做的基础。即使App进程被杀死重启后也能从上次中断的任务上下文继续执行。WorkManager负责定时健康检查确保后台守护服务Daemon Service持续运行并在网络异常恢复后自动重连消息通道。3. 关键技术实现与踩坑实录有了清晰的架构接下来就是如何用代码将其实现。这里我会分享几个关键模块的实现细节和开发中遇到的典型问题。3.1 无障碍服务的深度集成与权限处理无障碍服务是AndroidClaw的基石。在AndroidManifest.xml中声明服务只是第一步。service android:name.accessibility.PhoneAccessibilityService android:permissionandroid.permission.BIND_ACCESSIBILITY_SERVICE android:exportedtrue intent-filter action android:nameandroid.accessibilityservice.AccessibilityService / /intent-filter meta-data android:nameandroid.accessibilityservice android:resourcexml/accessibility_service_config / /service在accessibility_service_config.xml中需要精细配置服务能力accessibility-service xmlns:androidhttp://schemas.android.com/apk/res/android android:accessibilityEventTypestypeWindowStateChanged|typeWindowContentChanged|typeViewClicked android:accessibilityFeedbackTypefeedbackGeneric android:accessibilityFlagsflagRetrieveInteractiveWindows|flagReportViewIds android:canRetrieveWindowContenttrue android:descriptionstring/accessibility_service_description android:notificationTimeout100 /这里flagReportViewIds非常重要它让我们能获取到控件的唯一资源ID对于精准定位相同文本的不同控件如列表项有巨大帮助。踩坑记录权限引导的“温柔”与“强制”用户主动去系统设置里开启无障碍服务是最大的转化漏斗。我们的策略是首次启动引导应用启动后直接进入一个图文并茂的引导页用箭头和动画示意用户需要去哪里打开开关并提供一个“一键跳转”按钮通过Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)。权限门禁在引导页我们阻止用户跳过。不开启无障碍服务就无法进入应用主界面。这虽然有些“强硬”但对于核心功能依赖此权限的应用来说避免了用户进入后功能全部失效的糟糕体验。动态监听与提示即使用户当时同意了也可能在系统设置中随时关闭。我们在主Activity中监听无障碍服务状态一旦发现被关闭立即通过一个非阻塞的Snackbar或对话框提醒用户并再次提供快捷跳转入口。3.2 基于LangChain4j的Agent实现我们选择LangChain4j而非直接调用OpenAI API是因为它提供了更高层次的抽象比如内置的工具调用Tool Calling格式支持、对话内存管理、以及对不同模型供应商OpenAI, Anthropic, Local LLM等的统一接口。关键代码片段Agent的组装fun createAgent(llm: ChatLanguageModel): ConversationalAgent { // 1. 创建工具集 val tools listOf( PhoneClickTool(), PhoneInputTool(), FileReadTool(), // ... 其他工具 ) // 2. 创建工具执行器 val toolExecutor ToolExecutor(tools) // 3. 构建Agent指定工具、记忆和提示词模板 return ConversationalAgent.builder() .chatLanguageModel(llm) .tools(tools) .memory(MessageWindowChatMemory.withMaxMessages(10)) // 保留最近10轮记忆 .promptTemplate( 你是一个手机操作助手。当前屏幕信息如下 {{screen_snapshot}} 用户的目标是{{user_goal}} 历史操作记录{{chat_history}} 请规划下一步操作。你必须从可用工具中选择一个并以指定JSON格式回复。 .trimIndent()) .outputParser(JsonOutputParser.builderAgentAction().build()) // 强制JSON输出 .build() }执行循环的核心逻辑suspend fun executeGoal(userGoal: String) { var currentState getUiSnapshot() // 获取初始屏幕状态 val agent createAgent(llm) while (!taskIsComplete(currentState, userGoal)) { // 将当前状态和目标喂给Agent val agentResponse agent.execute(currentState, userGoal) // 解析出要调用的工具和参数 val toolToCall agentResponse.toolName val toolInput agentResponse.toolInput // 执行工具 val toolResult toolExecutor.execute(toolToCall, toolInput) // 更新状态等待UI变化后获取新快照 delay(500) // 简单的延迟等待操作响应 currentState getUiSnapshot() // 将工具执行结果作为新的上下文加入到下一轮循环的记忆中 agent.memory.add(AiMessage(工具 ${toolToCall} 执行结果${toolResult})) } }3.3 守护服务与可靠性工程手机上的自动化Agent必须是“打不死的小强”。我们通过ForegroundService实现一个常驻的守护进程即使App退到后台Agent依然能监听远程命令如来自飞书的消息。防止服务被杀死的关键点前台服务通知启动服务时必须显示一个持续的通知。我们将其分类为ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE并给用户清晰的说明如“AI助手正在运行”。WorkManager健康检查定时如每30分钟启动一个Worker检查守护服务是否在运行。如果不在则尝试重新启动它。这个Worker本身会被系统妥善调度保证检查机制存活。BOOT_COMPLETED广播接收器监听开机广播实现开机自启确保设备重启后助手能自动恢复工作。网络状态监听对于依赖远程消息通道如飞书WebSocket的场景需要监听网络连接变化。当网络从无到有自动触发重连逻辑。4. 实战从零构建一个“自动保存图片”技能理论说了这么多我们通过一个具体的例子看看如何利用AndroidClaw的架构开发一个实用的功能自动保存微信群聊中的图片到指定相册。4.1 技能定义与规划用户指令“保存群里今天所有的美食图片到‘我的美食’相册。”终极目标分解这是一个复杂的多应用任务涉及微信、相册或文件管理。高层规划由LLM或我们预设子任务A打开微信进入目标群聊。子任务B在聊天记录中定位并筛选出今天的图片消息。子任务C逐一将图片保存到手机存储。子任务D将保存的图片移动到“我的美食”相册可能需要操作文件管理器或相册App。4.2 工具链组合与实现我们不需要从头造轮子而是组合现有工具并可能编写一两个专用的“技能工具”。phone_open_app打开微信。phone_click_by_text点击群聊名称。phone_swipe/phone_scroll滚动浏览聊天记录。phone_get_ui_snapshot_compact不断获取屏幕快照LLM分析快照中是否存在图片消息通过识别“[图片]”文字或特定的控件类型。phone_long_click_by_text长按图片消息弹出菜单。phone_click_by_text点击弹出菜单中的“保存图片”。这里需要处理不同微信版本的UI差异可能需要多个尝试策略。file_listfile_move图片默认保存在/Pictures/WeChat/目录下使用文件工具将其移动到/Pictures/我的美食/目录。编写一个专用的“保存微信图片”技能工具 这个工具封装了上述从识别到保存的多个步骤对LLM来说它就是一个黑盒save_wechat_image_from_current_screen()。这简化了LLM的规划复杂度。class SaveWeChatImageTool : Tool { override fun name(): String save_wechat_image override fun description(): String 在当前微信聊天界面识别并保存最新的图片消息。 fun execute(): String { // 1. 获取当前快照 val snapshot phoneGetUiSnapshotCompact() // 2. 分析快照寻找图片消息节点简化逻辑找包含‘图片’文本或特定资源ID的节点 val imageNode findImageMessageNode(snapshot) if (imageNode null) return 未在当前屏幕找到图片消息。 // 3. 执行长按、点击保存等系列操作 phoneLongClickNode(imageNode) delay(300) // 尝试点击可能的“保存”按钮文本 val saved phoneClickByText(listOf(保存图片, 存储, Save)) return if (saved) 图片保存成功。 else 保存操作可能失败。 } }4.3 处理不确定性UI差异与操作失败现实中的UI千变万化。我们的工具和Agent必须足够健壮。多文本匹配如phone_click_by_text工具内部应接受一个文本列表按顺序尝试直到成功或全部失败。例如点击“保存”按钮可以传入[保存图片, 存储, Save to device]。坐标回退当通过文本无法定位时如果UI快照中节点有准确的坐标则回退到坐标点击phone_click_node。超时与重试每个操作后等待合理时间如500ms-2s让UI响应。如果操作后预期的新UI状态没有出现例如点击“保存”后没有出现保存成功的提示则触发重试或失败处理流程。状态验证在执行关键操作前后通过快照对比进行验证。例如保存图片后可以检查是否出现了“已保存”的Toast提示如果快照能捕获到。5. 进阶思考与优化方向一个基础可用的Agent只是起点。要让其真正智能、可靠还有很长的路要走。5.1 增强UI理解从文本到视觉语义当前严重依赖无障碍服务的文本信息。下一步是深度融合视觉理解集成离线OCR引入如Tesseract或PaddleOCR的移动端引擎直接从截图提取文字作为无障碍信息的补充彻底解决“控件无文本”的问题。图标与元素识别使用轻量级图像分类模型如MobileNet对屏幕截图进行语义分割识别出“返回箭头”、“搜索图标”、“复选框”、“视频播放按钮”等通用UI元素。这能让LLM的理解不再局限于文字。结构化UI grounding将屏幕信息组织成更丰富的结构化数据例如{ “type”: “list”, “items”: [ {“text”: “张三”, “has_unread”: true}, {“text”: “李四”, “has_unread”: false} ] }。这能极大提升LLM对界面布局和状态的认知精度。5.2 规划与恢复能力的强化目前的规划多是单步或简单链式。复杂任务需要更高级的规划能力分层任务规划让LLM先制定高级别计划打开App - 导航到目标页 - 执行操作再逐层细化。这符合人类的思考方式也更容易纠错。子目标与回滚当某个步骤失败如找不到按钮Agent应能尝试替代方案如使用全局返回重新尝试或者将问题抛给用户。记录完整的执行轨迹便于在失败时进行“回滚”到上一个稳定状态。长期记忆与学习将成功的操作序列如“在XX版本微信中保存图片的点击路径”存入知识库。下次遇到相同应用和场景时可以优先尝试历史成功路径提高效率。5.3 生态扩展与部署考量更多连接通道除了飞书可以集成微信机器人、Telegram Bot、甚至邮件让触发指令的方式更多元。技能市场/插件化将SaveWeChatImageTool这类技能封装成插件允许用户动态安装、卸载。社区可以贡献各种针对特定App淘宝、抖音、钉钉的自动化技能包。隐私与安全所有手机操作都在设备本地完成敏感数据屏幕截图、聊天记录无需上传云端。这是本地AI Agent的核心优势。在代码实现上要确保权限的透明化告知用户每个工具所需权限及其用途。性能与耗电优化持续运行的无障碍服务和后台守护服务是耗电大户。需要精细控制快照采集频率、LLM调用时机是否每次都需要调用大模型能否用小模型或规则判断并在设备空闲时进入低功耗模式。开发AndroidClaw这类项目最大的感触是“边界感”很重要。我们要清晰地界定哪些应该交给LLM这个“大脑”如意图理解、复杂规划哪些应该由确定性的代码“小脑”来完成如具体的控件查找算法、失败重试逻辑。让两者各司其职才能构建出既灵活又稳定的智能体。这个过程就像在教一个非常聪明但缺乏常识的孩子如何使用手机你需要给它明确的规则、反馈和足够多的示例它才能越来越可靠地帮你完成任务。