根据报错日志定位插件挂载失败或 patch 未生效的原因,并给出可执行的修复步骤。
上一课你刚用 headless 一次性 runner 跑通了端到端流水线:工具插件与自定义 LLM 适配器串成一条链,一次任务从启动到完成,退出码、命令行输出与会话轨迹全部核对无误。一切正常时,插件加载几乎是隐形的;可一旦加载失败,就得立刻换一副排查眼光。插件加载失败分两大家族:挂载失败,和补丁未生效。挂载失败会报错,很响;补丁未生效常常一声不吭。排查路径不同,别混在一起。
先弄清你面对的是什么。一个运行中的 dsh,是启动时按有序层堆叠出的插件树:每个 bundle 贡献配置行和代码,profile 把它们列出来,并持有你自己的 cordis.patch.yml。没有特权核心需要打补丁——你要扩展 dsh,就在其他插件旁边挂载一个插件;注册是 effect,插件卸载时自动回卷。
Cordis 内核负责挂载、卸载插件并解析依赖。插件挂不上,先问三个问题:它在不在插件树里?它的依赖有没有人提供?它的版本和环境对不对得上?
排查按固定顺序推进:读启动输出定位失败阶段,核对插件树里的行 id,查依赖声明,区分版本问题与配置问题,最后用最小化配置回滚。每步都缩小范围。
先读日志,再动配置。
启动输出按层顺序推进:bundle 按 profile 中的列出顺序应用,之后才是补丁层。失败打印在哪一行,哪一行就是分界线。分界线之前都正常,问题出在这一层;分界线之后才炸,往往是下游依赖了没挂上的服务。
Cordis 的加载是依赖驱动的:一个插件要等它依赖的服务可用才挂载。日志里第一个失败的插件不一定是元凶——它可能只是没等到依赖。往前看一层,找它 inject 的服务由谁提供。
还有一类故障不在启动期:插件挂载成功,但直到某次事件或工具调用才报错。这种别先查补丁,先确认该服务确实由某个已挂载插件提供。
补丁排查只需要两个坐标。
层序。配置按这个顺序应用:profile 列表中的每个 bundle 按列出顺序,接着是 profile 自己的 cordis.patch.yml,再是 home 级 cordis.patch.yml,最后是 --patch 覆盖。后面的层覆盖前面的层。
行 id。补丁按 id 定位一行,然后整体替换该行配置,或插入新行。id 对不上,补丁什么都不做,也不报错。id 对上了,整行都被替换:你只写想改的一个字段,其余字段会被清空。启动时打印的插件树,每一行都可以被你的补丁替换。字段名以生成的 config catalog 为准。
版本问题还是配置问题?看报错的时间点和内容。
版本问题出现在启动早期:引擎校验不过、依赖解析失败、插件与 dsh 的 API 对不上。特征是换环境就复现,和配置内容无关。配置问题出现在补丁应用之后:字段校验失败、服务行为不对、补丁静默不生效。特征是改配置就变,换环境不变。
分不清时,做两个实验:把插件版本固定到文档实测过的版本,看故障是否消失;把配置回滚到最小,看故障是否消失。前者变好是版本问题,后者变好是配置问题。
遇到问题:启动时输出报错,某个插件的 Fiber 没有进入就绪状态,依赖它的服务、工具或事件的其他插件跟着失败。日志能告诉你失败发生在哪个阶段,但原因不止一种。
可能原因
- 依赖声明不完整:插件 inject 了某个服务,但插件树里没有任何插件提供它。Cordis 解析依赖时失败。
- 版本不兼容:DeepSeek Harness 是 developer preview,兼容性破坏随时发生,插件与 dsh 的版本矩阵没有对齐。
- Node.js 版本不满足引擎要求:仓库要求 Node.js ^22.19.0 或 >=24.0.0。旧版本可能 npx 能跑,引擎校验却不过。
- 插件根本没进树:代码写进了 bundle,但 profile 的 bundles 列表没列出它,或 patch 没把它插进配置行。
排查步骤
- 先定位失败阶段:读启动输出,找到失败插件名和它前面最后一个成功的行,决定往依赖还是版本方向查。
- 核对依赖:列出该插件 inject 的每个服务,逐个确认启动树里有插件提供。注册是 effect,插件卸载会回卷,别假设旧注册还在。
- 固定版本:钉住 dsh 包版本,对照当前文档验证;插件与 dsh 一起升级,不要各升各的。
- 检查 Node 版本,确认满足 ^22.19.0 或 >=24.0.0。
- 最小化回滚:把 profile 减到 dsh-base 加你的插件,再逐层加回 bundle,直到失败复现。失败的层就是元凶。
遇到问题:cordis.patch.yml 改了配置,重启后行为完全没变;打印插件树,目标行仍是旧配置。全程没有报错。
可能原因
- 目标行 id 不匹配:补丁按 id 定位,启动树里没有这个 id 就被跳过。手打 id、少了前缀或改了大小写,都容易踩中。
- 补丁写错了层:生效顺序是 bundle 按列出顺序 → profile 的 cordis.patch.yml → home 级 cordis.patch.yml → --patch。你的改动被后面的层盖掉了。
- 字段与 config catalog 不符:字段名、类型或嵌套结构不对,行替换后校验失败,或得到意料外的默认值。
- 忘了整行替换:补丁整体替换目标行配置。只写一个字段,其余字段全部清空,插件行为反而更怪。
排查步骤
- 打印启动树,把目标行 id 原样复制进补丁,不要手打。
- 确认补丁在哪个层:profile 级、home 级还是 --patch。被覆盖就把改动移到更高层,或删掉更高层的同 id 补丁。
- 逐字段对照 config catalog 校验名称与结构。
- 最小化验证:一次只改一行、一个字段,重启确认变化,再改下一处。
记住补丁的粒度:它按 id 定位一行,然后整体替换该行配置。想改一个字段,就得把该行其他字段一起写全。漏写的字段会回到默认值或直接消失,表现成“配置改了但行为更怪”。这也是为什么排查时先对照 config catalog 列出目标行全部字段,再动笔。
复现一次补丁未生效并完成修复
- 1
查看 profile 声明:dsh.profile 列出它堆叠的 bundles,你的 cordis.patch.yml 属于这个 profile。启动 dsh 并打印插件树,抄下一个真实行 id。
shnpx @deepseek-ai/dsh web - 2
在 cordis.patch.yml 里写一条补丁,故意把目标 id 改错一个字符,其余字段照抄。
- 3
重启 dsh。观察现象:没有任何报错,但该行配置与插件行为都没变——这就是 id 不匹配的静默 no-op。
shnpx @deepseek-ai/dsh web - 4
对照启动树逐字符核对 id,修正补丁中的目标 id,内容不动。
- 5
再次重启 dsh,打印插件树确认目标行已变成补丁中的配置。
你应该看到
你复现了一个真实的加载失败场景(补丁静默不生效),并走完了从启动输出定位、核对行 id、修正补丁到验证生效的完整闭环。同一流程可以套用到依赖不完整与版本不兼容两类故障。
DeepSeek Harness 目前是 developer preview,官方态度明确:兼容性破坏的变更随时会来。两个习惯能省掉一大半排障时间。
钉版本。实测组合是 @deepseek-ai/dsh 0.1.0-rc.6 配 Node.js 22.23.2。把包版本固定,升级时插件与 dsh 一起升。
把环境当兼容边界。插件版本、权限和共享服务需要一起测试。项目推进很快,采纳前核对当前文档。报错先分版本问题还是配置问题,再动手改配置。
自检:补丁未生效、又没有报错时,第一步应该核对什么?为什么?
第一步核对目标行 id:打印启动树,把补丁中的 id 与实际行 id 逐字符比对。补丁按 id 定位一行并整体替换其配置,id 不匹配时补丁被静默跳过。id 一致后,再核对层序(bundle 顺序 → profile 补丁 → home 级补丁 → --patch)以及字段是否与 config catalog 一致。