patch 层与层序

别名:补丁层、层栈、cordis.patch.yml、layer、patch 层

DSH 的一棵插件树不是写出来的,是叠出来的:bundle 层 → profile 层 → home 层 → --patch 覆盖,每一层都是一串补丁;同 id 的行被后面应用的补丁整段覆盖。

一句话

DSH 启动时挂上来的那棵树,没有任何一行是直接写的。profile 目录里的根配置文件永远是一个空数组,整棵树由若干层补丁按顺序叠出来。

层序(后者覆盖前者)

@deepseek-ai/dsh 的 profile 启动代码把补丁按固定顺序拼成一串,再一次性应用到空根上。顺序是:

  1. bundle 层:dsh.profile.bundles 列出的每个包各贡献一段补丁,按列表顺序(composed.bundlePatches);
  2. profile 层:该 profile 目录下的 cordis.patch.yml;
  3. home 层:$DSH_HOME/cordis.patch.yml——机器级偏好,对所有 profile 生效,所以它高于 per-profile 层;
  4. --patch 覆盖:命令行按 argv 顺序给的补丁文件;
  5. 之后还有程序自己追加的遥测开关补丁(DSH_TELEMETRY_DISABLED 非空且组合里有遥测行时生成)。

补丁怎么作用在行上

补丁列表是一份顶层 YAML 数组,每项要么带 insert,要么靠 id 定位已有行:

  • id + insert:把行插进该 id 的分组(目标不是分组就告警跳过);没有 id 的 insert 直接追加到顶层。
  • id + 其它键:把这些顶层键逐个赋到目标行上。disabled: true 就是靠这条路摘掉一行。
  • 匹配不到任何行的补丁只告警、不报错,被跳过。

关键细节:config 是整段替换,不是深合并。补丁写 config: {...} 时,目标行的整个 config 被换掉——所以只想改一个键也要把想保留的键一起写全。这也是「改默认模型」这类配方最容易踩的坑,实战写法见 tutorial/2。

热重载与空根

profile 的根配置每次启动都被重写成空数组:树是被 patch 出来的,如果某次运行把组合后的行写回了根文件,下次启动就会把每个 bundle 的行重复插一遍。运行时期间,profile 层与 home 层两个补丁文件都在监视之下,保存即重新组合,不必重启。

出处

  • 层序与组合:@deepseek-ai/dsh 的 lib/profile-boot-DG5t9aNs.js(composeProfile、allPatches),实测版本 0.1.0-rc.6。
  • 补丁语义(唯一权威实现):@deepseek-ai/dsh-app-boot 的 lib/index.js 里 applyEntryPatches 与 composeEntries。
  • 现场样本:市场整合包 hxh230802.smoother-deepseek-harness 的 manifest patch 字段就是一层真实补丁(含 disabled 与整段 config 两种写法)。

相关教程

谁引用了这一条

信息表

category
concept.runtime
layer
runtime
spec
~/.dsh/profiles/node_modules/@deepseek-ai/dsh/lib/profile-boot-DG5t9aNs.js(composeProfile / allPatches)
updatedAt
2026-10-02

标签:层序、机制、启动