A model, a prediction table and a saved workflow manager can all arrive as .pkl. They are not the same permission decision. Qlib's artifact-loading changes offer a useful example of keeping those decisions separate.
Give consent to the object that needs it
The artifact migration guide distinguishes supported data-only reads from restoring executable Python objects. The built-in recorder defaults to a restricted unpickler; trusted=True deliberately selects ordinary pickle. It does not authenticate the file.
The linked guide labels these changes unreleased; check your installed source before using these APIs. This is a comparison of the linked implementation, not a claim that a particular PyPI installation contains it.
For a mixed collection, the loading options are keyed by artifact alias, not filename. With an existing recorder rec for the intended run, and after independently verifying the model's producer and storage write permissions:
from qlib.workflow.task.collect import RecorderCollector
collector = RecorderCollector(
experiment=lambda: [rec],
artifacts_path={"model": "params.pkl", "pred": "pred.pkl"},
artifact_load_kwargs={"model": {"trusted": True}},
)
artifacts = collector.collect(only_exist=False)Only model receives that opt-in. The prediction load keeps the recorder's restricted default. The collector implementation forwards each alias's options separately; it does not retry a refused load with broader permissions.
The guide also treats a custom unpickler as trusted code, not a sandbox. Supplying one is a different loader choice, even if trusted remains false.
Require complete results when the comparison needs them
The example uses only_exist=False for a reason. The default, only_exist=True, catches LoadObjectError, logs the reason and skips that artifact read. Other artifacts from the same recorder can still appear in the returned dictionaries.
That is useful for best-effort collection. For an analysis that needs a model/prediction pair from every selected run, silently accepting a partial result changes what you are comparing. Fail on loading errors when those inputs are mandatory, and check the selected recorders and returned keys too: this switch is not a guarantee that your filters selected every intended run.
A saved manager is an earlier trust boundary
The guide separately warns that OnlineManager.load(...) uses unrestricted pickle/dill. Its strategies' later artifact settings cannot protect that initial restoration.
The inherited serialization loader shows the ordering in this excerpt:
with open(filepath, "rb") as f:
object = cls.get_backend().load(f)
if isinstance(object, cls):
return objectThe type check validates what reconstruction returned. It cannot undo code already executed during reconstruction. Likewise, changing a strategy's trust setting after loading the manager does not authorize—or secure—the manager file retroactively.
My preference is narrow per-artifact consent, explicit failure when required artifacts cannot load, and a separately visible decision for restoring executable workflow state. One successful load should not silently answer all three questions.
For saved managers that genuinely need executable objects, would you prefer an explicit trust opt-in on the restore API, or a non-executable manager manifest with separately restored components? What compatibility would make the latter impractical?
