# T18 · 端侧小模型 SPIKE·MNN + Qwen3-0.6B 真机可行性与意图分类原型 > 对应需求:端云分工方案(弱网兜底 + 意图识别下沉端侧,见本文 §0 背景)| 分支:`task/t18-ondevice-llm-spike` > 先读《README-FE批次.md》,ArkTS 严格模式铁律全部适用。**本任务是 SPIKE:产出是可运行的调试页 + 真机基准报告 + 结论,不是生产代码。** ## 0. 背景与分工结论(决策依据,勿复述进代码) - 端侧只做「确定性高、判错代价低」的事:意图分类/越界拦截/离线兜底;需要记忆、知识库、规则引擎的生成永远留云端。 - 端侧结论默认是「建议」,云端权威。本 SPIKE 只验证可行性,**不接生产链路**(ApiClient fallback、hint 上行是 P1~P3 的事)。 - 框架选型:MNN 3.6.1(官方 harmony 产物,CPU-only,Apache-2.0)+ ModelScope 官方导出 `MNN/Qwen3-0.6B-MNN`(Apache-2.0,权重 450,810,338 B)。鸿蒙上 GPU/NPU 加速均不可用,按 CPU 规划。 - 内存红线:官方性能体验建议前台峰值 ≤1500MB;本任务验收按峰值 <1GB(加载后 native 堆)。 ## 1. 目标(三问三答) 1. **跑不跑得动**:Qwen3-0.6B int4 在真机(麒麟芯片)上的首 token 延迟、生成速度、峰值内存——门槛:意图分类单次(输出 ≤32 token)**总耗时 <3s**、加载后 native 堆 **<1GB**、连续 9 次推理不因降频/内存崩掉; 2. **够不够准**:意图分类 smoke(标签集对齐《穿搭Agent-需求文档》§3.2 九类 + 越界)输出受 JSON schema 约束——门槛:20 条自建样例人工核对 **≥85%**(验证门槛;≥90% 上线门槛留待 P2 用服务端评测集); 3. **链路通不通**:局域网下载 → 校验 → 加载 → 生成全链路可重复执行——门槛:新机器按本文档 §3 能在 30 分钟内复现。 **不达标就换方案(换 1.7B / 只做离线兜底 / 放弃端侧),不要硬做。** ## 2. 已就绪的契约(协调提交已提供,直接用) - **MNN 预编译库**:`app/entry/src/main/libs/arm64-v8a/libMNN.so`(5.85MB,已验证含 `createLLM` 符号;官方 zip 只有 .so 没有头文件)。**不入库**(.gitignore),获取命令见 §3.1; - **头文件**:`app/entry/src/main/cpp/third_party/mnn/include/`(MNN/* + llm/llm.hpp,来自 3.6.1 tag 稀疏克隆,**版本与 .so 严格绑定,禁止混版本**——C4 图融合新旧不匹配会静默算错); - **模型**:`models/qwen3-0.6b-mnn/`(6 个文件 + manifest.json,SHA256 已逐个与 ModelScope 官方清单核对)。**不入库**; - **native 模块**:`app/entry/src/main/cpp/`(CMakeLists.txt / napi_init.cpp / local_llm.h/.cpp / types/liblocalllm/index.d.ts)。接口:`loadModel(dir)→Promise`(空串=成功)、`generate(prompt,maxNewTokens)→Promise`(JSON:text/first_token_ms/total_ms/chars)、`unloadModel()`、`isLoaded()`。计时在 native 侧(std::chrono); - **ArkTS 封装**:`ets/core/LocalLlm.ets`(下载/校验/加载状态机;SPIKE 只校文件尺寸,SHA256 流式校验留 P1); - **调试页**:`ets/pages/LlmTestPage.ets`(已注册 main_pages,入口在「我的」页底部「端侧LLM调试」,正式版移除); - **局域网服务器**:`scripts/serve-models.mjs`(零依赖,`node scripts/serve-models.mjs 8080`,打印局域网 IP); - **构建配置**:`entry/build-profile.json5` 已加 externalNativeOptions(仅 arm64-v8a;模拟器 x86_64 不在本任务范围——MNN 无官方 x86 鸿蒙包,且模拟器性能不代表真机)。 **FE 批次规程破例清单**(本任务新增/改动,超出 F 批次禁改范围,特此登记): `entry/build-profile.json5`(+externalNativeOptions)、`ets/core/LocalLlm.ets`(新文件)、`main_pages.json`(+1 页)、`components/f1/ProfileContent.ets`(+调试入口)、根 `.gitignore`(+models/ 与 libs/)。 ## 3. 实现要点 ### 3.1 环境复现(新机器 30 分钟口径) ```bash # ① MNN 库(2MB) mkdir -p models/mnn && cd models/mnn curl -LO https://github.com/alibaba/MNN/releases/download/3.6.1/mnn_3.6.1_harmony_arm64_cpu.zip unzip mnn_3.6.1_harmony_arm64_cpu.zip cp mnn_3.6.1_harmony_arm64_cpu/arm64-v8a/libMNN.so ../../app/entry/src/main/libs/arm64-v8a/ # 头文件(与 .so 同 tag): git clone --depth 1 --branch 3.6.1 --filter=blob:none --sparse https://github.com/alibaba/MNN.git mnn-src cd mnn-src && git sparse-checkout set include transformers/llm/engine/include cp -r include ../../app/entry/src/main/cpp/third_party/mnn/ cp -r transformers/llm/engine/include/llm ../../app/entry/src/main/cpp/third_party/mnn/include/ # ② 模型(456MB) mkdir -p models/qwen3-0.6b-mnn && cd models/qwen3-0.6b-mnn for f in config.json configuration.json llm_config.json llm.mnn tokenizer.txt llm.mnn.weight; do curl -LO "https://modelscope.cn/models/MNN/Qwen3-0.6B-MNN/resolve/master/$f" done sha256sum -c <<< "953afb7e0165818add34a7a6caf0af5d0ed9428da102eb22c9b98ee9da292e9f llm.mnn.weight" # ③ 局域网服务(手机与电脑同 Wi-Fi) node scripts/serve-models.mjs 8080 # 打印本机局域网 IP ``` ### 3.2 真机验证步骤(用户在 DevEco 做,agent 不跑) 1. `app/.build-gate.bat`(或同等 hvigor 命令)构建 → 签名安装到真机(现有 debug 签名配置可直接用); 2. App「我的」→「端侧LLM调试」→ 填开发机 IP → ① 下载 → ② 加载(记录加载耗时与 native 堆)→ ③ Benchmark(3 prompt × 3 次,首个为预热)→ ④ 意图 smoke(4 条样例人工核对 JSON 输出); 3. 全程页面保持前台亮屏(无计算类长时任务,退后台会被冻结——这是产品约束不是 bug); 4. 局域网 HTTP 明文:Network Kit 自 API 18 起默认支持明文 HTTP(本项目 targetSdk 6.1.1(24),预期零配置可用;开发后端本身也是 `http://`)。真机若仍拦截,建 `resources/base/profile/network_config.json`(network-security-config/domain-config 白名单,仅调试,不得提交 release);兜底改用 `hdc file send models/qwen3-0.6b-mnn /data/app/el2/100/base//haps/entry/files/models/`(先装 App 才有目录;注意物理路径与沙箱映射路径的区别); 5. 若 `loadModel` 报 tokenizer 相关错误:该模型仓库用的是旧格式 `tokenizer.txt`(文档默认新格式 `tokenizer.mtok`),引擎回退行为未证实——在下载目录的 `config.json` 里加一行 `"tokenizer_file": "tokenizer.txt"` 再试(只改设备上的副本,不改 manifest)。 ### 3.3 报告(`docs/开发任务包/T18-REPORT.md`,核心交付物) 结构对齐 T9:结论先行(三问三答:通过 / 有条件通过 / 不达标换方案)→ 基准数字表(机型/芯片/内存档位,首 token、总耗时、字/s、加载耗时、加载前后 native 堆、连续 9 次的降频曲线)→ 意图 smoke 明细 → 偏差与坑 → 对 P1(离线兜底)/P2(意图 shadow)的建议。 ## 4. 验收标准 1. `assembleHap` 构建绿(.build-gate.bat); 2. 真机三问三答有数字、有结论; 3. 复现文档(§3.1+§3.2)可独立执行; 4. 不入库的东西没入库:models/、libs/、任何模型权重; 5. 不改生产链路:ApiClient/ChatPage 的云端对话路径零改动(离线 fallback 是 P1 任务)。 ## 5. 禁改清单 `ets/core/ApiClient.ets`、`AuthStore.ets`、`ApiTypes.ets`、`ContextHolder.ets`、除 LlmTestPage/ProfileContent 调试入口外的全部页面与组件、`module.json5`、后端仓库一切文件。 ## 6. 提交信息 ``` spike(ondevice-llm): MNN+Qwen3-0.6B harmony pipeline, bench page, LAN model serving T18 可行性验证:native 骨架(liblocalllm+MNN 3.6.1)+ 局域网模型下载 + LlmTestPage 基准/意图 smoke + T18-REPORT 三问三答(P1~P3 的决策依据)。 偏差说明:<写在这里,无则写"无"> ```