ARTICLE · 1340013
DSH 插件开发:从 Cordis 原理到 Bundle 分发的实战指南
从 Cordis 插件机制,到工具、服务、事件、配置、打包与发布。
DeepSeek Harness 开发者学习手册 | 官方仓库与文档调研 | 资料核对日期:2026 年 11 月 9 日
本手册解决三个问题
DSH 插件究竟是什么,运行时如何被加载、依赖、调用和卸载? 如何从零写一个能被模型调用的工具插件,并完成本地调试? 如何把开发完成的插件打包成 Bundle,安装到 Profile 并交付给其他人?
目录
01 先理解 DSH:它是什么,插件解决什么问题 02 插件的整体架构:Cordis、Context、Fiber、Profile 与 Bundle 03 插件加载和运行原理:依赖注入、注册、卸载与热替换 04 DSH 插件的几种形态:工具、服务、事件、模型适配器、Web UI 05 开发环境准备:版本、仓库与启动方式 06 第一个可运行插件:添加 greet 工具 07 配置、事件和资源生命周期:从 Demo 到可维护插件 08 打包、安装与分发:Bundle / Profile 工作流 09 Web 客户端插件:Host 与 Browser 双端结构 10 测试、调试与常见问题 11 插件安全:必须认真对待的边界 12 DSH Plugin、MCP、Skill 的区别与选型 13 建议学习路线与项目练习 附录 A 常用命令速查 附录 B 官方资料与源码入口
DSH 是 DeepSeek Harness 的命令行名称,由 DeepSeek AI 开源。它不是一个新的大模型,也不只是一个聊天前端,而是运行在模型外部的一层 Agent Harness:负责组织模型请求、工具调用、会话、权限与审批、文件和命令执行、模型接入以及用户界面。官方 README 将其定义为基于 Cordis 的“一切皆插件”架构。[1]
可以把大模型理解为“负责理解与生成的决策引擎”,把 Harness 理解为“让模型能够在真实环境里完成工作的运行时”。DSH 中的很多能力并不写死在一个大核心里,而是由插件组合出来。模型适配器、工具注册表、会话日志和 Agent Loop 等都通过插件机制参与运行。[2]
在常见的 Agent 框架中,开发者经常把插件理解为“给模型加一个 function calling 工具”。DSH 的插件范围更广:
因此,“写一个 DSH 插件”之前,第一步不是立刻写代码,而是先确认你要扩展的是工具能力、运行时服务、事件行为,还是浏览器界面。普通的内部 API 封装通常从 Tool 插件起步;需要多个插件共用的能力,才抽象成 Service。
理解 DSH 插件,核心是理解 Cordis。Cordis 是 DSH 底层的插件框架。插件向共享 Context 贡献服务、类型化事件以及可撤销的注册动作;框架负责依赖关系、加载生命周期和资源清理。[2][3]

