Files
meida/docs/开发任务包/T18-端侧小模型SPIKE.md
T
xsl b31c7490f2 spike(llm): T18 端侧小模型——MNN native 骨架 + 局域网模型服务
- cpp/:liblocalllm.so NAPI 骨架(loadModel/chat/benchmark/release),预置 libMNN.so 3.6.1 鸿蒙包(已验证含 createLLM 符号;.gitignore 挡预编译库与 models/,获取方式见任务文档)
- LocalLlm.ets:native 调用的 ets 封装;LlmTestPage:模型从局域网服务器下载→加载→首 token/速度 benchmark
- scripts/serve-models.mjs:开发期局域网模型文件服务器(Qwen3-0.6B-MNN 456MB,SHA256 与 ModelScope 官方清单逐文件核对)
- build-profile.json5 接 externalNativeOptions(arm64-v8a);ProfileContent 加「端侧LLM调试」入口(内部调试用)
- docs/开发任务包/T18-端侧小模型SPIKE.md:目标/验证项/回退线;真机 benchmark 数据回来后写 T18-REPORT 三问三答
2026-08-30 13:18:03 +08:00

96 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<string>`(空串=成功)、`generate(prompt,maxNewTokens)→Promise<string>`(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/<bundle>/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 的决策依据)。
偏差说明:<写在这里,无则写"无">
```