跳转到主要内容
patch.py
plugin.yaml
这就是一个完整的 algorithm 插件。框架运行桥接层在训练入口进程中 import patch、调用 install, 你的改动在这个作业接下来的全过程中生效。

你的函数什么时候被调用

两种装载模式,选哪个只取决于一件事:你的补丁需不需要训练期的上下文。
在训练入口解释器内、框架代码运行之前调用,参数是这次作业的超参。
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 后重新提交。