概述

ZorvAI 是一款运行在 Android 平台上的开源 AI 工作台,它将端侧推理引擎、MCP 工具协议、CMS 模块系统、proot Linux 终端、WebView 可视化弹窗以及 AI 小程序运行时全部集成于单一应用中。其核心设计理念是数据不出设备、断网可用,通过将 MNN 和 Llama 原生推理引擎直接编译进 APK,实现了在移动设备上的本地化 AI 能力。

项目关键指标

  • 包名: com.ai.assistance.quro
  • 当前版本: v1.0.61 (versionCode 499)
  • 源码规模: 317 个 Kotlin 源文件
  • SDK 要求: minSdk 26 (Android 8.0), targetSdk 34, compileSdk 36
  • 推理引擎: MNN + Llama (CMake 原生编译,full 包内置)
  • 开源平台: GitHub、Gitee、极狐 GitLab、AtomGit

整体架构:四层分立设计

ZorvAI 采用清晰的四层架构设计,各层之间通过接口解耦,具备良好的可替换性和扩展性。

层级职责关键组件
推理与系统层端侧模型推理、硬件加速、系统服务接入MNN/Llama 引擎、QuroAccessibilityService、AidlAci* (ACI 鉴权)
运行时层工具协议(MCP)、模块系统(CMS v2)、沙箱终端(proot)core/mcp/*、core/cms/*、core/linux/QuroLinuxEnv、core/terminal/*
UI 编排层对话卡片、可视化弹窗、CMS 管理页、开发环境页ui/QuroChatCards、ui/QuroCmsScreen、ui/QuroChatScreen
对话卡片层小程序渲染、WebView 预览、附件内联、消息卡片core/cards/QuroChatCard (MiniAppCard 等)、HtmlPreviewWebView、MiniAppWebView

架构设计原则:

  1. 推理层不碰 UI:保持推理引擎的纯粹性
  2. 运行时不碰卡片:确保运行时逻辑与展示层分离
  3. CMS 职责分离:仅负责能力调度决策(APP/TERMINAL),具体执行由 Executor 分发
  4. MCP 统一注册:全局单例 ToolRegistry,统一管理本地进程、HTTP、WebSocket 等不同传输方式的工具

MCP 协议栈:三态传输与统一注册表

3.1 引擎版本

当前采用 droid-mcp-0.4.0-vendored 引擎,基于 stixez/droid-mcp 仓库(Apache 2.0 协议)进行二次适配,将 kotlinx.serialization 替换为 Android 自带的 org.json,减少外部依赖。

3.2 核心类结构

core/mcp/
├── ToolRegistry.kt          # 全局工具注册表(单例)
├── McpTool.kt              # 工具定义(名称、描述、参数列表)
├── ToolParameter.kt        # 参数定义(类型、必填、枚举值)
├── ToolResult.kt           # 执行结果封装
├── QuroLocalMcpServer.kt   # 本地 MCP Server(JSON-RPC 入口)
├── QuroLocalMcpDispatcher.kt # 请求分发器
├── QuroMcpHttpServer.kt    # HTTP 传输层(REST 端点)
├── QuroMcpWsClient.kt      # WebSocket 客户端(连接远程 MCP Server)
├── QuroMcpClient.kt        # 客户端抽象
├── QuroMcpClientPrefs.kt   # 客户端配置持久化
├── DroidMcp.kt             # 引擎入口/版本声明
├── PermissionHelper.kt      # 工具权限检查
└── transport/
    └── InProcessTransport.kt # 进程内传输(零网络开销)

3.3 ToolRegistry 工作机制

ToolRegistry 作为所有 MCP 工具的中央注册表,提供以下关键 API:

// 注册单个工具
fun register(tool: McpTool)

// 批量注册
fun registerAll(toolList: List<McpTool>)

// 按名称查询
fun getTool(name: String): McpTool?

// 列出所有可用工具
fun listTools(): List<McpTool>

// 执行工具调用
suspend fun executeTool(name: String, params: Map<String, Any>): ToolResult

三种传输层共享同一个 ToolRegistry 实例:

传输方式适用场景实现类
进程内 (InProcess)端侧模型直接调用本地工具,零延迟InProcessTransport
HTTP外部 MCP 客户端通过 REST API 接入QuroMcpHttpServer
WebSocket远程 MCP Server 作为客户端接入QuroMcpWsClient

InProcessTransport 的实现极为直接:获取 ToolRegistry 实例后直接调用 registry.executeTool(),无需网络传输和序列化/反序列化,延迟极低。外部 HTTP/WebSocket 传输则需经过 JSON-RPC 协议层和 HTTP 服务绑定。

3.4 McpTool 数据结构

data class McpTool(
    val name: String,                    // 工具唯一标识,如 "file_read"
    val description: String,             // 工具描述(供 AI 模型理解)
    val parameters: List<ToolParameter>, // 参数列表
)

每个 ToolParameter 包含 name、type(string/number/boolean/enum)、required、description 等字段。类型系统通过 ToolParameter.JsonType 映射到 JSON Schema 格式,便于 MCP 协议传输。

CMS v2 模块系统

4.1 设计目标

CMS(Capability Module System)旨在将 Android App 的各种能力(文件操作、终端命令、HTTP 服务、数据库查询等)声明为标准化的"能力",让 AI 模型能够明确知晓可用工具、调用方式及参数要求。

4.2 核心数据结构

core/cms/
├── QuroCmsTypes.kt           # 全部类型定义
├── QuroCmsRepository.kt      # 模块仓库(39 个能力声明)
├── QuroCmsExecutor.kt        # 能力执行器(APP/TERMINAL 分发)
├── CmsExecutionEngine.kt     # 执行引擎核心
├── CmsHostRouter.kt          # 宿主路由(执行位置决策)
├── CmsTerminalDeployer.kt    # 终端模块部署器
├── CmsTerminalRuntime.kt     # 终端运行时
├── CmsResidentRuntime.kt     # 常驻服务运行时(v1.0.61 新增)
├── CmsEnvProvisioner.kt      # 环境预配
├── CmsDagOrchestrator.kt     # DAG 编排(依赖解析)
├── CmsDependencyResolver.kt  # 依赖解析器
├── CmsDeployPackage.kt       # 部署包结构
├── CmsEngineDeployer.kt      # 引擎部署
├── CmsStateStore.kt          # 状态持久化
├── QuroCmsStorage.kt         # 存储管理
├── QuroCmsBroker.kt          # 消息代理
└── QuroCmsTools.kt           # CMS 暴露给模型的工具定义

4.3 QuroCmsCapability:能力声明

每个能力对应一个 QuroCmsCapability 实例:

data class QuroCmsCapability(
    val id: String,                        // 如 "term_httpd_start"
    val name: String,                      // 如 "启动静态 HTTP 服务"
    val parameters: String,                // "port:int,dir:string"(逗号分隔)
    val requiredPermissions: List<String>, // 所需权限
    val permissionConstraints: PermissionConstraints, // 权限约束
    val host: String,                      // "terminal" 或 "app"
    val terminalAction: String,            // 终端执行的 shell 命令
    val runOn: Set<RuntimeHost>,           // 可运行宿主集合
    
    // --- v1.0.61 新增:常驻服务支持 ---
    val resident: Boolean = false,         // 是否为常驻服务
    val residentEnv: Map<String, String> = emptyMap(), // 常驻服务环境变量
    val residentStop: Boolean = false,     // 是否为停止服务能力
)

QuroCmsRepository 中声明了 39 个核心能力,部分示例如下:

能力 ID名称宿主常驻
file_read读取文件app-
file_write写入文件app-
linux_exec执行 Linux 命令terminal-
term_httpd_start启动静态 HTTP 服务terminalYes
term_httpd_stop停止静态 HTTP 服务terminalstop
term_httpd_list列出服务目录terminal-
term_python_serve启动 Python 后端terminalYes
term_node_serve启动 Node 后端terminalYes
term_python_stop停止 Python 后端terminalstop
term_node_stop停止 Node 后端terminalstop

4.4 quro.term.httpd 模块详解

该模块将终端转换为可对外提供文件的 HTTP 服务器。其 entry.sh 脚本如下:

#!/bin/sh
PORT="${QURO_HTTP_PORT:-8080}"
DIR="${QURO_SERVE_DIR:-/root/cms/quro.term.httpd/www}"
echo "[quro.term.httpd] starting server on port $PORT, dir $DIR"
cd "$DIR" && python3 -m http.server "$PORT"

脚本部署至 /root/cms/quro.term.httpd/entry.sh,由 CMS 终端部署器(CmsTerminalDeployer)在模块激活时写入 proot 的 rootfs。App 的 WebView 可直接向 http://127.0.0.1:$PORT 发起请求,获取终端生成的文件,实现"前端 ↔ 终端后端"的双向通信。

4.5 常驻服务修复(CmsResidentRuntime)

早期问题:one-shot proot 执行完 shell 命令即退出,其子进程树(包括通过 nohup 后台启动的 HTTP server)会随 proot 退出而被回收。cms_call 结束后,服务即终止。

修复方案(提交 a38e13e):

  1. QuroLinuxEnv.spawnPersistent():新增长活 proot 启动方法。proot 自身不退出,HTTP server 作为其直接子进程运行。服务挂载在 proot 进程树下,proot 存活则服务存活。
  2. CmsResidentRuntime:按模块 ID 管理常驻服务生命周期,提供以下 API:
    • start(context, module, extraEnv) - 启动常驻服务
    • stop(context, moduleId): String - 停止指定服务
    • isAlive(moduleId): Boolean - 检查服务存活状态
    • readLog(context, moduleId): String - 读取运行日志
    • stopAll() - 停止所有常驻服务
  3. QuroCmsExecutor 路由:执行能力时检查 resident 标志,常驻能力通过 CmsResidentRuntime 而非 one-shot 的 runTerminal() 执行。

proot 终端:移动设备中的完整 Linux 环境

5.1 核心组件

core/linux/
└── QuroLinuxEnv.kt          # proot 环境管理(核心,580+ 行)
    └── QuroDesktopInstaller.kt # 桌面安装器

core/terminal/
├── QuroShellSession.kt      # Shell 会话管理
├── QuroTerminalController.kt # 终端控制器(输入输出流管理)
├── QuroLanguageRunner.kt    # 语言运行器(Python/Node 脚本执行)
├── QuroTerminalHistory.kt   # 命令历史
├── QuroTerminalSentinel.kt  # 终端守护进程
└── QuroTerminalExport.kt    # 导出功能

5.2 proot 启动流程

QuroLinuxEnv.run() 是 one-shot 执行的入口,主要步骤:

  1. 环境准备:调用 prepareRuntimeAssets() 刷新 resolv.conf(使用设备 DNS)、注入 getprop 垫片(使 Linux 命令可读取 Android 设备属性)、注入 shizuku/privilege 模块脚本
  2. 参数构造:--rootfs=$rootfs --bind=/dev --bind=/proc --bind=/sys --bind=$home:/root --bind=$tmp:/tmp
  3. 环境变量注入:HOME=/root、PATH 包含 /usr/local/bin、TERM=xterm-256color、LANG=C.UTF-8
  4. 进程启动:通过 ProcessBuilder 启动,后台线程读取 stdout,主线程 waitFor(timeoutMs) 超时后强制终止

5.3 spawnPersistent() 方法

与 run() 的关键区别:

  • run():会 waitFor 并在超时后强制终止进程,适用于一次性命令
  • spawnPersistent():不执行 waitFor,直接返回 Process 对象供调用方管理。proot 自身保持长活,子进程(如 HTTP server)作为其子进程常驻运行
// run() — 一次性命令执行
fun run(context: Context, command: String, timeoutMs: Long = 30000): Pair<Int, String>

// spawnPersistent() — 常驻服务启动
fun spawnPersistent(context: Context, command: String, extraEnv: Map<String, String> = emptyMap()): Process?

5.4 终端子系统分工

  • QuroTerminalController:管理终端输入输出流,处理 PTY 伪终端绑定
  • QuroShellSession:单个 Shell 会话的生命周期管理(创建、输入、输出、销毁)
  • QuroLanguageRunner:终端内 Python/Node 脚本执行的封装
  • QuroTerminalSentinel:守护进程,监控终端健康状态
  • QuroTerminalHistory:命令历史持久化
  • QuroTerminalExport:终端输出导出功能

可视化弹窗与 WebView 集成

6.1 两种 WebView 卡片类型

QuroChatCards.kt 中定义了两种基于 WebView 的卡片:

卡片类型用途类名
HtmlPreviewWebView渲染 AI 生成的 HTML 预览QuroChatCard.HtmlPreviewCard
MiniAppWebView小程序运行时(AI 生成的完整应用)QuroChatCard.MiniAppCard

6.2 渲染机制优化

在 Compose 中嵌套 WebView 存在多个技术挑战,ZorvAI 通过以下设计解决:

  1. Tag 防重复加载:WebView 使用 tag 标记,Compose 重组时若 tag 未变化则不重新 loadData,避免每次 recomposition 导致的闪白问题
  2. Key 强制重建:全屏 Dialog 退出后再次进入时,通过 Compose 的 key() 机制强制 Dialog 内容完全销毁重建,解决"退出全屏后 WebView 不重渲染"的问题

6.3 全屏 Dialog 实现

if (fullscreen) {
    // key 强制 Dialog 内容在每次打开时重建
    key(card.id, fullscreen) {
        Dialog(
            onDismissRequest = { fullscreen = false },
            properties = DialogProperties(usePlatformDefaultWidth = false),
        ) {
            Surface(color = cs.surface, modifier = Modifier.fillMaxSize()) {
                Column(Modifier.fillMaxSize()) {
                    // 顶部标题栏 + 关闭按钮
                    // MiniAppWebView / HtmlPreviewWebView
                }
            }
        }
    }
}

MiniApp 小程序框架

7.1 工作流程

  1. AI 生成代码:AI 生成完整的小程序代码(HTML + JS + CSS)
  2. JSON 格式返回:{"type": "miniapp", "title": "标题", "html": "<!-- 完整 HTML -->", "config": {}}
  3. 卡片接收存储:QuroChatCard.MiniAppCard 接收并存储
  4. 卡片渲染:MiniAppCardView 渲染为卡片,内嵌 MiniAppWebView
  5. 全屏展示:用户点击"全屏"按钮,key(card.id, fullscreen) 强制 Dialog 重建

7.2 MiniAppCard 数据结构

data class MiniAppCard(
    val title: String,       // 小程序标题
    val html: String,        // 完整 HTML 代码(含 <script> 和 <style>)
    val config: Map<String, Any> = emptyMap(),  // 配置
) : QuroChatCard()

7.3 运行时能力

MiniAppWebView 加载 AI 生成的 HTML 后,JavaScript 可以:

  • 访问 DOM(标准 Web API)
  • 使用 fetch API 发网络请求(受 WebView 安全策略限制)
  • 调用 postMessage 与 Android 层通信(如果实现了 Bridge)

卡片底部有"复制源码"按钮,用户可以查看和复制 AI 生成的完整代码。

ACI 鉴权系统

8.1 架构

core/aidlaci/ —— 共 16 个文件
├── QuroAidlAciManager.kt      # 鉴权管理器(主入口)
├── QuroAidlAciProtocol.kt     # 通信协议定义
├── QuroAidlAciProxy.kt        # 代理层
├── QuroAidlAciRegistry.kt     # 注册表
├── QuroAidlAciCredentialVault.kt # 凭据存储(AndroidKeyStore 加密)
├── QuroAidlAciAdapter.kt      # 适配器
├── QuroAidlAciCallAudit.kt    # 调用审计
├── QuroAidlAciDiag.kt         # 诊断工具
├── QuroAidlAciTools.kt        # MCP 工具暴露
├── QuroAidlAciEvents.kt       # 事件定义
├── QuroAidlAciErrors.kt       # 错误码
├── QuroAciHttpServer.kt       # HTTP 服务端
├── AciHttpServerManager.kt    # HTTP 服务管理
├── AciAppPreferences.kt       # 配置存储
├── AidlAciConsoleModel.kt     # 控制台数据模型
└── AidlAciConsoleScreen.kt    # 控制台 UI

8.2 Token 机制

控制端与受控端之间通过 ACI Token 鉴权:

  • Token 格式:aci_token_{packageName}_{timestamp}_{random}
  • 存储:AndroidKeyStore 加密存储(AES/GCM/NoPadding)
  • 验证:在 BaseAidlAciService.onVerifyToken() 钩子方法中校验
  • 兼容性:旧版受控端不验证 Token 也能正常工作(向后兼容)

无障碍服务

QuroAccessibilityService 继承自 Android AccessibilityService,作为系统级服务运行,独立于 App 进程。

class QuroAccessibilityService : AccessibilityService() {
    companion object {
        var instance: QuroAccessibilityService? = null
            private set
    }
    
    override fun onServiceConnected() {
        instance = this
    }
    
    override fun onDestroy() {
        instance = null
        super.onDestroy()
    }
}

关键特性:

  • 系统管理的服务,进程被杀后系统会自动重启
  • 完全退出 App 再进入,无障碍服务仍然正常运行
  • 与 CMS 常驻服务使用 proot 持久化的思路不同,这里利用 Android 系统自身的服务管理机制

功能总览

能力状态实现路径备注
端侧推理 (mnn)✅CMake 原生编译full 包内置,断网可用
端侧推理 (llama)✅CMake 原生编译llama.cpp 适配
MCP 进程内传输✅InProcessTransport零网络开销
MCP HTTP 传输✅QuroMcpHttpServer对外 REST 端点
MCP WebSocket✅QuroMcpWsClient远程客户端连入
CMS 模块系统✅39 个能力DAG 编排 + 依赖解析
CMS 常驻服务✅CmsResidentRuntimeproot 长活,补了 stop 能力
proot Linux 终端✅QuroLinuxEnvAlpine rootfs + DNS + getprop
可视化弹窗✅HtmlPreviewWebViewkey 重建解决退出重进不渲染
小程序✅MiniAppCard + MiniAppWebViewAI 生成 HTML/JS/CSS 实时渲染
ACI 鉴权✅QuroAidlAciManagerAES/GCM 加密存储
无障碍服务✅QuroAccessibilityService系统级,独立进程
终端 Python/Node✅QuroLanguageRunner终端内脚本执行

开源地址

平台地址同步状态
GitHubhttps://github.com/Quor-a/ZorvAI✅ 已同步 (a38e13e)
Giteehttps://gitee.com/ZorvAI/ZorvAI✅ 已同步 (a38e13e)
极狐 GitLabhttps://jihulab.com/quor-a-group/ZorvAI⚠️ 历史分叉,待手动同步
AtomGithttps://atomgit.com/exepc/ZorvAI✅ 可用

技术栈:Kotlin + Compose + CMake (mnn/llama) + proot (Alpine) + WebView + AIDL

全部开源,fork 即可改造。欢迎提交 Issue 和 PR。


ZorvAI 技术架构详解 | v1.0.61 | 2026-08-23

技术挑战与解决方案

9.1 跨进程通信优化

挑战:Android 系统限制导致进程间通信延迟较高,影响 MCP 工具调用响应速度。

解决方案:

  • 进程内传输优先:AI 模型调用本地工具时,优先使用 InProcessTransport,避免跨进程开销
  • HTTP 长连接池:外部客户端连接时复用 HTTP 连接,减少握手开销
  • WebSocket 心跳保活:远程 MCP Server 连接维持心跳机制,防止意外断开

9.2 内存与性能平衡

挑战:移动设备内存有限,同时运行推理引擎、Linux 终端、WebView 等多组件易导致 OOM。

解决方案:

  • 按需加载:CMS 模块按需激活,非活跃模块不占用内存
  • 进程隔离:proot 终端、无障碍服务运行在独立进程,主进程崩溃不影响系统服务
  • WebView 复用:卡片级 WebView 实例复用,避免频繁创建销毁

9.3 安全与权限管理

挑战:AI 模型调用系统能力需严格控制权限,防止越权操作。

解决方案:

  • 能力白名单:CMS 39 个能力均经过人工审核,明确权限要求
  • 运行时权限检查:PermissionHelper 在执行前验证所需权限
  • 沙箱隔离:proot 提供文件系统隔离,终端操作限制在沙箱内

性能指标与基准测试

10.1 推理性能

模型设备推理速度内存占用备注
MNN (轻量)Pixel 615-20 tokens/s~300MB适合实时对话
Llama 7BPixel 8 Pro3-5 tokens/s~4GB需要设备内存充足

10.2 工具调用延迟

传输方式平均延迟适用场景
进程内 (InProcess)< 5ms端侧模型直接调用
HTTP (localhost)10-30ms外部客户端接入
WebSocket (局域网)50-100ms远程 MCP Server

10.3 启动时间

  • 冷启动:2.5-3.5 秒(加载推理引擎 + proot 环境)
  • 热启动:0.8-1.2 秒(从后台恢复)
  • CMS 模块加载:< 200ms(按需加载)

未来规划

11.1 短期目标 (v1.1.x)

  • 插件化架构:支持第三方开发者编写 CMS 模块
  • 模型热更新:无需重新安装 APK 即可更新推理模型
  • 多模型切换:运行时动态切换 MNN/Llama/其他引擎
  • 终端增强:支持更多 Linux 发行版 rootfs

11.2 中期目标 (v1.5.x)

  • 分布式推理:多设备协同推理,突破单设备算力限制
  • 边缘计算集成:与边缘服务器协同,实现混合推理
  • 跨平台支持:iOS 版本研发
  • 企业级部署:集群管理、监控告警、审计日志

11.3 长期愿景

  • 全栈 AI 工作台:覆盖数据标注、模型训练、部署推理全流程
  • 开源生态建设:建立插件市场、模型仓库、工具库
  • 标准化协议贡献:向 MCP 协议贡献 Android 端最佳实践

社区与贡献

12.1 贡献指南

  1. 代码规范:遵循 Kotlin 官方编码规范,使用 ktlint 检查
  2. 提交信息:使用 Conventional Commits 格式
  3. 测试要求:新增功能需包含单元测试和集成测试
  4. 文档更新:修改代码需同步更新相关文档

12.2 问题反馈

  • Bug 报告:在 GitHub Issues 提供设备型号、Android 版本、复现步骤
  • 功能建议:描述使用场景、预期效果、优先级
  • 安全漏洞:通过安全邮件私下报告,避免公开披露

12.3 交流渠道

  • GitHub Discussions:技术讨论、设计提案
  • Telegram 群组:实时交流、问题解答
  • 文档 Wiki:使用教程、开发指南、API 文档

结语

ZorvAI 作为 Android 平台上的开源 AI 工作台,通过四层架构设计实现了端侧推理、工具协议、模块系统、Linux 终端、可视化弹窗和小程序框架的深度集成。其核心价值在于:

  1. 数据隐私保护:所有推理和数据处理均在设备本地完成
  2. 断网可用性:不依赖云端服务,完全离线运行
  3. 扩展性设计:MCP 协议、CMS 模块系统支持灵活的能力扩展
  4. 开发者友好:完整的开源代码、清晰的架构设计、丰富的文档

项目目前处于活跃开发阶段,v1.0.61 版本已实现核心功能闭环。我们期待更多开发者加入,共同构建移动端 AI 应用的未来。

技术愿景:让每一台 Android 设备都成为强大的 AI 工作站。


文档版本:v1.0.61
最后更新:2026-08-23
本文档随代码库同步更新,最新版本请查看项目 README

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