🐳 第一章 Harness不只是另一个Coding Agent
📄 一句话概括

DSH 把模型适配、工具、会话存储、沙箱、调度、甚至界面全都做成了插件;落到代码上,一个能力就是配置里的一行——挂上就有,删掉就没。

📝 本节和前面几节的关系

1.1 把产品和 Runtime 分开了,这一节钻进 Runtime 内部看它是怎么搭起来的。这里立下的【插件】和【两个平面】两个概念,是第 3 章整章、以及第 5 章 Cordis 的地基。

一、哪些东西被做成了插件

这句口号听着像营销词,但它有一份确定的清单。在 DSH 里,下面这些全部是插件:

注意最后两项。一般人不会把【界面】和【主循环】当成可以替换的东西, 而 DSH 把它们也做成了插件——这意味着连【这个 Agent 长什么样、怎么往前走】都能换。

二、一个能力,就是配置里的一行

这是它在代码层面最好懂的一点。能力不是藏在某个函数里的开关,而是配置里的一行:

- id: tool-web
  name: '@deepseek-ai/dsh-tool-web'

这行挂上,模型就多了一个联网搜索工具;删掉这行,它就没了。 没有另一种配置语言——改能力就是改组成它的行。

行还能带条件。真实配置树里能看到这样的写法:

- id: settings
  name: '@deepseek-ai/dsh-settings'
  disabled: !!js '!ctx.get(''profileContext'')'

读法是:只有当这个进程带上了 profile 上下文时,才加载 settings。 这就是【可组合】在代码里的样子——连加载不加载都是配置算出来的。

三、插件分两个平面

插件不是平铺在一层上的,它们分属两个平面。这是新手最容易搞混、 也是最需要一次性弄清的地方:

两者最直观的区别是生命周期:宿主平面在进程启动时就位,一直到进程结束; 会话平面随会话创建、随会话销毁。

所以四套预设(Standard、PTC、Minimal、Creative)并不是四份完整系统, 它们只是会话平面上的四套装配方案——底下共用同一个宿主平面。

四、两个平面为什么必须分开

这不是为了好看,是不分开就会出事。原因有两条:

  1. 多会话要隔离,基础设施要共享。一个 Web 界面进程同时服务很多会话。 如果工具是进程级的单例,那么所有会话看到的工具就得一模一样—— 没法定成【这个会话用极简、那个会话用标准】,同一个工具名还会重复注册直接冲突。
  2. 注册表是分层的,不是全局一张表。工具注册表按作用域归档, 解析顺序是【会话 → 预设 → 全局】,近者遮蔽远者。 全局层默认是空的——所以一个没挂任何预设的 Agent,到模型那儿既没有工具、也没有提示词段。

判断口诀只有一句:

只有会话内才需要的东西进预设,跨会话共享的底座留在宿主平面。

放反了会很难查。举个真实会踩的例子:子代理注册表要回答跨会话的查询, 它就该待在宿主平面;塞进预设会让宿主平面饿死,第二个会话还会撞名。

五、动手 Demo:数一数【皆插件】到底覆盖了多少东西

把被做成插件的能力列出来,再算一下组合空间有多大:

abilities = ["模型适配", "工具注册表", "会话存储", "沙箱",
             "调度", "界面(UI)", "Agent 循环"]
for a in abilities:
    print("  %-14s → 可单独装卸" % a)
print()
n = 20
print("若有 %d 个可选的会话级插件:" % n)
print("  理论组合数 = 2^%d = %.1f 万种" % (n, pow(2, n) / 10000))

实跑输出:

--------------------------------------------------------------------
一切皆插件,到底是【什么】皆插件
--------------------------------------------------------------------
  这句口号听着像营销词,但它有确定的清单。
  DSH 把下面这些东西全部做成了插件:

  组成部分       做成了插件吗     换成别的好处
  ---------------------------------------------------
  模型适配       是               换模型只改一行配置
  工具注册表     是               加工具是加一行
  会话存储       是               换存储后端不动业务
  沙箱           是               换执行环境不动工具
  调度           是               定时任务可插可拔
  界面(UI)     是               换皮、换终端都行
  Agent 循环     是               换推进策略不动其它
  ---------------------------------------------------

  在代码层面,一个能力就是配置里的一行:

        - id: tool-web
          name: '@deepseek-ai/dsh-tool-web'

  这一行挂上,模型就多了一个联网搜索工具;删掉这行,它就没了。
  没有【另一种配置语言】——改能力就是改组成它的行。

  但插件不是平铺在一层上的,它们分属两个平面:

                 宿主组合(Host)           Agent 预设(Preset)
  ---------------------------------------------------------------
  实例数         一个进程一份               每个会话一份
  放什么         注册表、沙箱、模型路由     工具、人格、提示词段
  什么时候建     进程启动时                 会话创建时
  谁管           进程级配置                 预设目录里的组装文件
  ---------------------------------------------------------------

  判断口诀只有一句:只有会话内需要的东西进 Preset,
  跨会话共享的底座留在宿主层。
  反过来放会出事——子代理注册表要回答跨会话查询,
  塞进 Preset 会让宿主层饿死,第二个会话还会撞名。

  小算例:假设有 20 个可选的会话级能力插件,
    理论上能组合出的能力集合 = 2 的 20 次方 = 104.9 万种。
  实际当然不会全试,但这就是【一切皆插件】的分量——
  能力的上限不再是某个人写了多少代码,而是组合的自由度。

104.9 万这个数字不用当真,它只是说明【组合的自由度】有多大。 真正的意义在上一段:能力不用挤在同一个核心程序里, 每个都能单独装卸,装错了还能卸干净。 这也是下一节要展开的——为什么这就不是【多加几个工具】那么简单。

30 秒实验

  1. 在终端跑一次 dsh --profile web --dump-default-config,看真实的配置树——每一行就是一个插件。
  2. 在打印出来的配置里找 - id: 开头的行,数一下前 50 行里有多少个插件。
  3. 想一个你希望 Agent 拥有的能力,先判断它该放宿主平面还是会话平面,再用上面的口诀验一遍。

常见误区

  1. 错误:【一切皆插件】等于没有内核。——纠正:内核还在(Cordis),它只管加载、卸载、依赖这三件事;插件里放能力,内核里放机制。
  2. 错误:插件就是第三方扩展。——纠正:官方自己那 100 多个插件也是插件,官方没有任何特权接口——这是开放生态能成立的结构基础。
  3. 错误:所有插件都平铺在同一层。——纠正:分宿主平面和会话平面,放错层会饿死宿主、还会撞名。
自测三问(点开对答案) 1. DSH 把哪些东西做成了插件? —— 模型适配、工具注册表、会话存储、沙箱、调度、界面、Agent 循环——全都做成了插件。 2. 两个平面各管什么? —— 宿主平面管跨会话共享的底座(注册表、沙箱、模型路由、持久化、界面服务);会话平面管只有这个会话才需要的东西(工具、人格、提示词段、压缩策略)。 3. 判断一个东西该放哪一层的口诀是什么? —— **只有会话内才需要的东西进预设,跨会话共享的底座留在宿主平面。**

相关笔记