AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载本文围绕 Operit 项目中 STT语音识别模型在 Android 构建期的自动准备方案展开默认的 Sherpa NCNN 本地语音识别模型不再依赖 Google Drive 上的models.zip手动归档而是由 Gradle 依据一份受版本控制的模型清单app/config/stt-model-assets.properties在preBuild阶段自动从 Hugging Face 下载、校验并合入 APK assets。读完本文你将掌握如何构建声明式模型资产清单、如何在 Gradle 中实现带 SHA-256 强校验的下载任务、如何把生成目录挂载到 main source set以及 CI 与文档如何随之迁移。一、背景旧实现的两个痛点在本次改造之前Operit 的完整 Android 构建依赖如下旧流程完整 Android 构建需要先从 Google Drive 下载models.zip模型文件被解压到app/src/main/assets/models直接作为源码 assets 参与打包运行时由SherpaSpeechProvider从该 assets 目录复制模型再通过文件路径创建识别器模型的来源仓库、revision 和文件 hash没有任何受版本控制的声明——构建输入不可复现、不可审计。这意味着构建机器或 CI 一旦缺失models.zip构建就只能中断等待开发者手动准备归档同时谁也无法确认 APK 里的模型文件到底是哪个 commit、是否被篡改过。二、新方案总体设计新实现围绕一份固定清单和一个生成任务展开验收目标非常明确清单固定 Hugging Face 仓库、commit、文件路径、大小和 SHA-256Gradle 在preBuild之前准备受管理的生成 assets 目录当前models/模型目录资源路径和运行时加载行为保持不变Google Drive 仅保留非模型的libs.zip、subpack.zip和jniLibs.zip。整体改动范围限定在 app/config/、app/build.gradle.kts、Android 依赖下载与解包脚本以及 Android 构建与贡献者文档。方案拆分为三个实现步骤对应三份文档步骤 1STT 模型来源清单步骤 2生成 STT assets 任务步骤 3CI 与文档迁移三、步骤 1模型来源清单——用 properties 文件声明全部输入3.1 清单文件位置与格式清单位于 app/config/stt-model-assets.properties每一行代表一个模型资产字段以|分隔共 6 列目标 assets 路径 | 来源 URL | 字节大小 | SHA-256 | 来源描述 | 许可说明文件头部的注释即字段说明行首#为注释行解析器会跳过。这一格式同时被 Gradle 解析器parseSttModelAssetManifest和人类评审复用保证了清单即真相。3.2 八个受管资产Silero VAD Sherpa NCNN清单记录了 8 个输入文件分为两组Silero VAD1 个文件MIT 许可目标路径大小字节SHA-256前 16 位models/silero_vad.onnx1,289,6037ed98ddb...来源为safestack/silero-vad仓库的data/silero_vad_16k_op15.onnx固定 commit8a63e2e86cf654d7ba19fbedbccce5ff55de3c60与既有 assets 中使用的文件完全一致。Sherpa NCNN 流式 Zipformer 双语模型7 个文件上游元数据未声明许可来源仓库为csukuangfj/sherpa-ncnn-streaming-zipformer-bilingual-zh-en-2023-02-13固定 commit05945efc40afe4b572542f01104ca5c413a9f6e1目标目录models/sherpa-ncnn-streaming-zipformer-bilingual-zh-en-2023-02-13/文件大小字节SHA-256前 16 位decoder_jit_trace-pnnx.ncnn.bin6,412,296dc4df2d8...decoder_jit_trace-pnnx.ncnn.param439cb88f589...encoder_jit_trace-pnnx.ncnn.bin127,364,0564ed65f05...encoder_jit_trace-pnnx.ncnn.param161,88897ad0954...joiner_jit_trace-pnnx.ncnn.bin7,350,7240e6c4370...joiner_jit_trace-pnnx.ncnn.param49046c339f3...tokens.txt56,317a8e0e4ec...3.3 为什么用 commit 而不是分支清单中的 URL 遵循https://huggingface.co/repo/resolve/commit/path形式。在 URL 中直接固化 commit hash而非main分支名即可构造出不可变下载 URL——分支可以漂移commit 不可变。这正是revision 和来源路径可直接构造不可变下载 URL这一验收项的落地方式为后续 SHA-256 校验提供了稳定的输入基础。四、步骤 2Gradle 生成任务——下载、强校验、合并、挂载核心实现集中在 app/build.gradle.kts从构建脚本的数据结构到任务装配形成了完整闭环。4.1 数据结构与清单解析脚本顶部定义了资产的数据结构app/build.gradle.kts#L30-L35data class SttModelAsset( val targetPath: String, val sourceUrl: String, val expectedBytes: Long, val expectedSha256: String, )parseSttModelAssetManifestapp/build.gradle.kts#L225-L245逐行读取清单丢弃空行与#注释行要求每行恰好 6 个字段否则报错并指出行号对目标路径做安全校验不允许以/开头、不允许包含..、不允许包含反斜杠\从源头杜绝路径越界。4.2 双因子校验大小 SHA-256verifySttModelAssetapp/build.gradle.kts#L247-L251是全部下载与增量逻辑的唯一判定标准fun verifySttModelAsset(file: File, asset: SttModelAsset): Boolean { return file.isFile file.length() asset.expectedBytes sha256(file) asset.expectedSha256 }sha256app/build.gradle.kts#L212-L223以缓冲方式逐块计算摘要避免大文件一次性读入内存。先比大小再算哈希大小不符时直接短路省去无谓的哈希计算注意最大的 encoder.bin超过 127 MB。4.3 下载函数临时文件 校验后原子替换downloadSttModelAssetapp/build.gradle.kts#L253-L287的关键细节先写入同目录下的name.download临时文件成功校验后才Files.move(..., REPLACE_EXISTING)替换目标避免半截文件污染 assets通过HttpURLConnection下载设置 30 秒连接超时、120 秒读取超时instanceFollowRedirects true跟随 Hugging Face 的 302 重定向实际文件位于 CDN携带User-Agent: Operit Android build STT asset syncHTTP 状态码非 2xx 直接构建失败并给出资产路径与响应码。4.4 syncSttModelAssets 任务声明式输入输出 过期清理任务本体app/build.gradle.kts#L293-L345val syncSttModelAssets by tasks.registering { description Downloads and verifies generated assets for local STT recognition. group build setup inputs.file(sttModelAssetsManifestFile) outputs.dir(generatedSttModelAssetsDir) outputs.upToDateWhen { false } doLast { ... } }执行逻辑分三步增量校验对每个资产先检查目标文件是否已通过verifySttModelAsset通过则跳过下载不通过则删除旧文件重新下载下载后再校验一次过期清理遍历输出目录删除清单之外的多余文件再自底向上删除空目录——保证任务输出仅包含 manifest 中定义的 assets这一验收项边界防护目标路径normalize()后必须仍位于生成目录内startsWith检查与解析阶段的路径校验形成双重防线。值得注意outputs.upToDateWhen { false }该任务每次都重新执行校验逻辑因为仅凭 Gradle 的输入输出快照无法判断已有文件是否损坏——本地文件可能被外部修改或磁盘损坏而校验成本只是本地哈希计算。4.5 合并与挂载syncMainAssets 与 sourceSets生成目录并不直接参与打包而是经过一个Sync任务合并app/build.gradle.kts#L347-L357val syncMainAssets by tasks.registering(Sync::class) { dependsOn(syncSttModelAssets) from(src/main/assets) { exclude(models/**) } from(generatedSttModelAssetsDir) into(generatedMainAssetsDir) }它把src/main/assets排除旧版models/**与生成的stt-model-assets目录合并到generated/main-assets随后在android.sourceSets中把 main source set 的 assets 源目录指向这个生成目录app/build.gradle.kts#L359-L367sourceSets { getByName(main) { assets.setSrcDirs(listOf(generatedMainAssetsDir.get().asFile)) } }最后完成任务挂载app/build.gradle.kts#L563-L570tasks.named(preBuild) { dependsOn(syncMainAssets) dependsOn(verifyExternallyBuiltNativeLibraries) } tasks.matching { it.name.matches(Regex(merge.*Assets)) }.configureEach { dependsOn(syncMainAssets) }preBuild与所有merge*Assets变体任务都依赖syncMainAssets因此debug / release / nightly 等所有 Android 打包入口使用完全相同的模型输入models/作为生成资产进入 APK而不再是仓库源码的一部分。五、步骤 3CI 与文档迁移5.1 依赖脚本只保留三个非模型归档ci/script/download_android_dependencies.sh 中Google Drive 的下载清单仅剩三个固定归档download 1Va1os7PRpCF3xtTwfx5kO11D7eIAvARG libs.zip download 1SQs_dVPD6ldvwteqoUVjvBLjTWvr5Fpv subpack.zip # full profile download 1-W4fjjUwoShnB8Rh9RT5Gl8sHiGyQUaM jniLibs.zip # full profile对应的解包策略在 ci/script/prepare_android_dependencies.py 中定义jvm配置只解libs.zip解到app/libsfull配置解全部三个归档subpack.zip解到app/src/main/assets/subpackjniLibs.zip解到app/src/main/jniLibs。models.zip不再出现在任何声明中。该脚本同时执行安全约束限制成员数量、解压大小、压缩比和文件类型重建固定输出根目录并拒绝越界路径、重复成员及符号链接。5.2 CI 与文档现状CI 的完整 Android lane 只下载上述三个归档STT assets 由 Gradle 在打包时按清单自动准备见 ci/README.md 对依赖策略的说明中英文 README、编译指南、贡献指南和 CI 文档保持一致文档会说明首次构建需要访问固定的 Hugging Face 模型来源但不再要求开发者手动处理模型归档。对开发者而言这意味着克隆仓库后第一次构建会自动联网拉取模型约 143 MB 合计之后构建走本地增量校验无需任何手动下载步骤。六、运行时行为保持不变从 assets 复制到识别器构建期的改动不触及运行时。SherpaSpeechProvider的初始化逻辑SherpaSpeechProvider.kt仍是读取 assets 中的models/sherpa-ncnn-streaming-zipformer-bilingual-zh-en-2023-02-13目录通过AssetCopyUtils.copyAssetDirRecursive复制到本地目录OperitPaths.sherpaNcnnModelsDir下再由文件路径创建 sherpa-ncnn 识别器。整个初始化在Dispatchers.IO上执行并受互斥锁保护initializeMutex.withLock失败会落入RecognitionState.ERROR与RecognitionError由上层语音服务统一处理。因此只要 assets 里的模型目录结构与文件名不变这正是清单targetPath保证的运行时代码一行都不用改。清单中的models/silero_vad.onnx与models/sherpa-.../各文件路径与运行时读取路径完全对应。七、验收标准与验证方式方案自带的验收条件见 总计划可直接作为验证清单干净工作树执行 Android 打包时不需要models.zip——依赖仅来自三个 Drive 归档 Gradle 自动同步生成 assets 中的八个 STT 文件均通过声明的 SHA-256 与大小校验——任何内容不匹配都会使构建中止require抛出明确错误信息APK 内模型路径仍为models/sherpa-ncnn-streaming-zipformer-bilingual-zh-en-2023-02-13/...——可解包 APK 检查assets/models目录验证CI 不再请求 Google Drive 的models.zip——可在download_android_dependencies.sh与prepare_android_dependencies.py中确认。其中模型下载失败或内容不匹配会中止构建由多级require保证清单格式错误、目标路径越界、HTTP 非 2xx、下载后校验失败、同步后校验失败、过期文件无法删除任一情况都会抛出含具体资产路径的错误信息这正是构建输入可复现、可审计的工程落地。八、小结从手工归档到可复现构建这套方案的价值在于把模型从哪来、是什么版本、是否完整从隐式的下载习惯变成了显式、受版本控制、自动校验的构建契约可复现commit 级 URL 大小 SHA-256 三要素锁定每一个字节可审计清单文件随仓库演进任何模型变更都体现为 diff零手工步骤开发者与 CI 都不再需要手动下载、解压、放置模型向后兼容APK 内路径与运行时加载逻辑完全不变。如果未来需要升级模型例如切换到新的 sherpa-ncnn 版本只需更新清单中的 URL、commit、大小与 SHA-256 即可Gradle 任务与 CI 无需任何改动——这份清单本身就是整个同步机制的契约接口。赞分享AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载相关推荐AMD Ryzen SDT调试工具终极指南解锁处理器隐藏性能的专家级方案AMD Ryzen SDT调试工具终极指南解锁处理器隐藏性能的专家级方案 还在为AMD Ryzen处理器的性能优化感到困惑 AMD Ryzen SDT调试工AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化如何快速掌握WzComparerR2冒险岛数据提取的终极指南如何快速掌握WzComparerR2冒险岛数据提取的终极指南 WzComparerR2是一款专为《冒险岛》游戏设计的强大开源数据提取工具能够解析游戏WZ文件AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化Operit 构建期自动同步 STT 模型 assetsmanifest 驱动、SHA-256 校验的 Gradle 生成资产管线Operit 构建期自动同步 STT 模型 assetsmanifest 驱动、SHA 256 校验的 Gradle 生成资产管线 导读 本文讲解 OperiAI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化上一篇3步跑通 shadPS4PS4模拟器新手避坑指南下一篇Gradle Versions Plugin高级配置如何过滤不稳定版本和自定义组件选择规则创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考