一、1. 数据类型治理:写入前统一 cast double
特征工程 SDK 不识别 decimal(p, s) 类型。若特征表中存在 decimal 列,fe.log_model 记录模型时将无法推断模型签名(signature),进而引发在线、离线推理的类型不匹配,甚至 MLmodel 文件中没有 signature 段。
做法 :在写入特征表之前,把所有 decimal 列统一 cast("double"),并将该处理沉淀为可复用的工具函数,在所有建表、写入管道中统一调用。时间戳列应保留为时间类型,不要 转为字符串,否则无法用作时间戳键。
二、2. Pipeline 铁律:预处理必须封装进 sklearn.Pipeline
所有预处理(OneHotEncoder、StandardScaler、自定义变换等)必须 封装进 sklearn.Pipeline,把整条 Pipeline 作为 model 传给 fe.log_model。
禁止 在 Pipeline 外部用 pd.get_dummies / 手动 StandardScaler.fit_transform 后再 model.fit(…):外部预处理步骤不会被序列化进 MLmodel,推理时 SDK 从特征表 lookup 出来的是原始 string 列,喂给只认 OHE 后列名的裸模型,轻则报 Feature names unseen at fit time,重则所有样本静默输出训练集均值。
from sklearn.pipeline import Pipelinefrom sklearn.compose import ColumnTransformerfrom sklearn.preprocessing import OneHotEncoder, StandardScalerpreprocessor = ColumnTransformer(transformers=[ ("cat", OneHotEncoder(handle_unknown="ignore"), ["<categorical_feature>"]), ("num", StandardScaler(), ["<numeric_feature>"]),])model = Pipeline(steps=[("preprocessor", preprocessor), ("clf", <estimator>)])
三、3. 模型记录:必须用 fe.log_model 且传 training_set
·必须用 fe.log_model 而非 mlflow.<flavor>.log_model:只有前者会把 FeatureSpec 打包进 MLmodel,否则 score_batch 找不到特征来源。
·必须传 training_set,并开启 infer_input_example=True:模型签名与 input_example 由 SDK 自动推断,是部署在线推理服务的前置条件。
·不要 再手动 infer_signature(…) 或手动传 input_example=…,否则会与 feature_spec 的原始列错位、污染契约。
四、4. Point-in-time 一致性:显式带上时间戳列
训练时声明了 timestamp_lookup_key 的场景下,score_batch 的输入 df 强烈建议显式带上同名时间戳列 以保证 point-in-time 一致性。若不带,SDK 会自动注入 current_timestamp() 取当前最新可见的特征,等同于用今天的特征预测历史样本,语义不严谨。
如需限制回溯时间范围,在 FeatureLookup 中配合 lookback_window(非负 datetime.timedelta)使用,且必须与 timestamp_lookup_key 同时出现。
五、5. 命名规范:统一使用三段式全限定名
引用特征表、注册模型统一使用三段式全限定名 catalog.schema.table / catalog.schema.model_name,避免依赖默认 Catalog 带来的副作用,迁移到不同工作空间时也无需改代码。
六、异常分类
| 异常类型 | 触发场景 | 处理建议 |
| ImportError | 运行环境中的 MLflow 版本低于 SDK 要求 | 安装带 mlflow3 extra 的 SDK,或升级 MLflow 至要求版本 |
| EnvironmentError(wedata.common.utils.env_utils) | 必需环境变量未设置(项目 ID、地域、引擎名等) | 在 DataBuddy Notebook 内核中运行,或在本地配置好对应环境变量 |
| KeyError | 临时密钥环境变量缺失 | 在 DataBuddy Notebook 内核中运行,或本地配置好对应环境变量 |
| ValueError | 业务参数校验失败 | 按错误信息修正参数 |
| RuntimeError | 底层云 API、计算资源、连接调用失败 | 查看错误信息中的 request_id,结合 DataBuddy 控制台排查 |
| TencentCloudSDKException | 调用云 API 时返回错误(鉴权失败、资源不存在、限流等) | 根据 code / message / request_id 联系平台支持 |
| IllegalArgumentException / Exception(PySpark) | Spark SQL 执行失败(DDL、写入、读取) | 排查 Spark 日志与表权限 |
七、ValueError 常见错误速查
ValueError 多由参数不合法引起,可对照错误信息快速修正:
| 错误信息(节选) | 场景 | 修复方式 |
| name must be a non-empty string | 表名、库名传入空字符串 | 检查参数 |
| name '…' contains too many segments | 表名段数超过 3 | 使用 catalog.schema.table 三段式或更短形式 |
| database_name '…' contains too many segments | 数据库名段数超过 2 | 使用 catalog.schema 二段式或单段名 |
| catalog_name '…' conflicts with catalog '…' | 显式 catalog_name 与多段名解析结果不一致 | 二者保持一致,或仅传一种 |
| Primary keys have duplicates: […] | create_table 主键重复 | 去重;复合主键传列表而非字符串 |
| Engine type … is not supported | engine_type 不在受支持的枚举值内 | 使用 EngineTypes.ICEBERG_ENGINE 或 EngineTypes.HIVE_ENGINE |
| Invalid write mode '…' | write_table 的 mode 取值非法 | 使用 append 或 overwrite |
| Streaming write requires checkpoint_location parameter | 流式写入未传 checkpoint_location | 显式指定 checkpoint 路径 |
| table '…' not exists | 读 / 写 / 删 / 打标的表不存在 | 先 create_table 或检查 catalog / database / 表名 |
| properties must be a non-empty dictionary | 设置标签时未提供有效 key / value | 传入非空 key、value |
| All properties are reserved and cannot be set: […] | 标签 key 命中系统保留字段(如主键、时间戳键等) | 换一个非保留 key |
| Either 'feature_spec' or 'feature_lookups' must be provided, but not both. | create_training_set 同时传或都不传两者 | 二者择一 |
| Invalid feature_spec format | feature_spec 不是三段式 | 改成 catalog.schema.spec_name |
| FeatureLookup must specify a table_name | FeatureLookup.table_name 为空 | 显式指定表名 |
| When table_name '…' is a single-level name, both 'catalog_name' and 'database_name' must be provided. | FeatureLookup 用了单段表名但未补 catalog / database | 改用三段式表名(推荐),或显式传 catalog_name + database_name |
| When table_name '…' is a two-level name (schema.table), 'catalog_name' must be provided. | FeatureLookup 用了两段表名但未传 catalog | 补 catalog_name 或改用三段式 |
| Unexpected lookback_window value: … | lookback_window 未配套 timestamp_lookup_key | 同时设置 timestamp_lookup_key |
| only non-negative datetime.timedelta allowed. | lookback_window 不是 timedelta 或为负 | 使用非负的 datetime.timedelta(…) |
| Setting multiple timestamp lookup keys is not supported. | timestamp_lookup_key 传入多元素列表 | 仅传单列名(str)或单元素列表 |
| cloud_secret_id is empty / cloud_secret_key is empty | 临时密钥未注入 | 检查鉴权环境变量 |
八、RuntimeError 常见错误速查
RuntimeError 多与计算资源、数据源连接或云 API 调用有关,错误信息中通常带 request_id 便于定位:
| 错误信息(节选) | 场景 | 修复方式 |
| Failed to delete table '…' | 底层 DROP TABLE 失败 | 查看异常 cause、表锁 / 权限 |
| Failed to modify properties for table '…' | 修改表标签失败 | 查看异常 cause |
| Failed to list compute resources: no compute resources found | 工作空间无运行中的计算资源组 | 在 DataBuddy 控制台创建 / 启用数据计算资源组 |
| Unable to get compute resource basic info | 资源组基础信息缺失 | 联系工作空间管理员 |
| Failed to list connections: no connections found | 发布在线表时工作空间无 PostgreSQL 类型连接 | 在控制台创建 PostgreSQL 类型连接 |
| Failed to create online table '…' | publish_table 调用创建在线表失败 | 凭 request_id 联系平台支持 |
| Failed to drop online table | drop_online_table 失败 | 同上 |
| TargetPath.CatalogName 不能为空 | 特定版本 / 环境下 publish_table 缺少在线 Catalog 信息 | 补传 online_catalog_name(具体名称参考所在工作空间已配置的在线 Catalog) |
九、全链路排错清单
下表按现象 → 根因 → 解决方法整理了从建表到在线发布全链路中最易踩的坑:
| 现象 | 根因 | 解决方法 |
| MLmodel 文件中没有 signature 段 | 特征表存在 decimal(p, s) 列,SDK 类型映射不支持 | 写入前把所有 decimal 列 cast("double") |
| log_model 推断 signature 失败 / score_batch 列对不上 | 使用了原生 mlflow.<flavor>.log_model,或手动传了 signature / input_example | 改用 fe.log_model(…, training_set=…, infer_input_example=True) |
| ImportError: … mlflow >= … | 环境里的 MLflow 版本过低 | 重装带 mlflow3 extra 的 SDK |
| EnvironmentError / KeyError | 本地调试缺环境变量 | 在 DataBuddy Notebook 中运行,或本地手动配置对应环境变量 |
| When table_name '…' is a single-level name … | FeatureLookup 用了单段表名但没补 catalog / db | 改三段式表名(推荐),或显式传 catalog_name + database_name |
| Unexpected lookback_window value … | lookback_window 没有配套 timestamp_lookup_key | 同时设置 timestamp_lookup_key |
| only non-negative datetime.timedelta allowed. | lookback_window 不是 timedelta 或为负数 | 使用非负的 datetime.timedelta(days=N) |
| Failed to list compute resources: no compute resources found | 工作空间无运行中的计算资源组 | 在 DataBuddy 控制台启动一个数据计算资源组 |
| Failed to list connections: no connections found | 发布在线表时无 PostgreSQL 连接 | 在控制台先创建 PostgreSQL 类型连接 |
| TargetPath.CatalogName 不能为空 | 特定版本 / 环境下 publish_table 缺少在线 Catalog 信息 | 补传 online_catalog_name(名称参考所在工作空间已配置的在线 Catalog) |
| Primary keys have duplicates | create_table 主键重复 | 去重;复合主键传列表而非字符串 |
| Streaming write requires checkpoint_location parameter | 流式 write_table 未传 checkpoint | 显式传 checkpoint_location |
| score_batch 抛 schema 不匹配 | 推理 DataFrame 类型与训练时查出的特征类型不一致(典型:decimal vs double) | 训练前统一 cast,所有特征表存 double |
| score_batch 抛 Feature names unseen at fit time,或所有样本预测值完全相同 | 训练时在 Pipeline 外裸调用了 pd.get_dummies / StandardScaler 等预处理,未被序列化进模型 | 用 sklearn.Pipeline + ColumnTransformer + OneHotEncoder(handle_unknown="ignore") 把预处理与模型一起作为 model 传给 fe.log_model |
十、错误处理建议
在生产化调用中,建议对不同异常类型分别捕获,便于区分参数错误与资源、云 API 错误,并保留 request_id 以便排查:
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKExceptionfrom wedata.common.utils.env_utils import EnvironmentError as WedataEnvErrortry: fe.publish_table( catalog_name="<offline_catalog_name>", schema_name="<offline_schema_name>", table_name="<offline_table_name>", online_db_name="<online_schema_name>", online_table_name="<online_table_name>", )except ValueError as e: # 业务参数错误,直接记录并返回 logger.error("invalid params: %s", e)except RuntimeError as e: # 资源 / 云 API 错误,错误信息中的 request_id 可用于联系支持 logger.error("publish failed: %s", e)except TencentCloudSDKException as e: logger.error("cloud api error code=%s msg=%s reqid=%s", e.get_code(), e.get_message(), e.get_request_id())except WedataEnvError as e: logger.error("env not ready: %s", e)
注意:如遇文档未覆盖的接口或异常场景,请在工单系统提单,并附上 request_id、SDK 版本与错误堆栈,便于快速定位。
十一、Q:score_batch 提示找不到 feature_spec.yaml 怎么办?
feature_spec.yaml 是在 fe.log_model 时自动写入的。如果训练时使用的是原生 mlflow.<flavor>.log_model 而非 fe.log_model,则不会写入特征来源信息。请改用 fe.log_model 重新记录模型。
十二、Q:发布模型为在线服务时报错存在未发布为在线表的离线特征表?
模型工件中引用到的所有离线特征表都必须先发布为在线特征表,在线服务才能创建。请回到对应的离线特征表,完成发布并等待同步任务首次执行成功后再重试(详见 界面操作)。
十三、Q:在线特征数据看起来比离线表落后,是什么原因?
在线特征表的数据来自同步任务,时延取决于同步方式:定时触发 受调度周期限制;持续运行 实时性最高但仍存在任务执行间隔。如对实时性有较高要求,建议在持续运行模式下配置较小的执行间隔,并监控同步任务的运行状态与延迟。
十四、相关文档
·界面操作
·特征工程 SDK