170 lines
8.5 KiB
Markdown
170 lines
8.5 KiB
Markdown
# algpkg-sdk —— 算法包开发 SDK
|
||||
|
|
|
|||
|
|
独立项目:**算法作者只依赖本项目**,即可完成「写算法 → 本地跑通 → 打包 → 自检 → 交付 `.mbank`」,
|
|||
|
|
不需要 worker 仓库,也不需要了解 worker 内部结构。
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
算法包开发(本项目) 设备运行(worker)
|
|||
|
|
────────────────────── ──────────────────
|
|||
|
|
写代码 / 本地测试 / 打包 / 签名 → .mbank → 上传 → 挂载 → 推理
|
|||
|
|
用的接口 = algpkg_sdk.api 同一份接口(别名 video_algpkg_api)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 1. 安装
|
|||
|
|
|
|||
|
|
> 本项目是**独立仓库**(2026-10-16 从 sophon-edge 仓库拆出):
|
|||
|
|
> `git clone <本仓库地址> && cd algpkg-sdk`(目录名即项目名;下文命令都在**本项目根**执行)。
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 从本项目根安装(开发态;也可打成 wheel 分发给第三方)
|
|||
|
|
uv pip install -e . # 只做静态检查/打包的机器
|
|||
|
|
uv pip install -e '.[device]' # 设备上(含 sophon-arm,可跑推理)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
配套的 **worker(视频推理系统)仓库**只在使用 `algpkg` 工具链做集成联调时才需要:
|
|||
|
|
worker 侧 `workers/video/pyproject.toml` 以**路径依赖**指向本项目(`path = "../../../algpkg-sdk"`),
|
|||
|
|
交付打包(`deploy/pack_worker.py`)会把本项目打成 wheel 放进 worker 交付包的 `vendor/`;
|
|||
|
|
路径可用环境变量 **`ALGPKG_SDK_DIR`** 覆盖(三个开发脚本与打包脚本都认它)。
|
|||
|
|
|
|||
|
|
依赖很轻:`module-bank`(`.mbank` 读写)、`orjson`、`numpy`。
|
|||
|
|
**设备推理包(sophon-arm)是可选的** —— 没有 NPU 的机器照样能打包与做静态校验。
|
|||
|
|
|
|||
|
|
## 2. 三步上手
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# ① 生成脚手架(目录骨架 + manifest 模板 + 可运行示例 + 自测说明)
|
|||
|
|
algpkg init /tmp/smoke-detect --id demo.smoke-detect --name 抽烟检测 --version 1.0.0
|
|||
|
|
|
|||
|
|
# ② 实现 /tmp/smoke-detect/<包内代码目录>/__init__.py 的 load() / infer()
|
|||
|
|
# 模型放 /tmp/smoke-detect/models/,或打包时用 --model 指定
|
|||
|
|
|
|||
|
|
# ③ 打包 + 自检(不需要设备)
|
|||
|
|
algpkg build /tmp/smoke-detect --out demo.smoke-detect-1.0.0.mbank
|
|||
|
|
algpkg inspect demo.smoke-detect-1.0.0.mbank # 清单/模型/快照/签名
|
|||
|
|
algpkg verify demo.smoke-detect-1.0.0.mbank # 结构与模型校验
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3. 本地测试(不需要 worker)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 静态检查:manifest 合法性 / 包结构 / 模型文件 / 代码可导入(试加载)
|
|||
|
|
algpkg check demo.smoke-detect-1.0.0.mbank
|
|||
|
|
algpkg check demo.smoke-detect-1.0.0.mbank --deep # 再实例化入口类(需设备)
|
|||
|
|
|
|||
|
|
# 设备上真跑:图片或视频,warmup 后统计单帧耗时,可画框存图
|
|||
|
|
algpkg run demo.smoke-detect-1.0.0.mbank --source clip.mp4 --frames 5 --save-annotated out/
|
|||
|
|
algpkg run a-1.0.0.mbank --source a.jpg --params '{"conf":0.3}'
|
|||
|
|
|
|||
|
|
# 两个包/版本对拍(逐帧检出数 + 逐位一致性)
|
|||
|
|
algpkg compare a-1.0.0.mbank b-1.0.0.mbank --source clip.mp4
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
同样能力在 Python 里直接调(适合写进自己的 pytest):
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from algpkg_sdk import testkit
|
|||
|
|
|
|||
|
|
report = testkit.check("demo.smoke-detect-1.0.0.mbank") # 任意机器
|
|||
|
|
runs = testkit.run("demo.smoke-detect-1.0.0.mbank", source="clip.mp4",
|
|||
|
|
params={"conf": 0.3}, save_annotated="out/") # 设备
|
|||
|
|
print(runs.summary())
|
|||
|
|
print(testkit.compare(runs, testkit.run("old.mbank", source="clip.mp4")))
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4. 写算法:接口契约
|
|||
|
|
|
|||
|
|
包内代码只应导入 `video_algpkg_api`(接口契约名,= `algpkg_sdk.api` 的同一批对象)+ 设备框架:
|
|||
|
|
|
|||
|
|
> 装好本 SDK 后,这个导入名在**任何工具里都直接可用**:IDE 补全 / mypy / pyright /
|
|||
|
|
> 本地 `python -c "import video_algpkg_api"`(SDK 自带同名真实模块 + PEP 561 `py.typed`)。
|
|||
|
|
> 设备运行时 worker 仍注册同名别名,两条解析路径拿到的是同一批对象;
|
|||
|
|
> **不要**自己 vendor/复制一份同名模块(两份实现分叉 = "本地过、设备挂")。
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from video_algpkg_api import Algorithm, AlgoContext, AlgoResult, AlgoError
|
|||
|
|
|
|||
|
|
class Algorithm(Algorithm): # 类名与 manifest.entry 一致
|
|||
|
|
kind = "detector"
|
|||
|
|
|
|||
|
|
def load(self) -> None:
|
|||
|
|
data = self.ctx.model_bytes() # 包内模型字节(不落盘,直接给引擎)
|
|||
|
|
conf = float(self.ctx.param("conf", 0.25))
|
|||
|
|
self._labels = self.ctx.read_asset("assets/labels.txt").decode().splitlines()
|
|||
|
|
self._engine = MyEngine(data, self.ctx.dev_id, conf) # 例如 sail.Engine(bytes, …)
|
|||
|
|
|
|||
|
|
def infer(self, frame) -> AlgoResult: # 单帧;抽帧由运行方控制
|
|||
|
|
return AlgoResult(boxes=self._engine(frame)) # (N,6): x1,y1,x2,y2,score,cls
|
|||
|
|
|
|||
|
|
def draw(self, frame, result): # 可选:自定义叠加
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def close(self) -> None: # 实例回收时调用
|
|||
|
|
self._engine = None
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 用法 | 说明 |
|
|||
|
|
| ---- | ---- |
|
|||
|
|
| `ctx.model_bytes(name)` / `ctx.read_asset(rel)` | 读**包内**文件(模型/类别表/阈值表),不落盘;不给名 = 选中模型,给名 = 任意已声明模型(多模型包,见下) |
|
|||
|
|
| `ctx.param(key, default)` | 任务参数(`params_schema` 默认值已合并,越界在装载时被拒) |
|
|||
|
|
| `ctx.dev_id` / `ctx.logger` / `ctx.meta` | 设备号 / 日志 / 选中模型的元信息(精度、分辨率…) |
|
|||
|
|
| `infer` 返回 | `AlgoResult(boxes=(N,6), counts={"person": n}, value=n, labels=[...])` |
|
|||
|
|
| `load()` 抛异常 | 任务转 failed,错误信息带包 id/版本(先在本地 `algpkg check --deep` 拦住) |
|
|||
|
|
|
|||
|
|
约定:同一实例不会被并发调用(运行方串行化),但 `infer` 不要长时间阻塞;
|
|||
|
|
线程与内存预算见《算法包开发指南》§5。
|
|||
|
|
|
|||
|
|
**多模型包**(如"检测 + 二次判定/误报过滤"):一个包声明多份模型(`manifest.models[]`),
|
|||
|
|
代码里 `ctx.model_bytes("fp_filter")` 按名字取第二份——不必硬编码包内路径:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
def load(self):
|
|||
|
|
self._det = ctx.model_bytes() # 选中模型(manifest 里 default 的那份)
|
|||
|
|
self._cls = ctx.model_bytes("fp_filter") # 第二份:裁检测框 → 分类判误报
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
两级管线写在 `infer()` 里:返回**过滤后**的 boxes 即可,告警/统计/叠加图自动跟着走
|
|||
|
|
(运行时不做级联,这就是"包内级联"的标准形态;细节与算力旋钮见《算法包开发指南》§5)。
|
|||
|
|
|
|||
|
|
## 5. 打包选项
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
algpkg build <源目录> --out x.mbank \
|
|||
|
|
--model <模型名>=<磁盘路径> \ # 模型不在源码目录时显式指定(包内路径以 manifest 为准)
|
|||
|
|
--encrypt <Fernet KEY> \ # 加密包内代码(明文源码会被删除;模型不加密)
|
|||
|
|
--pyarmor \ # PyArmor 混淆(需授权)
|
|||
|
|
--sign <KEY> --key-id <ID> # HMAC-SHA256 签名(对称;设备侧配相同密钥验签)
|
|||
|
|
algpkg list <目录> # 列目录内的包(id/版本/大小)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 6. 目录结构
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
algpkg-sdk/
|
|||
|
|
├── pyproject.toml 项目定义(scripts: algpkg)
|
|||
|
|
├── src/algpkg_sdk/
|
|||
|
|
│ ├── api.py 接口契约(Algorithm / AlgoContext / AlgoResult)+ 别名注册
|
|||
|
|
│ ├── manifest.py 清单字段与校验(models / params_schema / alarm / legacy…)
|
|||
|
|
│ ├── loader.py AlgoPackage:读 svfs、挂代码(试加载)、构造实例
|
|||
|
|
│ ├── builder.py 打包(module_bank 打代码 + svfs 放模型/资源 + 快照=版本 + 签名)
|
|||
|
|
│ ├── testkit.py 本地测试台:check / run / compare(无需 worker)
|
|||
|
|
│ └── cli.py algpkg build|inspect|verify|init|list|check|run|compare
|
|||
|
|
├── examples/person-count/ 完整示例:人员计数(自带实现,只依赖本 SDK)
|
|||
|
|
│ └── …
|
|||
|
|
└── tests/ pytest(无设备跑静态,有设备跑推理,自动跳过)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 7. 自测
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd algpkg-sdk
|
|||
|
|
uv run pytest # 静态用例;标记为 device 的用例在无 NPU 机器上自动跳过
|
|||
|
|
uv run pytest -m device # 设备上跑推理用例
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 8. 与设备的对接
|
|||
|
|
|
|||
|
|
1. `algpkg build` 产出单文件 `.mbank`;
|
|||
|
|
2. 在设备「算法管理」页上传(或 `POST /api/v1/video/algorithm/install` 分片上传 → `commit`);
|
|||
|
|
3. 任务里选择该算法(参数表单由 `manifest.params_schema` 生成),可与其他算法并行;
|
|||
|
|
4. 现场排障与完整字段表见仓库文档《算法包开发指南》;设计依据见《算法包与多算法任务设计》。
|