patch.py
plugin.yaml
patch、调用 install,
你的改动在这个作业接下来的全过程中生效。
你的函数什么时候被调用
两种装载模式,选哪个只取决于一件事:你的补丁需不需要训练期的上下文。- eager(默认)
- deferred
在训练入口解释器内、框架代码运行之前调用,参数是这次作业的超参。
params 就是 spec.hyperparams——本次提交摊平后的 --set 覆盖值和解析后的配置值。
补丁只需要替换一个函数时,用 eager。传给
install_deferred 的名字是 manifest 里的 name,不是完整的 <owner>/<name>。
传一个没登记的名字会抛错,并列出已登记的有哪些。启动校验与进程内安装
1
校验注入的包
重算
tuneplane_plugins/<name>/ 的摘要,与 JobSpec 比对。不一致就停止作业。2
检查 SDK 兼容性
manifest 声明了
requires.core 时,运行中的 tuneplane 必须满足它。3
把包根加入 sys.path 并 import
这正是「顶层名不得遮蔽真实依赖」要在发布期就拒绝的原因。
4
调用或登记
eager → 立即调用 install(params)。
deferred → 按 manifest 里的名字登记,等训练入口来调。capability: algorithm。其他代码类能力会打一行日志跳过——
environment 插件由环境机制装载,data-prep 插件根本不离开你的机器。
怎么写这个函数
- 出真问题就抛。
install里抛异常会停止作业,这是对的: 一个悄悄没生效的补丁,会产出一个数字含义与实验声明不符的 run。 - 保持幂等。 多节点或带重启的执行器下,你的模块可能被 import 多次。 防住「把已经包过一次的函数又包一次」。
- 不要在模块 import 期 import 训练框架,除非你确定它一定在。
放进
install里 import——那时你已经确定这是训练容器。 - 配置从
params读,不要从环境变量读。params会随作业记录下来, 读的人能看到你的补丁被告知了什么。环境变量不会。
为什么这里把 monkey-patch 当成一等机制
它不是意外,也不是权宜之计
它不是意外,也不是权宜之计
对于装不了新依赖的内网集群,一个零依赖、运行期生效的补丁,
相比分发一个 fork 过的框架镜像是决定性的优势。让它可接受而不是鲁莽的,是「补丁被声明、被版本化、被 digest 锁定」这件事:
平台说得出哪个作业用了哪个补丁的哪个版本。
一个埋在训练脚本里、没人声明过的 monkey-patch,风险一样大,但完全没有这份记录。
显式绑定和执行范围
spec.recipe.plugins 声明所需算法名,每个都必须由确切的 spec.plugins 引用满足。没有 common/algorithms/registry.py 回退入口,也没有旧插件兼容适配层。
算法在真正运行训练入口的解释器内初始化,并早于框架代码执行。仅支持 scope: entrypoint,包括重新进入运行桥接层的训练 rank;不会自动注入框架另行创建的 Ray actor 或 worker。此类场景须使用框架原生初始化钩子。custom 和 evalkit 不经过算法桥接层,因此拒绝算法插件。
优先使用框架原生回调和注册机制。进程内插件共享 Python 依赖,独立 wheel 不等于独立依赖环境。
确认成功
作业日志里每个插件一行:plugins.lock.json 后重新提交。