架构

一个库,六个 Service,一层 dsh

@mc/dsh-agent-kit 是一组 DeepSeek Harness(dsh)插件 Service。业务包通过 cordis 的 inject 使用它们,自己只写协议、路由、模板和流程。运维工具放在另一个可选的包 @mc/dsh-agent-kit-admin 中。

dsh-agent-kit 架构图:业务包在上,dsh-agent-kit 的六个 Service 在中间,其中五个连接外部系统,通知渠道转发给钉钉或飞书中的一个,可选的 admin 包提供设置页与命令行,dsh 在最下层
业务包 → dsh-agent-kit → dsh;通知渠道把消息转发给当前渠道(钉钉或飞书),其余 Service 各自连接一个外部系统;设置页与 doctor / setup 在可选的 admin 包中。

两个包

包作用谁依赖它
@mc/dsh-agent-kit六个 Service,以及 /ws、/dingtalk、/feishu、/notify、/agent-tasks、/jev、/secrets、/testing 子路径。没有命令行,也没有设置页业务包(peer 依赖)
@mc/dsh-agent-kit-admin可选的运维工具:dsh-agent-kit doctor / setup 命令行、dsh Web「设置 → Agent Kit」页、agentKitAdmin 服务与业务包设置入口的登记(@mc/dsh-agent-kit-admin/entry)运维按需加入 Profile;业务包不依赖它
dsh plugin --profile my-agent add @mc/dsh-agent-kit @mc/dsh-agent-kit-admin

bundle 与 patch 层

两个包都在 package.json 中声明为 dsh 的 bundle。dsh plugin add 之后,dsh 会把它们的 patch.yml 加入 Profile 的层叠配置。基础包的 patch 注册六行,admin 包的 patch 注册一行:

行 id模块Context 属性默认
agent-kit-ws @mc/dsh-agent-kit/ws ctx.agentWs 禁用
agent-kit-dingtalk @mc/dsh-agent-kit/dingtalk ctx.dingtalk 禁用
agent-kit-feishu @mc/dsh-agent-kit/feishu ctx.feishu 禁用
agent-kit-notify @mc/dsh-agent-kit/notify ctx.notify 禁用
agent-kit-agent-tasks @mc/dsh-agent-kit/agent-tasks ctx.agentTasks 禁用
agent-kit-jev @mc/dsh-agent-kit/jev ctx.jev 禁用
agent-kit-admin@mc/dsh-agent-kit-adminctx.agentKitAdmin常驻(仅装了 admin 包时)

六个 Service 默认禁用:dsh 不会导入禁用行的模块,也不会校验它的配置,所以未启用的 Service 缺少配置不影响启动。在 Profile 的 cordis.patch.yml 中按 id 启用即可;按 id 修改 config 时整段替换,不与 bundle 中的值合并。

# Profile 的 cordis.patch.yml
- id: agent-kit-dingtalk
  disabled: false
  config:
    identity: bot
    robotCode: dingxxxx
    defaultTarget: { chatId: cidxxxx }

admin 包的常驻行 agent-kit-admin 很轻,不连接任何外部服务。它挂载 AgentKitAdmin(dsh Web 设置页调用的服务端接口),并让 dsh 发现 admin 包的前端模块,也就是「设置 → Agent Kit」页。它不影响基础包各行的启停;不装 admin 包时没有这一行,六个 Service 照常工作。

业务包如何接入

业务包把 @mc/dsh-agent-kit 声明为 peer 依赖,保证一个 Profile 中只加载一份实例;开发期可以用 link: 指向本包的本地 checkout。业务包不需要依赖 admin 包;只有要把自己的配置登记到设置页时,才从 @mc/dsh-agent-kit-admin/entry 导入 defineEntry(放进 devDependencies 即可),运行时调用 ctx.agentKitAdmin.registerEntry() 的插件则要求 Profile 中装有 admin 包。

{
  "name": "@your-org/dsh-agent-your-project",
  "type": "module",
  "peerDependencies": {
    "@deepseek-ai/cordis": "4.0.2",
    "@mc/dsh-agent-kit": "^0.1.0"
  },
  "devDependencies": {
    "@deepseek-ai/cordis": "4.0.2",
    "@mc/dsh-agent-kit": "^0.1.0"
  },
  "dsh": { "bundle": { "patch": "./patch.yml" } }
}

业务包的插件只 inject 用到的 Service,例如 export const inject = ['agentWs', 'notify']。Service 注册的连接、定时器、子进程都随业务包插件的生命周期释放。

只需要发通知时,推荐 inject notify 而不是 dingtalk 或 feishu。notify 不 inject 渠道 Service,而是在发送时查找它们:cordis 4 的 inject 都是必需的,如果 inject 了渠道,停用任一渠道都会连带卸载 notify 与依赖它的业务插件。现在启用、停用渠道行只影响发送结果;修改 notify 自己的 channel 时,notify 在自己的 internal/update 钩子里原地换上新值,拦截了 cordis 默认的重启。两种情况都不会重新加载 notify,也不会重新加载只 inject notify 的业务插件。

依赖与版本对齐

  • @deepseek-ai/cordis、@deepseek-ai/schemastery、@deepseek-ai/dsh-typert-protocol 是 peer 依赖,版本与目标 dsh 对齐。dsh 会把它自己的依赖提供给 Profile,peer 声明保证只有一份实例;放进 dependencies 会装出第二份,破坏 Context 类型扩展和远程方法标记。
  • @typesafe-ai/sdk 是可选 peer 依赖,只有启用 Jev 时才需要安装。
  • 基础包自己的运行时依赖是 ws、ajv、yaml 与 @deepseek-ai/dsh-atomic-write;admin 包另外依赖 @inquirer/prompts、diff、yaml,并把 @mc/dsh-agent-kit 声明为 peer 依赖。

配置从哪里来

配置只有一个来源:Profile 的 cordis.patch.yml。admin 包的 setup、Web 设置页和手动编辑读写的都是它。Profile 为 patchReload: live(web 模板的默认值)时,dsh 监视这个文件,修改后用新配置重新加载对应 Service(notify 例外:修改 channel 时原地生效,不重新加载);为 startup 时需要重启 dsh。密钥不写进这个文件,而是保存在环境变量、钥匙串或 dsh 凭据文件中。