插件生命周期:能力归属、作用域与工作结算
工具注册、文件服务和 Agent 工作各有所有者。Cordis 回收插件贡献,scope 限定可见性与登记归属;在途执行的取消和等待由工作所有者负责。
箭头表示标注的调用或依赖关系;点击组件定位正文。完整关系见下方文字说明。
工具注册与文件提供者分别持有能力
启动配置包含 dsh-tools、dsh-fs-local 和 dsh-tool-fs。dsh-tools 提供工具注册表 ctx.tools,dsh-fs-local 提供文件服务 ctx.fs,dsh-tool-fs 使用这两个服务注册 read 等工具。ctx 是插件收到的上下文对象,插件通过它查找服务、注册工具和监听事件。
dsh-tools · 接口摘录,省略上下文 · ctx.tools.register · packages/core/tools/README.md// 工具插件登记定义,返回可撤销注册;模型可见描述由注册表参与组装。
const dispose = ctx.tools.register(definition)这三个包承担不同操作:工具注册表保存名称与执行器,文件提供者负责磁盘访问,文件工具把模型参数转换成文件服务调用。服务定义约定方法和类型,提供者实现方法,消费者调用方法;本例中的文件工具就是文件服务的消费者。
消费者通过 inject 声明必需服务。Cordis 在依赖就绪后激活相应插件,因此配置中写有 dsh-tool-fs,并不等于 read 已可用:缺少必需服务时,消费者仍需等待。Cordis 管理每个插件运行实例的依赖和退出过程,这个运行实例称为 fiber。
工具分派依赖可替换的文件服务
插件完成注册后,工具注册表可以向模型请求提供 read 的名称、说明和参数。模型返回 read(README.md) 时,AgentLoop 把调用交给 dsh-tools,后者分派给 dsh-tool-fs;文件工具再调用 ctx.fs,由 dsh-fs-local 访问磁盘。文件工具调用的是文件服务的方法,不需要直接导入本地文件实现。
这也说明替换提供者为什么有意义:只要新的提供者满足相同文件服务约定,文件工具就可以继续使用同一套调用方式。替换过程仍须正确处理旧实例和依赖关系;修改配置后是否已应用、是否需要重启,应以装配结果为准。
本例为应用级注册。某项工具最终是否对某个 Agent 可见,还取决于注册作用域和工具限制;注册成功不等于所有 Agent 都能调用。
权限策略加入调用前的事件链
直接调用 ctx.fs 用于读取文件;执行前审批等附加行为则通过事件接入。例如工具执行链发出 tools/pre-execute,权限插件监听这个事件,参与是否允许本次调用的决定。这样文件工具仍负责读取,权限插件负责自己的检查,两者不需要复制 AgentLoop。
dsh-tools · 接口摘录,省略上下文 · tools/pre-execute · packages/core/tools/README.md// 权限等插件在执行前参与允许、拒绝或审批;需委托后续监听时调用 next()。
'tools/pre-execute'ctx.on 把监听器登记到插件所属的上下文,并关联清理操作。对于 waterfall 事件,监听器调用 next() 才会继续执行后续监听器;直接返回会截断后续处理。审批插件可以在自己负责的决定上截断,单纯记录信息的监听器则应继续委托。若遗漏 next(),后面的插件即使已加载,也不会收到这次处理机会。
卸载回收贡献并由所有者结算工作
现在关闭 dsh-tool-fs,并假定配置变更已经应用到运行中的应用。该插件注册的 read、write、edit 等工具应随实例退出而撤回。随后重新组装模型请求时,不能继续把这些已撤回的工具当作可用能力;已经发出的模型请求不会因此被改写。dsh-fs-local 是另一个插件,关闭文件工具不等于也关闭文件服务,其他消费者是否继续工作取决于各自的依赖。
注册需要清理操作。例如工具注册 API 返回的 disposer 用于撤销注册,ctx.effect 关联资源的创建与清理,ctx.on 关联监听器的注册与移除。插件退出时,框架执行这些清理;插件作者仍须把自己创建的注册和资源正确纳入管理。
撤回注册只阻止后续继续使用该注册,不能证明先前的读取、模型请求或后台任务已经停止。拥有这些工作的组件还需要发出取消、等待工作结束,再释放相关资源。例如 Agent 的拥有者应等待其循环和工作结束。同一进程中不响应取消的代码仍可能拖延退出,依赖注入不会强制终止它。
重新激活建立新一轮注册
重新启用文件工具时,Cordis 再根据依赖建立可运行的插件实例,文件工具重新注册 read 等能力。下一次模型请求组装时,工具注册表据当前注册和 Agent 的可见范围生成工具描述。先前的会话记录仍是历史事实,不会因为卸载、重载工具而自动删除。
如果旧实例没有撤销监听器,重新加载后就可能对同一个事件处理两次;如果资源没有释放,旧任务也可能继续占用连接或进程。可撤销注册的作用,是让每次启用对应自己的注册,每次退出撤销自己创建的注册。配置操作若只写入磁盘、尚未应用,以上运行变化也尚未发生;需要重启的组合应重启后再确认。
Agent 作用域承载贡献与生命周期
文件工具也可能只为某个 Agent 注册。例如插件通过该 Agent 的 agent.ctx 注册一个专用工具,这个注册只对该 Agent 的作用域可见。dsh-scope 将注册的可见范围和存活时间关联起来:关闭该 Agent 的作用域时,撤销其中拥有的工具、提示词片段和监听器,不应连带删除其他 Agent 的独立注册。
dsh-scope · 接口摘录,省略上下文 · createScope(ctx, key) · packages/core/scope/README.md · scope.dispose() · packages/core/scope/README.md// 组合方为 Agent 等拥有者建立作用域,关联注册的可见范围与存活时间。
const scope = createScope(ctx, agent)
// 作用域拥有者等待清理其中的注册;作用域本身不构成文件或进程沙箱。
await scope.dispose()dsh-agent 提供 AgentHandle;拥有者调用并等待 handle.dispose(),以完成该 Agent 的停止与清理。调用 agent.cancel() 主要用于取消当前工作,并不等于销毁 Agent,也不等于卸载应用级插件。
dsh-agent · 接口摘录,省略上下文 · AgentHandle.dispose() · packages/core/agent/README.md// 仅拥有 AgentHandle 的组件销毁 Agent,等待循环停止、注销并展开作用域清理。
await handle.dispose()因此,排查一个工具为何消失时,需要区分三个操作:停用提供工具的插件、结束拥有专属注册的 Agent、修改工具可见限制。作用域管理的是注册归属与可见性;它不是文件或进程沙箱,也不会让任意 ctx 服务自动变成每个 Agent 各有一份。
辅助示例
观察同一项文件读取能力的变化:启动文件服务与文件工具 → read 完成注册 → 模型调用 read 读取 README → 停用文件工具并撤回注册 → 重新启用并建立新注册。每一步分别对应服务是否就绪、工具是否可见、执行器是否可调用和旧工作是否已结束;配置文件中的启用状态不能单独回答这些问题。
设计取舍
文件工具依赖 ctx.fs,而不是固定依赖本地实现,因此文件后端可以独立替换;工具和监听器随所属实例退出而撤销,因此重载时不必另行维护全局清理名单。代价是插件作者必须明确资源归属,并为注册、请求、进程和子任务实现相应的清理。排错时要分别确认有效配置、依赖就绪、作用域可见范围、注册状态与在途工作,不能仅凭插件名称出现在清单中判断能力正常。
相关包与插件
- 类型化工具注册与执行
@deepseek-ai/dsh-tools - 本机文件系统提供者
@deepseek-ai/dsh-fs-local - 文本图像读取与文件编辑工具
@deepseek-ai/dsh-tool-fs - 继承式作用域原语
@deepseek-ai/dsh-scope - 活动 Agent 生命周期接口
@deepseek-ai/dsh-agent - 标准模型工具执行循环
@deepseek-ai/dsh-agent-loop
资料来源与核验范围
本章依据固定版本的官方文档整理,设计取舍根据所述机制分析;核验范围为文档,不包含运行实测。