十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ZenML 领域模型(Domain Models)架构指南:从 Request 到 Response 的模型设计与跨层协作规范

ZenML 领域模型(Domain Models)架构指南:从 Request 到 Response 的模型设计与跨层协作规范 ZenML 领域模型Domain Models架构指南从 Request 到 Response 的模型设计与跨层协作规范【免费下载链接】zenmlZenML : One AI Platform from Pipelines to Agents. https://zenml.io.项目地址: https://gitcode.com/GitHub_Trending/ze/zenml本文面向 ZenML 的贡献者与二次开发者系统讲解src/zenml/models目录下领域模型Domain Models的设计规范包括 Request / Update / Response 三种载荷的职责划分、Filter 过滤器的全链路实现清单、作用域global / user / project的选取原则以及 Triggers、资源池、运行等待条件、嵌套流水线运行等跨层模型族在 CLI、客户端、服务端、Schema、迁移、测试与文档中的联动修改要求。读完本文你将能够按照 ZenML 的官方约定正确新增、扩展或重构一个领域模型并确保它同时兼容 REST API、Client 方法、CLI 列表命令与底层 ORM 存储。一、模型层是什么ZenML 的领域边界在 ZenML 中src/zenml/models是**领域模型Domain Models**的唯一存放位置其核心职责由 AGENTS.md 明确为保持模型与 ORM Schemasrc/zenml/zen_stores/schemas以及 ZenStore 的行为保持一致。也就是说领域模型是介于 REST 层与存储层之间的中立契约——它既不直接触碰数据库也不直接处理 HTTP而是用 Pydantic 模型统一描述客户端与服务端之间交换的数据形态。从目录结构看src/zenml/models模型层被组织为三个子包v2/base基础抽象模型定义所有领域模型共享的骨架base.py、过滤器基类filter.py、分页page.py与作用域scoped.pyv2/core核心领域模型如pipeline.py、pipeline_run.py、step_run.py、artifact.py、schedule.py、triggers.py、resource_pool.py、run_wait_condition.py等 40 余个实体v2/misc其他辅助模型。AGENTS.md 同时约定当在src/zenml/models/下工作时Codex / Agent 应遵循该指南更详细的模型继承层级与示例记录在仓库级工作流技能文档.agents/skills/zenml-repo-workflows/SKILL.md该文件位于仓库根目录的.agents/skills路径下可按需查阅。二、核心模式四类模型载荷的分工AGENTS.md 将 ZenML 的领域模型抽象为四种职责明确的基本形态对应 base.py 中的实现模型形态基类职责RequestBaseRequest创建资源时的载荷描述我要创建什么UpdateBaseUpdate部分修改资源的载荷只携带需要变更的字段ResponseBaseResponse由BaseResponseBody、BaseResponseMetadata、BaseResponseResources三部分组成读取/返回资源时的载荷描述资源当前是什么样FilterBaseFilter定义查询的字段、操作符、排序、作用域与分页2.1 Request 与 Update创建与部分修改的分离在 base.py 中可以看到BaseRequest与BaseUpdate都是直接继承自BaseZenModel的标记性基类。它们的分离是有意为之Request 是完整的创建载荷如 schedule.py 中的ScheduleRequest(ProjectScopedRequest)创建调度器时必须提供调度所需的全量信息Update 是部分修改载荷只声明允许被修改的字段配合 Pydantic 的字段校验实现只更新传入的字段避免客户端必须回传完整对象。BaseZenModel本身有两个值得注意的全局配置base.py继承YAMLSerializationMixin使所有模型天然支持 YAML 序列化/反序列化ZenML 的 stack 配置、run template 等都以 YAML 形态持久化继承AnalyticsTrackedModelMixin使模型能上报分析元数据如UserScopedRequest.get_analytics_metadata()中会附加user_id见 scoped.pymodel_config ConfigDict(extraignore)忽略所有未知字段这是实现前后向兼容的关键——新版本服务器返回的字段可以被旧版本客户端安全忽略反之亦然。2.2 Response 的三段式结构Body / Metadata / ResourcesResponse 模型是所有领域模型中最复杂、也最能体现 ZenML 设计思想的部分。其通用基类定义在 base.pyclass BaseResponse(BaseZenModel, Generic[AnyBody, AnyMetadata, AnyResources]): body: Optional[AnyBody] # 资源主体的核心字段 metadata: Optional[AnyMetadata] # 与该资源相关的元数据 resources: Optional[AnyResources] # 与该资源关联的其他资源Body资源自身的核心数据例如 pipeline run 的status、start_timeMetadata关于数据的数据例如created、updated时间戳见BaseDatedResponseBodyResources与该资源关联的其他资源对象例如创建该资源的user、所属的project其基类BaseResponseResources特别设置了extraallowbase.py允许携带附加资源字段。这种三段式拆分带来两个实际收益惰性加载Lazy Loading列表接口只返回轻量的 Body只有需要详情时才通过水合Hydration补齐 Metadata 与 Resources避免大列表响应携带冗余数据水合校验BaseResponse内置_validate_hydrated_version方法base.py通过ResponseUpdateStrategyALLOW/IGNORE/DENY控制当水合版本与原版本字段不一致时是覆盖更新 告警、静默忽略还是抛HydrationError。以用户作用域为例scoped.py 中UserScopedResponseBody声明user_idUserScopedResponseResources声明完整的user: UserResponse——这正体现了Body 存 ID、Resources 存完整对象的拆分原则。2.3 作用域选择最窄的所有权语义AGENTS.md 明确规定选择与所有权语义匹配的最窄作用域global、user 或 project。这句话对应 scoped.py 中的继承层级BaseRequest→UserScopedRequest增加user: Optional[UUID]由服务器自动设置excludeTrue不参与序列化→ProjectScopedRequest增加必填的project: UUID响应侧同理UserScopedResponse→ProjectScopedResponse。实际选型规则可以从核心模型看出全局资源不属于任何用户/项目如Flavor、Stack的部分能力直接继承BaseRequest/BaseResponse用户级资源如ResourcePoolRequest(UserScopedRequest)resource_pool.py、ResourceRequestRequest(UserScopedRequest)resource_request.py项目级资源如ScheduleRequest(ProjectScopedRequest)schedule.py、RunWaitConditionRequest(ProjectScopedRequest)run_wait_condition.py、TriggerRequest(ProjectScopedRequest, ...)triggers.py。选择更窄的作用域意味着更严格的所有权校验与 RBAC 约束——项目级资源天然受项目隔离保护用户级资源受用户隔离保护而全局资源则需要自己声明权限要求。因此选最窄既是语义准确性的要求也是安全模型的要求。三、Filter 模型查询能力的统一入口Filter 是 ZenML 列表类 API 的查询语言。其基类BaseFilter定义在 filter.py一个典型的用法源码 docstring 示例是ResourceListModel( namecontains:default, # 操作符:值 语法 projectdefault, # 等值过滤 count_stepsgte:5, # 数值范围 sort_bycreated, # 排序字段 page2, # 页码从 1 开始 size20 # 每页条数 )3.1 通用字段与操作符语法BaseFilter自带一组通用字段filter.py字段类型默认值说明sort_bystrcreated排序字段支持desc:created或asc:name前缀语法非法前缀会回退为升序并告警logical_operatorLogicalOperatorsAND多个过滤条件之间的逻辑关系可选and/orpageint1PAGINATION_STARTING_PAGE页码ge1sizeintPAGE_SIZE_DEFAULT每页条数约束在PAGE_SIZE_MAXIMUM以内id/created/updated各类 FilterOptionNone按 ID、创建时间、更新时间过滤过滤值使用**操作符:值字符串语法**如contains:default、gte:5操作符定义在zenml.enums的GenericFilterOps中。对于无值操作符有专门的保护filter.pyoneof:/notoneof:要求值是 JSON 格式的列表否则报ONEOF_ERRORisnull:/isnotnull:是无值操作符必须使用显式operator:形式且不带值否则报NO_VALUE_ERRORIS_NULL与IS_NOT_NULL被归类进VALUELESS_FILTER_OPS。每个字段类型的ALLOWED_OPS由具体字段模型类声明validate_operation校验器filter.py会拒绝该数据类型不支持的操作符。generate_query_conditionsfilter.py则负责把 Filter 对象转换为 SQLModel 的查询条件交给 ZenStore 层执行——这就是模型即查询的实现方式。3.2 三个关键类变量控制暴露面BaseFilter定义了三个 ClassVar 列表filter.py它们决定了过滤字段的暴露边界类变量作用FILTER_EXCLUDE_FIELDS不能作为过滤/排序字段的保留字段sort_by、page、size、logical_operator自身不能参与过滤CLI_EXCLUDE_FIELDS不生成 CLI 命令行选项的字段见下文 3.3API_SINGLE_INPUT_PARAMS在 API 层用fastapi.Query(default)包装为单值参数的字段sort_by、logical_operator、page、size3.3 Filter 字段清单一处改动三处跟进AGENTS.md 给出新增一个 Filter 字段时必须同步完成的检查清单Filter 模型本身在对应的 Filter 模型如PipelineRunFilter、TriggerFilter中声明新字段Client 的 list 方法签名Client().list_xxx(**kwargs)的方法签名必须增加该参数例如Client().list_pipeline_runs(parent_run_id...)该 Client 方法内部的 Filter 实例化在方法体中将参数传入并构造 Filter 实例。此外若该字段不应暴露给 CLI例如仅内部使用的过滤能力将其加入CLI_EXCLUDE_FIELDS若该字段依赖关系表relationship如按关联资源属性过滤可能还需要在 ZenStore 层编写自定义 ORM join 逻辑。CLI_EXCLUDE_FIELDS的实际消费点在 cli/utils.py 的list_options装饰器中它遍历 Filter 模型的model_fields凡是不在CLI_EXCLUDE_FIELDS中的字段都会自动生成一个click.option(--字段名, ...)命令行选项。换句话说Filter 模型是 CLI 列表命令选项的单一数据源——新增过滤字段后只要不排除CLI 自动获得对应--xxx参数默认排序还会被智能改写为desc:created。这大大降低了模型、Client、CLI 三者不同步的维护成本。四、跨层模型族牵一发而动全身的全家桶AGENTS.md 特别提醒以下模型族在改动时必须追踪完整路径Full Path因为它们横跨 CLI、Client 方法、服务器端点、Schema、迁移、测试与文档4.1 触发器与调度Triggers / Schedule / Platform Event触发器模型族位于 triggers.py其继承结构相当精细TriggerRequest(ProjectScopedRequest, TriggerBase, ABC)——触发器创建请求抽象基类ScheduleTriggerRequest(TriggerRequest, ScheduleTrigger)——定时触发按 cron 表达式触发PlatformEventTriggerRequest(TriggerRequest, PlatformEventTrigger)——平台事件触发按 ZenML 内部事件如流水线状态变化触发TriggerResponse/ScheduleTriggerResponse/PlatformEventTriggerResponse及各自的 Body / Metadata / ResourcesTriggerFilter/UnScopedTriggerFilter——触发器查询过滤器。调度器schedule.py与触发器紧密配合调度器定义什么时候运行触发器定义满足什么条件时运行。改任何一个都要求 CLIzenml trigger命令族、Client 方法Client().list_triggers()等、服务器 REST 端点、ORM Schemasrc/zenml/zen_stores/schemas、Alembic 迁移alembic.ini管理的迁移链与测试同步跟进。4.2 资源池体系Resource Pools / Subject Policies / Resource Requests资源池模型族包含三个文件resource_pool.pyResourcePoolRequest(UserScopedRequest)定义可调度的计算资源池resource_pool_subject_policy.pyResourcePoolSubjectPolicyRequest(UserScopedRequest)定义资源池的分配策略哪些主体可用哪些资源resource_request.pyResourceRequestRequest(UserScopedRequest)ResourceRequestResponseResourceRequestFilter定义对资源的具体请求及其生命周期。三者构成池—策略—请求的闭环策略约束请求、请求消费池中资源。任何一端的字段变化都会沿 Client ↔ Server ↔ Store 链路传导。4.3 运行等待条件Run Wait Conditionsrun_wait_condition.py 中的RunWaitConditionRequest(ProjectScopedRequest)用于在流水线运行流程中挂起/恢复等待条件是 Agent 编排如 Human-in-the-Loop的基础能力之一。4.4 嵌套子流水线运行parent_run_id / child_key / root_run_id当一条流水线在运行中触发子流水线时会产生嵌套运行nested child pipeline run。相关字段定义在 pipeline_run.pyparent_run_id: Optional[UUID]L165与child_key: Optional[str]L169必须成对出现——模型校验器L210-L229保证二者同设同缺因为数据库在(parent_run_id, child_key)上有唯一约束单边设置会破坏约束且无法被唯一约束捕获root_run_id: Optional[UUID]L302标识整棵嵌套运行树的根运行。此外PipelineRunFilter中声明了parent_run_id: UUIDFilterOptionL1027且源码注释L1032与查询逻辑L1430提示在支持parent_run_id IS NULL过滤之前存在一处 TODO——这说明过滤 NULL 值的能力与 ORM 查询实现直接相关也印证了 AGENTS.md 中关系型/空值过滤可能需要自定义 store 层逻辑的提醒。响应侧的child_key/root_run_id则通过 Body 的属性代理L827-L842暴露。五、兼容性红线安全改动 vs 破坏性改动AGENTS.md 给出了模型演进的兼容性准则这也是 ZenML 多版本客户端/服务器混跑场景server 与 client 版本可不同的生存之道通常安全添加可选属性Optional字段——结合BaseZenModel的extraignore旧客户端忽略新字段新客户端容忍缺失字段。有风险或破坏性应尽量避免删除属性重命名属性将必填字段改为可选——但旧代码可能无法容忍旧代码假定字段必填新响应却缺失该字段以不兼容的方式改变属性类型。演进建议对公共 Response 模型的演进使用弃用期deprecation periods与默认值先新增字段并标记旧字段弃用经过足够的过渡版本后再移除。这一策略与 base.py 中的_response_update_strategy/_warn_on_response_updates配合——当水合响应中字段变化时通过日志告警而不是静默失败让开发者尽早感知模型变化。六、实战清单新增一个领域模型字段的完整流程综合以上规范当你要为某个资源新增一个可过滤字段例如给 pipeline run 增加一个业务字段run_key时完整流程是Response 模型在对应xxxResponseBody中新增可选字段优先Optional确保列表接口只返回轻量 BodyUpdate 模型在xxxUpdate中声明该字段可被部分更新若允许修改Request 模型在xxxRequest中声明创建时可传入该字段若允许创建时指定Filter 模型在xxxFilter中新增过滤字段并确定其ALLOWED_OPSClient 方法更新Client().list_xxx()的方法签名并在方法体内将参数注入 Filter 实例对应 AGENTS.md 的检查清单第 1~3 条CLI 暴露面若不需要 CLI 选项将该字段加入CLI_EXCLUDE_FIELDS否则list_options装饰器会自动生成--run-key选项Store 层若字段需要 join 关系表或 NULL 过滤在 ZenStore 中补充 ORM 查询逻辑如 pipeline_run.py 所示的自定义条件Schema 与迁移更新src/zenml/zen_stores/schemas中的 ORM 模型并生成 Alembic 迁移测试与文档补充单元/集成测试并更新对应 API 文档与 CLI 文档兼容性审查确认改动属于添加可选属性这一安全类别若涉及重命名/删除则走弃用期流程。七、结语ZenML 的领域模型层src/zenml/models是贯穿 REST API、Client SDK、CLI 与 ORM 存储的数据契约中心。AGENTS.md 以极简篇幅概括了 ZenML 多年演进的建模纪律Request / Update / Response / Filter 四种形态职责分明Body / Metadata / Resources 三段式响应兼顾性能与信息完整度作用域选择决定所有权与安全边界而模型—Client—CLI—Store的四点联动清单则是防止功能漂移的工程护栏。理解并遵循这套规范是安全地在 ZenML 中扩展任何新能力的起点。【免费下载链接】zenmlZenML : One AI Platform from Pipelines to Agents. https://zenml.io.项目地址: https://gitcode.com/GitHub_Trending/ze/zenml创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表