AI图鉴

AI图鉴首页 › AI 资讯 › 手机端 Agent 怎么动起来:Kotlin 与 Python 之间的桥

手机端 Agent 怎么动起来:Kotlin 与 Python 之间的桥

来源:掘金 · 发布时间:10-12 · 由 AI图鉴每小时自动聚合

手机端 Agent 能看屏幕、能点按钮,靠的不是魔法,是 Python 内核指挥安卓原生操作。上篇把 Python 打进了 APK,但这篇才讲清 Agent 真正"动起来"。

上篇把 Python 打进了 APK,但有个问题没回答:Agent 要"动"起来,它得能看屏幕、能点按钮。

可 Python 又不是安卓亲生的——它自己不会截图、不会点控件。谁来看屏?谁来点击?在我这个项目里,答案是:Python 内核发令,Kotlin 壳去执行。中间那座桥,就是这篇要讲的。

没有这座桥,Agent 只是个会说话、不会动手的大脑。

先放一张总览,免得后面绕晕——整座桥就这么几块:

虚线是"注入/回传",粗实线是"Python 真的下指令"。看懂这张,后面两段就是把它拆开讲。

第一向:Kotlin 把门先开开

App 一启动,Kotlin 这边先把 Python 解释器拉起来,再把一个"派发器"塞进 Python 的世界:

// KernelHostService.attachKernel()

if (!Python.isStarted()) {

Python.start(AndroidPlatform(this))

}

val py = Python.getInstance()

val bridgeMod = py.getModule("hachimi_kernel.bridge")

// 把安卓侧的派发器注入 Python

bridgeMod.callAttr("register_bridge", PrimitiveDispatcher())

callAttr 就是 Chaquopy 给的"跨语言函数调用"——Kotlin 这边写 模块.callAttr("函数名", 参数),对面就是 Python 里一个同名函数。

这一步干的事很简单:把能操作真机的对象,交给 Python 握着。后面 Python 想干啥,调这个对象就行。

第二向:Python 反过来指挥真机(最反直觉)

一般人会以为,Python 要截图,得 import android 然后调安卓 API。但在 Chaquopy 里,Python 不import 安卓任何东西——它只调 Kotlin 刚才塞进来的那个对象:

# bridge.py:Kotlin 注入的派发器,存在模块全局

_bridge = None

def register_bridge(dispatcher):

global _bridge

_bridge = dispatcher

# Agent 要观察屏幕时,就这么一行

raw = _bridge.call("observe", json.dumps({"compact": True}, ensure_ascii=False))

result = json.loads(raw)

observe 这个名字会被传回 Kotlin,那边有个 when 把它派发到真正的安卓代码:

// PrimitiveDispatcher.call()

fun call(name: String, argsJson: String): String {

val svc = HachimiAccessibilityService.instance ?: return error("a11y not bound")

val args = JSONObject(argsJson)

return when (name) {

"observe" -> svc.observe(args.optBoolean("compact", false))

"tap_by_id" -> svc.tapById(args.optString("view_id"))

"tap_xy" -> svc.tapXY(args.optDouble("x"), args.optDouble("y"))

"type_text" -> svc.typeText(args.optString("text"))

"launch_app" -> svc.launchApp(args.optString("package"))

// ……还有 gesture / screenshot / press

}.toString()

}

看到没?Agent 的"看一眼""点一下",最后全落到这段 when 里。它就是 Agent 的手和眼睛在具体手机上的落点。

这反直觉的一点很重要:Python 不直接碰安卓,它只发指令;真活儿是 Kotlin 干的。这样内核可以保持纯净,不依赖任何安卓细节,换台设备也好迁移。

数据怎么传:一个真实的坑

两层语言之间传数据,最顺手的方式是用 JSON 字符串。原因是个坑:

Chaquopy 返回的 PyObject,你直接 .toString() 拿到的是 Python 的 repr(带单引号那种),Kotlin 这边的 JSONObject 解析不了。所以两侧约定:进出都先 json.dumps / json.loads 包一层,字符串过去了再各自解开。

raw = _bridge.call("observe", json.dumps(args, ensure_ascii=False))

return json.loads(raw) # 解开再用

这个坑不大,但第一次遇到会莫名其妙——明明数据是对的,Kotlin 那边就是解析失败。记一笔:跨语言传复杂数据,显式 JSON 字符串最稳。

用一张图对比两种传法就清楚了:

其实这个 JSON 协议不只跨语言用——Agent 的工具参数和返回、喂给大模型的消息、本地轨迹落盘,全是用同一套 dict,边界完全同构。一座桥的协议,顺手把整条链路也统一了。

工具面:Agent 的手和眼到底有哪些

桥通了,但 Python 能调的"原语"具体是哪些?它们写在 bridge.py 的 TOOL_SPECS 里,每个原语注册成一个工具,背后就是一行 _bridge.call:

# bridge.py:把八原语注册成 Agent 可用的工具

for spec in TOOL_SPECS:

tools[spec["name"]] = {"description": spec["description"],

"parameters": spec.get("parameters", {"type": "object"}),

"call": _call(spec["name"])} # _call 闭包里就是 _bridge.call(name, ...)

列一下 Agent 真正能做的动作,基本就是"看"和"动"两类:

- 看:observe(读当前界面 UI 树)、screenshot / grab_frame(拿像素图,视觉兜底用)

- 动:tap_by_id(按控件 id 点)、tap_xy(按坐标点)、type_text(输字)、gesture(手势)、launch_app(开应用)、press(按键,如返回)

- 辅助:wait(等界面加载)、look(显式问一眼屏幕)

还有两个不在 Kotlin 那边、纯 Python 实现的:load_skill / search_skill——让 Agent 调出以前沉淀过的操作套路。这俩是下一篇的主角,先埋个伏笔。

