DeepSeek Harness / 03

运行中的 DSH:插件树、组合层与能力接缝

知道“一切皆插件”还不够。真正运行时,系统必须回答三件事:这次启动究竟装了什么,多个配置发生冲突时谁获胜,替换一个文件系统为什么能让 Bash、终端和 LSP 一起迁移到沙箱。

DSH 的答案是:产品不是一份写死的启动代码,而是一棵在启动时由有序配置层组合出来的插件树。

从产品到插件树

官方 Web 和 headless 不是两个独立内核,而是两份不同的产品配方。它们共享基础能力,再装入各自的宿主插件。

flowchart TD
  A["Bundle patches"] --> B["Profile patch"]
  B --> C["Harness Home patch"]
  C --> D["CLI patch overlays"]
  D --> E["启动器附加配置"]
  E --> F["最终插件树"]
  F --> G["Cordis Loader 激活 Fiber"]

这棵树让“本次运行由什么组成”变成可检查的事实。--dump-config 的价值不只是方便排错,它给构建、审计和复现提供了一份组装结果。

Profile、Bundle 与 Patch 各管什么

设计责任 不应该承担的责任
Bundle 分发一组可以共同安装的插件行 决定每台机器的最终偏好
Profile 定义 Web、headless 或定制产品的有序配方 偷偷修改全局环境
Patch 在明确层级替换或插入条目 依赖不透明的隐式合并

Bundle 解决复用和分发,Profile 解决产品形态,Patch 解决环境差异与临时实验。把三者混在一个配置文件里同样能启动,但很难判断一项变化应该跟着产品发布、跟着机器生效,还是只属于一次运行。

覆盖顺序就是架构事实

当前 rc.8 文档给出的组合顺序是:Bundle 层按 Profile 声明顺序叠加,然后是 Profile Patch、Harness Home Patch、命令行 Patch,最后还有启动器附加的 Agent preset 和遥测开关等配置。后层拥有更高优先级。

这里有一个容易误判的限制:id 覆盖配置时是整段替换,不做深合并。 只写一个新字段可能使旧字段全部消失。这降低了合并语义的魔法程度,却把复述配置和版本迁移的责任交给使用者。

!!js 则是配置中的窄逃生口,可以在 configdisabled 等受限位置引用运行时值。它提高了表达力,也意味着 Patch 不是天然安全的数据文件:来源、评审和执行权限必须像代码一样管理。

Seam:替换能力,不只是替换包名

插件树解释“装了什么”,能力接缝解释“为什么替换能传播”。一个完整 Seam 有三个角色:

角色 责任 文件系统例子
Service Definition 定义稳定契约 ctx.fs 能做哪些文件操作
Service Provider 实现契约 本地文件系统或远程沙箱
Consumer 只依赖契约使用能力 文件工具、终端、LSP

真正的可替换性不来自“用了接口”这句口号,而来自消费者没有绕过接缝直接访问本地实现。只要 Bash 工具仍直接调用宿主机进程,替换 ctx.fs 就不能把完整执行环境迁移到沙箱。

因此评审一个 Seam 时,要沿依赖链追问:定义是否稳定,提供方是否完整,消费者是否都经过它,错误与取消语义是否一致。三者缺一,替换就只存在于配置表面。

官方预置:四种 Agent Preset

Profile 组合层最直观的产物是四种预置模式,每种是一份 apps/cli/config/agent-presets/ 下的 YAML 配方:

模式 预置名 能力范围 适合场景
Standard standard 文件编辑、Shell、检索、Skills、规划 日常 Coding Agent
PTC code TypeScript 程序组合,多步操作 需要精确多步流程编排
Minimal minimal 仅 bash + str_replace_editor 基线测试、能力隔离实验
Creative cordis 运行时检查、插件实验 开发者探索 Cordis 机制

四种模式说明一个关键事实:产品差异不在代码分支,而在 Profile 组合。Standard 装了搜索、规划和完整工具集;Minimal 只保留两个工具,其余能力通过不装对应插件来”关闭”。这比在代码里 if (mode === 'minimal') 更符合可替换设计:需要新模式时,写一份新 YAML 即可。

热更新改变的是依赖分支

Patch 变化后,理想行为不是重启全部系统,而是找到受影响条目,卸载旧 Fiber,再让依赖解析器重新激活相关分支。Cordis 的可逆 Effect 使这件事可行;Profile 的确定顺序则使重组结果可预测。

但 HMR 不会自动迁移所有业务状态。一个插件若持有无法重建的内存状态,仍需定义迁移或降级策略。配置解析失败、插件激活失败和 disposer 失败,也必须有清晰诊断;“最后一棵好树”只能保护框架状态,不能撤销已经提交到外部系统的副作用。

设计检查

  • 能否从 Profile 和 Patch 重建一次运行,而不是依赖某台机器上的隐式状态?
  • 覆盖规则是否明确,整段替换造成的字段丢失能否在评审或测试中发现?
  • 每项可替换能力是否都有 Definition、Provider、Consumer 三个角色?
  • 切换提供方后,是否仍有消费者绕过 Seam 访问旧实现?
  • HMR 失败时,运行时状态和外部副作用分别怎样收场?

事实基线见官方 ArchitectureCapability Seams

小结

  • 运行中的 DSH 是一棵由有序配置层启动出来的插件树。
  • Bundle、Profile、Patch 分别承担分发、产品组合和覆盖责任。
  • Patch 的顺序、整段替换和 !!js 都是需要审计的运行时语义。
  • Seam 由定义、提供方和消费者共同形成,替换能力必须沿完整依赖链成立。

下一篇建议继续看: