ZorvAI 技术架构深度解析:Android 端侧 AI 工作台的四层架构设计
概述
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 |
架构设计原则:
- 推理层不碰 UI:保持推理引擎的纯粹性
- 运行时不碰卡片:确保运行时逻辑与展示层分离
- CMS 职责分离:仅负责能力调度决策(APP/TERMINAL),具体执行由 Executor 分发
- 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 服务 | terminal | Yes |
term_httpd_stop |
停止静态 HTTP 服务 | terminal | stop |
term_httpd_list |
列出服务目录 | terminal | - |
term_python_serve |
启动 Python 后端 | terminal | Yes |
term_node_serve |
启动 Node 后端 | terminal | Yes |
term_python_stop |
停止 Python 后端 | terminal | stop |
term_node_stop |
停止 Node 后端 | terminal | stop |
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):
QuroLinuxEnv.spawnPersistent():新增长活 proot 启动方法。proot 自身不退出,HTTP server 作为其直接子进程运行。服务挂载在 proot 进程树下,proot 存活则服务存活。CmsResidentRuntime:按模块 ID 管理常驻服务生命周期,提供以下 API:start(context, module, extraEnv)- 启动常驻服务stop(context, moduleId): String- 停止指定服务isAlive(moduleId): Boolean- 检查服务存活状态readLog(context, moduleId): String- 读取运行日志stopAll()- 停止所有常驻服务
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 执行的入口,主要步骤:
- 环境准备:调用
prepareRuntimeAssets()刷新resolv.conf(使用设备 DNS)、注入getprop垫片(使 Linux 命令可读取 Android 设备属性)、注入 shizuku/privilege 模块脚本 - 参数构造:
--rootfs=$rootfs --bind=/dev --bind=/proc --bind=/sys --bind=$home:/root --bind=$tmp:/tmp - 环境变量注入:
HOME=/root、PATH包含/usr/local/bin、TERM=xterm-256color、LANG=C.UTF-8 - 进程启动:通过
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 通过以下设计解决:
- Tag 防重复加载:WebView 使用 tag 标记,Compose 重组时若 tag 未变化则不重新
loadData,避免每次 recomposition 导致的闪白问题 - 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 工作流程
- AI 生成代码:AI 生成完整的小程序代码(HTML + JS + CSS)
- JSON 格式返回:
{"type": "miniapp", "title": "标题", "html": "<!-- 完整 HTML -->", "config": {}} - 卡片接收存储:
QuroChatCard.MiniAppCard接收并存储 - 卡片渲染:
MiniAppCardView渲染为卡片,内嵌MiniAppWebView - 全屏展示:用户点击"全屏"按钮,
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 常驻服务 | ✅ | CmsResidentRuntime | proot 长活,补了 stop 能力 |
| proot Linux 终端 | ✅ | QuroLinuxEnv | Alpine rootfs + DNS + getprop |
| 可视化弹窗 | ✅ | HtmlPreviewWebView | key 重建解决退出重进不渲染 |
| 小程序 | ✅ | MiniAppCard + MiniAppWebView | AI 生成 HTML/JS/CSS 实时渲染 |
| ACI 鉴权 | ✅ | QuroAidlAciManager | AES/GCM 加密存储 |
| 无障碍服务 | ✅ | QuroAccessibilityService | 系统级,独立进程 |
| 终端 Python/Node | ✅ | QuroLanguageRunner | 终端内脚本执行 |
开源地址
| 平台 | 地址 | 同步状态 |
|---|---|---|
| GitHub | https://github.com/Quor-a/ZorvAI | ✅ 已同步 (a38e13e) |
| Gitee | https://gitee.com/ZorvAI/ZorvAI | ✅ 已同步 (a38e13e) |
| 极狐 GitLab | https://jihulab.com/quor-a-group/ZorvAI | ⚠️ 历史分叉,待手动同步 |
| AtomGit | https://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 6 | 15-20 tokens/s | ~300MB | 适合实时对话 |
| Llama 7B | Pixel 8 Pro | 3-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 贡献指南
- 代码规范:遵循 Kotlin 官方编码规范,使用 ktlint 检查
- 提交信息:使用 Conventional Commits 格式
- 测试要求:新增功能需包含单元测试和集成测试
- 文档更新:修改代码需同步更新相关文档
12.2 问题反馈
- Bug 报告:在 GitHub Issues 提供设备型号、Android 版本、复现步骤
- 功能建议:描述使用场景、预期效果、优先级
- 安全漏洞:通过安全邮件私下报告,避免公开披露
12.3 交流渠道
- GitHub Discussions:技术讨论、设计提案
- Telegram 群组:实时交流、问题解答
- 文档 Wiki:使用教程、开发指南、API 文档
结语
ZorvAI 作为 Android 平台上的开源 AI 工作台,通过四层架构设计实现了端侧推理、工具协议、模块系统、Linux 终端、可视化弹窗和小程序框架的深度集成。其核心价值在于:
- 数据隐私保护:所有推理和数据处理均在设备本地完成
- 断网可用性:不依赖云端服务,完全离线运行
- 扩展性设计:MCP 协议、CMS 模块系统支持灵活的能力扩展
- 开发者友好:完整的开源代码、清晰的架构设计、丰富的文档
项目目前处于活跃开发阶段,v1.0.61 版本已实现核心功能闭环。我们期待更多开发者加入,共同构建移动端 AI 应用的未来。
技术愿景:让每一台 Android 设备都成为强大的 AI 工作站。
文档版本:v1.0.61
最后更新:2026-08-23
本文档随代码库同步更新,最新版本请查看项目 README
更多推荐

所有评论(0)