DSH plugin 开发示例:最小插件、工具插件、事件插件、带配置插件与服务插件的完整代码示例
DSH plugin 开发示例比规范更好上手:五个示例(最小插件、模型工具、事件监听、带配置、提供服务)覆盖了插件开发的所有常见形态,每个都可以直接抄进 scratch-plugin/src/,用一个 --patch 覆盖层跑起来。 无论叫 DSH插件 还是 DeepSeek插件,跑的步骤完全一样。
五个示例共用同一套目录与运行方式,差别只在源码里注册了什么。先按三步把骨架跑通(来源):
- 建目录 —— 在检出根创建
scratch-plugin/src/。预期:目录结构如下。
deepseek-harness/ # 检出根(已完成 run-from-source)
└── scratch-plugin/
├── src/my-plugin.ts # 下面五个示例都写在这里
└── cordis.yml # 覆盖层配置
- 写覆盖层配置 —— 创建
scratch-plugin/cordis.yml,name填插件源码的绝对路径:
# scratch-plugin/cordis.yml
- insert:
- id: demo
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'
- 启动 —— 执行
pnpm dsh web --patch ./scratch-plugin/cordis.yml。预期:Web UI 起来,并把my-plugin.ts挂进插件树。
路径必须写绝对路径,patch 只贡献配置、不改变 loader 解析模块路径的 profile 目录。环境没搭好的先看 开发环境搭建。
DSH plugin 示例一:最小插件
用途:验证环境与加载链路。 只有一个会打日志的 apply:
import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
console.log('[hello-plugin] plugin loaded!')
}
预期结果:启动时终端打印 [hello-plugin] plugin loaded!。看不到这行就别往下走,先排查路径与构建。
DSH plugin 示例二:模型工具
用途:让模型能调用你的能力。 注意 inject 与 defineTool 三件套:
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: { type: 'string', required: true, description: 'The name to greet' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))
}
预期结果:向模型输入「Use the greet tool to greet Ada.」,模型调用 greet 并拿到 Hello, Ada!。execute 契约与卡片渲染的完整规则见 怎么写工具插件。
DSH plugin 示例三:事件监听与自动清理
用途:在框架时机插入逻辑,并正确释放手动资源。 通过 ctx.on 注册监听、通过 ctx.effect 交出处置器:
import type { Context } from '@deepseek-ai/cordis'
export const name = 'heartbeat-plugin'
export function apply(ctx: Context) {
ctx.effect(() => {
const timer = setInterval(() => console.log('heartbeat'), 5000)
// 返回的函数在插件卸载时执行。
return () => clearInterval(timer)
})
}
预期结果:插件运行后每 5 秒打印一次 heartbeat;卸载插件后定时器停止。把定时器直接写在 apply 里而不包 ctx.effect,插件卸载后仍会继续跑——这是最常见的资源泄漏来源。
DSH plugin 示例四:带配置的插件
用途:让用户能改你的行为。 声明 Config 类型与同名 schema,apply 接收第二个参数:
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
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 const name = 'configured-plugin'
export function apply(ctx: Context, config: Config) {
console.log(`${config.greeting} (retries: ${config.maxRetries})`)
}
预期结果:不提供配置时打印默认值 Hello (retries: 3);提供部分配置时,未提供的字段被补上默认值。配置在界面上的展示见 插件配置怎么用。
DSH plugin 示例五:提供服务
用途:让其他插件调用你。 这时才需要类形态——构造函数里注册服务名,消费方用 inject 声明:
import { Service, type Context } from '@deepseek-ai/cordis'
export default class MetricsService extends Service {
static inject = ['tools']
constructor(ctx: Context) {
super(ctx, 'metrics')
// 在这里做同步初始化。
}
count(name: string) {
console.log(`[metrics] ${name}`)
}
}
预期结果:插件加载后,其他插件用 export const inject = ['metrics'] 即可在 apply 里拿到 ctx.metrics。只有对外提供服务时才用类形态,普通插件用函数形态更简单(来源)。
从 DSH plugin 示例到可安装的包
示例只在检出里跑通,还不能分发。 要变成可安装的包,需要补三样:package.json 里声明 main(指向入口)、type: module、files(包含入口与 cordis.patch.yml)与 dsh.bundle.patch(指向该 patch 文件),并把它放进一个带 cordis.patch.yml 的工程目录。
发布路径:先照 打包成 bundle 生成可安装产物,再用 发布到插件中心 提交收录;装好后可在 DSH Plugin Hub 的已安装列表核对插件与配置项。写代码时的硬性自检项见 开发规范。
常见问题
**DSH plugin 开发示例里先跑最小插件**:只有 name 与一个会打日志的 apply,用 --patch 覆盖层挂进 Web UI,看到日志就说明环境与加载链路都通了。**官方也把最小插件放在教程第一步**,因为后面所有示例都只是往这个骨架里加注册代码(来源:官方「你的第一个插件」)。
**DSH plugin 示例的目录结构分两阶段**:本地调试阶段官方从仓库根创建 scratch-plugin/src/,用绝对路径挂载;准备分发时再把工程独立出来,带上 package.json(main、type: module、files、dsh.bundle.patch)与 cordis.patch.yml。**两者不是二选一**:先在检出里调通,再把它抽成可安装的包(来源:官方「你的第一个插件」与「打包与安装插件」)。
**想在一个 DSH plugin 里同时展示工具与配置,把工具示例与配置示例叠起来就是**:先声明 inject = ['tools'] 与 Config schema,再在 apply(ctx, config) 里用 config 的值构造 defineTool 的 description 或行为。**注意 apply 的第二个参数来自框架按 schema 补齐后的配置对象**,可直接使用(来源:官方「构建一个工具」与「插件配置」)。
**DSH plugin 的服务示例用类形态**:export default class ... extends Service,在构造函数里 super(ctx, 'myService') 把服务名注册进上下文,其他插件通过 inject: ['myService'] 消费。**普通插件注册能力就结束,服务插件还对外提供实例**——所以只有「别的插件要调用你」时才用类形态(来源:官方「服务与依赖」)。
相关术语
- 示例工程(scratch-plugin)
- scratch-plugin 是 DSH plugin 官方教程里用于本地调试的示例目录,位于 Harness 检出根下,通过 --patch 覆盖层以绝对路径挂载,不参与分发。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/index.md
- defineTool
- defineTool 是 DSH plugin 里声明模型可调用工具的函数,接收 name、description、parameters、output 与 execute,并从 parameters 推导 execute 的入参类型。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/tool.md
- Config schema
- Config 是 DSH plugin 用 Schemastery 声明配置类型与默认值的方式;框架加载插件时校验配置并补齐默认值,再把结果作为 apply 的第二个参数传入。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/config.md
- Service
- Service 是 DSH plugin 类形态插件的基类,构造函数里 super(ctx, name) 会把该服务注册到上下文,其他插件通过 inject 声明依赖后即可使用。— https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/framework/service.md
来源
- DeepSeek Harness 官方文档 - 你的第一个插件· deepseek-ai
- DeepSeek Harness 官方文档 - 构建一个工具· deepseek-ai
- DeepSeek Harness 官方文档 - 插件配置· deepseek-ai
- DeepSeek Harness 官方文档 - 服务与依赖· deepseek-ai