您的位置:首页 > 手游攻略 > DeepSeek Harness中插件开发新手指南

DeepSeek Harness中插件开发新手指南

作者:互联网  时间: 2026-08-25 20:38:54  

面向已有 Node.js / TypeScript 基础的开发者。读完本文,你可以独立完成:开发一个 DSH 插件 → 本地调试挂载 → 发布到社区并被他人安装。

DeepSeek Harness中插件开发新手指南

版本说明:DeepSeek Harness 目前处于开发者预览(Developer Preview)阶段,迭代很快,官方明确声明会有兼容性破坏变更。文中机制基于 @deepseek-ai/dsh 0.1.0-rc.x 时代的公开资料整理,动手前请以官方仓库文档(GitHub:deepseek-ai/deepseek-harness)和 dsh --dump-config 的实际输出为准。

一、为什么现在是风口

DeepSeek Harness(命令行简称 dsh)是 DeepSeek 开源的 Agent 框架(agent harness),架构上「一切皆插件」:模型适配器、工具注册表、会话日志、甚至 Agent 主循环本身都是插件,整个产品就是启动时从若干配置层组合出来的一棵插件树。

对开发者友好的几点现状:

  1. 核心仓库暂不接受外部 PR,官方把贡献路径明确指向生态:发布插件、写教程、答社区问题、报 issue。
  2. 插件就是普通 npm 包,没有专门的注册中心,发布门槛极低。
  3. 官方指定的发现渠道只是给 GitHub 仓库打一个 dsh-plugin 话题(Topic),就会被社区聚合目录收录。
  4. 社区内测期间已出现数百个公开插件(awesome 目录统计 900+),但大量细分领域仍是空白。

二、核心概念:一切皆插件

2.1 没有「内核 + 插件」的分层

