DSH plugin 能力拆三个包:Service Definition、Provider 与 Consumer

概念与架构发布于 2026-10-17作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH pluginService DefinitionProviderConsumerseam
DSH plugin 的通用能力为什么拆成服务定义、提供方与消费方三个包:依赖方向、可替换性与独立演进,附以 Bash 为例的三步代码与「什么时候不该拆」的边界。

DSH plugin 里一项足够通用的能力,官方推荐拆成三个角色:Service Definition 定义契约、Service Provider 实现能力、Consumer 把能力暴露出去。 这不是为了架构好看,而是为了让「换实现」只改一行配置——调用方只认 Definition,所以 Provider 换成另一个包时,Definition 和 Consumer 都不用动。判断该不该拆的标准也很清楚:三个角色是否需要独立演进。

不需要,就放一个包里;需要,才拆。以 Bash 能力为例,dsh-shell 是定义、dsh-bash-local 是提供方、dsh-tool-bash 是消费方——三者构成一个 seam,少了任何一个都不完整。

DSH plugin 能力的三种角色:Definition、Provider 与 Consumer

一句话结论:Definition 管契约、Provider 管实现、Consumer 管暴露——三者依赖单向,Provider 与 Consumer 互不依赖。

角色职责依赖谁例(Bash 能力)
Service Definition定义服务与请求/结果类型无dsh-shell
Service Provider真正执行能力Definitiondsh-bash-local
Consumer暴露为工具 / 命令Definitiondsh-tool-bash
  1. 调用方只认契约:Consumer 通过 inject: ['shell'] 拿到服务,不关心背后是本地执行还是远程执行。
  2. 替换只发生在组合层:换 Provider 就是改 cordis.yml 里的一行,Definition 与 Consumer 原样不动。
  3. 三角色不等于一个包:角色是逻辑分工,物理上可以一个包承担多个角色,也可以拆成三个包。

为什么要把 DSH plugin 能力拆成三个包

一句话结论:三个好处——提供方可替换、三角色可独立演进、依赖解耦。

  1. 提供方可替换:同一个 Definition 可以有多个 Provider,通过组合配置选择。换实现时调用约定不变。
  2. 独立演进:Definition 一旦被调用方依赖就很少改;Provider 可以独立优化性能与安全;Consumer 可以调整能力向模型呈现的方式。
  3. 依赖解耦:Provider 与 Consumer 互不依赖,任何一方改动都不会牵动另一方。
  4. 诊断更清晰:出问题时先定位是哪一角色坏了——契约不符、实现异常、还是暴露方式不对。

实战:三步写出一项 DSH plugin 能力

一句话结论:先写 Definition(含 Request/Result 类型),再写 Provider,最后写 Consumer,三步的顺序不能反。

  1. 写 Service Definition:
ts
// packages/my-cap/my-cap/src/index.ts
import { Service, type Context } from '@deepseek-ai/cordis'

declare module '@deepseek-ai/cordis' {
  interface Context { myCap: MyCapService }
}

export abstract class MyCapService extends Service {
  constructor(ctx: Context) { super(ctx, 'myCap') }
  abstract execute(request: MyCapRequest): Promise<MyCapResult>
}

export interface MyCapRequest { input: string }
export interface MyCapResult { output: string }
  1. 写 Service Provider:
ts
import type { Context } from '@deepseek-ai/cordis'
import { MyCapService, type MyCapRequest, type MyCapResult } from '@deepseek-ai/dsh-my-cap'

class MyCapLocal extends MyCapService {
  async execute(request: MyCapRequest): Promise<MyCapResult> {
    return { output: request.input.toUpperCase() }
  }
}

export const name = 'my-cap-local'
export function apply(ctx: Context) { ctx.plugin(MyCapLocal) }
  1. 写 Consumer(以工具形式暴露):
ts
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'tool-my-cap'
export const inject = ['tools', 'myCap']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'my_cap',
    description: 'Execute my capability.',
    parameters: { input: { type: 'string', required: true } },
    output: { schema: { type: 'string' }, render: (_a, v) => [{ type: 'text', text: v }] },
    async execute(args) { return (await ctx.myCap.execute({ input: args.input })).output },
  }))
}
  1. 在组合里挂两条:
yaml
- name: '@deepseek-ai/dsh-my-cap-local'
- name: '@deepseek-ai/dsh-tool-my-cap'
  1. 验证替换性:把 Provider 换成另一个实现,只改第一行;Consumer 与工具名保持不变,模型侧调用方式完全不变。

DSH plugin 三角色拆分的边界:什么时候不要拆

