|
1 | | -# 自定义算子开发指南 |
| 1 | +# 自定义算子开发规范指南 |
2 | 2 |
|
3 | | -## 算子规范 |
| 3 | +本文档旨在为开发者提供一套标准的自定义数据处理算子开发规范。通过遵循本指南,您可以开发出能够无缝集成到数据处理系统中的高质量算子。 |
4 | 4 |
|
5 | | -### 算子元数据格式 |
| 5 | +## 1. 目录结构规范 |
6 | 6 |
|
7 | | -每个自定义算子都需要包含一个 `metadata.yml` 文件: |
| 7 | +一个标准的算子开发包(Package)应包含以下核心文件。请确保文件命名准确,以便系统正确识别。 |
| 8 | + |
| 9 | +```text |
| 10 | +operator_package/ |
| 11 | +├── __init__.py # [必要] 算子注册入口,用于将算子注册到全局工厂 |
| 12 | +├── metadata.yml # [必要] 算子元数据、UI 参数定义及资源配置 |
| 13 | +├── process.py # [必要] 算子核心逻辑代码 |
| 14 | +├── requirements.txt # [可选] 算子运行所需的第三方 Python 依赖 |
| 15 | +└── README.md # [可选] 算子功能说明文档 |
| 16 | +
|
| 17 | +``` |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## 2. 元数据与配置 (metadata.yml) |
| 22 | + |
| 23 | +`metadata.yml` 定义了算子在系统中的“身份”、前端显示的配置组件以及运行时资源限制。 |
| 24 | + |
| 25 | +### 2.1 基础信息配置 |
| 26 | + |
| 27 | +| 字段 | 说明 | 示例 | |
| 28 | +| --- |------------------------------------| --- | |
| 29 | +| `name` | 算子显示名称 | 测试算子 | |
| 30 | +| `description` | 算子描述 | 这是一个测试算子。 | |
| 31 | +| `language` | 算子使用的语言,当前仅支持python | python | |
| 32 | +| `raw_id` | **关键字段**,必须与 `process.py` 中的类名完全一致 | TestMapper | |
| 33 | +| `version` | 语义化版本号 | 1.0.0 | |
| 34 | +| `modal` / `inputs` / `outputs` | 支持的数据模态 (text/image/audio/video) | text | |
| 35 | + |
| 36 | +### 2.2 算子版本更新日志 (release) |
| 37 | + |
| 38 | +定义算子当前版本较上版本更新内容。 |
8 | 39 |
|
9 | 40 | ```yaml |
10 | | -name: '测试算子' |
11 | | -description: '这是一个测试算子。' |
12 | | -language: 'python' |
13 | | -vendor: 'huawei' |
14 | | -raw_id: 'TestMapper' |
15 | | -version: '1.0.0' |
16 | | -modal: 'text' # text/image/audio/video/multimodal |
17 | | -inputs: 'text' |
18 | | -outputs: 'text' |
| 41 | +release: |
| 42 | + - '首次发布' |
| 43 | + - '支持基本处理操作' |
19 | 44 | ``` |
20 | 45 |
|
21 | | -### 算子实现 |
| 46 | +### 2.2 运行时资源与指标 (runtime & metrics) |
22 | 47 |
|
23 | | -#### process.py |
| 48 | +定义算子运行时的资源配额及性能指标参考。 |
24 | 49 |
|
25 | | -```python |
26 | | -# -*- coding: utf-8 -*- |
| 50 | +```yaml |
| 51 | +runtime: |
| 52 | + memory: 10MB # 内存限制 |
| 53 | + cpu: 1000m # CPU 核心数 (m代表毫核) |
| 54 | + gpu: 0.1 # GPU 卡数 |
| 55 | + npu: 0.1 # NPU 卡数 |
| 56 | + storage: 10MB # 存储空间 |
27 | 57 |
|
28 | | -# 导入所需数据结构,可以通过以下方式直接导入使用 |
29 | | -# 提供两种算子类: |
30 | | -# Mapper用于映射和转换数据,使用时直接修改数据内容 |
31 | | -from datamate.core.base_op import Mapper |
| 58 | +metrics: # 算子性能参考指标 |
| 59 | + - name: '吞吐量' |
| 60 | + metric: '20 images/sec' |
| 61 | + - name: '准确率' |
| 62 | + metric: '99.5%' |
| 63 | +``` |
32 | 64 |
|
33 | | -class TestMapper(Mapper): |
34 | | - def execute(self, sample): |
35 | | - sample[self.text_key] += "\n新增的数据" |
36 | | - return sample |
| 65 | +### 2.3 参数设置 (settings) - UI 组件规范 |
| 66 | +
|
| 67 | +通过 `settings` 字段,开发者可以自定义用户在前端界面配置算子时的交互组件。系统支持以下类型: |
37 | 68 |
|
| 69 | +* **Slider (滑动条)** - 用于数值范围调整 |
| 70 | +```yaml |
| 71 | +sliderParam: # 参数的唯一标识符,process.py 中通过该参数获取值 |
| 72 | + name: '参数展示名称' # 界面上显示给用户的参数标题 |
| 73 | + description: '参数展示描述' # 用户鼠标悬停时显示的详细功能说明或帮助文本 |
| 74 | + type: 'slider' # 组件类型 |
| 75 | + defaultVal: 0.5 # 算子加载时的初始值,必须位于 min 和 max 之间 |
| 76 | + required: false # 是否必填 |
| 77 | + min: 0 # [下限] 滑动条允许调整的最小值 |
| 78 | + max: 1 # [上限] 滑动条允许调整的最大值 |
| 79 | + step: 0.1 # [步长] 每次拖动的增量 (例如 0.1 代表保留一位小数,1 代表整数) |
| 80 | +``` |
38 | 81 |
|
39 | | -# Filter用于过滤和选择性保留数据,使用时将需要过滤的数据的text或data置为空值 |
40 | | -from datamate.core.base_op import Filter |
| 82 | +* **Switch (开关)** - 用于布尔值控制 |
| 83 | +```yaml |
| 84 | +switchParam: |
| 85 | + name: '参数展示名称' |
| 86 | + description: '参数展示描述' |
| 87 | + type: 'switch' |
| 88 | + defaultVal: 'true' # 注意 yaml 中布尔值建议使用引号或标准写法 |
| 89 | + required: false # 是否必须参数 |
| 90 | + checkedLabel: '选中' # 选中时的展示 |
| 91 | + unCheckedLabel: '未选中' # 未选中时的展示 |
| 92 | +``` |
41 | 93 |
|
42 | | -class TestFilter(Filter): |
43 | | - def execute(self, sample): |
44 | | - if len(sample[self.text_key]) > 100: |
45 | | - sample[self.text_key] += "" |
46 | | - return sample |
| 94 | +* **Select / Radio (下拉 / 单选)** - 用于枚举选项 |
| 95 | +```yaml |
| 96 | +selectParam: |
| 97 | + name: '参数展示名称' |
| 98 | + description: '参数展示描述' |
| 99 | + type: 'select' # 或 'radio' |
| 100 | + defaultVal: 'option1' # 默认后端传递值,为options其中一个选项 |
| 101 | + required: false |
| 102 | + options: |
| 103 | + - label: '选项1' # 前端显示标签 |
| 104 | + value: 'option1' # 后端传递值 |
| 105 | + - label: '选项2' |
| 106 | + value: 'option2' |
| 107 | +``` |
47 | 108 |
|
| 109 | +* **Range (范围区间)** |
| 110 | +```yaml |
| 111 | +rangeParam: |
| 112 | + name: '参数展示名称' |
| 113 | + description: '参数展示描述' |
| 114 | + type: 'range' |
| 115 | + properties: |
| 116 | + - name: 'rangeLeft' # 范围下限 |
| 117 | + type: 'inputNumber' |
| 118 | + defaultVal: 100 |
| 119 | + min: 0 |
| 120 | + max: 10000 |
| 121 | + step: 1 |
| 122 | + - name: 'rangeRight' # 范围上限 |
| 123 | + type: 'inputNumber' |
| 124 | + defaultVal: 8000 |
| 125 | + min: 0 |
| 126 | + max: 10000 |
| 127 | + step: 1 |
48 | 128 | ``` |
49 | 129 |
|
50 | | -其中,sample的数据结构如下所示: |
51 | | -```json lines |
52 | | -// 数据结构 |
53 | | -{ |
54 | | - "text": "数据文件的文本内容", |
55 | | - "data": "多模态文件的内容", |
56 | | - "fileName": "文件名称", |
57 | | - "fileType": "文件类型", |
58 | | - "filePath": "文件路径", |
59 | | - "fileSize": "文件大小", |
60 | | - "export_path": "保存的文件路径", |
61 | | - "extraFileType": "导出的文件类型" |
62 | | -} |
63 | | - |
64 | | -// 数据示例 |
65 | | -{ |
66 | | - "text": "text", |
67 | | - "data": "data", |
68 | | - "fileName": "test", |
69 | | - "fileType": "pdf", |
70 | | - "filePath": "/dataset/test.pdf", |
71 | | - "fileSize": "100B", |
72 | | - "export_path": "/dataset/test.txt", |
73 | | - "extraFileType": "txt" |
74 | | -} |
| 130 | +* **Checkbox (多选)** |
| 131 | +```yaml |
| 132 | +checkboxParam: |
| 133 | + name: '参数展示名称' |
| 134 | + description: '参数展示描述' |
| 135 | + type: 'checkbox' |
| 136 | + defaultVal: 'option1,option2' # 多个值用逗号分隔 |
| 137 | + required: false |
| 138 | + options: |
| 139 | + - label: '选项1' |
| 140 | + value: 'option1' |
| 141 | + - label: '选项2' |
| 142 | + value: 'option2' |
| 143 | +
|
| 144 | +``` |
| 145 | + |
| 146 | +* **Input (文本输入)** |
| 147 | +```yaml |
| 148 | +inputParam: |
| 149 | + name: '参数展示名称' |
| 150 | + description: '参数展示描述' |
| 151 | + type: 'input' |
| 152 | + defaultVal: '默认值' |
| 153 | + required: false |
75 | 154 | ``` |
76 | 155 |
|
77 | | -#### \_\_init__.py |
| 156 | +--- |
| 157 | + |
| 158 | +## 3. 核心逻辑实现 (`process.py`) |
| 159 | + |
| 160 | +`process.py` 是算子的执行主体。开发者需继承基础类并实现数据处理逻辑。 |
| 161 | + |
| 162 | +### 开发规范 |
| 163 | + |
| 164 | +1. **继承基类**:必须从 `datamate.core.base_op` 继承 `Mapper`或 `Filter`。 |
| 165 | +2. **类名一致性**:Python 类名建议与后续 `metadata.yml` 中的 `raw_id` 保持一致。 |
| 166 | +3. **Execute 方法**:必须实现 `execute` 方法,接收 `sample` (字典) 并返回处理后的字典。 |
| 167 | + |
| 168 | +### 代码模板 |
78 | 169 |
|
79 | 170 | ```python |
80 | | -# -*- coding: utf-8 -*- |
| 171 | +from typing import Dict, Any |
| 172 | +from datamate.core.base_op import Mapper |
| 173 | +
|
| 174 | +class YourOperatorName(Mapper): |
| 175 | + """ |
| 176 | + 算子类名建议使用驼峰命名法定义,例如 TestMapper |
| 177 | + """ |
| 178 | + |
| 179 | + def __init__(self, *args, **kwargs): |
| 180 | + super().__init__(*args, **kwargs) |
| 181 | + self.slider_param = float(kwargs.get("sliderParam", 0.5)) |
| 182 | + self.switch_param = kwargs.get('switchParam', False) |
| 183 | + self.select_param = kwargs.get('selectParam', '') |
| 184 | + self.radio_param = kwargs.get('radioParam', '') |
| 185 | + self.range_param = kwargs.get('rangeParam', [0, 0]) |
| 186 | + self.checkbox_param = kwargs.get('checkboxParam', []) |
| 187 | + self.input_param = kwargs.get('inputParam', '').strip() |
| 188 | + |
| 189 | + def execute(self, sample: Dict[str, Any]) -> Dict[str, Any]: |
| 190 | + """ |
| 191 | + 核心处理逻辑 |
| 192 | + :param sample: 输入的数据样本,通常包含 text_key 等字段 |
| 193 | + :return: 处理后的数据样本 |
| 194 | + """ |
| 195 | + # 示例:获取文本并进行修改 |
| 196 | + # input_text = sample['text'] |
| 197 | + # processed_text = do_something(input_text) |
| 198 | + # sample['text'] = processed_text |
| 199 | + |
| 200 | + return sample |
| 201 | +
|
| 202 | +``` |
| 203 | + |
| 204 | +--- |
| 205 | + |
| 206 | +## 4. 算子注册 (`__init__.py`) |
81 | 207 |
|
82 | | -# 导入OPERATORS用于进行模块注册,可以通过以下方式直接导入使用 |
| 208 | +`__init__.py` 用于将开发好的算子注册到系统中。 |
| 209 | + |
| 210 | +### 注册规范 |
| 211 | + |
| 212 | +使用 `OPERATORS.register_module` 方法进行注册。 |
| 213 | + |
| 214 | +* **module_name**: 对应 `metadata.yml` 中的 `raw_id` 及 Python 类名。 |
| 215 | +* **module_path**: 指向 `process.py` 的引用路径(注意路径层级)。 |
| 216 | + |
| 217 | +### 代码模板 |
| 218 | + |
| 219 | +```python |
| 220 | +# -*- coding: utf-8 -*- |
83 | 221 | from datamate.core.base_op import OPERATORS |
84 | 222 |
|
85 | | -# module_name必须填写算子类名称;module_path中须替换模块的算子压缩包名称:python_operator.user.压缩包名.process |
86 | | -OPERATORS.register_module(module_name='TestMapper', |
87 | | - module_path="ops.user.test_operator.process") |
| 223 | +# 假设 process.py 位于 operator_package 目录下 |
| 224 | +OPERATORS.register_module( |
| 225 | + module_name='YourOperatorName', |
| 226 | + module_path="ops.user.operator_package.process" |
| 227 | +) |
88 | 228 |
|
89 | 229 | ``` |
| 230 | + |
| 231 | +--- |
| 232 | + |
| 233 | +## 5. 开发注意事项 |
| 234 | + |
| 235 | +1. **依赖管理**:如果算子依赖非标准库(如 pandas, numpy),请在 `requirements.txt` 中列出。 |
| 236 | +2. **异常处理**:在 `process.py` 中建议添加适当的 try-catch 逻辑,避免单条数据异常导致整个任务崩溃。 |
| 237 | +3. **数据类型**:在 `metadata.yml` 中定义的参数类型(如 slider 返回 float,input 返回 string),在 Python 代码中使用时需注意类型转换。 |
| 238 | + |
| 239 | +--- |
| 240 | + |
| 241 | +## 6. 算子打包与上传 |
| 242 | + |
| 243 | +开发完成后,需将所有文件打包为一个压缩包进行上传。**包名必须严格遵循注册路径规范**。 |
| 244 | + |
| 245 | +### 打包规范 |
| 246 | + |
| 247 | +1. **文件完整性**:压缩包内必须包含 `__init__.py`, `metadata.yml`, `process.py` 及相关依赖。 |
| 248 | +2. **命名一致性(重要)**:压缩包的文件名(不含后缀)必须与 `__init__.py` 中 `module_path` 所指定的**包目录名**保持一致。 |
| 249 | + |
| 250 | +### 示例说明 |
| 251 | + |
| 252 | +假设您在 `__init__.py` 中的注册代码如下: |
| 253 | + |
| 254 | +```python |
| 255 | +OPERATORS.register_module( |
| 256 | + module_name='TestMapper', |
| 257 | + # 这里 ops.user.my_custom_op.process 中的 'my_custom_op' 为包目录名 |
| 258 | + module_path="ops.user.my_custom_op.process" |
| 259 | +) |
| 260 | +
|
| 261 | +``` |
| 262 | + |
| 263 | +则您的压缩包名称必须命名为: |
| 264 | + |
| 265 | +> **`my_custom_op.zip`** (或 .tar) |
| 266 | + |
| 267 | +系统解压后将基于此名称构建导入路径,名称不一致将导致 `ModuleNotFoundError`。 |
0 commit comments