DSH 把模型适配、工具、会话存储、沙箱、调度、甚至界面全都做成了插件;落到代码上,一个能力就是配置里的一行——挂上就有,删掉就没。
1.1 把产品和 Runtime 分开了,这一节钻进 Runtime 内部看它是怎么搭起来的。这里立下的【插件】和【两个平面】两个概念,是第 3 章整章、以及第 5 章 Cordis 的地基。
一、哪些东西被做成了插件
这句口号听着像营销词,但它有一份确定的清单。在 DSH 里,下面这些全部是插件:
- 模型适配——接哪家的模型、怎么调。
- 工具注册表——有哪些工具、谁能用。
- 会话存储——这一轮怎么记得住上一轮。
- 沙箱——命令在哪儿跑、能碰哪些文件。
- 调度——定时、批处理这类安排。
- 界面(UI)——人怎么看、怎么插手。
- Agent 循环——一轮一轮推进的那套逻辑。
注意最后两项。一般人不会把【界面】和【主循环】当成可以替换的东西, 而 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。 这就是【可组合】在代码里的样子——连加载不加载都是配置算出来的。
三、插件分两个平面
插件不是平铺在一层上的,它们分属两个平面。这是新手最容易搞混、 也是最需要一次性弄清的地方:
- 宿主平面(Host):一个进程只有一份。放着注册表本身、沙箱、模型路由、 持久化、界面服务这类跨会话共享的底座。
- 会话平面(Preset):每个会话各一份,随会话销毁。放着工具、人格、 提示词段、压缩策略这类只有这个会话才需要的东西。
两者最直观的区别是生命周期:宿主平面在进程启动时就位,一直到进程结束; 会话平面随会话创建、随会话销毁。
所以四套预设(Standard、PTC、Minimal、Creative)并不是四份完整系统, 它们只是会话平面上的四套装配方案——底下共用同一个宿主平面。
四、两个平面为什么必须分开
这不是为了好看,是不分开就会出事。原因有两条:
- 多会话要隔离,基础设施要共享。一个 Web 界面进程同时服务很多会话。 如果工具是进程级的单例,那么所有会话看到的工具就得一模一样—— 没法定成【这个会话用极简、那个会话用标准】,同一个工具名还会重复注册直接冲突。
- 注册表是分层的,不是全局一张表。工具注册表按作用域归档, 解析顺序是【会话 → 预设 → 全局】,近者遮蔽远者。 全局层默认是空的——所以一个没挂任何预设的 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 秒实验
- 在终端跑一次
dsh --profile web --dump-default-config,看真实的配置树——每一行就是一个插件。 - 在打印出来的配置里找
- id:开头的行,数一下前 50 行里有多少个插件。 - 想一个你希望 Agent 拥有的能力,先判断它该放宿主平面还是会话平面,再用上面的口诀验一遍。
常见误区
- 错误:【一切皆插件】等于没有内核。——纠正:内核还在(Cordis),它只管加载、卸载、依赖这三件事;插件里放能力,内核里放机制。
- 错误:插件就是第三方扩展。——纠正:官方自己那 100 多个插件也是插件,官方没有任何特权接口——这是开放生态能成立的结构基础。
- 错误:所有插件都平铺在同一层。——纠正:分宿主平面和会话平面,放错层会饿死宿主、还会撞名。
自测三问(点开对答案)
1. DSH 把哪些东西做成了插件? —— 模型适配、工具注册表、会话存储、沙箱、调度、界面、Agent 循环——全都做成了插件。 2. 两个平面各管什么? —— 宿主平面管跨会话共享的底座(注册表、沙箱、模型路由、持久化、界面服务);会话平面管只有这个会话才需要的东西(工具、人格、提示词段、压缩策略)。 3. 判断一个东西该放哪一层的口诀是什么? —— **只有会话内才需要的东西进预设,跨会话共享的底座留在宿主平面。**- ☐ 下一节(1.3 为什么这不只是【多加几个 Tool】)
相关笔记
- 先把【产品】和【Runtime】区分开 —— 上一篇在层与层之间划线,这一篇钻进 Runtime 内部看它怎么搭
- 为什么这不只是【多加几个 Tool】 —— 下一篇讲为什么非得是插件,而不只是多挂几个工具
- Agent 的核心能力 —— 能力的另一种拆法:理解、规划、调工具、记忆、反思
- 🐳 DeepSeek Harness · 索引