dshkit

DeepSeek Harness 的 profile 与 patch 分层讲清楚

一个 dsh profile 怎么把 bundle 组装成运行中的 agent:四层 patch 顺序、profile 级与 home 级 cordis.patch.yml、--patch 运行时覆盖,以及用 --dump-config 看真正启动了什么。

更新于 2026-08-132 min
一句话结论

profile 是 $DSH_HOME/profiles/<名字> 下的一份命名组装。patch 应用到一个空条目列表上,顺序固定:profile 列出的每个 bundle、profile 自己的 cordis.patch.yml、home 级的 $DSH_HOME/cordis.patch.yml、最后是 --patch 覆盖。后面的层赢,dsh --profile <名字> --dump-config 打印组装结果。

DeepSeek Harness 上手第一周,多数人栽的是分层问题:改了个配置文件,毫无反应,还看不出为什么。答案基本 只有两种——被更靠后的层覆盖了,或者改错了那两个同名文件中的一个。

这套组装模型小到能装进脑子里,值得花十分钟。

profile 是什么

profile 是 harness home 目录下的一份命名组装,位置在 $DSH_HOME/profiles/<名字>。它干三件事:

  1. 列出自己要叠哪些 bundle
  2. 存放自己安装的 out-of-tree 插件
  3. 保管用户自己的 cordis.patch.yml

预览版自带两个模板:webheadless。用 --profile 启动:

dsh --profile web
dsh --profile headless "总结一下失败的测试"

dsh web 是前者的简写。headless 那种形态接一个任务字符串,跑完一次会话、输出结果、退出——这就是你在 CI 里要的形状。

bundle 是什么

bundle 是 Cordis 配置行及其挂载代码的分发格式。定义里最关键的性质是:bundle 插入的任何东西, 仍然能被它上面的层 patch 掉。bundle 不是黑盒,它只是一组你之后能覆盖的起始行。

自带的三个:

Bundle提供
dsh-base模型适配器、工具、持久化、沙箱与审批策略、设置、凭证、遥测
dsh-web-app浏览器 UI 层
dsh-headless单次执行,无服务端

dsh-base 是所有 profile 的地基。web profile 本质上就是 dsh-base + dsh-web-appheadless 就是 dsh-base + dsh-headless

patch 顺序

这段要背下来。patch 应用到一个空条目列表上,顺序如下:

  1. 每个 bundle,按 profile 列出的次序
  2. profile 的 cordis.patch.yml —— $DSH_HOME/profiles/<名字>/cordis.patch.yml
  3. home 级的 cordis.patch.yml —— $DSH_HOME/cordis.patch.yml
  4. 运行时传入的 --patch 覆盖

后面的层赢。patch 按标识符命中某一行,要么整行替换其配置,要么插入新行。

由此直接推出两个实用结论:

你的 home 级文件压过 profile 文件。 如果你按 profile 设了某项却被忽略,先查你几个月前是不是也在 home 级设过、然后忘了。这是「我配了但完全没生效」最常见的成因。

--patch 压过一切。 所以它是一次性实验的正确工具——换个模型、收紧审批策略,只对这一次运行生效, 磁盘上任何文件都不动:

dsh --profile web --patch ./experiments/strict-approval.yml

看真正启动了什么

永远别靠读源文件去推组装结果。打印它:

dsh --profile web --dump-config

这会显示四层全部应用之后、每一行可被 patch 修改的配置。行为和预期打架时,以这份输出为准。

它的兄弟回答的是另一个问题:

dsh --profile web --dump-default-config

--dump-default-config 不初始化 profile,直接显示组装出来的树——一个干净 profile 长什么样, 不受你本地改动污染。两份输出 diff 一下,就是你到底改了些什么。

往 profile 里装插件

Out-of-tree 插件住在 profile 自己的 node_modules 里。launcher 提供了一个直接把参数转发给 pnpm 的 子命令:

dsh plugin --profile web add some-cordis-plugin

底层就是 pnpm,所以 pnpm 的全套词汇都能用——addremoveupdate、版本号。bundle 的解析顺序是 先从 dsh 安装目录找,再从 profile 自己的 node_modules,这正是本地装的插件能遮盖或扩展自带 插件的机制。

参数顺序有讲究

launcher 先解析自己的 flag。遇到第一个它不认识的 token,后面全部算 app 的参数。 所以这样是对的:

dsh --profile web --port 3100

因为 --port 是透传给启动的 app 的,不被 launcher 吃掉。

无效命令、属于别的模式的选项、配置错误和启动失败,一律非零退出。如果你要用脚本包 dsh,记得检查 退出码——放错位置的 flag 会大声失败,而不是悄悄干错事。

一个好用的心智模型

把它当成 agent 运行时的 CSS 层叠。bundle 是浏览器默认样式表:给合理默认值,完全可覆盖。profile 的 patch 文件是你的样式表。home 级 patch 文件是你的 !important--patch 则是审查器——先试,再决定要 不要落盘。

常见问题

profile 和 mode 有什么区别?

profile 是磁盘上那份会被启动的组装,自带 web 和 headless 两个模板。mode 是组装出来的 agent 呈现的工具面。profile 是机制,mode 是这个机制产出的其中一种结果。

profile 级和 home 级的 cordis.patch.yml 谁赢?

home 级赢。patch 按 bundle → profile patch → home patch → --patch 的顺序应用,后面的层覆盖前面的。

怎么看真正启动起来的配置?

dsh --profile <名字> --dump-config,打印组装后每一行可被 patch 命中的配置。--dump-default-config 则不初始化 profile,显示干净基线。

第三方插件装到哪?

装进 profile 自己的 node_modules。用 dsh plugin --profile <名字> <pnpm 参数>,它转发给 pnpm。

接着看