Profile 与 Bundle 组合模型
本教程共 32 篇 · 第 7 篇 · 更新于 2026-08-15 · 约 6 分钟阅读
本节目标:搞清 Profile(装配方案)和 Bundle(组合包)的分工,理解 dsh 启动时按什么顺序把各层配置叠成最终插件树。
上一章说了,dsh 的一切都是插件。那么问题来了:几百个插件,谁来选、按什么顺序装?
答案是两个概念:Profile 和 Bundle。把 dsh 想成点菜:Profile 是菜单,Bundle 是后厨的菜谱。菜单决定上哪些菜、按什么顺序;菜谱决定每道菜怎么做。你点菜时(dsh --profile web),后厨照着菜单执行。
两者都写在 package.json 里,靠 dsh 字段声明自己。但回答的问题完全不同。
两个概念,两种 manifest
Bundle 是 Cordis 配置项及其挂载代码的分发格式。它随 npm 包分发,manifest 声明 dsh.bundle。它回答「这个包贡献什么」:一个插入或覆盖插件行的 patch 文件。
Profile 是存放在 Harness home 中的具名组装。它列出要叠放的 Bundle,存放自己安装的树外插件,还保存用户自己的 cordis.patch.yml。manifest 声明 dsh.profile,回答「这套配置由哪些 Bundle 按什么顺序组成」。
一句话区分:Bundle 是作者编写并分发的东西;Profile 是用户用 dsh --profile <name> 启动的东西。 没有东西同时是两者。
NoteProfile 目录位于安装目录之外,路径模板是
$DSH_HOME/profiles/<name>($DSH_HOME默认为~/.dsh)。web和headless两个 Profile 在首次使用时从随附模板自动初始化;其他 Profile 要通过dsh plugin --profile <name> <pnpm 参数>创建。
dsh-base:每个 Profile 的第一层
官方随发行版交付三个组合包:
| 组合包 | 作用 |
|---|---|
@deepseek-ai/dsh-base | 每个 Profile 的第一层:模型适配器、工具、持久化、沙箱与审批策略、设置、凭据、遥测 |
@deepseek-ai/dsh-web-app | 在 base 之上增加浏览器应用(Web UI) |
@deepseek-ai/dsh-headless | 在 base 之上增加一次性运行器,完全不带服务器 |
web Profile 叠放 base + web-app;headless Profile 叠放 base + headless。不管哪种形态,base 都是地基。你日常看到的能力——LLM 适配器、工具流水线、会话日志、沙箱、审批——几乎全部来自这一层。
一个 Bundle 的 package.json 长这样:
{
"name": "dsh-hello-plugin",
"version": "0.1.0",
"type": "module",
"main": "index.js",
"files": ["index.js", "cordis.patch.yml"],
"dsh": {
"bundle": {
"patch": "./cordis.patch.yml"
}
}
}
关键字段是 dsh.bundle.patch:指向这个包贡献的 patch 文件。patch 文件里按包名引用插件(如 - name: 'dsh-hello-plugin'),安装后 pnpm 会把包链接进 node_modules,模块解析就能找到代码。没有 dsh.bundle 声明的包仍然可以安装,但只作为普通依赖,不会激活任何配置层。
新手最常问:普通 npm 包和 Bundle 有什么区别?判断标准就一条——有没有 dsh.bundle 声明。
组合顺序:四层叠加
运行中的 dsh 是一棵插件树,由启动时按序叠加的各层组合而成。以空条目列表为起点,顺序如下:
- 按 Profile 列出的顺序,应用每个 Bundle 的 patch
- 应用 Profile 自己的
cordis.patch.yml - 应用 home 级
$DSH_HOME/cordis.patch.yml - 应用任意
--patch覆盖层(可多个)
空根 → bundle 序 → profile patch → home patch → --patch 覆盖层
一条 patch 按 id 定位某个条目,替换其整个 config,或插入新条目。靠后的层覆盖靠前的层,这就是「分层可替换」的来源:出厂默认配置在 Bundle 层,你的个人偏好写在 Profile 或 home 层,临时调试用 --patch,互不干扰。
人话版:像在桌上贴便利贴,越晚贴的越靠上,盖住下面同位置的文字。同 id 的条目,后盖的赢。
profile 的 manifest 同样写在 package.json 里:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app"
]
}
}
}
dsh.profile.bundles 中列出的 Bundle 先从 dsh 安装目录解析,再从 Profile 自身的 node_modules 解析;树外插件由 dsh plugin 安装进 Profile 目录。
用命令验证组合结果
组合模型不需要靠猜。两个命令直接看结果:
# 查看 web Profile 组合后的完整配置树(不启动)
dsh --profile web --dump-config
# 查看排除用户层后的默认配置树
dsh --profile web --dump-default-config
# 正常启动
dsh web # --profile web 的别名
dsh --profile headless "任务描述" # 一次性运行任务
--dump-config 打印出的任何条目,都可以由你自己的 patch 替换。想确认某个插件是否被加载、某个配置项生效没有,先 dump 再看,比翻文档快得多。
Tip
dsh web是dsh --profile web的别名。启动器只解析自己的 flag,--profile之后、第一个无法识别的 token 之前,都属于应用参数。例如dsh --profile web --port 8080里--port属于 web 应用,不是启动器参数。
三层视角:Bundle / Profile / Patch
把三者的关系放在一起看:
| 概念 | manifest 键 | 回答的问题 | 谁编写 / 谁使用 |
|---|---|---|---|
| Bundle | dsh.bundle | 这个包贡献什么(一个 patch 文件) | 插件作者编写,随包分发 |
| Profile | dsh.profile | 这套配置由哪些 bundle 按什么顺序组成 | 用户用 dsh --profile 启动 |
| Patch | 无(YAML 文件) | 按 id 增删改插件条目 | 任何人,在任意覆盖层使用 |
Warning版本基线
@deepseek-ai/dsh0.1.0-rc.6 处于 Developer Preview,profile/bundle 机制仍在快速迭代。命令以dsh --version实测为准;--dump-config看到的树,永远比文档描述更接近真相。
小结
- Profile 是菜单,Bundle 是菜谱:一个决定装什么,一个决定怎么装。
- Bundle 随 npm 包分发,声明
dsh.bundle;Profile 存在 Harness home,声明dsh.profile。 - 没有东西同时是两者;普通 npm 包没有
dsh.bundle就不激活配置层。 - 四层叠加:Bundle 序 → Profile patch → home patch →
--patch,后层覆盖前层。 - patch 按 id 定位条目、替换整个 config;验证用
dsh --profile web --dump-config。