桥在 Agent 主循环里的实际位置

光说"能调"还不够直观。我把一次真实任务的完整走查摆出来,你看桥站在哪:

1. 你在 app 里点"开始",Kotlin 收到 HTTP 的 /task 请求,调 bridgeMod.callAttr("start_task", body);

2. Python 的 start_task 在后台线程拉起 MiniLoop(Agent 主循环),把目标和大模型配置传进去;

3. MiniLoop 每轮让大模型决策:reply = brain.chat(messages, tool_specs),模型返回"下一步调 observe";

4. 循环里 self._dispatch(name, args, i) 执行这个工具 → 也就是 tools["observe"]["call"](args) → 背后 _bridge.call("observe", ...);

5. Kotlin 真去读无障碍树,把结果 JSON 传回 Python;

6. Python 把这个"看屏幕"的结果当工具消息喂回模型,模型看着屏幕决定"点这个按钮" → 又走一遍 _bridge.call("tap_by_id", ...);

7. Kotlin 真去点。模型确认目标达成 → 调 task_done,循环结束。

看一眼、点一下、再决定——Agent 在手机上跑起来的每一帧,桥都在中间传话两次(出一次指令、回一次结果)。上篇把 Python 塞进去只是地基,这篇才是 Agent 长出手脚、还接上了大脑的那一步。

把上面 1~7 步走查画成一张循环图,桥站在哪一目了然:

几个真实护栏:Agent 在手机上得"安全动"

能跑还不够,得防止它瞎点。这些都在 MiniLoop 里,是真实代码不是设想:

- 卡住检测:连续 4 次调完全相同的 (工具, 参数),直接判 stuck 终止——避免它在一个点不下来的按钮上死循环。

- 换方案提醒(RepeatGuard):同一个工具连续失败 3 次,往上下文里塞一句"请更换执行方案",逼模型别再重复同一个失败调用。

- task_done 强制校验:启用了完成判据时,模型说"我做完了"会被 verify 拦下来复核;没通过就打回让它继续,防止幻觉式宣布完成。

- 预算:默认最多 24 步、墙钟 600 秒,超限按"未完成"终止,不伪装成功。

这几道护栏加在一起,Agent 才是一个"能动、但乱动会被叫停"的东西,而不是一个你不敢放手让它跑的脚本。

为什么这么设计桥接(几点取舍)

顺手说下这么搭桥的理由,都是踩过或权衡过的:

- 注入式,而不是 Python 直接 import 安卓:内核保持纯净、能在电脑上单测,不绑任何安卓细节。代价是 Python 侧的 API 是"被喂进来的",得约定清楚。

- 八原语单一事实来源:PrimitiveDispatcher 同时被调试桥(电脑直连发指令测)和 Python 桥共用,保证两边说的"原语"是同一套,不会各写各的。

- JSON 协议贯穿三层:Python↔Kotlin、模型↔工具、轨迹落盘,全用同一套 dict。边界同构,出问题时一眼能追到是哪一层。

桥的另一头:手机里的 Agent 也能被远程指挥

桥接层还顺带解决了一个更实用的需求——Agent 不只能在你点"开始"时跑,也能被外部驱动。

KernelHostService 起了一个本地回环 HTTP(8071 端口):/task 让电脑或脚本 POST 一段目标就触发手机 Agent 跑任务,/health 查状态,/stop /pause /resume 控制它。也就是说,你人在电脑前,发一条指令,手机自己就开始操作自己——桥把"手机 Agent"变成了一个可被远程调用的能力,而不只是个 app 里的按钮。

收尾

把上面几块拼起来:Kotlin 起解释器、把派发器注入 Python、Python 用 JSON 指挥真机、MiniLoop 把这变成 observe→思考→行动的循环、再加几道护栏和一个远程控制面——Agent 在手机上"动起来"这件事,底座就是这座桥。

上篇是地基,这篇是手脚。到这,一个手机端 Agent 从打包进 APK、到两层语言打通、到真能在手机上动起来的完整链路,就讲完了。

仓库地址:github.com/momo-null/hachifish

本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。

相关 AI 工具

Floatboat

专为”一人公司”打造的 AI Agent 原生工作空间

觅游

美团推出的 AI 原生 Agent 社区

蛙蛙写作

AI小说和内容创作工具

讯飞绘文

AI批量原创,多平台矩阵号管理

相关免费羊毛

Arize AX

每月 2.5 万条 span 免费

Cerebras Inference

$5 API 免费试用额度

Future AGI

免费档含每月 10 万次网关请求

GNAME

eu.cc 二级域名:免费注册 + 免费续费

同源资讯

你让AI干的活,它可能只干了十分之一——而你根本看不出来

掘金 · 10-12

浅析模型量化的数学原理 — 本文从数学角度完整拆解模型量化(Quantization):它如何把 FP32 浮点

掘金 · 10-12

[Agent Memory / 强化学习] MemPO源码学习笔记 --- (7)--- 混合机制

掘金 · 10-12

我用AI做了一款「三国吃鸡」,开局孔明灯,落地青龙刀 !

掘金 · 10-12

LLM 驱动的自动化测试系列(10):自愈定位——当启发式打分撞上语义理解的天花板

掘金 · 10-12

GitHub 11万star:AI拆软件神器保姆级实操教程

掘金 · 10-12

阅读原文(juejin.cn)

标题、摘要与正文均来自该信源的公开页面,版权归原作者所有;本站自动聚合与翻译,仅作出处跳转。

← 返回 AI 资讯列表AI工具图鉴首页

返回 AI图鉴首页 · 提交工具 · 合作与友链 · 关于本站 · 528 款 AI 工具分类导航 · 每日 AI 热门项目与动态