第一幕 · 它是什么 / 02 万物皆插件(乐高积木)
0%
第一幕 · 它是什么 · 第 02 章

万物皆插件(乐高积木)

没有「特权内核」。模型适配器、工具、会话日志、循环本身 —— 全是积木,全可替换。

🧱开场小故事:买整机 vs 自己攒机

大多数软件像「买整机」:厂家把主板、显卡、电源焊死在一起。你想换个显卡?不好意思,请换整台机器——商家最爱这种顾客。

dsh 像「乐高」:每一块功能都是一块积木——模型适配器一块、工具注册表一块、会话日志一块,连「驱动 agent 循环」本身都是一块。想换模型?换积木。想加审批?插积木。嫌内置工具碍事?拆积木。拆装之间整台机器照常运转——这才是装机佬的快乐。

一句话讲清楚:一个运行中的 dsh 就是一棵插件树 —— 由「profile(套餐蓝图)」按顺序叠放「组合包(成套零件包)」、 再用「patch(贴纸图层)」微调,最终拼装出的一棵活的积木树。
插件树:profile 叠加组合包与补丁,拼装成插件树
图 02-1 左侧是「怎么拼」(profile + bundle + patch),右侧是「拼出来什么」(一棵插件树)。

三个关键词,一次说清

① profile —— 套餐蓝图

profile 是存放在 Harness home(默认 ~/.dsh,可用 $DSH_HOME 改)里的 具名组装方案。它只回答一个问题:这套机器要装哪几个零件包?

  • dsh web 用的是 web profile:浏览器界面版;
  • dsh --profile headless "任务" 用的是 headless profile:命令行一次性跑任务,完全不带服务器;
  • 你还可以创建自己的 profile:dsh --profile 我的方案 启动 $DSH_HOME/profiles/我的方案

profile 目录里有自己的 package.json(在 dsh.profile.bundles 字段里列出要叠放的组合包) 和一个用户自己的 cordis.patch.yml

② 组合包(bundle)—— 成套零件包

组合包是一个普通的 npm 包,但它自带「拼装说明书」:package.json 里声明 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }, 指向一个 cordis.patch.yml,里面写着要插入哪些插件行。

官方随发行版交付三个基础组合包:

组合包装了什么
dsh-base每个 profile 的第一层:模型适配器、工具、持久化、沙箱与审批策略、设置、凭据、遥测。
dsh-web-app在 base 之上增加浏览器应用(Web UI)。
dsh-headless在 base 之上增加一次性运行器,完全不带服务器。

③ patch —— 贴纸图层

patch 是一层「贴纸」:用 insert 往树上加新积木, 或者按 id 定位某块积木并整块替换它的 config(不深合并,后贴的盖先贴的)。

🪜层叠顺序(从下往上,后层盖前层)

① 空条目列表 → ② 每个组合包的 patch(按 profile 里 bundles 的顺序)→ ③ profile 自己的 cordis.patch.yml → ④ home 级 $DSH_HOME/cordis.patch.yml → ⑤ 命令行 --patch <路径> 的覆盖层。 想看你机器上实际拼出了什么?dsh --profile web --dump-config 会把整棵树打印出来, 而且每一行都标注了它来自哪一层 —— 打印出的任何条目,你都能用 patch 替换。

「没有特权内核」到底牛在哪?

传统框架里总有一块「神圣不可侵犯」的核心,你想改它得 fork 整个项目。 dsh 的哲学是:扩展 dsh 的方式就是把插件挂到其他插件旁边。 没有需要打补丁的特权内核,所以:

  • 换模型:注册一个新适配器即可(第 03、06 章展开);
  • 改行为:监听事件、替换结果,不用动循环代码;
  • 卸得干净:每个插件在卸载时会自动撤销自己的所有注册(这是 Cordis 的「可逆副作用」,下一章讲)。
🔬源码对照:dsh-base 的一行一个插件

打开仓库里的 packages/bundle/base/cordis.patch.yml,你会看到 base 用一次 insert 把整个共享核心全部写成插件行:llmllm-deepseektools(工具注册表)、tool-bashtool-fssessionsession-persistence-jsonlsandboxapprovalsettingscredentialsagent-loop…… 连「沙箱默认模式」都是 sandbox-policy 插件的配置。 没有魔法,全是行。

费曼自查

1profile、组合包、patch 三者分别是什么?用自己的比喻讲。
参考:profile = 套餐蓝图(决定装哪几套零件);组合包 = 成套零件包(自带拼装说明书 cordis.patch.yml);patch = 贴纸图层(加积木 insert,或整块替换某块积木的配置)。
2「没有特权内核」意味着什么?举一个例子。
参考:任何部分都可被插件替换。例如 agent-loop(驱动对话循环的插件)本身也只是插件,扩展包只依赖 dsh-agent 的事件和服务,不直接依赖循环实现,因此整个循环都可以换掉。
3用什么命令能查看自己机器上实际拼出的插件树?
参考:dsh --profile web --dump-config(或换成任意 profile 名)。它会打印完整配置树并标注每行来源。
4patch 的「整块替换」和「深合并」有什么区别?为什么选整块替换?
参考:整块替换 = 用新 config 完全顶掉旧的;深合并 = 逐字段叠加。整块替换让 patch 语义简单、可预测:后贴的贴纸完整覆盖先贴的,不会出现「想改一个字段却悄悄带上了旧字段」的意外。