跳到正文
原文
Google AI:DEV 作者专属(RSS)· Taqin·· 2 小时前精选AI 评分68

AndroPI:在 Android 手机上本地运行 AI 编码智能体,开源免后端

I put an AI coding agent inside an Android app. Here's everything that fought back.

AI 导读

开发者 Taqin 发布 AndroPI,一个在手机本地运行的 AI 编码智能体,开源 MIT、无后端,支持 Claude、GPT、Gemini、OpenRouter 等模型,密钥留在手机上。

推荐理由

作者亲历把 AI 编码智能体塞进 Android 的全过程,绕过二进制执行限制、Play Store 审核等坑的解法可直接迁移。

正文 · AI 翻译

我想用手机修个 bug。不是那种“用手机审 PR”。而是真正打开一个项目,让 AI agent 写代码、运行、看结果并发布。

我找到的所有工具都是跟别人服务器聊天的聊天应用。所以我做了我想要的东西:AndroPI,一个在手机本身上运行的 AI 编程 agent。开源,MIT,无后端。

现在它能用了。但一路走来,全程都在跟 Android 搏斗。

AndroPI screens: home, live code, live preview, Linux terminal, deploy

计划很简单

拿 pi,一个用 TypeScript 写的开源编程 agent。在前面套一个 Flutter UI。搞定。

问题在于:pi 需要 Node.js。还需要 git。还需要 shell、ripgrep、ssh、curl。在手机上。

Three layers in one APK: Flutter UI, Kotlin shell, Node agent host

Android 不会运行你的二进制文件

第一堵墙。我把一个 Node 二进制文件丢进应用的数据文件夹,然后尝试执行它。

权限被拒绝。

从 Android 10 开始,你无法从应用的可写存储中执行文件。应用唯一能运行原生代码的地方是它的原生库目录。而打包器只有在文件命名为 lib*.so 时才会把它们放进去。

所以我就这么做了。Node.js 以 libnode.so 的形式打包进 APK。Git 是 libgit.so。它们不是库。Android 不会检查。

# package -> {binary inside the Termux package: name inside the APK}
BINARIES = {
    "nodejs-lts": {"bin/node": "libnode.so"},
    "ripgrep": {"bin/rg": "librg.so"},
    "git": {"bin/git": "libgit.so"},
    "openssh": {"bin/ssh": "libssh_cli.so"},
    "proot": {"bin/proot": "libproot.so"},
}

这些二进制文件来自 Termux 软件包仓库。一个脚本下载 .deb 文件,提取出二进制文件,并用 lief 重写它们的库名,因为像 libicuuc.so.78 这样的真实依赖也必须变成 libicuuc_78.so,否则打包器会丢弃它。

我最喜欢的部分:容器启动器是一个 shell 脚本。它以 libbox.so 的形式打包。Flutter 构建每次都会抱怨它“无法剥离调试符号”。是啊。它就是个 shell 脚本。

真正的 Linux,无需 root

一个不能 apt install 的 agent 只能算半个 agent。它会想要 Python,或者更新的 Node,或者某个我没打包的构建工具。

所以 AndroPI 运行一个带有 proot 的 Debian(或 Alpine)用户空间。无需 root。应用直接从 Docker Hub 的 registry API 拉取官方镜像,并自行解包各层。agent 的 shell 就在其中运行。

在手机上,从聊天中 apt install python3。那感觉真不错。

没人会警告你的事

我的 ISP 劫持了 DNS。 agent 的网页搜索一直失败。原来运营商在重定向对它不喜欢的网站的查询。我在 Node 宿主里加入了 DNS over HTTPS。现在无论网络怎么想,agent 都能搜索。

模型会发送垃圾参数。 有个模型一直用 offset: "None" 调用读取工具。一个 Python 式的写法,作为字符串,传进一个数字字段。验证失败,运行中断。我加了一个小层,在检查之前清理工具参数。

前台服务吃掉了我的通知。 应用在运行时显示“Agent 正在工作”,完成时显示“pi 已完成”。完成通知从未出现。两者都用了通知 ID 1。当服务停止时,Android 移除了 ID 1。我花了相当尴尬的一段时间才看出来,而且我之所以发现,是因为我当时正在录制演示视频。

73 MB。哎哟。

第一个发布版 APK 是 73 MB。我以为 Flutter 是那个臃肿的部分。并不是。

What's inside the APK: ICU data and Node.js are the biggest parts

应用大约 80% 是运行时。仅 Node 压缩后就有 15 MB。Node 用于 Intl 所需的 ICU 数据又是 15 MB。

我最初的想法是把 agent 打包成一个压缩后的文件。那破坏了登录。pi 通过它在运行时构建的 import() 路径加载其 OAuth 流程,而打包器无法跟踪这些路径。

所以我保留了包布局,转而做了些无聊的事:

  • 删除了 pi 预构建的 CLI 包(SDK 入口从不加载的 8 MB)
  • 从 node_modules 中去掉了文档、TypeScript 源码和 README 图片
  • 就地压缩了每个 JS 文件

Agent 包体:从 16.6 MB 降到 8 MB。APK:从 73 降到 63。然后我加入了 armv7 和 x86_64 构建,因为 Termux 对两者都有软件包。

然后 Play 商店说不行

两次。

第一次:“所有文件访问权限”。 我加了 MANAGE_EXTERNAL_STORAGE,这样你就能在手机上的任意文件夹里打开项目。Google 只允许文件管理器和少数其他类别拥有该权限。编程 agent 不在名单上。

合理。工作区本来就位于应用存储中,所以只有一个功能需要它。我把构建拆成了两个变体:

<!-- android/app/src/play/AndroidManifest.xml -->
<uses-permission
    android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    tools:node="remove"/>

Play 构建去掉了该权限。文件通过系统选择器进入,通过 MediaStore 或分享面板出去。GitHub 构建保留完整版本。

第二次:前台服务。 Android 14 要求每个前台服务都有一个类型,而“运行 AI agent”不在其中。所以它是 specialUse,这意味着需要一份书面说明,以及一段展示应用在后台时通知的视频。就是在那段视频里,我发现了通知的 bug。

它今天能做什么

  • 与 agent 聊天,看代码流式输出
  • 实时 HTML 预览,页面还在编写时就随之更新
  • 带 apt、pip、npm 的 Debian 容器,外加多标签终端
  • 用设备码登录 GitHub,克隆、提交、推送、PR
  • 部署到 GitHub Pages、Vercel 或通过 SSH 部署到你自己的服务器
  • 审批模式、计划模式、用于撤销一次运行的检查点
  • MCP 服务器、技能、后台任务

模型由你自带:Claude、GPT、Gemini、OpenRouter。你的密钥留在你的手机上。中间没有我的服务器,因为根本没有我的服务器。

试试看

如果你曾在 APK 里塞进过什么奇怪的东西,我想听听你是怎么让它通过审核的。如果你发现了 bug,请开一个 issue。那多半是我造成的。

来源:Google AI:DEV 作者专属(RSS) · dev.to