离线模型晋级项目与自测

项目目标

本项目不训练、不加载真实模型,不访问网络、Registry、Kubernetes 或云服务,也不需要密钥。它用 Python 标准库把四类容易混淆的 MLOps 证据串起来:

  1. 候选 manifest 绑定数据、标签、代码、环境、训练配置、模型摘要和签名;
  2. promotion gate 比较测试、整体指标、关键切片、延迟、大小和接口兼容性;
  3. 线上观察先绑定已通过的候选门指纹、固定制品与参考身份,再区分 shadow 和 Canary;
  4. 显式检查总样本、有标签样本、关键切片样本与标签覆盖,区分健康、证据不足、只有漂移、标签确认的质量退化和技术故障;
  5. 决策输出 release、阶段、策略版本、证据指纹、理由以及继续、调查、阻断 rollout 或回滚动作。

所有阈值只是本地教学策略,不是行业推荐值。真实阈值必须由业务损失、样本量、测量波动、风险容忍度和 SLO 共同确定。

项目文件

  • promotion_gate.py:严格 schema 校验、晋级门、线上判定和 CLI。
  • candidates.json:策略、Champion 基线和两个候选 manifest。
  • observations.json:一个固定发布与五种 shadow/Canary 观察窗口。
  • test_promotion_gate.py:52 个 unittest,覆盖重复 JSON key、非标准常量、坏输入、候选门绑定、阶段、样本分母、漂移、回滚和 CLI。

candidates.json 中的摘要是格式正确的教学占位符,不对应真实模型文件。项目验证的是证据组织与控制逻辑,不是模型质量。

运行环境

以下代码块按顺序从同时包含 docs/ 与 .website/ 的项目根目录开始运行;语法检查结束后会返回项目根目录:

Push-Location -LiteralPath 'docs\MLOps' # 进入项目目录;文末的 Pop-Location 会恢复工作目录。
python --version # 先确认当前环境实际调用的 Python 版本。

脚本只使用 Python 3 标准库,不需要 pip install。若希望隔离解释器,请按照 目录中的环境路线 在 vault 外创建 .venv,再返回本目录运行。不要在知识库内创建 .venv。

第一步:检查候选晋级

证据完整且通过本地策略

python -B .\examples\promotion_gate.py candidate --id candidate-safe # 对完整候选运行离线晋级门;-B 不写入字节码缓存。
$LASTEXITCODE # 显示刚才 CLI 的稳定退出码,便于与文中 0/1/2 契约对照。

预期 decision 为 promote,退出码为 0。输出同时包含 policy_version 和由候选、基线与策略生成的 evidence_fingerprint。

整体指标更高,但关键切片和数据测试失败

python -B .\examples\promotion_gate.py candidate --id candidate-regression # 运行含关键切片与数据失败的候选 fixture。
$LASTEXITCODE # 预期为 1,表示脚本正确完成并阻断晋级。

预期 decision 为 block,退出码为 1。理由应包含 required test failed: data 和关键切片低于策略门槛。这个例子说明平均指标不能抵消关键切片或数据契约失败。

manifest 中的血缘

每个候选必须有:

  • 不可变数据快照和标签版本;
  • 代码提交、环境摘要和训练配置摘要;
  • 模型制品摘要、格式、大小、输入/输出 schema;
  • 明确的测试结果与整体、切片、延迟指标。

校验器拒绝未知字段、缺失字段、latest、非法 SHA-256、布尔型伪数值、非有限数和重复 ID。严格 schema 的代价是升级时需要显式迁移,收益是不会静默忽略拼错字段。

线上 fixture 还必须证明:部署的 candidate 已通过当前策略,promotion_evidence_fingerprint 与该次候选决策完全一致,模型 ID、制品摘要和观察参考身份互相匹配。指纹绑定能发现“评测后换制品”或“沿用旧策略结论”这类明显错绑;它不是签名,也不能证明声明的外部制品真实存在。

第二步:解释漂移和回滚

健康窗口

python -B .\examples\promotion_gate.py observe --id window-healthy # 评估证据充分的健康线上窗口。

预期 action 为 continue,退出码为 0。

有输入漂移,但真值覆盖不足

python -B .\examples\promotion_gate.py observe --id window-drift-no-label # 输入漂移但标签证据不足,只应进入调查路径。
$LASTEXITCODE # 预期为 1,说明需要动作而非 Python 运行错误。

预期 action 为 investigate,退出码为 1。输入漂移只是调查信号;标签覆盖不足时,脚本不会把它升级成“质量已下降”或“自动再训练”。

漂移伴随有标签的关键切片退化

python -B .\examples\promotion_gate.py observe --id window-quality-regression # 用有标签的关键切片退化 fixture 验证回滚优先级。

预期 action 为 rollback_and_review_retraining。这里的顺序是先限制影响,再审查数据与标签是否适合产生新候选;名称中的 review_retraining 不代表自动上线。

技术故障