DSH 不是简单地“扫描一个 plugins 文件夹,然后把里面的 JS 全部加载”。一个启动中的实例由 Profile 组织,Profile 按顺序堆叠 Bundle,并在上面再应用用户自己的 Patch。官方架构文档给出的典型层次是:[2]
配置分层示意
Patch 通过条目的 id 定位插件行,可以插入新行,也可以替换某行的配置。需要注意:后层优先级更高;Patch 对目标条目的 config 采用整块替换语义,并非所有字段都做深度合并。修改已有插件配置时,不能只写一个字段就假设旧 config 的其他字段一定保留。[2]
Bundle 是开发者编写、版本化和分发的东西;Profile 是用户启动的一份运行组合。两者都可能在 package.json 的 dsh 字段中声明,但声明类型不同:Bundle 用 dsh.bundle,Profile 用 dsh.profile。官方文档明确强调,一个对象不是同时承担这两种角色。[4]
最常见的插件形态是导出 name、可选 inject,以及 apply(ctx, config)。当插件依赖的服务准备好后,Cordis 会执行 apply。一个只有日志输出的最小插件可以写成:
最小插件
name 是插件在组合树中的稳定名称。apply 是挂载入口,config 是由配置层传入并经过 schema 处理的运行参数。开发工具插件时,通常会显式声明 inject: ['tools']。
假设插件需要注册工具,它依赖工具注册表服务。写 inject: ['tools'] 后,Cordis 会等 tools 服务可用时才执行 apply。若服务尚未准备好,插件会处于等待状态,而不是在 apply 内部盲目访问一个可能不存在的 ctx.tools。[3]
依赖注入
插件状态机
如果插件依赖的服务后来消失,例如某个服务提供者被替换,依赖它的插件也可能被卸载;服务恢复后,再按依赖关系重新加载。这让能力替换可以在插件树层面完成,而不必强行维持某个固定加载顺序。[3]
通过 ctx.on() 注册的事件监听器、通过 ctx.tools.register() 注册的工具,以及通过 ctx.effect() 注册的自定义资源,都会归属于当前插件 Fiber。Fiber 卸载时,框架会撤销这些注册。对于数据库连接、WebSocket、文件 watcher 等资源,应在 ctx.effect() 中返回释放函数。[3]
自定义资源的清理
在启用 DSH 的 HMR 支持后,文件变化可以触发旧插件卸载、清理注册、重新加载模块并再次执行 apply。因为注册动作属于 Fiber 的 effect,正确使用 ctx 注册资源时,热替换不会累积一堆旧 listener 或重复工具。[3] 但 HMR 不会替代测试:文件可重载,不代表资源清理或外部 API 调用就一定正确。
这是最适合入门的扩展方式。Tool 通过 ctx.tools.register() 注册到工具运行时。工具描述和参数 schema 会参与模型请求的工具定义;模型返回工具调用后,运行时校验参数,再调用 execute(),最后按 output 的约定把规范结果渲染成模型可读的 content。[5][6]

