1. 项目概述:在手机上构建一个操作系统级的智能体
最近几年,本地运行的大语言模型发展得飞快。像 Ollama 这样的框架,让在个人电脑或服务器上加载和运行模型变得轻而易举。但去年,我接触到了一个名为 Cactus 的项目。它是一个专门为在任何智能手机(包括低端设备)上运行 LLM、视觉模型和语音模型而构建的推理引擎。你可以把它理解为移动端的 Ollama。
Cactus 为 Flutter、Kotlin 和 React Native 提供了 SDK,让开发者能够构建具备智能体工具调用、RAG 等功能的复杂工作流。但这些 SDK 是应用层面的,它们存在于你的应用程序内部。当时,还没有一个将手机本身视为计算平台的方案——没有系统级的编排层,没有可供其他应用接入的、持久化的智能体运行时环境。
这正是我们团队计划填补的空白。我们正在 Cactus 之上构建一个智能体系统,它将以特权 Android 系统服务的形式运行。所有计算都留在设备上——没有云端路由,没有回家的 API 调用。手机不再是连接某个远程服务器的接口,它自己就是服务器。我们做了一些研究,明确了需求,然后开始了构建。如果你也想参与贡献,接下来的内容将涵盖让这个系统跑起来所需的一切。
2. 环境准备与系统构建
要构建 JarvisOS,你需要对操作系统有真正的控制权。这意味着需要一个自定义的 Android 发行版。我们选择了 LineageOS——一个免费、开源的 Android 发行版,它扩展了来自超过 20 家制造商的移动设备的功能和寿命,并赋予我们修改系统服务本身的能力。我们最初在 LineageOS 23 上尝试,遇到了一些问题,随后回退到 LineageOS 21 进行活跃开发。LineageOS 22.1 基于 Android 15 QPR1,这是我们未来进行实际设备部署时的目标版本。
2.1 硬件与软件需求
在开始之前,你需要一台合适的开发机器。
- 操作系统:Linux 上的 Ubuntu 22.04 或更新版本。我们在 Windows 上使用了 WSL2。
- 内存:最低 16GB。但对于 LineageOS 21 及更高版本,推荐 64GB(内存越少,构建时间越长,你的机器会不断提醒你这一点)。
- 存储空间:LineageOS 21 及以上版本需要 400GB 的可用空间。
- 测试设备:一部 Nothing Phone 2(代号 Pong)或一部 Google Pixel 6。这两款设备都配备了用于设备端推理的 NPU。
一旦你的机器准备就绪,第一步就是安装repo工具。这是 Google 用于将 Android 数百个独立的 Git 仓库作为一个协调的源代码树进行管理的工具。
2.2 初始化 LineageOS 源代码
首先,创建目录并初始化 LineageOS 的源代码仓库。我们选择lineage-21.0分支作为开发基础。
mkdir -p ~/android/lineage && cd ~/android/lineage repo init -u https://github.com/LineageOS/android.git -b lineage-21.0 --git-lfs --no-clone-bundle注意:
--git-lfs参数用于启用 Git Large File Storage,这对于处理 Android 源码中的大文件(如预编译的二进制文件)很重要。--no-clone-bundle可以避免克隆时使用 bundle 文件,在某些网络环境下可能更稳定。
2.3 添加 JarvisOS 自定义清单
LineageOS 使用清单文件来定义所有需要同步的代码仓库。我们需要创建一个本地清单,将 JarvisOS 相关的自定义仓库添加进去。
mkdir -p ~/android/lineage/.repo/local_manifests cat > ~/android/lineage/.repo/local_manifests/jarvos.xml << 'EOF' <?xml version="1.0" encoding="UTF-8"?> <manifest> <remote name="JarvisOs" fetch="ssh://git@github.com/" revision="main" /> <project name="ocansey11/android_frameworks_base" path="frameworks/base" remote="JarvisOs" revision="lineage-21.0" /> <project name="ocansey11/vendor_jarvisos" path="vendor/jarvisos" remote="JarvisOs" revision="main" /> <project name="ocansey11/cactus" path="vendor/cactus" remote="JarvisOs" revision="main" /> </manifest> EOF这个清单文件告诉repo工具去哪里找项目的每个部分,以及把它们放在你构建树的哪个位置。每个<project>条目包含三个关键信息:要克隆的 GitHub 仓库(name)、本地存放路径(path)和使用的分支(revision)。顶部的<remote>块只是定义了 GitHub 作为源,这样你就不必为每个项目重复 URL 了。它本质上就是一张地图。repo读取它,拉取所有代码,你的源码树就组装好了。
2.4 同步源代码
执行同步命令,这将根据default.xml和我们的本地清单jarvos.xml拉取所有仓库的代码。这个过程会下载数十GB的数据,耗时取决于你的网络速度。
repo sync同步完成后,你的目录结构应该大致如下:
~/android/lineage/ ← LineageOS 构建根目录 │ ├── frameworks/ │ └── base/ ← ocansey11/android_frameworks_base │ └── services/core/java/com/android/server/rag/ <- Jarvis 作为系统服务 │ ├── RagService.java │ ├── core/ │ ├── indexing/ │ ├── inference/ │ ├── model/ │ ├── search/ │ └── tools/ │ └── vendor/ ├── jarvisos/ ← ocansey11/vendor_jarvisos │ ├── sepolicy/ │ ├── prebuilts/objectbox/ │ └── (documentation) │ └── cactus/ ← ocansey11/cactus这个结构清晰地展示了 JarvisOS 如何集成到 Android 系统中:核心服务代码位于frameworks/base(修改了系统框架),而供应商特定的配置、策略和预编译库则放在vendor/jarvisos和vendor/cactus目录下。这是一种标准的 AOSP 项目组织方式,确保了与主线代码的清晰分离和可维护性。
3. 系统服务架构与设计思路
我们已经探索过在应用层使用 Cactus。目前,Jarvis 将作为一个后台服务运行。你可以把它想象成system_server的扩展——system_server是运行ActivityManager、WindowManager和所有其他特权 Android 服务的同一个进程。我们的RagService在系统启动时与这些服务一同启动。
通信遵循标准的 Android 模式:应用程序通过 Binder IPC 使用 AIDL 接口,与我们的公共 APIRagManager对话,后者跨进程边界,最终进入RagService,由它来协调一切——RAG 流水线、模型选择、工具分发。
3.1 性能权衡与架构选择
这是性能最优的方案吗?坦率地说,不是。最快的设备端 AI 流水线应该是专门构建的本地代码:定制的 C++ 推理引擎,配合手动调优的 ARM 内核,直接与硬件加速器对话,零 JVM 开销,每次调用没有 Binder 序列化的成本。这是高通或谷歌的团队会交付的方案。
而我们拥有的是一个通过 JNI 包装器调用 Cactus 的 Java 系统服务,每次查询跨越边界时 Binder IPC 都会增加延迟。每个工具分发都会增加一个带有 10 秒超时窗口的广播往返。
选择这个架构的原因很实际:我个人在 Android 系统编程方面的经验有限,我依靠像 Roger Ye 的《Android Systems Programming》和 Joshua Bloch 的《Effective Java》这样的书籍,以及 Claude Code 的帮助来设计规范和编写各种流水线代码。更重要的是,Cactus 已经解决了最难的部分——ARM SIMD 内核、KV 缓存量化。在其之上构建,意味着我们可以专注于那个真正不存在的东西:智能体编排层。
系统服务的方法为我们提供了进程隔离、内核强制的权限,以及一个在应用重启后依然存活的持久化运行时环境——这些是你无法从一个应用级 SDK 中获得的。我们想从一个地方开始,而这是一个坚实的起点。在积累足够经验后,我们才能弄清楚如何从头开始正确地实现它。你可以把这看作是我们验证其可行性的一个实验。
3.2 Jarvis 系统服务架构详解
下面是我们正在添加的新系统后台服务的架构,它扩展了系统服务器以处理 RAG、持久化内存等,为智能体行为奠定基础。
frameworks/base/services/core/java/com/android/server/rag/ │ ├── RagService.java ← 主协调器,入口点 ├── IRagService.aidl ← Binder 契约 ├── Android.bp ← 构建文件 │ ├── core/ │ ├── RagManager.java ← 公共 API 包装器 │ ├── RagException.java ← 异常定义 │ ├── JarvisStore.java ← ObjectBox 存储初始化 │ ├── ModelRegistry.java ← 管理模型和索引句柄对 │ └── IndexQueue.java ← 队列化索引任务 │ ├── indexing/ │ ├── RagIndexWorker.java ← 处理索引队列 │ ├── TextExtractor.java ← 处理多种文件类型 │ ├── ChunkingStrategy.java← 将文档分割成块 │ └── JarvisFileObserver.java ← 监视文件系统变化 │ ├── search/ │ └── MetadataSearch.java ← 基于元数据的搜索 │ ├── model/ ← ObjectBox 数据实体 │ ├── SourceFile.java │ ├── DocumentChunk.java │ ├── Folder.java │ ├── Chunk.java │ ├── Conversation.java │ ├── Message.java │ ├── UserContext.java │ ├── AccessLog.java │ └── TaskMemory.java │ ├── inference/ │ └── CactusWrapper.java ← 访问 Cactus 的唯一入口点 │ └── tools/ ← 管理来自应用的工具 ├── AppRecord.java ← 每个已安装应用一个条目 ├── ToolRecord.java ← 每个工具一个条目 ├── ToolScannerService.java ← 安装时扫描 APK └── ToolDispatcher.java ← 解析并触发工具这个架构的核心是RagService,它作为系统服务常驻内存。RagManager是面向应用开发者的门面,隐藏了 Binder IPC 的复杂性。ModelRegistry负责管理不同任务所需的模型和索引句柄对,例如,一个轻量模型用于快速语义搜索,一个更大模型用于复杂推理。IndexQueue和RagIndexWorker实现了异步文档处理流水线,避免阻塞主线程。JarvisFileObserver监听文件系统事件,实现文档的实时增量索引。
CactusWrapper.java是整个系统与 Cactus 推理引擎交互的单一桥梁,这种设计遵循了依赖倒置原则,将核心 AI 能力与具体的实现解耦,为未来替换或升级推理引擎留下了清晰的接口。
4. 供应商层:支撑系统的基石
vendor/jarvisos包含了支持系统服务但又不属于服务本身的一切:安全策略和数据库层。
4.1 SELinux 策略:让 Android 信任我们
Android 默认不信任新的系统服务。SELinux 是内置于每个 Android 设备的安全层,它强制执行严格的规则,规定每个进程允许做什么。没有正确的策略,我们的服务将被阻止读取文件、发送广播或与其他服务通信,无论代码怎么写。
vendor/jarvisos/sepolicy/中的 sepolicy 规则就是在操作系统层面授予 JarvisOS 所需权限的关键。例如,我们需要为rag_service定义域(domain),并允许它访问模型文件所在目录、与system_server通信、以及执行必要的网络操作(如果未来需要点对点联邦学习)。编写 SELinux 策略是一个精细活,一个错误的规则可能导致服务无法启动或被严重限制。我们的策略文件通常包括:
file_contexts: 为 JarvisOS 相关的文件打上安全标签。rag_service.te: 定义rag_service域的类型强制规则。service_contexts: 将 Binder 服务名称与安全上下文关联。
实操心得:调试 SELinux 拒绝(avc: denied)日志是集成新系统服务时最常见的步骤之一。务必使用
adb logcat | grep avc来捕获这些信息,并据此逐步添加允许规则。切忌一开始就使用permissive模式,那会掩盖潜在的安全问题。
4.2 ObjectBox:数据库层与未来方向
编译好的 ObjectBox 库位于vendor/jarvisos/prebuilts/objectbox/,在构建时被引入。在运行时,它们为model/文件夹中的所有实体(如SourceFile、DocumentChunk、Conversation、TaskMemory、AccessLog)提供动力。ObjectBox 是一个嵌入式数据库,能在同一个存储中处理结构化查询和向量搜索。
我们的索引器存储元数据和指针,从不存储内容或嵌入向量本身。SourceFile保存文件路径、哈希值和 MIME 类型。DocumentChunk存储一个简短的摘要和一个cactusIndexId,这是一个指向 Cactus 二进制索引的整数指针。实际的嵌入向量将存在于 Cactus 中。
当RagIndexWorker索引一个文件时,它调用CactusWrapper.embed()来生成向量,然后调用CactusWrapper.indexAdd()来存储它。ObjectBox 只保存指向那里的 ID。这种分离也为我们的未来方向奠定了基础。
传统的 RAG 检索看起来相似的文本块,这很有用,但它无法连接存在于不同文档中的事实。此外,对于移动设备,用户可能以不同的方式引用任务,仅靠语义相似性可能不够。GraphRAG 在索引器(ObjectBox)和检索器(Cactus)之间增加了一个知识图谱,因此它返回的不再是孤立的文本块,而是实体及其之间的关系。ObjectBox 已经为存储这个图谱层做好了准备。我们还没有构建它,但架构不需要改变就能实现它。我们可以在model/中增加Entity和Relationship实体,并在索引阶段通过 LLM 提取实体和关系来构建图谱,检索时先查询图谱再获取相关文本块。
5. Cactus 集成:单一入口的推理引擎
vendor/cactus是我们分叉的 Cactus 推理引擎。在运行时,它为所有需要模型的功能提供动力——嵌入向量生成、向量搜索和 LLM 补全。整个代码库通过一个文件与它对话:CactusWrapper.java(位于android/framework/base目录)。
这个包装器只暴露了我们实际使用的五个基本原语:
init: 加载模型并返回一个句柄。embed: 将文本转换为浮点数数组(向量)。indexInit,indexAdd,indexQuery: 管理向量索引。complete: 运行推理,可选择将 RAG 上下文和工具定义作为系统消息注入。
所有调用都通过 JNI——Java 通过本地方法调用 Cactus 的 C++ 引擎。它在设计上是阻塞的。不过RagIndexWorker和RagService已经在后台线程上运行,所以这没问题。
这个包装器有意保持轻薄。Cactus 处理困难的部分——ARM 内核、量化、注意力机制。我们处理它之上的编排。如果我们将来需要换掉 Cactus 或者向上游贡献更改,只需要改动这一个文件夹。
注意事项:JNI 调用是有开销的。频繁地在 Java 和 C++ 之间穿梭进行小操作(如单个嵌入)可能会成为瓶颈。我们的设计将批量操作(如索引多个文档块)在 Java 层组织好,然后通过一次 JNI 调用传递给 Cactus 进行高效处理,这比每个块单独调用一次
embed和indexAdd要高效得多。
6. 小型模型的演进与 JarvisOS 的应对
小型语言模型领域的发展速度是惊人的快。就在本周,Gemma 4 发布了——有四种尺寸,其中 E2B 和 E4B 是专门为设备端使用而构建的。它比前代快达 4 倍,电池消耗减少高达 60%。整个系列超越了简单的聊天,能够处理复杂的逻辑和智能体工作流,并内置了原生函数调用功能。
这对 JarvisOS 意义重大。我们目前通过ModelRegistry管理多个模型和索引句柄对,因为不同的任务需要不同的模型。一个能够原生处理推理、嵌入和工具调用的强大单一模型,开始改变这个等式。此外,Gemma 还是多模态的。
但这正是为什么缓慢而审慎地推进很重要。这个领域每几个月就会发生变化。如果我们在六个月前将架构与某个特定模型紧密耦合,现在就得重写它。相反,CactusWrapper是一个清晰的边界,ModelRegistry是灵活的,换入 Gemma 4 或未来任何新模型,只是一个简单的配置更改。
6.1 值得关注的研究方向
在手机上运行模型与在云端运行是不同的问题。生成的每个标记都需要计算成本。模型已经看过的每个标记都需要内存成本。在服务器上,你可以用硬件解决这两个问题。在手机上,你不行。我们一直在关注的两个研究方向直接解决了这个问题。
CALM:连续自回归语言模型,是从离散的下一个标记预测到连续的下一个向量预测的范式转变。CALM 使用一个高保真自编码器将 K 个标记块压缩成一个连续的向量,这使得团队能够将语言建模为连续向量的序列,而不是离散的标记。随着研究空间的增长,这项技术对于进一步减少模型所需的自动回归步骤数量将是必要的,使得设备端推理更快、更实用,而无需更大的硬件。
TurboQuant:量化将 LLM 压缩,使其可以在有限 RAM 的设备上运行,但这需要权衡。将权重从 16 位压缩到 4 位意味着对数值进行舍入,这些微小的误差在成千上万次操作中累积,会悄悄降低准确性。谷歌的 TurboQuant 通过将 KV 缓存专门压缩到 3 位来不同地处理这个问题,无需训练或微调,且没有可测量的精度损失。
强化学习:强化学习是让系统通过经验而非显式编程来改进的方法。对于 JarvisOS,实际应用是工具选择。目前ToolDispatcher基于语义相似性选择工具。随着时间的推移,它应该学习哪些工具实际上对哪些查询产生了好的结果。RL 提供了这种反馈循环,而不需要标记的训练数据,只需要来自成功和失败经验的信号。
联邦学习:联邦学习是一种无需集中数据即可改进模型的技术。每个设备在其自身的交互上进行本地训练,并且只共享模型更新,从不共享原始数据。通常的假设是这需要一个中央服务器来聚合这些更新——但最近的研究表明这实际上并非必要。像 Plexus 这样的无服务器方法表明,设备可以直接彼此协调,进行点对点通信,完全不需要中央基础设施。
对于 JarvisOS 来说,这很重要。一个隐私优先的操作系统如果通过中央服务器路由模型改进,将与自身的前提相矛盾。点对点联邦学习意味着运行 JarvisOS 的手机可以随着时间的推移从彼此那里变得更智能,而任何人的数据都不会离开他们的设备。我们设想在TaskMemory中存储本地交互的“经验片段”,在设备空闲和充电时,通过安全的点对点协议与附近其他受信任的 JarvisOS 设备交换模型梯度更新。
7. 应用集成与开发者生态构想
目前,每个想要本地 AI 功能的应用程序都捆绑了自己的模型。一个用户可能会安装 GPT、Claude、DeepSeek、Perplexity 等等。想象一下:一个笔记应用要求用户下载一个模型。一个日历应用;下载一个模型。一个导航应用下载另一个。手机最终会拥有多个相似模型的副本,占用存储空间和 RAM,每个都是孤立的,彼此 unaware。这是错误的方向。
MCP——由 Anthropic 推广的模型上下文协议——试图在桌面上解决类似的问题。它让 AI 模型通过标准化接口连接到外部工具和服务。这是个好主意,但它是为一个模型在服务器上、工具是远程服务的世界设计的。在手机上,在后台运行一个 HTTP 服务器正是 Android 为了省电而会杀掉的那种东西。
JarvisOS 采取了不同的立场。模型存在于操作系统层面。应用程序不下载模型——它们注册工具。一个日历应用通过在其清单中声明能力来告诉 JarvisOS 它能做什么,而 JarvisOS 处理智能部分。Binder IPC 取代了 HTTP——它是内核强制的,具有微秒级延迟,并且 Android 不会杀死它,因为它是一个系统服务。
这种转变对应用开发者的要求其实很小。你不需要在你的应用中构建 AI,你只需要描述你的应用能做什么(具有清晰输入和输出的明确定义的操作,在你的清单中声明。可以把它想象成一个用于意图的ContentProvider。你不是在暴露数据,你是在暴露能力)。JarvisOS 会判断何时调用它。
这种方式让应用在不承担模型重量的情况下变得更智能,而手机则获得了一个单一的智能层,能够跨越所有应用进行观察,而不是分散在几十个孤立的 AI 栈中。开发者只需在AndroidManifest.xml中添加一个<meta-data>标签,描述工具的名称、描述、输入输出格式,并在应用中实现一个特定的BroadcastReceiver或Service来响应 JarvisOS 的调用意图。
8. 构建、刷机与调试实战
理论说完了,我们来点实际的。如何将这套代码编译成一个可以刷入手机的 ROM,并进行调试?
8.1 配置构建环境与编译
首先,确保你的构建环境已就绪。进入 LineageOS 源码根目录,导入构建环境变量。
cd ~/android/lineage source build/envsetup.sh接着,选择你要构建的设备目标。对于 Nothing Phone 2 (Pong),执行:
lunch lineage_pong-userdebuguserdebug版本带有 root 调试权限,适合开发。然后,就可以开始漫长的编译了。利用多核处理器可以显著加快速度,-j后面的数字通常设置为你的 CPU 核心数的 1-1.5 倍。
m -j$(nproc)编译过程可能持续数小时。如果内存不足(小于 32GB),你可能会遇到编译因OutOfMemoryError而失败。这时,可以尝试减少并行任务数,例如m -j4,或者为 Java 虚拟机分配更多内存(通过设置_JAVA_OPTIONS环境变量)。
8.2 刷机与基础验证
编译成功后,在out/target/product/pong/目录下会生成刷机包(通常是lineage-21.0-xxx-UNOFFICIAL-pong.zip)。对于 A/B 分区的设备(如 Pixel 和 Nothing Phone),刷机通常通过fastboot进行。警告:刷机会清除手机数据,请务必提前备份。
# 进入 fastboot 模式 adb reboot bootloader # 刷入编译好的镜像 fastboot flash boot out/target/product/pong/boot.img fastboot flash dtbo out/target/product/pong/dtbo.img fastboot flash vendor_boot out/target/product/pong/vendor_boot.img fastboot flash super out/target/product/pong/super.img # 重启设备 fastboot reboot设备启动后,首先验证 JarvisOS 服务是否正常运行。通过adb shell连接设备,然后检查服务列表和日志。
adb shell # 检查 RagService 是否在运行 service list | grep rag # 预期输出应包含 `android.os.IRagService` # 查看系统日志,过滤 RagService 相关条目 logcat -s RagService如果服务未启动,首先检查 SELinux 权限。使用dmesg | grep avc或logcat | grep avc查看是否有拒绝信息。根据拒绝信息,修改vendor/jarvisos/sepolicy下的策略文件,重新编译boot.img并刷入。
8.3 开发调试技巧与常见问题
1. 增量编译与快速迭代:修改 Java 代码后,不需要每次都进行完整的m编译。可以只编译特定的模块。例如,修改了RagService,可以执行:
m RagService编译出的.jar或.so文件会输出到out/目录。对于系统服务,有时需要同步推送到设备并重启服务。
adb root adb remount adb push out/target/product/pong/system/framework/oat/arm64/RagService.odex /system/framework/oat/arm64/ adb shell stop && adb shell start但更可靠的方法是制作一个增量 OTA 包 (make otapackage) 并通过 recovery 刷入,或者直接重新编译system.img并刷入。
2. 调试 JNI 代码:Cactus 引擎是 C++ 的。调试 JNI 层的问题需要addr2line和ndk-stack工具。确保在编译时启用了调试符号(在Android.bp或Android.mk中设置cppflags: ["-g"])。当发生 native crash 时,logcat会输出堆栈跟踪信息(backtrace)。将设备上的/system/lib64/libcactus.so和带有符号的编译输出中的libcactus.so保存下来,使用ndk-stack可以将内存地址解析为具体的代码行。
3. 性能分析与优化:使用 Android Studio 的 Profiler 或命令行工具simpleperf来监控RagService的 CPU、内存和电量消耗。特别注意 Binder 调用的频率和耗时。如果发现ToolDispatcher的广播响应超时,需要检查应用注册的BroadcastReceiver是否在后台线程执行耗时操作,或者考虑将部分工具调用改为同步的 Binder 调用(需在 AIDL 接口中定义)。
4. 存储与数据库调试:ObjectBox 提供了一个浏览器调试工具。在开发版本中,可以将数据库文件导出到电脑上用 ObjectBox Studio 查看。
adb pull /data/data/com.android.rag/databases/objectbox .检查DocumentChunk表中的cactusIndexId是否与 Cactus 内部的索引有效关联。如果检索结果为空,可能是索引 ID 映射出错,或者 Cactus 的索引文件损坏。
5. 模型管理问题:ModelRegistry可能因为模型文件路径错误、权限不足或模型格式不兼容而加载失败。日志是关键。确保模型文件被正确放置在/vendor/etc/jarvisos/models/目录下,并且rag_service有读取权限。首次加载大型模型(如 7B 参数)可能需要几十秒,需要在RagService的init方法中做好超时和状态管理,避免 ANR。
构建一个操作系统级的智能体平台是一项庞大的工程,从系统底层修改到 AI 模型集成,每一步都充满挑战。JarvisOS 的当前实现是一个起点,它验证了在现有 Android 架构上构建持久化、跨应用智能体服务的可行性。通过清晰的模块化设计(如单一的CactusWrapper、灵活的ModelRegistry),它为拥抱快速演进的 AI 研究(如 CALM、TurboQuant、联邦学习)奠定了基础。对于开发者而言,它指向了一个未来:应用不再需要内置沉重的 AI 模型,而是通过声明式接口提供能力,由操作系统统一调度,最终为用户带来更高效、更隐私、更一致的设备智能体验。这条路还很长,但每一步都让我们更接近那个手机真正成为个人智能服务器的未来。