DSH plugin 怎么写:从 apply 与 ctx 的关系、四种常见能力的代码写法,到新手最容易写错的三个地方
DSH plugin 怎么写,一句话就够:写一个导出 apply 函数的 TypeScript 模块,能力全部在 apply 里通过 ctx 注册——插件不 import 框架内部对象,ctx 就是唯一入口。 无论你叫它 DSH插件 还是 DeepSeek插件,写法都是这一套。
DSH plugin 的心智模型:插件 = 模块 + apply + ctx
在 DSH plugin 体系里,插件不是一个要继承的基类,而是一个导出 apply 的普通模块。 框架加载插件时调用 apply 并传入 ctx(上下文对象),你在其中注册能力(来源):
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin'
export function apply(ctx: Context) {
// 在这里注册能力。
}
上面这段就是完整配置,没有别的必填项。 记住三个角色:name 是插件的唯一标识,apply 是加载入口,ctx 是访问框架能力的唯一入口。想要更完整的选型判断(函数 / 对象 / 类该用哪种)见 开发指南。
DSH plugin 第一段代码:最小可跑骨架
先用一句日志验证「插件被加载了」,再往里加能力。 创建 src/my-plugin.ts:
import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
console.log('[hello-plugin] plugin loaded!')
}
启动后终端打印这行日志,说明模块导出与加载链路都通了。写代码的顺序建议永远是「先让 apply 被执行 → 再注册能力」,否则出问题时无法区分是加载失败还是能力没生效。
DSH plugin 第二段代码:把工具写进去
给模型用的能力写成 tool,inject 与 defineTool 缺一不可。 先声明依赖,再注册:
import { defineTool } from '@deepseek-ai/dsh-tools'
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-tool-plugin'
export const inject = ['tools']
export function apply(ctx: Context) {
// 到这里 ctx.tools 已经就绪。
ctx.tools.register(defineTool({
name: 'my_cap',
description: 'Execute my capability.',
parameters: {
input: { type: 'string', required: true },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return args.input.toUpperCase()
},
}))
}
description 是写给模型看的,不是注释——模型靠它判断什么时候调用这个工具。更完整的工具写法(参数类型、输出渲染、错误处理)见 怎么写一个工具插件。
DSH plugin 第三段代码:让插件接受配置
配置用 Config 类型 + 同名 Schemastery schema 声明,默认值直接写在 schema 里。 然后 apply 接收第二个参数:
import Schema from '@deepseek-ai/schemastery'
import type { Context } from '@deepseek-ai/cordis'
export interface Config {
greeting: string
maxRetries: number
}
export const Config: Schema<Config> = Schema.object({
greeting: Schema.string().default('Hello'),
maxRetries: Schema.number().default(3),
})
export function apply(ctx: Context, config: Config) {
console.log(config.greeting)
}
框架加载插件时会用 schema 校验配置并补默认值,所以 apply 里拿到的 config 一定是完整对象。配置项与界面展示的对应关系,详见 插件配置怎么用。
DSH plugin 第四段代码:监听事件与手动清理
事件监听用 ctx.on,需要手动清理的资源用 ctx.effect() 提供处置器。 通过 ctx 注册的东西会自动清理,但定时器、网络连接这类资源要显式交出清理函数:
export function apply(ctx: Context) {
ctx.effect(() => {
const timer = setInterval(() => console.log('heartbeat'), 5000)
// 返回的函数在插件卸载时执行。
return () => clearInterval(timer)
})
}
注意事件有不同触发模式(广播、短路、有序、管道),管道模式下必须调用 next() 才会继续传递。选错模式会出现「逻辑写了但不执行」,详见 开发指南 的事件小节。
DSH plugin 写完怎么自查:三个易错点与 --patch 验证
写完先自查这三条,能挡掉大部分「加载失败」与「资源泄漏」。
- 混写导出方式——函数形态用具名导出(
export const name+export function apply),对象形态与类形态用默认导出(export default)。两者不能混写,混写会让加载器识别不出形态。 - 用了
ctx.tools却没写inject——框架只在inject声明后才保证依赖服务就绪;漏写会在某些启动顺序下拿到undefined。 - 手动资源忘了
ctx.effect()——setInterval、连接池、文件句柄不包进ctx.effect,插件卸载后仍在运行。
这三条属于开发规范的硬性自检项,完整清单见 DSH plugin 开发规范。
再跑一次确认加载成功,本地最省事的方式是用 --patch 覆盖层挂载,三步:
- 取插件源码的绝对路径 —— 在插件工程根执行
pwd。预期:拿到下一步name要用的绝对路径。 - 写覆盖层配置 —— 创建
cordis.yml,在insert段的name里填入上一步的绝对路径:
- insert:
- id: hello
name: '/absolute/path/to/scratch-plugin/src/my-plugin.ts'
- 启动并查看日志 —— 执行
pnpm dsh web --patch ./scratch-plugin/cordis.yml。预期:终端打印出插件里的console.log,说明模块导出与加载链路都通了。
这一步只验证「代码写对了」,还没验证「能分发」——要装进 profile 验证分发,就照 开发教程 的后三步走,装好后回到 DSH Plugin Hub 的已安装列表核对是否出现、配置项是否正常渲染。
常见问题
DSH plugin 怎么写可以浓缩成一句:**写一个导出 apply 函数的 TypeScript 模块,能力全部在 apply 里通过 ctx 注册**。框架加载插件时调用 apply 并传入 ctx,你不需要 import 框架内部对象,也不需要写注册表——ctx 就是唯一的入口(来源:官方「你的第一个插件」)。
**写 DSH plugin 时两个都写,但职责不同:name 是插件的唯一标识,apply 是加载入口。**官方最小插件同时导出 export const name 与 export function apply(ctx),apply 可以接受第二个参数 config(配合 Config schema 使用时)。只写 apply 也能运行,但缺少稳定标识会让日志与依赖解析变模糊(来源:官方「你的第一个插件」)。
**在 DSH plugin 里写一个模型可调用的工具分三步**:先 export const inject = ['tools'] 声明依赖,再在 apply 里 ctx.tools.register(defineTool({ ... })),最后在 defineTool 的 parameters 里声明参数、output 里声明输出与 render。**inject 不能省**——没有它,框架不保证 ctx.tools 已经就绪(来源:官方「工具」)。
**写 DSH plugin 最容易写错三个地方:① 混写具名导出与默认导出**(函数形态用具名导出,对象 / 类形态用默认导出);**② 用了 ctx.tools 却没写 inject**;**③ 手动资源忘了用 ctx.effect() 提供处置器**,插件卸载时连接和定时器不会释放。前两个会导致加载失败或时序错误,第三个会造成资源泄漏(来源:官方「你的第一个插件」)。
**写完 DSH plugin 用 --patch 覆盖层跑一次,看插件是否被加载。** 把插件绝对路径写进一份 cordis.yml 的 insert 段,然后 pnpm dsh web --patch ./scratch-plugin/cordis.yml 启动;插件里写一句 console.log 就能在终端确认加载时机。确认加载后,再照开发规范的清单过一遍导出、依赖、清理与配置(来源:官方「你的第一个插件」)。
相关术语
- apply
- apply 是 DSH plugin 的入口函数,签名通常为 apply(ctx, config)。框架加载插件时调用它,插件在其中通过 ctx 注册工具、服务、事件等能力。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/index.md
- ctx(上下文对象)
- ctx 是 DSH plugin 框架传给 apply 的上下文对象,也是插件访问框架能力的唯一入口:注册工具用 ctx.tools,注册事件用 ctx.on,注册处置器用 ctx.effect,读取其他服务用 ctx.get。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/framework/service.md
- defineTool
- defineTool 是 DSH plugin 里声明工具能力的辅助函数,接收 name、description、parameters、output、execute 等字段,返回可直接交给 ctx.tools.register 的工具定义。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/tool.md
- Config schema
- Config 是 DSH plugin 声明配置的类型与校验规则,通常用 Schemastery 写同名 Schema 并给出默认值,框架加载插件时据此校验配置并补默认。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/config.md
来源
- DeepSeek Harness 官方文档 - 你的第一个插件· deepseek-ai
- DeepSeek Harness 官方文档 - 工具(defineTool)· deepseek-ai
- DeepSeek Harness 官方文档 - 插件配置(Config schema)· deepseek-ai
- DeepSeek Harness 官方文档 - 服务与依赖(inject)· deepseek-ai