DSH plugin 怎么写:从 apply 与 ctx 的关系、四种常见能力的代码写法,到新手最容易写错的三个地方

插件开发发布于 2026-09-12作者: DeepSeek Plugin 插件市场
DSH pluginDeepSeek Harness插件怎么写applyCordis
DeepSeek Harness(DSH)插件怎么写:先理解插件就是导出 apply 的模块、能力都在 apply 里通过 ctx 注册,再照四段代码把工具、配置、依赖、事件写进去,最后避开混写导出、漏写 inject、忘记清理这三个高频错误。

DSH plugin 怎么写,一句话就够:写一个导出 apply 函数的 TypeScript 模块,能力全部在 apply 里通过 ctx 注册——插件不 import 框架内部对象,ctx 就是唯一入口。 无论你叫它 DSH插件 还是 DeepSeek插件,写法都是这一套。

DSH plugin 的心智模型:插件 = 模块 + apply + ctx

在 DSH plugin 体系里,插件不是一个要继承的基类,而是一个导出 apply 的普通模块。 框架加载插件时调用 apply 并传入 ctx(上下文对象),你在其中注册能力(来源):

ts
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

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,injectdefineTool 缺一不可。 先声明依赖,再注册:

ts
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 接收第二个参数:

ts
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 注册的东西会自动清理,但定时器、网络连接这类资源要显式交出清理函数:

ts
export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => console.log('heartbeat'), 5000)
    // 返回的函数在插件卸载时执行。
    return () => clearInterval(timer)
  })
}

注意事件有不同触发模式(广播、短路、有序、管道),管道模式下必须调用 next() 才会继续传递。选错模式会出现「逻辑写了但不执行」,详见 开发指南 的事件小节。

DSH plugin 写完怎么自查:三个易错点与 --patch 验证

写完先自查这三条,能挡掉大部分「加载失败」与「资源泄漏」。

  1. 混写导出方式——函数形态用具名导出export const name + export function apply),对象形态与类形态用默认导出export default)。两者不能混写,混写会让加载器识别不出形态。
  2. 用了 ctx.tools 却没写 inject——框架只在 inject 声明后才保证依赖服务就绪;漏写会在某些启动顺序下拿到 undefined
  3. 手动资源忘了 ctx.effect()——setInterval、连接池、文件句柄不包进 ctx.effect,插件卸载后仍在运行。

这三条属于开发规范的硬性自检项,完整清单见 DSH plugin 开发规范

再跑一次确认加载成功,本地最省事的方式是用 --patch 覆盖层挂载,三步:

  1. 取插件源码的绝对路径 —— 在插件工程根执行 pwd预期:拿到下一步 name 要用的绝对路径。
  2. 写覆盖层配置 —— 创建 cordis.yml,在 insert 段的 name 里填入上一步的绝对路径:
yaml
- insert:
    - id: hello
      name: '/absolute/path/to/scratch-plugin/src/my-plugin.ts'
  1. 启动并查看日志 —— 执行 pnpm dsh web --patch ./scratch-plugin/cordis.yml预期:终端打印出插件里的 console.log,说明模块导出与加载链路都通了。

这一步只验证「代码写对了」,还没验证「能分发」——要装进 profile 验证分发,就照 开发教程 的后三步走,装好后回到 DSH Plugin Hub 的已安装列表核对是否出现、配置项是否正常渲染。

常见问题

DSH plugin 怎么写?最核心的一句话是什么?

DSH plugin 怎么写可以浓缩成一句:**写一个导出 apply 函数的 TypeScript 模块,能力全部在 apply 里通过 ctx 注册**。框架加载插件时调用 apply 并传入 ctx,你不需要 import 框架内部对象,也不需要写注册表——ctx 就是唯一的入口(来源:官方「你的第一个插件」)。

写 DSH plugin 时,name 和 apply 哪个是必须的?

**写 DSH plugin 时两个都写,但职责不同:name 是插件的唯一标识,apply 是加载入口。**官方最小插件同时导出 export const nameexport function apply(ctx)apply 可以接受第二个参数 config(配合 Config schema 使用时)。只写 apply 也能运行,但缺少稳定标识会让日志与依赖解析变模糊(来源:官方「你的第一个插件」)。

DSH plugin 怎么写一个模型可调用的工具?

**在 DSH plugin 里写一个模型可调用的工具分三步**:先 export const inject = ['tools'] 声明依赖,再在 applyctx.tools.register(defineTool({ ... })),最后在 defineToolparameters 里声明参数、output 里声明输出与 render。**inject 不能省**——没有它,框架不保证 ctx.tools 已经就绪(来源:官方「工具」)。

写 DSH plugin 最容易写错的地方有哪些?

**写 DSH plugin 最容易写错三个地方:① 混写具名导出与默认导出**(函数形态用具名导出,对象 / 类形态用默认导出);**② 用了 ctx.tools 却没写 inject**;**③ 手动资源忘了用 ctx.effect() 提供处置器**,插件卸载时连接和定时器不会释放。前两个会导致加载失败或时序错误,第三个会造成资源泄漏(来源:官方「你的第一个插件」)。

DSH plugin 写完怎么确认写对了?

**写完 DSH plugin 用 --patch 覆盖层跑一次,看插件是否被加载。** 把插件绝对路径写进一份 cordis.ymlinsert 段,然后 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

来源