首页 / DeepSeek Harness 入门教程 / 插件开发起步

DeepSeek Harness 入门教程

插件开发起步

本教程共 32 篇 · 第 20 篇 · 更新于 2026-08-15 · 约 6 分钟阅读

插件插件开发applyctxpatch加载顺序hello-dsh

本节目标:理解插件的本质与最小结构,写出第一个 hello 插件,并搞清 dsh 按什么顺序加载插件。

一切皆插件

DeepSeek Harness 的标语是 Everything is a Plugin(一切皆插件)。这不是宣传口号。默认 web profile 会挂载 133 个插件(hello-dsh 教程实测,以你本机插件列表为准)。模型适配器(llm)、会话历史(session)、你正在看的网页(webserver)、左侧边栏(ui-sidebar),甚至 agent 的主循环(agent-loop)——全部是插件。

这意味着框架没有「特权核心」。你想让 Agent 多一个能力,就挂一个插件;想换掉某个实现,就换一个插件。卸载时,插件注册的一切自动回退。这套模型来自底层元框架 Cordis(4.0.0-rc.8)。从本章起,我们开始用代码接触它。

最小插件:一个文件

插件是一个导出 apply 函数的 TypeScript 模块。apply 是插件的入口——框架加载插件时调用它,并传入 ctx(上下文对象)。

// hello.ts
import type { Context } from '@deepseek-ai/cordis'

export const name = 'hello'  // 插件名,用于日志与诊断

export function apply(ctx: Context) {
  console.log('hello from my first plugin')
}

name 是可选的显示元数据,只用于标识插件。apply 是唯一必须的东西。这段代码没有注册任何能力,但已经是一个合格的插件。

apply 与 ctx

ctx 是框架传给每个插件的上下文对象。它有两个身份:

  • 注册入口ctx.on() 监听事件、ctx.tools.register() 注册工具,都通过它完成。
  • 注册记录本:通过 ctx 注册的一切资源都记在插件的 Fiber 作用域里,插件卸载时自动清理。

新手容易把 ctx 当成全局对象。它其实绑定当前插件实例的生命周期——插件没了,注册的东西跟着没了。

三种插件形态

函数形态最常见,但 Cordis 还接受对象形态和类形态:

import { Service, type Context } from '@deepseek-ai/cordis'

// 1. 函数形态:直接导出 apply
export function apply(ctx: Context) {}

// 2. 对象形态:一个带 apply 方法的对象
export const objectPlugin = {
  name: 'object-plugin',
  apply(ctx: Context) {},
}

// 3. 类形态:Service 子类,用于对外提供服务
export class MyService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'myService')
  }
}

选择规则很简单:大多数场景用函数形态;需要向其他插件提供服务时,用类形态(第 22 章展开)。

加载插件:—patch 覆盖层

写好的插件怎么进应用?开发期最常用 --patch。建一个本地项目目录:

scratch-plugin/
├── src/
│   └── my-plugin.ts    # 插件源码
└── cordis.yml          # patch 覆盖层

cordis.yml 告诉 dsh 插入哪个插件:

- insert:
    - id: hello
      name: '/absolute/path/to/scratch-plugin/src/my-plugin.ts'
Note

name 必须是绝对路径。patch 文件只贡献配置,不会改变 loader 解析模块路径时使用的目录。

--patch 启动 Web UI:

npx @deepseek-ai/dsh web --patch ./scratch-plugin/cordis.yml

终端打印 hello from my first plugin 即加载成功。打开 http://127.0.0.1:3080,在设置 → 插件列表里能搜到它。

插件加载顺序

生效配置在空根之上按固定顺序逐层组合,后应用的层按行胜出:

  1. profile 的 dsh.profile.bundles 列表(先是 @deepseek-ai/dsh-base,再是各已安装组合包,按加入顺序)
  2. profile 自己的 cordis.patch.yml
  3. home 级 $DSH_HOME/cordis.patch.yml(机器本地偏好,各 profile 共享)
  4. 每个 --patch <path> overlay(按命令行顺序)
Tip

patch 按 id 整行替换目标行的整个 config,不做深度合并。覆盖别人的行时,必须重述该行需要的每一个键。

查看本机实际生效的插件树用 dsh --profile web --dump-config,输出里能看到每一层来自哪个组合包。

插件如何被发现

插件有三条被发现路径:

  • 本地开发--patch 插入本地文件,路径写在 cordis.yml 里。
  • 打包分发:声明 dsh.bundle 的 npm 包,用 dsh plugin --profile <name> add <包名> 安装进 profile(第 24 章)。
  • 生态检索:GitHub 仓库打上 dsh-plugin topic,就会被社区插件列表收录。官方 awesome 列表与第三方插件市场都按这个 tag 检索。

hello-dsh:社区实例参考

社区仓库 pingfanfan/hello-dsh 是零基础插件开发教程,包含 22 个中文 skill 示例,面向 0.1.0-rc.6 实测过。它确认了几个容易踩的细节:web profile 默认禁用 skill 相关插件(需要 --patch 启用)、--patch 是插入而非覆盖(指 insert 条目;按 id 改既有行仍会替换整个 config)、端口被旧进程占用时新实例会报 EADDRINUSE。这些行为随版本变化,与你版本不一致时,以 dsh --version 实测为准。

Warning

项目处于 Developer Preview 阶段(基线 @deepseek-ai/dsh 0.1.0-rc.6),API 可能破坏性变更。写插件前先 dsh --version 确认版本。

加载失败会怎样

apply 抛异常,进程会因该错误终止——插件加载失败会明确报错,不会静默跳过。但有一条例外:如果 name 指向的模块无法解析(路径拼错、包名写错),具体行为以官方 CLI 参考和 dsh --version 实测为准。新加配置项没效果时,先查拼写。

小结

  • 插件 = 导出 apply 的模块,ctx 是注册入口,也是注册记录本。
  • 三种形态:函数、对象、Service 子类;大多数场景用函数。
  • 开发期用 --patch 加载本地插件,name 必须是绝对路径。
  • 四层加载顺序:Bundle 序 → Profile patch → home patch → --patch
  • apply 抛错进程终止;模块无法解析时以官方 CLI 参考和实测为准。