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

7.8 KiB
Raw Blame History

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 分钟口径)

# ① 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 的决策依据)。

偏差说明:<写在这里,无则写"无">