安装目录下的约 195 个 @deepseek-ai/*全部是 Cordis 插件——工具、LLM 适配器、会话持久化、Web 服务器、前端 UI、沙箱策略都不例外。你写的插件和官方的 dsh-tool-bash 地位完全相同,没有「插件 API」和「内核 API」之分。

  1. 加能力 = 往组合里加一行
  2. 改行为 = 用 patch 覆盖已有的行

2.2 底层框架 Cordis:五个必须记住的想法

想法含义
插件一个实现了 Service 的对象:最常见是带 apply(ctx) 的函数,也可以是带 inject 的对象或 Service 子类
上下文(Context)服务的仓库。服务挂到稳定的 ctx.(如 ctx.toolsctx.llmctx.sessions),插件之间通过 key 找服务,不 import 具体实现
inject声明插件需要的必需服务。loader 会等待这些服务存在后再执行插件,加载顺序由依赖决定而不是文件顺序
类型化事件服务通过声明合并定义事件,用 emit / waterfall / parallel / serial 分发给监听者
可逆的注册工具 schema、监听器等都通过 ctx.effect() / ctx.on() 注册;插件卸载(HMR、热重载、关停)时一切自动回滚

扩展点(事件 / 服务)就是 dsh 的「API」。改行为时优先挂在扩展点上,不要去改主循环。

2.3 Profile 与 Bundle:两个关键概念

概念manifest回答的问题
bundle(插件分发单元)dsh.bundle(指向 patch 文件)「这个包贡献什么」——一个配置层(cordis.patch.yml),由 npm 包分发
profile(可运行组合)dsh.profile(bundles 列表)「哪些 bundle 按什么顺序组成这个运行实例」

bundle 是作者分发的单元,profile 是用户启动的单元,dsh plugin 命令负责维护 profile。

启动时配置层的叠加顺序(后层覆盖前层):

  1. profile 清单里列出的各 bundle(按顺序)
  2. profile 自己的 cordis.patch.yml
  3. 家目录级 $DSH_HOME/cordis.patch.yml(对本机所有 profile 生效)
  4. 命令行 --patch 覆盖(按 argv 顺序)

查看你的机器实际组合出的插件树:

dsh --profile web --dump-config

打印出来的任何一行,都可以用你自己的 patch 替换。 patch 按行的 id 定位:要么整行替换其 config(不是深合并),要么插入新行。

三、开发环境准备

3.1 前置要求

Node.js:官方声明范围 ^22.19.0 || >=24.0.0,不确定时直接用 Node 24。

pnpmdsh plugin 子命令会把参数原样转发给 profile 目录里的 pnpm,没有 pnpm 会直接报错:

npm install -g pnpm

DeepSeek API Key(运行真实模型时需要):把 DEEPSEEK_API_KEY 放进根目录 .envpnpm dsh 会自动加载。没有 Key 也可以先写代码、跑单元测试和 --dump-config 验证。

3.2 安装 dsh

# 方式一:直接从 npm 运行(推荐普通开发者)npx @deepseek-ai/dsh web        # 默认在 http://127.0.0.1:3080 启动 Web UI# 方式二:克隆源码开发(推荐要深度调试的开发者)git clone https://github.com/deepseek-ai/deepseek-harness.gitcd deepseek-harnesspnpm installpnpm run build                  # 不要省!只装依赖不构建会导致 Web 页面缺产物pnpm dsh web

3.3 建议建一个隔离的调试 profile

开发期间用一个独立 profile(如 --profile dev)安装开发中的插件,日常使用的 web profile 保持稳定,两者互不干扰。

四、编写你的第一个插件

4.1 最小函数插件

创建 hello.ts

import type { Context } from '@deepseek-ai/cordis'export const name = 'hello'export function apply(ctx: Context) {  ctx.logger.info('hello from my first plugin')}

再创建 cordis.yml

- name: './hello.ts'

在仓库内可以用 vendored 的 Cordis 启动器直接跑通最小挂载链路(不需要 API Key):

node --import tsx ../../vendor/cordis/bin.js

4.2 插件的三种形态

import { Service, type Context } from '@deepseek-ai/cordis'// 1. 函数插件(最常见,推荐默认用它)export function apply(ctx: Context) {}// 2. 对象插件:带 apply 方法的对象export const objectPlugin = {  name: 'object-plugin',  apply(ctx: Context) {},}// 3. 类插件:Service 子类(适合对外提供一个 ctx. 服务)export class MyService extends Service {  constructor(ctx: Context) {    super(ctx, 'myService')  }}

4.3 正式插件的四个导出

一个正式的函数插件通常导出四个东西:

import type { Context } from '@deepseek-ai/cordis'import z from '@deepseek-ai/schemastery'/** 插件显示名,仅用于诊断。 */export const name = 'my-plugin'/** 声明依赖的必需服务;loader 会等它们存在再执行 apply。 */export const inject = ['tools']/** 部署期配置的 schemastery 校验 schema(可省略)。 */export interface Config {  greeting: string}export const Config: z = z.object({  greeting: z.string(),})/** 插件主体:注册一切贡献,并只注册为可逆 effect。 */export function apply(ctx: Context, config: Config) {  ctx.logger.info(config.greeting)}

要点:

  1. inject只声明必需服务;可选服务用 ctx.get(name) 读取。
  2. 函数插件必须命名导出,不要混用默认导出,否则 Loader 会丢掉 inject 元数据。
  3. apply 签名:有 Config 导出时是 (ctx, config),没有时是 (ctx)
  4. 配置错误要 fail loud:加载失败会明确报错,不会静默跳过。

五、插件开发的硬性规则

这一节汇总官方文档与社区实践中反复强调的规则,违反任何一条都可能导致插件加载失败或行为异常。

5.1 注册可逆性

  1. 一切注册(工具、监听器、prompt 段)必须通过 ctx.effect() / ctx.on() 等机制完成,插件卸载时自动回滚ctx.effect() 中注册的东西必须有 teardown,否则重载或切换 profile 时会留下重复监听器或资源。
  2. 工具只注册一次:注册借用的是只读 definition,不要事后改 schema;想换工具就释放所属 effect 再注册。

