本指南用于指导新模型算法服务的设计与实现,确保架构一致性和代码质量。
更新时间:2026年4月2日 - v1.4
- 最小可用:首先实现核心功能(训练+服务),确保端到端可用
- 避免过早抽象:不提前设计未来可能需要的功能(见下方详细说明)
- 易于扩展:通过清晰的抽象支持后续功能添加
- 统一架构:遵循现有三个服务的架构模式
核心理念:
- ✅ 先实现,后抽象:等真正需要时再提取公共逻辑
- ✅ 务实设计:代码重复优于错误的抽象
- ✅ 明确职责:基类定义接口契约,不实现业务逻辑
algorithms/
└── classify_{domain}_server/ # 模型服务根目录
├── README.md # 项目说明(简洁即可)
├── Makefile # 构建和运行脚本
├── pyproject.toml # Python项目配置(定义项目依赖与构建入口)
├── pytest.ini # 测试配置
├── .env.example # 环境变量示例
├── .gitignore # Git忽略文件
├── classify_{domain}_server/ # 主包
│ ├── __init__.py
│ │
│ ├── cli/ # 命令行接口
│ │ ├── __init__.py
│ │ └── bootstrap.py # CLI入口(使用fire)
│ │
│ ├── serving/ # 在线服务层
│ │ ├── __init__.py
│ │ ├── service.py # BentoML服务定义
│ │ ├── config.py # 服务配置
│ │ ├── schemas.py # API Schema(Pydantic)
│ │ ├── exceptions.py # 自定义异常
│ │ ├── metrics.py # Prometheus指标
│ │ └── models/ # 模型加载器
│ │ ├── __init__.py
│ │ ├── loader.py # 统一模型加载(支持 MLflow/本地/DummyModel)
│ │ └── dummy_model.py # 降级策略(可选,详见"DummyModel 降级策略"章节)
│ │
│ └── training/ # 离线训练层
│ ├── __init__.py
│ ├── trainer.py # 通用训练器
│ ├── data_loader.py # 数据加载
│ ├── mlflow_utils.py # MLflow工具函数
│ ├── config/ # 训练配置
│ │ ├── __init__.py
│ │ └── loader.py # 配置加载器
│ ├── preprocessing/ # 数据预处理
│ │ ├── __init__.py
│ │ ├── preprocessor.py # 基础预处理器
│ │ └── feature_engineering.py # 特征工程模块
│ │ # - 文件必须存在:每个服务都需要实现此模块
│ │ # - 至少实现一种方法:如滞后特征、滚动窗口等
│ │ # - 训练时可选:通过 use_feature_engineering 开关控制
│ │ # - 示例:时间序列的滞后特征,异常检测的统计特征
│ └── models/ # 训练模型实现
│ ├── __init__.py
│ ├── base.py # 抽象基类 + ModelRegistry
│ ├── {algorithm}_model.py # 具体模型实现
│ └── {algorithm}_wrapper.py # MLflow推理包装器(可选)
│
├── support-files/ # 支持文件
│ ├── release/ # 发布相关文件
│ │ ├── Dockerfile # 容器镜像定义
│ │ ├── startup.sh # 容器启动脚本
│ │ └── supervisor/ # Supervisor进程管理配置
│ │ ├── supervisord.conf
│ │ └── conf.d/
│ │ ├── bentoml.conf # BentoML服务配置
│ │ └── mlflow.conf # MLflow UI配置
│ ├── scripts/ # 脚本目录
│ │ ├── train.json # 默认训练配置
│ │ ├── train-model.sh # 训练执行脚本(调用 download_dataset.py 下载数据/配置并训练)
│ │ ├── download_dataset.py # MinIO 数据下载脚本(使用 MinIO Python Client SDK)
│ │ ├── test-predict.sh # 预测测试脚本
│ │ └── data/ # 训练时数据目录(运行时创建)
│ │ ├── downloads/ # download_dataset.py 下载的压缩包
│ │ ├── datasets/ # 解压后的数据集
│ │ └── configs/ # download_dataset.py 下载的配置文件
│ └── train.json.example # 训练配置示例(文档参考)
│
└── tests/ # 测试用例
├── __init__.py
├── conftest.py # pytest配置和fixtures
└── test_*.py # 测试文件
- cli/: 命令行工具(
train,serve) - serving/: 在线服务(BentoML + Prometheus监控)
- training/: 离线训练(Trainer + MLflow集成)
- release/: 容器化部署相关
Dockerfile: 统一的镜像构建(Python 3.12 + pip + fonts)startup.sh: 容器启动入口supervisor/: 进程管理配置(服务+MLflow UI)
- scripts/: 训练和测试脚本
train.json: 默认训练配置train-model.sh: 自动化训练流程(调用download_dataset.py从 MinIO 下载 + 解压 + 训练)download_dataset.py: MinIO 数据下载脚本(使用 MinIO Python Client SDK,而非mc二进制)data/: 运行时数据目录(不纳入版本控制)
数据集流向:
MinIO (datasets bucket)
↓ train-model.sh 调用 download_dataset.py 下载
scripts/data/downloads/*.zip
↓ unzip
scripts/data/datasets/*_data.csv (文件格式不固定,不同训练类型会有对应的格式)
↓ Trainer 读取
training/data_loader.py
↓ 预处理
training/preprocessing/
↓ 训练
training/models/
↓ 保存
MLflow Model Registry
↓ 加载
serving/models/loader.py
↓ 预测
serving/service.py
配置文件流向:
train-model.sh 接收 CONFIG 参数
├─ 传入对象路径 → 通过 download_dataset.py 从 MinIO 下载到本地
└─ 未传入 → 使用默认配置(./train.json)
↓
CLI bootstrap.py (--config 参数)
↓ 验证文件存在
TrainingConfig.from_file()
↓ 加载配置
UniversalTrainer(config_obj)
↓ 应用配置
模型训练(超参数、预处理、特征工程等)
标准10步训练流程:
- MLflow 实验设置
- 数据加载(支持目录/文件模式)
- 目录模式:传递的路径是目录,一般包含三个文件 train_data val_data test_data 文件类型视训练类型而定
- 文件模式:传递的路径是具体文件路径,需要在数据预处理时进行分割成train_data val_data test_data,分割比例代码写死固定比例
- 数据预处理
- 模型实例化(通过 ModelRegistry 动态加载)
- 开始 MLflow run
- 记录配置参数
- 超参数优化(使用 Hyperopt)
- 模型训练
- 模型评估(train/val/test)
- 模型保存和注册
self._save_model_to_mlflow(model, metrics): 保存模型到 MLflowself._register_model(model_uri): 注册模型到 MLflow Model Registry
超参数优化架构(Trainer 调度,Model 实现):
def _optimize_hyperparams(self, train_data, val_data) -> Optional[Dict[str, Any]]:
"""超参数优化统一调度
架构:Trainer 负责配置检查和错误处理,Model 实现具体优化逻辑
超参数优化流程:
1. Hyperopt 生成候选参数组合
2. 对每个候选参数:
- 创建临时模型
- 在 train_data 上训练临时模型
- 在 val_data 上评估性能(计算目标函数值)
3. 找到最优参数后:
- 传统ML算法:在 train_data + val_data 上训练最终模型(利用更多数据)
- 深度学习算法:在 train_data 上训练,val_data 用于 early stopping(YOLO框架)
val_data 的两种用途:
- 超参数搜索阶段:用于评估候选参数的性能
- 最终训练阶段:
* 传统ML: 合并到训练集
* 深度学习: 用于验证和early stopping
四种使用模式示例:
1. 时间序列(Prophet/RandomForest):
- 超参数搜索: fit(train_data), evaluate(val_data)
- 最终训练: fit(train_data + val_data)
2. 日志聚类(Spell):
- 超参数搜索: fit(train_data), evaluate(val_data)
- 最终训练: fit(train_data + val_data)
3. 异常检测(ECOD):
- 超参数搜索: fit(train_data), evaluate(val_data, val_labels)
- 最终训练: fit(train_data + val_data)
4. 图片分类/目标检测(YOLO):
- 超参数搜索: model.train(data='train+val.yaml')
- 最终训练: model.train(data='train+val.yaml') # YOLO框架内部处理
"""
# 1. 检查是否启用(max_evals=0 表示跳过)
max_evals = getattr(self.config, 'max_evals', 0)
if max_evals == 0:
return {}
# 2. 调用模型的优化方法(使用 Hyperopt)
try:
return self.model.optimize_hyperparams(train_data, val_data, max_evals)
except Exception as e:
logger.error(f"优化失败: {e}")
return {}关键方法:
_create_model(): 通过 ModelRegistry 动态创建模型实例_preprocess_data(): 数据预处理(子类实现)_optimize_hyperparams(): 统一的超参数优化调度_evaluate_train_fitting(): 评估训练集拟合度_evaluate_test(): 评估测试集性能_save_model_to_mlflow(): 保存模型为 MLflow 格式
详细实现请参考现有项目:classify_timeseries_server/training/trainer.py
基类职责:
- ✅ 定义接口契约:通过
@abstractmethod声明必须实现的方法 - ✅ 提供工具方法:如
get_params()、_check_fitted()等通用辅助 - ❌ 不实现业务逻辑:不在基类中实现具体的评估、预处理等逻辑
必须实现的抽象方法:
fit(train_data, val_data, **kwargs): 训练模型predict(X): 模型预测evaluate(test_data, ground_truth, prefix="test"): 评估性能,返回指标字典optimize_hyperparams(train_data, val_data, max_evals): 超参数优化(使用 Hyperopt)
可选工具方法:
get_params(): 获取模型参数_check_fitted(): 检查模型是否已训练
ModelRegistry 注册机制:
@ModelRegistry.register("my_model")
class MyModel(Base{Domain}Model):
...
# 动态创建
model = ModelRegistry.get("my_model")(**params)命名规范:
- 时间序列:
BaseTimeSeriesModel - 日志分析:
BaseLogClusterModel - 异常检测:
BaseAnomalyModel
评估指标要求:
-
返回格式:
{prefix}_{metric_name}: value- prefix: train/val/test
- metric_name: 小写下划线
- 示例:
{"test_rmse": 0.123, "test_mae": 0.456}
-
命名规范(按领域):
- 时间序列:rmse, mae, mape, r2
- 异常检测:f1, precision, recall, auc, roc_auc
- 日志聚类:template_quality_score, coverage_rate, num_templates
- 文本分类:accuracy, f1_macro, f1_weighted
- 图片分类/目标检测:accuracy, precision, recall, map50, map50_95
-
计算方式:
- 优先使用 sklearn.metrics(如
sklearn.metrics.mean_squared_error) - 自定义指标需在代码中详细注释计算逻辑
- 确保指标方向一致(越大越好 或 越小越好)
- 优先使用 sklearn.metrics(如
-
内部数据:
- 使用
_前缀(如_predictions,_raw_scores) - 这些数据不会被 MLflow 记录,仅用于调试
- 使用
完整实现参考:
classify_timeseries_server/training/models/base.pyclassify_anomaly_server/training/models/base.pyclassify_log_server/training/models/base.py
训练完成后自动执行两步:
-
保存到 MLflow:模型类实现
save_mlflow(artifact_path="model")方法- 创建 MLflow pyfunc Wrapper(如需要)
- 调用
mlflow.pyfunc.log_model()保存模型和 artifacts
-
注册到 Model Registry:Trainer 实现
_register_model(model_uri)方法- 默认启用,无需配置开关
- 使用
config.model_name作为注册名称 - 每次训练自动递增版本号
配置示例:
{
"model": {
"type": "Spell",
"name": "spell_log_clustering" // Model Registry 注册名
},
"mlflow": {
"_comment": "MLflow 实验跟踪配置",
"experiment_name": "log_clustering_spell", // 实验名(一般与model.name一致)
"_note": "tracking_uri 通过环境变量 MLFLOW_TRACKING_URI 配置"
}
}参考实现:
- 保存:
classify_log_server/training/models/spell_model.py-save_mlflow() - 注册:
classify_anomaly_server/training/trainer.py-_register_model()
何时需要:
- ✅ 推理需要额外的预处理/后处理逻辑
- ✅ 需要在推理时使用特征工程器
- ✅ 推理逻辑与训练差异大(如递归预测)
- ❌ 简单 sklearn 模型可直接用
mlflow.sklearn.log_model()
核心作用:
- 继承
mlflow.pyfunc.PythonModel - 实现
predict(context, model_input)方法(不加类型提示,避免警告) - 封装完整推理流程(解析输入 → 特征工程 → 预测 → 后处理)
参考实现:
classify_timeseries_server/training/models/gradient_boosting_wrapper.pyclassify_log_server/training/models/spell_wrapper.py
何时实现:
- ✅ 开发/测试环境:显式选择
MODEL_SOURCE=dummy,或明确设置ALLOW_DUMMY_FALLBACK=true - ❌ 生产环境:显式选择 MLflow / 本地模型后,加载失败必须让服务启动失败
核心作用:
- 为开发、测试和演示提供明确可识别的占位模型
- 仅在显式授权时为 MLflow / 本地模型加载失败提供降级
- 防止生产健康检查正常但实际返回 Dummy 预测
实现要求:
-
返回格式必须与真实模型完全一致
- 异常检测:
{'labels': list[int], 'scores': list[float], 'probabilities': list[float]} - 时间序列:
list[float](预测值列表) - 日志聚类:
float(单个预测值) - 文本/图片分类:根据具体任务定义
- 异常检测:
-
返回安全的默认值
- 异常检测:基于简单统计规则(如 z-score)
- 时间序列:naive forecast(重复最后观测值)
- 日志聚类:特征值求和
- 根据业务需求选择合理的降级策略
-
记录显式降级日志
logger.warning("ALLOW_DUMMY_FALLBACK=true,使用 DummyModel 降级") logger.info("DummyModel initialized for {domain}")
实现示例:
# serving/models/dummy_model.py
from loguru import logger
class DummyModel:
"""模拟模型,用于降级策略."""
def __init__(self):
self.version = "0.1.0-dummy"
logger.info("DummyModel initialized for {domain}")
def predict(self, model_input: dict) -> dict:
"""
模拟预测(返回格式必须与真实模型一致).
Args:
model_input: 模型输入(格式与真实模型相同)
Returns:
预测结果(格式与真实模型相同)
"""
# 实现简单的降级逻辑
# 示例:异常检测返回基于 z-score 的简单规则
# 示例:时间序列返回 naive forecast
pass服务层集成:
# serving/models/loader.py
from .dummy_model import DummyModel
def load_model(config: ModelConfig):
"""加载模型;生产加载失败时快速失败."""
if config.source == "mlflow":
try:
model = mlflow.pyfunc.load_model(config.mlflow_model_uri)
logger.info("MLflow model loaded successfully")
return model
except Exception as e:
logger.error(f"Failed to load MLflow model: {e}")
if os.getenv("ALLOW_DUMMY_FALLBACK", "false").lower() == "true":
logger.warning("Explicit fallback enabled, using DummyModel")
return DummyModel()
raise RuntimeError("Failed to load model from MLflow") from e
elif config.source == "dummy":
return DummyModel() # 显式使用 DummyModel
else:
raise ValueError(f"Unknown model source: {config.source}")参考实现:
- 异常检测:
classify_anomaly_server/serving/models/dummy_model.py(基于 z-score 规则) - 时间序列:
classify_timeseries_server/serving/models/dummy_model.py(naive forecast) - 日志聚类:
classify_log_server/serving/models/dummy_model.py(特征求和) - 加载器:
classify_anomaly_server/serving/models/loader.py(统一降级逻辑)
配置方式:
# bentofile.yaml 或环境变量
MODEL_SOURCE: "mlflow" # 优先使用 MLflow
# MODEL_SOURCE: "dummy" # 显式使用 DummyModel(测试用)
ALLOW_DUMMY_FALLBACK: "false" # 生产默认快速失败注意事项:
- DummyModel 不应用于生产预测;生产发布必须保持
ALLOW_DUMMY_FALLBACK=false - 开发/测试需要降级时必须显式开启,并通过日志区分真实模型与 DummyModel
- 容器编排应等待服务 readiness;模型加载失败不得被 Supervisor 或容器重启环掩盖
服务结构:
@bentoml.service: 服务装饰器,配置超时等参数@bentoml.on_deployment: 全局初始化(执行一次)__init__(): 服务实例初始化(加载配置和模型)@bentoml.on_shutdown: 服务关闭时的清理@bentoml.api: API 端点定义
关键功能:
- 模型加载:支持本地文件和 MLflow Registry
- 配置验证:启动时快速失败(Fast Fail)
- 监控指标:Prometheus metrics(加载次数、预测次数、延迟)
- 错误处理:统一的异常处理和日志记录
- DummyModel 降级:模型加载失败时的降级策略(可选,详见"DummyModel 降级策略"章节)
必需的 API 端点:
predict(): 主要预测接口(使用 Pydantic schemas)health(): 健康检查接口
详细实现请参考现有项目的 serving/service.py。
核心原则:
- 使用扁平化结构,避免过度嵌套
- 包含必要的注释字段(
_comment、_desc) - 提供合理的默认值
- 支持超参数搜索空间定义
- 配置外部化:配置应存储在文件中,而非代码中硬编码
固定的顶层字段:
model: 模型配置(type, name)hyperparams: 超参数配置(含搜索空间,含use_feature_engineering开关)preprocessing: 数据预处理配置feature_engineering: 特征工程配置(必须实现,训练时由use_feature_engineering控制)mlflow: MLflow 实验跟踪配置experiment_name: 实验名称(必需,在配置文件中指定)tracking_uri:⚠️ 不在配置文件中设置,通过环境变量MLFLOW_TRACKING_URI注入
设计原则:
- ✅ tracking_uri 通过环境变量注入:符合云原生部署最佳实践
- ✅ 动态注入模式:bootstrap.py 读取环境变量并注入到配置对象
- ✅ 配置文件分离:配置文件只包含业务配置,运行时环境配置通过环境变量管理
实现流程:
环境变量 MLFLOW_TRACKING_URI
↓
bootstrap.py 读取并注入 → config.set("mlflow", "tracking_uri", value=tracking_uri)
↓
config.mlflow_tracking_uri → trainer 使用
配置文件示例:
{
"mlflow": {
"_comment": "MLflow 实验跟踪配置",
"experiment_name": "text_classification_xgboost",
"_note": "tracking_uri 通过环境变量 MLFLOW_TRACKING_URI 配置,不在此文件中设置"
}
}环境变量设置:
# 开发/生产环境 (开发者手动配置.env 文件)
export MLFLOW_TRACKING_URI=http://mlflow:5000
# 容器部署(环境变量或 Kubernetes ConfigMap)
MLFLOW_TRACKING_URI=http://mlflow-service:5000推荐方式:单一文件模式 + 职责分离
class TrainingConfig:
def __init__(self, config_path: str):
"""从文件加载配置
Args:
config_path: 配置文件路径(必需参数)
Raises:
FileNotFoundError: 配置文件不存在
"""
config_path = Path(config_path)
if not config_path.exists():
raise FileNotFoundError(f"配置文件不存在: {config_path}")
with open(config_path, "r", encoding="utf-8") as f:
self.config = json.load(f)
self._validate_config()设计理念(职责分离):
- ✅ 配置外部化:配置存储在文件中,便于版本控制
- ✅ 职责清晰:train-model.sh 管理配置来源,Python代码只负责加载
- ✅ 无硬编码:代码中不保存任何默认路径或默认配置
- ✅ 符合单一职责原则:每个组件只做一件事
职责划分:
| 组件 | 职责 |
|---|---|
| train-model.sh | • 决定配置文件路径(默认 ./train.json) • 从 MinIO 下载配置(如需要) • 传递明确路径给 CLI |
| CLI (bootstrap.py) | • 接收配置路径参数(必需) • 传递给 TrainingConfig |
| TrainingConfig | • 加载给定路径的配置文件 • 验证配置正确性 • 提供配置访问接口 |
配置来源管理流程:
train-model.sh 接收 CONFIG 参数
├─ 参数传入 → 使用指定配置
└─ 参数未传入 → 使用默认配置(./train.json)
↓
train-model.sh 准备配置文件
├─ MinIO 路径 → 下载到本地
└─ 本地路径 → 检查文件存在性
↓
train-model.sh 调用 CLI(总是传入明确路径)
uv run classify_{domain}_server train \
--dataset-path=... \
--config="$CONFIG"
↓
CLI 接收 --config 参数(必需)
↓
TrainingConfig(config_path) # config_path 是必需参数
↓
加载并验证配置文件
配置文件存放位置:
support-files/
├── train.json.example # 详细示例(文档参考,带大量注释)
└── scripts/
├── train.json # 默认配置(train-model.sh 使用)
└── data/ # 运行时数据目录
重要说明:
- ✅
train.json是完整配置,包含所有必需字段 - ✅ 代码中不存在
DEFAULT_CONFIG字典 - ✅ 配置文件是配置的唯一真实来源
- ✅ train-model.sh 决定使用哪个配置文件
测试环境配置:
tests/
└── fixtures/
└── test_config.json # 测试专用配置(完整配置)
大体结构,具体参数由类型实现而定 具体参考各类型具体实现的support-files文件夹中的示例文件
{
"model": {
"type": "模型类型标识符(对应 ModelRegistry 注册名)",
"name": "模型名称(用于 MLflow 记录)"
},
"hyperparams": {
"use_feature_engineering": "是否启用特征工程(布尔值)",
"random_state": "随机种子(整数)",
"max_evals": "超参数搜索次数(0表示跳过)",
"metric": "优化目标指标名称",
"search_space": {
"param_name": ["候选值列表"]
}
},
"feature_engineering": {
"...": "特征工程详细配置(由 use_feature_engineering 控制是否启用)"
},
"preprocessing": {
"...": "预处理配置"
},
"mlflow": {
"experiment_name": "实验名称"
}
}配置验证:
实现4层验证机制,确保配置的正确性和完整性:
def _validate_config(self):
"""配置验证(推荐实现4层)"""
# Layer 1: 结构完整性校验
self._validate_structure()
# Layer 2: 必需字段 + 基本类型校验
self._validate_required_fields()
# Layer 3: 业务规则校验
self._validate_business_rules()
# Layer 4: 依赖关系校验
self._validate_dependencies()验证层次说明:
- Layer 1: 检查必需的顶层字段是否存在
- Layer 2: 检查字段类型和必填项
- Layer 3: 检查参数值的合理性(如范围、枚举值)
- Layer 4: 检查条件依赖(如
use_feature_engineering=true时必须提供feature_engineering配置)
注意:
- ✅ 前两层为最小验证要求
- ✅ 后两层可根据项目复杂度选择性实现
⚠️ 验证失败应立即抛出异常(Fast Fail)⚠️ 由于没有代码默认值,配置文件必须完整
schema.py 的作用:
"""配置文件 Schema 定义"""
from typing import List
# ✅ 定义支持的枚举值(用于验证)
SUPPORTED_MODELS: List[str] = [
"model_type_1",
"model_type_2",
]
SUPPORTED_METRICS: List[str] = [
"metric_1",
"metric_2",
]
# ❌ 不再定义 DEFAULT_CONFIG
# 原因:配置应该外部化,不在代码中硬编码设计原则:
- ✅ 只定义枚举常量和验证规则
- ❌ 不定义默认配置字典
- ❌ 不定义默认文件路径
train 命令:
- 参数:
--dataset-path: 数据集路径(目录或文件,必需)--config: 配置文件路径(必需)--run-name: MLflow run 名称(可选)
- 环境变量:
MLFLOW_TRACKING_URI(必需)
使用示例:
# 基本训练
export MLFLOW_TRACKING_URI=http://mlflow:5000
classify_{domain}_server train \
--dataset-path ./data/ \
--config train.json
# 自定义run名称
classify_{domain}_server train \
--dataset-path ./data/ \
--config custom.json \
--run-name my_experiment_v1关键逻辑:
- 检查环境变量和配置文件存在性(Fast Fail)
- 加载配置:
TrainingConfig(config_path) - 动态注入 MLflow Tracking URI:
config.set("mlflow", "tracking_uri", value=os.getenv("MLFLOW_TRACKING_URI")) - 创建训练器:
UniversalTrainer(config=config_obj, dataset_path=dataset_path, run_name=run_name) - 执行训练:
trainer.train()
完整实现参考:classify_*/cli/bootstrap.py
所有服务使用统一 Dockerfile:support-files/release/Dockerfile
核心配置:
- 基础镜像:
python:3.12 - 包管理:
pip(Docker 构建阶段用于安装项目依赖) - 系统组件:
fonts-wqy-zenhei(中文字体);旧镜像中的 Supervisor 仅为待清理构建依赖 - 构建参数:
NEXUS_PYTHON_REPOSITY(可选,私有镜像源) - 入口点:
startup.sh通过exec启动 BentoML,使退出码和终止信号直达容器运行时
安装方式说明:
- Dockerfile 中统一通过
pip3 config set ...配置可选镜像源 - 项目依赖统一通过
pip3 install -e ".[dev]"安装 uv仍可用于本地开发与测试流程,但当前不用于 release Dockerfile 内的依赖安装
部署结构:
- release 容器仅运行一个 BentoML 主进程,不在 serving 镜像中启动 MLflow UI
startup.sh必须让 BentoML 成为 PID 1;生产加载失败由容器退出和编排 readiness 直接感知
位置:support-files/scripts/train-model.sh
核心功能:
- 调用
download_dataset.py从 MinIO 下载数据集(ZIP格式) - 解压到本地目录
- 管理配置文件路径(默认 ./train.json,或通过
download_dataset.py从 MinIO 下载) - 调用 CLI 训练命令(总是传入明确的配置路径)
- 可选的清理操作
配套脚本:
support-files/scripts/download_dataset.py- 使用 MinIO Python Client SDK(
minio包)下载对象,不再依赖mcCLI 二进制 - 支持单文件下载、批量下载、对象列表查看
- 通过
MINIO_ENDPOINT、MINIO_ACCESS_KEY、MINIO_SECRET_KEY、MINIO_USE_HTTPS控制连接参数
- 使用 MinIO Python Client SDK(
参数接口:
./train-model.sh [BUCKET] [DATASET] [CONFIG]
# 示例
./train-model.sh datasets my_data.zip
./train-model.sh datasets my_data.zip configs/train.json必需的环境变量:
export MINIO_ENDPOINT=http://minio-server:9000
export MINIO_ACCESS_KEY=your-access-key
export MINIO_SECRET_KEY=your-secret-key
export MLFLOW_TRACKING_URI=http://mlflow:15000 # ⚠️ 训练脚本必需,用于 MLflow 实验跟踪关键说明:
MLFLOW_TRACKING_URI:- 训练时必需,由 bootstrap.py 读取并注入到配置对象
- 不在配置文件中设置,遵循云原生配置外部化原则
- 支持本地 (
file:./mlruns) 或远程 (http://mlflow:5000) MLflow 服务器
脚本核心逻辑:
- 环境检查(python, unzip, download_dataset.py)
- MinIO 连接配置
- 调用
download_dataset.py下载数据集 - 解压数据集到本地目录
- 准备配置文件(从 MinIO 下载或使用本地默认配置)
- 执行训练:
classify_{domain}_server train --dataset-path=... --config=... - 错误处理和日志记录
详细实现请参考现有项目的 support-files/scripts/train-model.sh 和 support-files/scripts/download_dataset.py
- 项目结构按标准模板创建
-
Base{Domain}Model抽象基类 -
ModelRegistry注册机制 - 至少一个具体模型实现
-
UniversalTrainer训练器 - 数据加载器(支持目录/文件模式)
- 数据预处理器
- BentoML 服务定义
- Pydantic Schema 定义
-
DummyModel实现 (可选) - CLI 接口(train/serve命令)
-
train.json默认配置 -
MLFlowUtils工具函数 - Prometheus 指标定义
- README.md 文档
- 超参数优化(Hyperopt)
- 特征工程模块
- 模型可视化
- 包名:
classify_{domain}_server(小写+下划线) - 类名:
PascalCase(如UniversalTrainer) - 函数名:
snake_case(如load_model) - 常量:
UPPER_CASE(如MAX_RETRIES) - 私有方法:
_snake_case(如_validate_config) -- 模型注册名:PascalCase(如GradientBoosting,Spell,特殊的:ECOD(全大写,缩写特例))
查看现有三个服务的实现:
- classify_anomaly_server: 异常检测(PyOD)
- classify_log_server: 日志聚类(logparser3)
- classify_timeseries_server: 时间序列(Prophet/sklearn)
推荐学习路径:
- 先阅读
classify_timeseries_server(最完整) - 对比
classify_anomaly_server(了解异常检测特性) - 参考
classify_log_server(了解文本处理)
- 统一架构:遵循现有三个服务的模式
- 渐进式设计:先实现核心功能,再扩展
- 避免过早抽象:等真正需要时再提取公共逻辑
- 配置驱动:通过配置切换模型和参数
- 完善文档:代码即文档,注释清晰
- 容错机制:启动时配置验证,运行时异常处理
- 可观测性:Prometheus 指标 + 详细日志