11 运行时自我修改:动态插件的进程内存生命周期¶
本章回答一个问题:agent 能"给自己加插件"吗?上游的答案克制而有边界——能,但临时插件只存在于进程内存,明确不写配置文件、重启即失。我们复现这套生命周期语义本身(define → run → stop → undefine + 检查族),模型侧与审批往返不做。
对应 dsh 真实源码:
packages/extensions/(tool-cordis七工具 +cordis-host-runner宿主)。mini 复现于miniharness/dynamic.py。
11.1 上游的边界设计¶
自我修改在上游不是"agent 任意改配置":tool-cordis 的说明文档第一句就写了(已核实,tool-cordis/README.md:19):
临时插件只保存在进程内存中,明确不写入 cordis.yml,也不会在重启后恢复。
这是刻意的安全设计:模型可以在这个进程里定义/运行/停止插件(改"运行时"),但不允许改"启动配置"(改"持久状态"需要人工操作文件)。配套还有几层约束:
- 工具分两族:检查族(
inspect_list/inspect_query/inspect_self)+ 修改族(define/run/stop/undefine); - kind 限定:
define只支持kind new(mint 新的 dyn 包); - 宿主耦合:browser half 带审批往返,agent 不能单方面获得工具能力(审批侧)。web 的审批(第 09 章)管的是"模型要运行什么",这里的宿主管线管"模型要定义什么"。
11.2 生命周期(对应 runner.spec.ts:150-176 的完整流程)¶
上游测试里的标准路径(已核实):
define(kind: new)→ mintdyn-1/pkg-1,只登记,不生效;run→ 立即status: 'running'、mintrun-1,插件apply执行、ctx.provide生效;invoke→ 调用插件提供的服务,返回42;stop/undefine→ 逆序回收。
mini 复现的对应 API:
registry = DynamicPluginRegistry(host_ctx)
pkg_id = registry.define("new", "演示插件", provides=["dynDoubler"],
apply=lambda ctx: ctx.provide("dynDoubler", lambda x: x * 2))
run = registry.run(pkg_id) # {"runId": "run-1", "status": "running", ...}
registry.invoke(run["runId"], "dynDoubler", 21) # 42
registry.stop(run["runId"])
registry.undefine(pkg_id)
11.3 硬性规定(被测试钉住)¶
- define 只登记:run 之前服务不可用(invoke 抛 KeyError)。
- 运行中不可重复 run / 不可 undefine:
run抛 RuntimeError;undefine要求先stop。 - 进程级冲突 fail loud:插件声明的 provides 若祖先链上已存在(如 host 的
session-persistence),run 直接拒绝,host 服务不被覆盖。 - 只存进程内存:新 registry = "重启",定义与运行全部不恢复(
inspect_self快照为空)。 - kind 限定:非
new的 define 抛 ValueError。 - stop 逆序回收:dispose 隔离 scope(副作用逆序回滚),服务消失。
检查族(inspect_self / query / list)只读注册表快照,与修改族严格分离——与上游 inspect_* 工具族的边界一致。
11.4 简化标注¶
- 上游经模型调用(tool-cordis 是被工具调用的入口),mini 直接操作注册表对象;
- 上游带审批往返(browser half),mini 不做(载体简化,语义不造假);
- 上游
run会同步配置钩子/挂载包,mini 以apply(ctx)钩子 + 隔离 scope 表达等价效果。
11.5 检查点¶
- 说出"进程内存 + 不写配置文件"这个边界为什么重要(运行时 vs 持久状态分离);
- 手写一次完整生命周期并观察各阶段
inspect_self快照变化; - 构造一个进程级冲突场景,验证 fail loud 且 host 服务不被覆盖;
- 说出 mini 相对上游的简化(模型侧入口、审批往返)。
第 08-11 章至此构成"组合层"闭环:预设(静态装配)→ 干预(运行时控制)→ 轨迹(事后投影)→ 动态插件(运行中自修改),审批(09 章 §9.5)管住干预通道。外部入口(07 章 §7.6-7.8)已复现;下一步进入第 12 章:异步化与并行工具执行(mini 与 dsh 的最大差距)。