5.2 模型可见性规则

  1. 模型能看到的任何东西都必须能从会话日志重建(模型可见 ⟺ 已记录)。要给模型加新的可见输入,就扩展 SessionEventMap 加一种新事件类型、从日志渲染,而不是绕过日志。
  2. durable 会话事件(turn/*step/*tool/* 等)追加进会话日志,重启后可重建;live 事件(agent/*tools/*)只做运行期协调。两者分工不能乱。

5.3 配置规则

  1. 「两个部署环境可能需要不同的值」都必须做成 Config 字段,不能写死在代码里。
  2. cordis.yml!!js 只允许出现在插件 config 和条目 disabled 下;按环境选插件要用 overlay,不要滥用 !!js
  3. patch 覆盖是整行替换(不是深合并),覆盖时必须保留行的 id

5.4 工具的 execute() 契约

  1. args 自动校验defineTool 会在 execute 前校验模型生成的参数。
  2. 只返回一个规范 JSON 值output.schema 定义返回值;抛异常 = isError;领域内的失败结果(如非零退出码)也要放进规范值返回。
  3. 遵守 exec.signal:取消信号触发时必须中止进行中的工作。
  4. UI 卡片与模型看到的内容分离:模型看到的由 output.render 决定,UI 卡片由 presentCall / presentResult 返回渲染意图(generic / terminal / diff)。
  5. 后台长任务通过 ctx.jobs.start() 注册,模型侧返回带 jobId 的规范句柄,且开关必须由部署配置控制。

5.5 waterfall 事件最易踩的坑

tools/pre-execute 等 waterfall 事件的监听器收到 (...args, next):调用 next() 才把结果传给下一个监听器;不调 next() 直接 return 就是短路,截断整条链。这是写钩子插件时最容易犯的错。

六、实战一:模型可见的工具插件

工具是插件最常见的用途。工具注册在 ctx.tools 上,schema 会自动进入 prompt 组装,模型就能「看到」它。

import { readFile } from 'node:fs/promises'import type { Context } from '@deepseek-ai/cordis'import { defineTool } from '@deepseek-ai/dsh-tools'export const name = 'demo-tool'export const inject = ['tools']export function apply(ctx: Context) {  ctx.tools.register(defineTool({    name: 'read_file',    description: 'Read a file from disk.',   // 模型看到的能力描述,要写清前置条件与副作用    parameters: {      path: { type: 'string', required: true, description: 'Absolute path' },      limit: { type: 'number' },             // 可选参数    },    output: {      schema: { type: 'string' },      render: (_args, value) => [{ type: 'text', text: value }],    },    async execute(args, exec) {      // args 已被 defineTool 按 schema 校验并推导类型      return readFile(args.path, { encoding: 'utf8', signal: exec.signal })    },  }))}

工具描述(description)的写作要求:说明何时调用、必要前置条件、失败语义与副作用。

七、实战二:拦截事件的钩子插件

不需要新工具、只想在某个环节插一脚时,用事件监听器。主循环是事件驱动的,钩子插件就是在这些事件上挂监听器。

权限门示例——在 tools/pre-execute 上拦截每一次工具调用:

import type { Context } from '@deepseek-ai/cordis'import type { PreToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools'declare function isAllowed(exec: ToolExecution): Promiseexport const name = 'permission-gate'export function apply(ctx: Context) {  ctx.on('tools/pre-execute', async (exec, next): Promise => {    if (!(await isAllowed(exec))) {      return { kind: 'deny', reason: 'Denied by policy.' }    }    return next()  })}

常用扩展点速查:

你要做的用哪个
允许 / 拒绝 / 询问工具调用tools/pre-execute,返回 {kind:'deny'} / {kind:'ask'}
工具调用必须被最终否决、不可撤销ctx.tools.guard()
包裹工具执行生命周期(超时/重试/指标)tools/execute
显式改写工具结果或呈现内容tools/post-execute
只观察最终结果(审计/捕获)tools/result
改写模型请求配置agent/request(waterfall)
改写/拒绝进入 step 的消息agent/pre-step(waterfall)

八、声明式配置(Config)

8.1 定义 schema

@deepseek-ai/schemastery(它也是 Cordis 的校验器),类型和运行时校验合一:

import z from '@deepseek-ai/schemastery'export interface Config {  allowParallelInProgress: boolean}export const Config: z = z.object({  allowParallelInProgress: z.boolean().required(),})

8.2 在 cordis.yml 里装配

- id: todo  name: '@deepseek-ai/dsh-tool-todo'  config:    allowParallelInProgress: true

九、把插件挂载到 dsh(三条路径)

路径适用场景做法
外置插件(推荐大多数场景)自研、开源、单独发布独立 npm 包,用 dsh plugin add 安装进 profile;package.json 声明 dsh.bundle 可自动进 bundle 层
临时 overlay调试、演示dsh --profile --patch ./overlay.yml "任务"
仓库内包给 dsh 本身贡献代码packages//(预览期核心仓库暂不接受外部 PR)

9.1 方式一:正式安装(需要 pnpm)

dsh plugin 把参数原样转发给 profile 目录里的 pnpm,动词在最后:

dsh plugin --profile web add /path/to/my-plugin      # 本地路径dsh plugin --profile web add github:you/my-plugin    # Git 仓库dsh plugin --profile web add my-plugin               # npm 包dsh plugin --profile web add ./my-plugin-0.1.0.tgz   # tarballdsh plugin --profile web remove my-plugin            # 卸载
  1. 相对路径锚定到命令行所在目录。
  2. 包声明了 dsh.bundle 的,会自动追加进该 profile 的 dsh.profile.bundles 层栈;没声明的包只会作为普通依赖安装并收到警告。

9.2 方式二:临时 overlay(本地开发,不需要 pnpm)

# my-overlay.yml- insert:    - id: my-plugin      name: '/绝对路径/my-plugin/index.js'
dsh --profile headless --patch ./my-overlay.yml "任务"

9.3 验证插件已挂载

dsh --profile web --dump-config        # 应看到 # == your-plugin 层和对应 id 行dsh plugin --profile web why  # 确认依赖关系

十、如何上传 / 发布插件

DSH 没有专门的插件注册中心——发布 DSH 插件 ≈ 发布一个 npm 包,只是包内容遵循插件约定。官方提供三种分发途径,核心区别在于是否分发预构建产物

方式用户安装命令安装到的是什么是否需要构建授权
npm 发布dsh plugin add your-package预构建的 lib/ 代码不需要
tarball 交付dsh plugin add ./hello-0.1.0.tgzpnpm pack 打出的包不需要
Git 安装dsh plugin add github:you/repo源码(不是构建产物)需要(pnpm ≥ 10)

10.1 发布到 npm(推荐给普通用户分发的首选)

# 1. 准备 npm 账户并登录npm login# 2. 先构建再发布(prepublishOnly 里做构建也行)pnpm buildnpm publish        # 或 pnpm publish# 3. 验证:在某个 profile 里安装,确认能挂载dsh plugin --profile dev add your-plugindsh --profile dev --dump-config

发布前检查:入口正确导出 name / inject / applyinject 里依赖的服务提供方要声明进 package.json;版本从 0.x 起步并遵循语义化版本;选择明确的开源协议(MIT / Apache-2.0 常见)。

10.2 交付 tarball

# 作者侧:打出 tgzpnpm pack# 用户侧:直接安装 tarball 文件,零授权dsh plugin add ./hello-plugin-0.1.0.tgz

10.3 Git 安装(最灵活,但有一道坎)

Git 安装拉取的是源码,没有任何环节替你运行 build 脚本——TypeScript 包到手没有 lib/ 输出,加载会失败。所以:

  1. 作者侧:必须提供自包含的 prepare 脚本(pnpm 在 git 安装后运行它完成构建)。它不能假设仅开发环境存在的上下文(比如旁边有一份 monorepo checkout)。
  2. 用户侧:pnpm ≥ 10 首次安装会拒绝运行 git 依赖的构建脚本,需要在该 profile 的 pnpm-workspace.yaml 里添加 allowBuilds 授权(按报错提示复制 key 即可)。这等于允许该包的代码在你机器上执行。
  3. 安全建议:git 安装时锁定 commit——dsh plugin add github:you/repo#<完整commit-sha>,避免后续推送改变实际运行的代码。

10.4 让别人发现你的插件

官方指定的发现渠道非常简单——给插件仓库加上 GitHub 话题 dsh-plugin。加了话题的仓库会被社区聚合目录(awesome 清单、各类插件索引站)自动收录。

其他渠道:

  1. GitHub Discussions 社区板块分享插件与反馈
  2. DeepSeek Harness Discord 社区
  3. 第三方插件聚合站(会被 dsh-plugin 话题自动索引)

十一、打包规范与发布前检查清单

一个标准 DSH 社区插件包(bundle)的结构:

your-plugin/  package.json        # 声明 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }                      # main/types/exports 指向真实生成的 lib/                      # files 只收录运行入口/声明/许可证/README/组合层                      # Cordis 与 Service Definition 包放 peer + dev deps,自有实现放 dependencies  cordis.patch.yml    # bundle 的 patch 层:按行 id 插入插件行,插件按包名解析  src/index.ts        # 函数插件:命名导出 name/inject/Config/apply  README.md           # 服务 API、事件、扩展点、安装命令、Known Limitations  LICENSE

package.json 关键片段:

{  "name": "dsh-your-plugin",  "version": "0.1.0",  "type": "module",  "main": "./lib/index.js",  "types": "./lib/index.d.ts",  "exports": { ".": "./lib/index.js" },  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }}

cordis.patch.yml

- insert:    - id: your-plugin      name: 'dsh-your-plugin'   # 用包名,不要用 checkout 相对路径

发布前检查清单(可直接复制进 PR 描述):

  1. 架构:能力缝三角色(Service Definition / Provider / Consumer)是否设计完整
  2. 导出与依赖:name/inject/Config/apply 命名导出完整;inject 的服务提供方已声明依赖
  3. 生命周期:所有注册可逆,HMR 下释放 fiber 后注册消失
  4. Config 与错误:部署期可变项全部做成配置字段;误配置 fail loud
  5. 工具与 UI:execute() 契约遵守;output.render 与 UI 卡片分离
  6. 测试与文档:行为测试、真实组合测试、README 含 Model Experience 段
  7. 构建打包安装:pnpm pack 产物在干净 profile 里能 dsh plugin add 成功并出现在 --dump-config

十二、测试与质量门

在仓库内开发时,新增/修改包后逐级往上跑(本地只跑受影响的,CI 才全量):

pnpm run constraints   # workspace 约束pnpm run typecheck     # strict 类型检查,无 any 逃逸pnpm run lint          # oxlintpnpm run buildpnpm run hygiene       # knip + publint + NodeNext 消费检查pnpm run test          # vitest 单元测试

测试方针要点:

  1. 行为测试描述行为;改行为要同步改测试并说明原因。
  2. 产品可见的插件要有一个真实组合测试:通过 Loader 启动 cordis.yml,而不是只用手搭的 ctx.plugin(...) 单测。
  3. 注册可逆性用 HMR 安全测试验证。

十三、常见问题排查(FAQ)

Q:dsh plugin报错找不到 pnpm?

dsh plugin 是把参数转发给 profile 目录里的 pnpm 执行的。先 npm install -g pnpm

Q:Git 安装的 TypeScript 插件加载失败?

Git 安装拉的是源码,没人替你跑 build。插件作者要提供自包含 prepare 脚本;用户侧 pnpm ≥ 10 还需要在 profile 的 pnpm-workspace.yaml 里加 allowBuilds 授权。不想折腾就改用 npm 包或 tarball。

Q:怎么确认插件真的挂载了?

dsh --profile --dump-config,应该能看到 # == your-plugin 层和你的插件行 id。

Q:插件异常导致 dsh 无法启动,如何临时禁用?

在 profile 的 cordis.patch.yml 里加一行即可,无需卸载:

- id: your-plugin  disabled: true

Q:patch 覆盖了配置但没生效?

patch 是整行替换而非深合并——覆盖时必须保留行的 id,且被替换字段要全部重述。

Q:bundle 插件安装后还要手动 insert 吗?

不要。声明了 dsh.bundle 的包会被自动注册进 bundle 层栈;再往 profile 的 cordis.patch.yml 手动 insert 同 id 会报 duplicate loader entry id 导致无法启动。

Q:核心 API 会变吗?

会。开发者预览阶段官方明确声明有兼容性破坏变更。建议:pin 住你实验用的仓库 commit 或包版本;以官方文档和 --dump-config 实际输出为准。

最新游戏

更多

Copyright©2010-2019. All rights reserved | 波波三国游戏官网|[email protected]

备案编号:湘ICP备2022015115号-4