python -B .\examples\promotion_gate.py observe --id window-technical-failure # 单独模拟服务错误率/延迟故障,避免误归因为模型质量。

错误率和 P95 延迟越过本地策略时,动作是 rollback_and_investigate。技术故障没有被错误解释成模型需要再训练。

Shadow 技术故障

python -B .\examples\promotion_gate.py observe --id window-shadow-technical-failure # 验证 shadow 阶段只能阻断扩量,不能冒充已回滚用户流量。

预期动作为 block_rollout_and_investigate。Shadow 候选尚未承接用户决策,因此应阻断进入 Canary,而不是声称“已回滚用户流量”;若影子链路误接真实写操作,仍必须按事故处理副作用。

窗口用 sample_count、labeled_count 与 critical_labeled_count 表达分母。脚本由前两者计算标签覆盖率,同时要求绝对样本量、覆盖率和关键切片样本都过门;“80% 已标注”若只有几条样本,不能自动扩大。

一次查看所有教学场景

python -B .\examples\promotion_gate.py audit # 汇总全部离线 fixture,观察不同决策分支而不把它当单项发布门。

audit 用于汇总样例,因此只要两个 JSON 契约有效就返回 0;其中仍会同时出现通过、阻断、调查和回滚结果。自动化系统不应只看 audit 的进程码,而应使用单候选或单窗口命令,或解析每条结构化决策。

运行测试

Push-Location .\examples # 进入源码与测试所在目录,简化模块发现。
$env:PYTHONDONTWRITEBYTECODE = "1" # 双重禁止字节码缓存,保持教学目录干净。
 
python -B -m unittest -q test_promotion_gate.py # 正常解释器下运行全部回归测试。
python -B -O -m unittest -q test_promotion_gate.py # 验证生产校验不依赖 -O 会移除的 bare assert。
python -B -W error -m unittest -q test_promotion_gate.py # 把警告提升为失败,捕获潜在兼容性问题。
 
Pop-Location # 离开 examples,恢复到项目目录。

三种模式分别检查:

  • 普通模式:52 个契约和行为测试;
  • -O:生产校验不依赖会被优化移除的普通 assert;
  • -W error:Python warning 被视为失败。

也可以在不生成 .pyc 的情况下做语法检查:

python -B -c "from pathlib import Path; [compile(p.read_text(encoding='utf-8'), str(p), 'exec') for p in map(Path, ['examples/promotion_gate.py', 'examples/test_promotion_gate.py'])]" # 只在内存编译两份 Python 文件,检查语法且不生成 .pyc。
Pop-Location # 恢复最初的项目根目录。

从代码读懂控制边界

代码工程含义
validate_candidates_fixturemanifest schema 与不可变血缘入口
evaluate_candidate候选和 Champion 在版本化策略下比较
validate_observations_fixture部署必须绑定已通过的候选门指纹,参考身份必须匹配固定制品
assess_observation阶段、样本分母、漂移、标签证据、质量与技术故障分开判断
evidence_fingerprint决策输入的稳定指纹,不是数字签名或授权
CLI 退出码0 继续/晋级,1 阻断或需动作,2 输入错误

evidence_fingerprint 只能检测同一组 JSON 证据是否变化,不能证明证据来源可信。真实系统还需要身份、不可变存储、签名或证明、访问控制和审计。

综合扩展任务

  1. 为两个命名关键切片分别设置策略,并避免把平均值当作唯一判据;
  2. 加入多次重复运行区间,而不是只比较单点指标;
  3. 为候选增加风险审批对象,绑定审核者、摘要、目标环境和有效期;
  4. 为 shadow 增加只读工具或模拟副作用不变量,再证明进入 Canary 前会阻断错误;
  5. 模拟部署别名在评测后被移动,验证固定摘要和候选门指纹仍能阻断错误制品;
  6. 把每条决策写入新建、不可覆盖的临时报告,并为重复执行设计幂等键;
  7. 为旧 schema 编写显式迁移器,而不是放宽校验静默接受。

掌握检查

  • 能说明数据、标签、代码、环境、训练配置和制品摘要各自提供什么血缘。
  • 能解释候选整体指标更高仍被阻断的原因。
  • 能区分漂移、标签确认的质量退化和服务技术故障。
  • 能说明为什么漂移不能直接触发自动再训练和上线。
  • 能运行 52 个测试并解释 -O 模式的意义。
  • 能指出离线指纹、Registry 版本、审批和制品签名之间的差别。
  • 能列出本项目尚未验证的真实平台能力。

项目边界

本项目没有验证真实 MLflow 3.14.0、Kubeflow、KServe、Kubernetes、云对象存储、容器、模型推理、线上流量或业务因果效果;也没有生成模型权重。固定参考值不是同期随机 control,不能把差异解释为发布的因果效果;更严格的并行 control 绑定见 LLMOps 离线发布门项目。接入真实平台时必须在独立环境锁定版本,以相同契约运行集成、权限、失败恢复和渐进发布测试。

参考资料

以下资料核对于 2026-07-21: