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