Skip to content

1.3 配置树:组合包、profile 与 patch 层

本章概览:继 0.3 之后深入配置树的"数据结构"——cordis.yml 条目的结构、patch 的定位与替换算法、profile manifest,以及按会话组合的 agent preset。理解本章才能准确回答"某个插件是怎么被挂进运行时的"。

cordis.yml:配置项列表

一份 cordis.yml(或组合包的 cordis.patch.yml)本质上是一组 Cordis 配置项的列表(entry list)。每个条目描述一个要挂载的插件:

yaml
- id: bash-tool          # 可选:条目 id,patch 按它定位
  name: '@deepseek-ai/dsh-tool-bash'   # 模块指定符:包名或相对路径
  inject: [...]          # 可选:声明所需服务
  disabled: false        # 可选:可含 !!js 表达式
  config: {...}          # 插件的配置对象

Loader 的 entryListSchemaapplyEntryPatches 是这一数据结构的正式定义——配置转储(--dump-config)与启动挂载共用同一套解析与 patch 算法,因此组合结果不会漂移。

出处:packages/boot/app-boot/README.mddeepseek-harness-src/packages/boot/app-boot/README.md

patch 算法:按 id 替换整个 config

patch 层(如 profile 的 cordis.patch.yml、home 级、--patch overlay)是一组 PatchOptions 条目:

  • 按 id 定位- id: <target> 指向要覆盖的条目;
  • 替换整个 config:patch 会整体替换目标条目的 config——没有深层合并dsh-base README 明确提醒:profile 覆盖一个条目时,必须重述该条目保留的所有字段;
  • 插入新条目:也可用 insert 列表新增条目。

IMPORTANT

"一条 patch 替换一个条目的整个 config"这一语义,解释了为什么模式专属的值放在模式组合包里(如 dsh-web-app),而不是 base 里:任何模式无关的字段都可能被上层 patch 覆盖掉。

出处:packages/bundle/base/README.mddeepseek-harness-src/packages/bundle/base/README.md

profile manifest:dsh.profile

profile 目录的 package.json 中,dsh 字段声明 manifest:

jsonc
{
  "dependencies": { /* 树外插件依赖 */ },
  "dsh": {
    "profile": {
      "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app"]
      // bundles 顺序 = 叠加顺序
    }
  }
}

组合包侧则声明:

jsonc
{
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}

loadProfile 按两个锚点解析 bundle:先 dsh 安装目录,再 profile 目录;列出的包若没有 bundle 声明则响亮失败。composeEntries 通过 include 自身的 applyEntryPatches 把各层 patch 应用到空条目列表上。

出处:packages/boot/app-boot/README.md#profilesdeepseek-harness-src/packages/boot/app-boot/README.md

应用参数:dsh-cmdline

配置树中还藏着"命令行参数解析"这一能力:dsh-cmdline 把启动器之后的所有 token 解析为共享的不可变快照注入 ctx.cmdlineArgs。任意应用插件都可以消费这份快照。模式组合包通过"startup provider"(如 web-startupheadless-startup)解析自己的参数并提供 webStartup / headlessStartup 服务;flag 配置的行只读 lazy config,因此 dsh --profile web --help 不会启动任何服务器

出处:packages/boot/cmdline/README.mdpackages/bundle/web-app/README.md

agent preset:按会话组合

组合包把插件挂进进程级配置树;而 agent preset 把插件挂进每个 agent 会话的作用域。一个 preset 是存放一份 agent.cordis.yml 的目录;roster 在进程内以"standing scope"挂载一次,每个选择该 preset 的会话通过 dsh-scope 的父链加入(解析顺序 agent → preset → global,最近者遮蔽最远者)。

要点:

  • 发现即读盘list() / resolve() 每次重读根目录,运行期新建的 preset 立即可见;
  • 健康检查:组合缺失或不可加载的目录以 broken 原因列出,而非静默跳过;
  • 切换recompose() 仅在 agent 尚未产生任何内容时有效,切换会卸载旧子树、挂载新子树(两个组合不能共存,因为会在同一层注册同名工具);
  • 子代理继承:subagent 通过 composeFrom() 绑定父代理的 standing composition——因为所有模型可见行都在 agent 平面上,子代理若不绑定,将带着空工具注册表到达模型。

出处:packages/preset/agent-presets/README.mddeepseek-harness-src/packages/preset/agent-presets/README.md

配置目录:可配置字段的权威来源

每个可加载包 config: 块的逐字声明(含 JSDoc)由脚本生成在 docs/config-catalog.md,并校验运行时 schemastery schema 与声明的类型完全对应——schema 校验接受的每个键都能在声明类型上找到,因此声明无法隐藏 loader 接受的字段。查找"某个插件能配什么"以该文件为准。

小结

配置树的三层结构——进程级(profile + 组合包 + patch)、应用参数级(cmdline)、会话级(agent preset)——是 DSH "一切皆插件" 的组合载体。下一章 1.4 能力缝 讲解插件如何组织成"可替换能力"。

参考资料

本文档为学习用途的原创讲解,基于 MIT 许可的开源项目 deepseek-ai/deepseek-harness 编写;所有引用均注明出处。