DeepSeek Harness 的 Agent 轮次上限:包默认 256、出厂组合配 64

2026-08-16

先说清楚这篇文章的前提:deepseek-harness 这个仓库建立于 2026-08-13,我们采集事实的时间是 2026-08-16,前后只差三天;仓库版本是 0.1.0-rc.5,在 GitHub 上没有任何 Release,README 自述处于开发者预览阶段并明写未来会出现破坏兼容性的变更。下面提到的每一个字段名、每一个默认值,都要带着这层限定去读——它们随时可能变。

一个具体的问题:ralph 一次最多跑几轮

tool-ralphpackages/workflow/ 下的一个包,它的定位在 packages/workflow/tool-ralph/README.md:5 写得很直白:跑一个固定的前台 workflow,把同一个不可变的目标交给一串全新的子代理,每轮起一个子代理。既然是「一串」,就一定有个上限,这个上限的配置键叫 maxRounds

于是你去查它的默认值。查到的结果取决于你翻开的是哪个文件:

  • packages/workflow/tool-ralph/src/index.ts:37 的 Schemastery 定义里写的是 .default(256)
  • 同包 README 的 Config 表也写默认 256,并称它同时是「部署上限」;
  • packages/bundle/base/cordis.patch.yml:378-382 里那一行 tool-ralphconfig 下配的是 maxRounds: 64

两处不一致,位置就是上面这几行。我们只陈述这个差异,不去推断哪一个「才算数」、也不推断为什么会这样——以你实际读到的仓库状态为准。真正值得花时间的是另一件事:为什么一个默认值会有不止一处,以及作为使用者该按什么顺序去读。

默认值有几层,是文档明写的

docs/user/develop/basic/publish.md:116-119 记录了有效配置的合成顺序,一共四层,后面的层覆盖前面的:

  1. profile manifest 的 dsh.profile.bundles 里列出的每个 bundle patch,按列表顺序,@deepseek-ai/dsh-base 排在最前;
  2. profile 自己的 cordis.patch.yml
  3. home 级的 $DSH_HOME/cordis.patch.yml
  4. 每一个 --patch <path> overlay,按 argv 顺序。

apps/cli/reference/README.md:9 同样记录了这四层,并补了一句:home 级那一层的优先级高于 per-profile 层。

注意这四层之上还有一层,就是包源码里 Schemastery schema 上写死的默认值——上面那个 .default(256) 就在这一层。所以「包默认 256、出厂组合配 64」这件事,本质是两个不同层上的两个值,而不是同一个位置的两种说法。你在 src/index.ts 里读到的是「这个包在没人管它时的取值」,你在 packages/bundle/base/cordis.patch.yml 里读到的是「出厂这套组合选择怎么摆它」。

docs/user/develop/basic/config.md:80 把这套设计的判据写得很清楚:Harness 要求「任何两套部署可能想设成不同值的东西都成为一个配置字段」,判定标准是「cordis.yml 能不能在不改代码的前提下改掉这个值」。按这个判据,出厂组合里出现一个和包默认不同的值,是这套机制预期内会发生的事,而不是异常。

最容易翻车的一条语义:整行替换,不是深合并

docs/user/develop/basic/publish.md:123 的原话是:后面的层按行取胜,而且一个 patch 是替换该行整个 config,而不是按 key 深合并。文档紧接着在 :125 给出后果:覆盖一行时必须把该行需要的每一个 key 都重写一遍,不能只写你想改的那一个。

这条规则决定了你该怎么读 packages/bundle/base/cordis.patch.yml:378-382 那五行。那一行里除了 maxRounds: 64,还写了 subagentProvider: spawn;而同包 schema 里另外两个键 maxHandoffChars(默认 16384)与 maxResultChars(默认 16384)没有出现在这一行里。你把这五行和 schema 的四个键对着数一遍,就能明白为什么「读一个默认值」不能只读一个文件。

要知道某套 profile 下最终生效的值到底是什么,文档给的动作是现成的:docs/user/develop/basic/publish.md:106-107 记录了 dsh --profile demo --dump-configdsh --profile demo,前者就是用来验证组合结果的。我们没有运行过这个命令,也没有安装过这个项目,这里只是把文档记录的验证路径指出来——它比在源码里逐层推演更直接。

同一套机制的另外几个样子

一旦意识到「包里有」和「出厂组合里怎么配」是两件事,这个仓库里就有一批现象能被归到同一类,我们采集时数到的几处:

包在、但出厂被显式关掉。 skill-badge 这个包提供官方徽章素材,packages/bundle/base/cordis.patch.yml:243-245 里它那一行写着 disabled: truepackages/skill/skill-badge/README.md:7 也写了用户必须显式启用 skill-badge 这一行。

包在、但出厂组合里根本没有它的行。 packages/subagent/ 下有 11 个包,其中 subagent-acpsubagent-codexsubagent-claude-codesubagent-dsh-sdk 这四个 provider,我们对 packages/bundle/*/cordis.patch.yml 做 grep 后没有找到对应的组合行。packages/schedule/schedule 也类似:对全仓 *.ymldsh-schedule,只在 examples/web-schedule/cordis.yml:9 命中一次,packages/bundle/*/cordis.patch.yml 里没有。

