第 2 章 五个核心概念:插件、Bundle、Profile、Seam 与事件
"一切皆插件"听上去像口号,但它在 dsh 里是有具体含义的。这一章介绍五个绕不开的概念,懂了它们,后面所有章节都会顺理成章。
2.1 插件(Plugin):一切能力的单元
在 dsh 里,插件是一个导出 apply 函数的 TypeScript 模块。框架加载插件时调用 apply(ctx),把上下文对象 ctx 交给你,你通过 ctx 注册能力:
代码块
Plain Text
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin'
export function apply(ctx: Context) {
// 在这里注册能力:工具、事件监听、定时器……
}
这就是一个完整插件的骨架。模型适配器是插件,工具是插件,会话日志是插件,Web UI 是插件,连"智能体循环"本身都是插件。没有需要打补丁的特权内核——扩展 dsh 的方式,就是把插件挂载到其他插件旁边。
插件有三个关键特性:
•
依赖声明:需要 tools、llm 等服务时,用 inject 声明,框架会等服务就绪再加载你的插件。
•
自动清理:通过 ctx 注册的一切(事件监听、定时器、工具)在插件卸载时自动撤销。需要手动清理的资源用 ctx.effect() 声明。
•
三种形态:函数形式(最常见)、对象形式、类形式(需要对外提供服务时用)。
2.2 组合包(Bundle):插件的分发格式
单看一个插件不够,你得知道"怎么把一堆插件装到一起、分发给别人"。
组合包(bundle)是一个附带一层配置的 npm 包。它的 package.json 里用 dsh.bundle 字段声明自己贡献的 patch 文件:
代码块
Plain Text
{
"name": "dsh-hello-plugin",
"version": "0.1.0",
"type": "module",
"main": "index.js",
"files": ["index.js", "cordis.patch.yml"],
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
cordis.patch.yml 就是它要插入的配置层。别人把这个 bundle 装进自己的 profile,你贡献的插件行就生效了。
一句话记住:插件是能力,bundle 是"能力包"的快递盒。
2.3 Profile:一份可启动的组合
Profile 是存放在 Harness home($DSH_HOME/profiles/
它包含:
•
package.json:树外插件依赖 + dsh.profile 清单(有序的 bundles 列表);
•
cordis.patch.yml:你自己的 patch 层,在每个 bundle 层之后应用。
发行版内置两个模板 profile:
•
web:完整 Web UI(别名 dsh web);
•
headless:一次性运行器,无服务器。
其余 profile 通过 dsh plugin 命令创建和维护,一般不需要手写 manifest。
2.4 层叠与 patch:配置是怎么组合的
运行中的 dsh 是一棵由多层 patch 叠加而成的插件树。生效配置按以下顺序逐层应用(后层覆盖前层,按行覆盖):
profile 的 dsh.profile.bundles 所列各 bundle 的 patch(按列表顺序);
profile 自己的 cordis.patch.yml;
home 级的 $DSH_HOME/cordis.patch.yml(机器级共享偏好);
命令行 --patch
每条 patch 按 id 定位某一行,替换其整个 config 值(不是深合并)。想看自己机器上实际组合出来的配置树:
代码块
Plain Text
dsh --profile web --dump-config
打印出来的任何一行,理论上都可以被你的 patch 替换。这就是"一切皆插件"落地的关键机制:改配置就是改代码,改代码就是改配置。
2.5 Seam:可替换能力
一个 seam(能力接缝) 是一项可替换能力,由三种角色组成:
•
Service Definition:声明接口;
•
Service Provider:实现它;
•
Consumer:使用它(通常是面向模型的工具)。
举个例子:文件系统。本地提供方让你在本地改文件;把 provider 换成远程沙箱(官方示例里有 E2B 的 POC overlay),Bash、PTY、LSP 会一并搬过去,因为它们共享同一个执行世界。Consumer 的代码一行不用改。
这就是"换一个提供方就能改变整个产品"的原因,也是 seam 和普通"接口"的区别:单一角色不是 seam,把定义、实现、使用三者一起设计才是。
2.6 事件:真正的扩展点
在 dsh 里,事件不只是"通知",它们是架构意义上的扩展点。分三类:
Cordis(dsh 底层的插件框架)为事件提供四种分发模式:
•
emit:观察,监听器按注册顺序执行;
•
waterfall:中间件,可以包装或短路(next() 委托下游);
•
parallel:并行扇出;
•
serial:按序执行并传值。
你会经常打交道的是 agent/* 和 tools/* 系列事件。比如 agent/pre-step 决定模型这一轮看到什么,agent/request 可以在每次模型请求前替换推理档位,tools/pre-execute 可以给工具执行加审批策略。
2.7 三个容易混的概念
很多新手在这里绕晕,用一张表钉死:
2.8 本章小结
•
插件是能力单元,一切皆插件意味着没有特权内核;
•
bundle 是插件的分发格式,profile 是可启动的装配;
•
配置通过多层 patch 叠加组合,后层覆盖前层;
•
seam 是"接口 + 实现 + 使用"三位一体的可替换能力;
•
事件是扩展点,agent/* 和 tools/* 是最常用的两个域。
概念讲完了,下一章动手——用一行命令把 dsh 跑起来。