一句话结论:不要预防性拆分——只有角色需要独立演进时,才值得付出多包的维护成本。

  1. 一个简单工具插件不需要拆:它没有可替换的实现,拆开只会增加发布与依赖管理成本。
  2. 只有一个 Provider 且短期不会换:Definition 与 Provider 放一个包更省事。
  3. 组合数超过收益:包越多,安装、版本对齐、排查的成本越高;先单包跑通,再按真实需求拆。
  4. 拆分的信号:出现第二个 Provider 的需求、或 Consumer 需要被多个场景复用时,才动手拆。
  5. 另一种信号:团队边界——不同人维护不同角色时,拆包能让发布节奏互不阻塞。

DSH plugin 组合与替换:seam 在配置层收口

一句话结论:三角色的组合发生在组合配置里,替换 Provider 是配置改动而不是代码改动。

  1. 按包名挂载:cordis.yml 里按顺序列 Provider 与 Consumer。
  2. 换实现只换一行:注释掉旧 Provider、换上新的同契约包即可。
  3. 验证:替换后调用一次该工具,确认结果仍符合 Definition 的 Result 形状。
  4. 别忘了能力 seam 参考:内置能力已经有现成 seam 划分,扩展前先看它,避免重复造一套。

DSH plugin 排查注意事项

一句话结论:三角色架构的问题先问「哪一层坏了」,再问「代码哪里错了」。

  1. 调用报「服务不存在」 → 检查 Provider 是否挂载、inject 是否写对。
  2. 类型不符 → 检查 Request/Result 类型是否只有 Definition 一处定义。
  3. 换了 Provider 行为变了 → 说明实现违反了契约,回到 Definition 对齐。
  4. Consumer 里直接 new 了实现 → 破坏了依赖方向,替换 Provider 时会失效。
  5. 过早拆分 → 先单包跑通,再按真实变化拆。
  6. 修复状态如实:三角色定义、依赖方向与三步示例均来自官方「能力的三种角色设计」页面;defineTool 的完整字段与 Service 基类签名以官方 reference 与你所用版本为准。

来源:能力的三种角色设计、服务与依赖、能力 seam 参考。

常见问题

DSH plugin 的 Service Definition、Provider 和 Consumer 分别负责什么?

DSH plugin 的三角色分工是:Service Definition 定义服务与请求/结果类型,Provider 实现这项能力,Consumer 把它暴露给模型或其它插件。调用方只依赖 Definition,所以更换 Provider 时 Definition 与 Consumer 都不用改。

为什么 DSH plugin 要把一项能力拆成三个包,放一个包里不行吗?

DSH plugin 拆成三个包的前提是角色需要独立演进或替换;只为了「看起来架构清晰」而拆是过度设计。判断标准很简单:如果 Provider 与 Consumer 不会各自变化,放一个包里更好维护。

DSH plugin 更换能力提供方时,需要改哪些地方,具体要注意什么?

DSH plugin 更换 Provider 通常只改组合配置里的一行,Definition 与 Consumer 保持不变。这也正是把三者拆开的核心收益:替换实现不影响调用约定。

DSH plugin 的服务定义应该由谁拥有请求和结果类型?

DSH plugin 的 Service Definition 拥有 Request 与 Result 类型,Provider 和 Consumer 都只依赖这个包。这样类型只有一个来源,避免两端各自定义导致字段漂移。

DSH plugin 的三角色之间会不会互相依赖,导致循环引用?

DSH plugin 的三角色依赖是单向的:Provider 依赖 Definition,Consumer 同样依赖 Definition,而 Provider 与 Consumer 互不依赖。这种方向保证替换任一角色都不会牵动另一个。

相关术语

Service Definition(服务定义)
Service Definition 是 DSH plugin 能力的契约包,定义 Cordis 服务接口以及请求与结果类型。Provider 与 Consumer 都只依赖它,因此它是三者中变化最慢的一层。— https://deepseek-harness.github.io/deepseek-harness/develop/practice/
Service Provider(服务提供方)
Service Provider 是 DSH plugin 能力的实现包,负责真正执行请求。同一个 Service Definition 可以有多个 Provider,通过组合配置选择其中一个。— https://deepseek-harness.github.io/deepseek-harness/develop/practice/
Consumer(消费方)
Consumer 是把 DSH plugin 能力暴露给模型或其它调用方的包,通常以工具或命令的形式出现。它只依赖 Service Definition,不直接依赖 Provider。— https://deepseek-harness.github.io/deepseek-harness/develop/practice/
capability seam(能力接缝)
capability seam 是一项能力的三个角色共同构成的整体边界,任何单一角色都不等于 seam。它决定了这项能力在哪些位置可以替换实现。— https://deepseek-harness.github.io/deepseek-harness/reference/capability-seams

来源