同一个包在出厂组合里被摆了两次,配置还不一样。 packages/bundle/base/cordis.patch.yml:313-318 有一行 tool-subagent,配 provider: spawn / toolName: subagent / backgroundMode: continuable:324-329 又有一行 tool-subagent,配 provider: fork / toolName: subagent_fork / backgroundMode: one-shot。所以「tool-subagentbackgroundMode 是什么」这个问题,在出厂组合里本来就有两个答案,得先问是哪一行。

顺带记一条源码里的原样记录:packages/subagent/subagent-fork-in-process/src/index.ts:77-82 有一个 TODO(fork-continuable-prefix-reuse),注释里写着「no shipped composition calls this」,并指向 issue #2124。它标的是 TODO,就按 TODO 读,别当成已有能力。

反过来的一种:文档里有这个键,仓库里没有

上面几种是「包里有、组合里另配」,还有一种方向相反的情况,同样会让人读错默认值。

packages/extensions/tool-cordis/README.md:27 提到 runner 服务上有两个东西:vm 求值边界 vmTimeoutMs 与浏览器确认窗口 ackTimeoutMs。而 packages/extensions/cordis-host-runner/README.md:36-40 的配置表里只有 vmTimeoutMs 一项,默认值 5000,并在 :40 明写「One field is all there is」;源码 packages/extensions/cordis-host-runner/src/index.ts 里也只有 vmTimeoutMs:90 是类型、:128 是默认值 5000)。我们对整个 packages/ grep ackTimeoutMs,只命中那两行 README(英文版与中文版各一行)。

差异就在这两处位置,说到这里为止。要说的操作结论只有一句:在这个仓库里,一个配置键在文档里出现过,不代表它在源码的 schema 里存在。

一套读默认值的顺序

把上面的东西收成可执行的动作,读任何一个默认值时按这个顺序走:

  1. 先读包源码里的 schema 默认值——键的确切拼写、类型约束、默认值都在这里,例如 tool-ralphmaxRounds 默认 256packages/workflow/tool-ralph/src/index.ts:37)。
  2. 再读包 README 的 Config 表,看它和 schema 对不对得上;对不上就记下两处位置,不必替它下结论。
  3. 然后在 packages/bundle/*/cordis.patch.yml 里搜这个包名,看出厂组合有没有为它写行、写了哪些键、有没有 disabled: true、有没有被摆了两次。
  4. 最后才轮到你自己那三层:profile 的 cordis.patch.yml、home 级的 $DSH_HOME/cordis.patch.yml、以及命令行上的 --patch。改这一层时记住整行替换的规则,该行需要的每个 key 都得写全。
  5. 要最终生效值就别推演,文档给的动作是 dsh --profile <name> --dump-config

还有一个只能靠细心避开的坑:同一个数字在这个仓库里出现不止一次,但不是同一件事。packages/goal/goal/src/index.ts:187defaultMaxGoalRounds 的默认值也是 256,它属于 goal 服务,和 tool-ralphmaxRounds 没有关系。记数字不记出处,早晚会把两者混起来。

什么情况说明你遇到的不是「默认值分层」这个问题?如果你查到的两个值来自同一个文件的同一行,那不是分层;如果你压根没有用 profile、只在命令行挂了 --patch,那链条上就只有 schema 默认值和你这一个 overlay,别去 bundle 里找原因;如果两个值分别属于两个不同的包(比如上面那两个 256),那也不是分层,是同名不同物。

最后补一条与安装相关的、同样藏在「另一层配置」里的东西:docs/user/develop/basic/publish.md:153-178 记录,从 git 安装插件取到的是源码而非构建产物,TypeScript 包会缺 lib/ 而加载失败;因为 pnpm ≥10 默认拒绝执行 git 依赖的 prepare,用户侧需要在 profile 的 pnpm-workspace.yaml 里写 allowBuilds: { dsh-hello-plugin: true } 后重跑 add。文档对这条授权的定性原文是「permission to execute the package’s code on your machine at install time」,且在 agent 运行所依赖的任何沙箱之外,并建议只对自己信任源码的包这么做、并 pin 住 commit(:173)。这句话请照它的原样读,别把它当成一个无关紧要的开关。

延伸阅读


本文依据 DeepSeek Harness 官方仓库(github.com/deepseek-ai/deepseek-harness)的 README、docs/ 下的 架构与子系统文档、以及 packages/ 下的源码整理,核对日 2026-08-16,对应仓库快照 47f9438(版本 0.1.0-rc.5)。 本文内容为仓库源码与文档口径,我们没有安装、也没有运行过这个项目, 因此不涉及界面外观、操作手感与运行速度的任何描述。 该仓库建立于 2026-08-13,README 自述处于开发者预览阶段并明确说明未来会有破坏兼容性的变更, 文中出现的命令、配置与默认值随时可能变动,请以仓库最新内容为准。 安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。