一个 Tool 需要重点设计四件事:
name:稳定、简洁、能表达动作的工具名。 description:模型看到的工具说明。写清楚用途、输入、限制和何时调用。 parameters:声明参数类型、必填项、描述和必要约束。 execute + output:执行真实逻辑,并返回符合 output.schema 的规范结果;render 决定结果如何转换成模型可读内容。
当多个插件都需要调用同一个内部能力时,不应该把 HTTP 请求、鉴权、重试和缓存逻辑复制到每个 Tool 中。可以把它们封装为一个 Service,挂载在 ctx 的稳定 key 上,再由消费者通过 inject 声明依赖。DSH 文档中的典型服务包括 ctx.tools、ctx.llm 和 ctx.agents。[7]
更进一步的工程化拆分是把能力分成三层:Service Definition(接口契约)、Service Provider(真实实现)和 Consumer(调用方)。这样可以在不修改消费插件的情况下替换本地实现、云端实现或测试 Mock。
事件是 Cordis 插件之间松耦合通信的主要方式。基础形式是 ctx.on(event, handler) 监听和 ctx.emit(event, payload) 广播。Cordis 还提供 bail、serial 和 waterfall 等不同调用语义。[8]
waterfall 的监听器必须调用 next() 才会继续下游;如果不调用,流水线会被短路。这是有意设计,不是普通的事件广播。[8]
工具结果观察示例
常见的事件命名采用 namespace/action 风格,例如 agent/pre-step、agent/request-error、tools/result 和 session/event。要观察持久化的 turn/*、step/*、tool/call 等会话事件时,应监听 session/event 并检查 event.type;它们不等同于同名的 Cordis 事件。[8]
如果目标不是增加工具,而是让 DSH 通过新的网关或私有部署模型完成推理,就应该研究 LLM Adapter 接口,而不是把模型调用伪装成一个 Tool。适配器负责把 DSH 的模型调用契约转换为供应商 API 的请求与响应格式。建议在熟悉基础工具插件后再进入这一层。
Web UI 插件同时涉及 Node Host 和 Browser Client 两个运行面。Host 侧负责识别启用的插件包、组织客户端启动依赖图,并提供客户端 bundle;浏览器侧按需加载 bundle。普通 Node Tool 插件不需要声明 dsh.client;只有确实要扩展浏览器 UI 时才进入这套机制。[9]
官方 README 将 DSH 标为 Developer Preview,明确提醒项目迭代较快,未来可能出现破坏兼容性的变更。[1] 因此,学习时应把 DSH 版本、插件依赖版本和源码 commit 一起记录,不要把某个示例当成跨版本永久稳定的 API。
版本依据:当前仓库根 package.json 的 engines.node 为 >=24.9.0,packageManager 为 pnpm@11.28.5;README 的源码运行方式是 clone、pnpm install、pnpm run build,再运行 pnpm dsh web。[1][10]
快速启动
首次启动后,需要先按 DSH 界面完成模型 Provider、API Key 或账户凭据配置,才能验证模型是否会主动调用工具。注意:这个命令用于体验产品;如果希望按官方本地插件教程挂载 TypeScript 源文件,建议采用源码 checkout 开发路径。
源码准备
pnpm run build 会准备仓库运行所需的构建产物;pnpm dsh web 会使用这些已构建产物启动,不会自动重新构建。[1]
接下来写一个最小但完整的 Tool 插件:模型接收到“用 greet 工具问候 Ada”后,会调用 greet(name),插件返回一条问候语。这个例子沿用官方工具教程,并额外加入配置 schema,让你同时看到工具注册与插件配置如何配合。[5][11]
建议先不要自己设计复杂的 package 或构建系统。先通过一个源码目录 + cordis.yml patch,将插件挂载进 Web profile。
插件主体:my-plugin.ts
这段代码可以拆成四部分理解:
在 scratch-plugin/cordis.yml 中写入一个 patch。插件源码路径按官方本地挂载教程使用绝对路径。将下面的路径替换为你自己仓库的真实路径:
本地 patch
然后从 deepseek-harness 仓库根目录启动:
启动开发实例
打开 http://127.0.0.1:3080,确认模型 Provider 已配置,然后输入:
预期模型会选择 greet 工具,传入 name = Ada,execute() 返回“你好, Ada!”,工具结果再交还给 Agent Loop,供模型继续生成最终答复。官方教程使用的问候语是 “Hello, Ada!”;这里将 prefix 改成中文,只是为了演示插件配置如何影响行为。[5]
插件被 Loader 加载,Cordis 检查 inject,等待 ctx.tools 就绪。 apply() 运行,调用 ctx.tools.register(),把工具定义注册进运行时。 DSH 组装模型请求时,工具定义中的 name、description 和 parameters 成为模型可见工具契约。 模型决定调用 greet,返回结构化参数。运行时先按参数 schema 做校验。 通过校验后执行 execute(args),返回值必须符合 output.schema。 output.render 将规范结果转换为模型可读 content,Agent Loop 收到结果后继续处理。
这也是为什么工具 description 和 schema 必须认真写:模型并不会“理解插件源代码”,它主要依据对外暴露的工具契约决定是否调用、如何组织参数。
如果一个参数在不同部署、环境或客户间可能不同,就应该考虑将其定义为 Config,而不是写死在代码里。官方建议通过 @deepseek-ai/schemastery 定义 schema,并导出与 Config 同名的 schema 供 Cordis 校验。普通 JavaScript 对象不满足这里要求的 Standard Schema 接口。[11]
配置定义
配置里可放基础 URL、超时、重试次数、模式开关等非秘密参数。API Key、访问令牌等敏感数据不要作为普通配置写进仓库或示例文件,应使用 DSH 的凭据管理机制、受控环境变量或外部 Secret 管理方案。
适合先做的事件插件包括工具调用日志、错误跟踪、调用耗时统计、审计记录和策略检查。比如监听 tools/result 可以记录一次工具执行的最终结果;如需阻止某次工具执行,应研究对应的 pre-execute / guard 等正式策略扩展点,不要把一个普通观察事件误当成可靠的权限边界。[8][12]
插件里创建的资源包括:HTTP 客户端、数据库连接池、定时器、文件 watcher、WebSocket、后台队列和临时文件。应逐一回答:谁创建?谁拥有?何时停止?异常退出时是否清理?重复加载时是否会重复创建?
给网络请求设置超时,并将执行取消信号传递到底层 fetch / SDK。 参数校验除了类型,还要检查长度、数值范围、白名单和跨字段关系。 限制返回内容大小,不要把大段原始文档无上限塞回上下文。 对外部 API 错误返回可诊断但不泄露密钥、堆栈和内部地址的信息。 写操作默认采用显式确认或审批;先实现只读能力,再逐步增加写权限。 幂等性、重试和限流要按下游 API 的真实契约设计,不能对所有错误盲目重试。
本地 --patch 适合开发试验,但不适合给其他人长期安装。要让插件成为可安装的扩展,需要把代码和 Patch 包成一个 npm 包,并在 package.json 的 dsh.bundle.patch 中声明配置层。官方教程将该产物称为 Bundle。[4]
发布包结构
最小 Bundle manifest
真实插件通常还要补齐 repository、license、engines.dsh、peerDependencies 和必要的 devDependencies。若插件 import @deepseek-ai/cordis 或 DSH 服务包,需按官方打包规则处理与宿主共享实例的依赖:与宿主共享的 DSH 包应采用 peerDependencies,并用相应 devDependencies 支持本地类型检查和开发;版本范围要与实际测试过的 DSH 版本匹配,不建议无约束地写 *。[4]
Bundle patch
这里 name 使用已安装的 npm package 名称,而不是本地源文件的相对路径。Node 模块解析依靠包名找到插件入口。
本地 Bundle 安装
第一次向 demo Profile 安装 Bundle 时,CLI 会初始化 Profile,并以 @deepseek-ai/dsh-base 作为基础组合层;当安装包声明了 dsh.bundle 时,CLI 会把它登记到 dsh.profile.bundles。[4]
插件更新:通过 profile 的包管理依赖更新对应 Bundle,再核对 dump-config 与运行行为。 插件卸载:使用 dsh plugin --profile demo remove dsh-hello-plugin。 版本约束:推荐声明 engines.dsh 的 SemVer 范围;安装和启动时会检查兼容性。 修改 Patch 后必须重新检查组合结果。Patch 覆盖的是配置条目,不能只凭源码文件存在就判断插件已启用。
对于公开分发,可以通过 npm 包或 Git 仓库固定版本/commit 的方式安装。官方项目还提供实验性的 Custom plugin indexes:索引是一个 JSON 文件,列出可安装 Bundle 的名称、说明、作者、版本及来源;它不是插件运行机制本身,而是发现与分发目录。[13]
如果要开发侧边栏、设置面板、消息卡片或浏览器交互入口,只有 Node 侧插件是不够的。DSH Web 客户端插件采用 Host + Client 两部分:
官方客户端模块机制会把声明了 dsh.client 的插件包转换成可加载的浏览器 bundle。Host 侧在 Web composition 中提供资源;浏览器侧按需加载,模块副作用在模块真正被物化时才运行。官方说明客户端 bundle 的惰性加载是为了避免启动时执行所有 UI 插件代码。[9]
只给模型增加一个 search_xxx 工具:不需要 dsh.client。 监听工具事件并写入日志:不需要 dsh.client。 要在浏览器里新增面板、菜单、卡片或交互组件:需要研究 dsh.client。 要给 Web 页面同时提供 Host API 和浏览器组件:需要设计两端契约、依赖顺序与数据边界。
常用调试命令
dump-config 的价值在于验证最终结果,而不是只看自己写的 patch。遇到“插件没生效”,先确认最终组合树中是否出现插件行、是否被 disabled、name 指向是否正确、profile 是否真的安装了 bundle。具体命令参数应以当前安装版本的 CLI help 为准。[14]
不要把第三方插件理解成主题皮肤或静态配置。一个插件包会执行代码;安装和启用前,应把它当成一个需要安全评审的软件组件。至少检查:
发布者、仓库地址、Tag/Commit、版本号是否可信且可追溯。 插件入口代码是否读取用户目录、环境变量、SSH 配置、浏览器数据或凭据。 是否存在不必要的网络外发、遥测、下载执行、动态代码执行或子进程启动。 package.json 中的依赖和安装脚本是否必要;锁定依赖版本并审查更新差异。 Patch 是否插入/替换高权限插件配置,是否改变审批、沙箱、凭据或文件访问相关设置。 卸载后是否仍残留定时任务、后台进程、文件或持久化配置。
对于医疗场景,尤其不要把真实患者身份信息、病历原文、院内访问令牌或生产数据库权限直接暴露给一个未经安全评审的插件。推荐先在合成数据和隔离测试环境验证,再走医院或企业内部的安全评审、最小权限与审计流程。
实际工程可能混合使用:例如医院知识检索服务已经有独立 HTTP/MCP 接口,DSH Tool 插件负责将它封装成模型易于调用的工具;检索策略和循证流程放在 Skill;院内统一鉴权、日志与连接复用封装在 Service。这样可把协议层、运行时层和提示知识层分清。

如果要把学习成果逐步用到真实业务,可以做一个只读的内部知识库检索插件。它比 greet 更接近企业场景,但仍然能保持边界清晰:
定义模型工具 search_internal_kb(query, top_k, filters),description 里明确用途与限制。 在 execute 中调用已有的知识检索 HTTP API;不要在插件里重复实现向量数据库、重排和权限系统。 对 top_k 设置上下限,对 query 限制长度,对 filters 做白名单校验。 返回结构化、有限长度的摘要、来源标题、来源 ID 和可追溯链接;不要返回整个原始文档。 将 API Base URL、timeout、top_k 默认值放进 Config;Key 由受控凭据机制提供。 加入 tools/result 观测插件,记录耗时、命中数与错误类型;日志不得包含令牌和不必要的敏感原文。
这个练习能覆盖 DSH 插件开发中的核心能力:工具契约、配置注入、HTTP 调用、错误处理、结果规范化、审计和安全边界。下一步再扩展成复用 Service,或将检索服务放在 MCP 进程边界外运行。
以下优先列出 DeepSeek Harness 官方 GitHub 仓库中的 README、架构说明和开发教程。由于 DSH 处于 Developer Preview,建议阅读时以当前主分支及正在使用的 npm 版本为准。
[1] 官方仓库 README(中文):项目定位、运行方式、开发者预览状态https://github.com/deepseek-ai/deepseek-harness/blob/master/README.zh.md
[2] 官方架构文档(中文):Cordis、Profile、Bundle、Patch 分层https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/architecture.zh.md
[3] 插件与生命周期(中文):Fiber 状态机、inject、自动清理、HMRhttps://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/framework/index.zh.md
[4] 打包与安装插件(中文):dsh.bundle、Profile、dsh plugin addhttps://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/publish.zh.md
[5] 开发一个工具(中文):defineTool 与 greet 工具示例https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/tool.zh.md
[6] Tool authoring reference:参数校验、execute、output、执行契约https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/cookbook/adding-a-tool.md
[7] 服务与依赖(中文):ctx 服务、inject 与 Service 提供者https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/framework/service.zh.md
[8] 事件系统(中文):emit / bail / serial / waterfallhttps://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/framework/events.zh.md
[9] Client 模块(中文):Web Host/Browser 插件加载结构https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/client-modules.zh.md
[10] 官方主仓库 package.json:Node / pnpm 版本要求https://github.com/deepseek-ai/deepseek-harness/blob/master/package.json
[11] 插件配置(中文):Schemastery Config schemahttps://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/develop/basic/config.zh.md
[12] 扩展插件实操手册(中文):工具钩子与扩展模式https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/cookbook/extension-cookbook.zh.md
[13] Plugin indexes:实验性插件索引的发布和格式https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/guide/plugin-market.md
[14] CLI 行为参考(中文):Profile 启动、插件管理和 dump-confighttps://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/reference/README.zh.md
[15] 官方 SAFETY.md:实验性状态、沙箱限制和安全建议https://github.com/deepseek-ai/deepseek-harness/blob/master/SAFETY.md















































































