DeepSeek Harness 插件不生效?先跑 --dump-config,六类原因逐条排查
发布日期:2026-09-08 | 依据:DeepSeek Harness 官方文档(deepseek-harness.github.io)与仓库 README
DeepSeek Harness 的定位是「Everything is a Plugin」,插件装了却不生效是最高频的问题。官方文档里排查清单其实已经给全,只是散落在「打包与安装插件」「插件配置」「用户设置」三处。核心诊断命令只有一条:dsh --profile --dump-config,它不启动服务,只打印最终生效的配置层。看不到你那个包的层标记,问题就在安装或 manifest;层在但配置不对,问题就在覆盖顺序。本文按六类原因逐条给出判据和修法。
一、先用一条命令定位:问题在「层」还是在「配置」
不要凭感觉猜。Harness 提供了一个只验证、不启动的命令:
dsh --profile demo --dump-config它会打印最终叠加出来的生效配置,每个组合包(bundle)贡献的层都带有标记,形如:
# == dsh-hello-plugin据此可以一刀切开两种情况:
看不到你的包对应的层标记 → 包压根没被激活。跳到第二、第三、第五节。
层在,但配置值不是你写的那个 → 包加载了,被后面的层覆盖了。跳到第四节。
层在、配置也对,但功能仍不工作 → 大概率是配置校验或生效时机问题。跳到第六、第七节。
先做这一步,能省掉后面 80% 的无效排查。
二、原因一:package.json 里没有声明 dsh.bundle
这是最常见、也最隐蔽的一种——包确实装上了,node_modules 里能找到,但 Harness 完全不理它。
Harness 的安装机制建立在两个 manifest 概念上,都写在 package.json 的 dsh 字段里:
组合包(bundle):一个附带配置层的 npm 包,manifest 声明
dsh.bundle,回答「这个包贡献什么」,其内容是一个插入或覆盖插件行的 patch 文件profile:位于
$DSH_HOME/profiles/<name>下的目录,manifest 声明dsh.profile,描述「这套配置由哪些组合包按什么顺序组成」
关键在于:如果包没有声明 dsh.bundle,它仍然能被装上,但只作为普通依赖存在——dsh plugin 会打印一条警告,且不激活任何层。
这个设计是有意的,用于那些「被其他插件包 import、而不是供用户直接启用」的库。但如果你写的是一个要直接启用的插件,漏掉这个字段就等于白装。
正确的 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" } }
}对应的目录结构:
hello-plugin/
├── package.json # declares dsh.bundle
├── cordis.patch.yml # the layer applied when a profile lists this bundle
└── index.js # plugin modules the patch rows reference排查动作:打开该包的 package.json,确认 dsh.bundle.patch 存在且指向一个真实存在的 patch 文件;同时回看 dsh plugin add 那次执行有没有打印警告——那条警告就是答案。
三、原因二:patch 行的 name 写成了相对路径
cordis.patch.yml 里每一行的 name 字段,必须是包名,不是相对路径:
- insert:
- id: hello
name: dsh-hello-plugin写成 ./index.js 或 ./src/my-plugin.ts 在正式发布的包里是不行的——Node 的模块解析找不到已安装的代码,这一行就是死的。
容易混淆的地方在于:本地开发调试时确实要写路径,而且必须是绝对路径。开发阶段用的是 --patch 覆盖层:
- insert:
- id: hello
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'pnpm dsh web --patch ./scratch-plugin/cordis.yml官方对此有一句很关键的约束:「插件路径必须是绝对路径。patch 文件只贡献配置,不会改变 loader 解析模块路径时使用的 profile 目录。」
也就是说 patch 文件所在位置不会成为模块解析的基准目录——很多人以为把 patch 和插件放同一个文件夹就能用相对路径,这是错的。
排查动作:开发期用绝对路径,发布后改成包名。两者不能混用,也不能互相照抄。
四、原因三:被后面的层整行覆盖了
Harness 的生效配置是逐层叠加出来的,顺序固定:
profile 的
dsh.profile.bundles列表中各组合包的 patch,按加入顺序profile 自身的
cordis.patch.yml$DSH_HOME/cordis.patch.yml(机器级,跨 profile 共享)每个
--patch <path>overlay,按命令行参数顺序
有两条规则必须记住:
后应用的层按行 id 覆盖前者
被覆盖时,整行的
config被替换,而不是深度合并
第二条是踩坑重灾区。假设某个组合包给 id: hello 提供了三个配置项,你在 profile 的 cordis.patch.yml 里只想改其中一个,于是只写了那一个——结果另外两个不是「保持原值」,而是直接消失、回落到 schema 默认值。表现出来就是「我明明只改了一个开关,怎么整个插件行为都变了」。
同理,第 3 层的机器级 $DSH_HOME/cordis.patch.yml 是跨 profile 共享的。你在某个 profile 里改了半天没反应,很可能是很久以前在机器级 patch 里写过同一个 id,把它整行盖住了。
排查动作:--dump-config 的输出里搜你的行 id,看它最终落在哪个层标记之下。如果不是你以为的那一层,就是被覆盖了。同时检查 $DSH_HOME/cordis.patch.yml 有没有同名 id。
五、原因四:从 GitHub 装的包缺少构建产物
dsh plugin add github:you/hello-plugin 这种装法有个隐藏前提:git 安装拉取的是源码,不是构建产物。
如果包是 TypeScript 写的、发布时依赖 prepare 脚本构建,那么在 pnpm ≥ 10 下,构建脚本默认不会执行——需要用户在 profile 的 pnpm-workspace.yaml 里显式授权:
allowBuilds:
dsh-hello-plugin: true加完之后要重新执行一次 add,光加配置不重装是没用的。
官方在这里给了一句提醒:这项授权意味着「允许该包的代码在安装时于你的机器上执行」,因此建议锁定 commit:
dsh plugin --profile demo add github:you/hello-plugin#<sha>如果你是插件作者、不想让用户做这一步授权,有两条路:发布到 npm,或者用 pnpm pack 打成 tarball 让用户装预构建代码:
dsh plugin add ./hello-plugin-0.1.0.tgz排查动作:去 profile 的 node_modules 里看该包有没有 lib/(或 package.json main 指向的那个产物)。目录空着就是构建没跑。
六、原因五:配置 schema 校验没过,插件直接加载失败
Harness 插件的配置是强校验的。插件需要导出一个 Config 类型和同名的 Schemastery schema:
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
export const name = 'validated-plugin'
export interface Config {
apiKey: string
timeout: number
mode: 'fast' | 'accurate'
}
export const Config = Schema.object({
apiKey: Schema.string().required(),
timeout: Schema.number().default(30000),
mode: Schema.union(['fast', 'accurate']).default('fast'),
})
export function apply(ctx: Context, config: Config) {
// config is validated and type-safe.
}加载时「Cordis 会通过导出的 schema 校验配置,并填充未提供字段的默认值」;一旦「配置不合法,插件会加载失败并给出明确错误信息」。
所以「插件不生效」在这一类里其实是「插件加载失败」,日志里有明确报错,只是很多人没去看。少写一个 required() 字段、把 mode 写成枚举之外的值,都会直接让整个插件不上线。
还有一个作者侧的坑:不要导出普通对象作为 Config,因为它不满足 Cordis 要求的 Standard Schema 接口。写成普通对象时类型看着没问题,运行时却校验不起来。
七、原因六:改了设置但没重启,以及两处「不算故障」的情况
设置改了不生效,可能是设计如此。 Harness 的用户设置按三层解析:schema 默认值 → 注册方的组合 base → 用户分节。每个 namespace 注册时带一个 applies 字段:
type SettingsApplies = 'live' | 'restart'官方对它的定性很直白:「applies 是 UI 提示而非机制:restart 的 owner 从不 watch,其值在构造期读取一次,配置界面可为待生效变更加标。」
翻译过来就是:live 的项通过 watch 即时生效;restart 的项只在启动时读一次,你在界面上改完,值确实存下去了,但正在运行的实例根本不会去读——必须重启。这不是 bug。
另外要分清两个东西:组合配置留在 cordis.yml,settings 的 namespace 只承载用户可编辑的那一子集。想改的东西如果属于组合层,在设置界面里是找不到的,得去改 patch。
--help 时部分行不激活也是正常的。 官方明确说明:挂载了依赖 cmdlineArgs 服务的行,遇到 --help 时该服务不会发布,相关行也不会激活——这属正常现象而非故障。拿 --help 的输出去判断插件有没有装上,会得到错误结论。
八、一个高频子场景:自定义模型提供方配了却不出现
「插件不生效」的报障里,有相当一部分其实是自定义模型提供方没配对。这块的规则和插件层是分开的,单独说:
配置文件是 $DSH_HOME/settings.yaml,API 密钥单独存放在 $DSH_HOME/.credentials.yaml,且密钥是「只写」的——保存后界面只显示脱敏描述符,你没法回读校对,所以填错了不会有任何提示。
一个典型的自定义提供方配置:
llm-pi-ai:
providers:
my-gateway:
apiKeyEnv: GATEWAY_API_KEY
api: openai-completions
baseURL: https://gateway.example/v1
models:
- id: my-model这里有三个必须核对的点:
Provider ID 是永久的——官方原话是「请求、已保存会话、模型默认值和凭据引用都会使用它」。想改名只能新建再删旧的;显示名称、URL、协议、凭据和模型都可以事后编辑,唯独 ID 不行。
input/compat声明只是断言,不是检查——官方明确说这是「对你端点的断言,而不是对它的检查」。你声明支持图片输入而端点实际不支持,Harness 不会拦,请求会被提供方直接拒绝,表现为「配好了但一用就报错」。空值的 compat 键会被拒绝而非忽略——理由是「空值会抹掉 catalog 已知的信息,却又没有给出任何替代」。未知的模态或开关名同样会被拒绝,报错会列出可用选项。
以国内的模型平台为例,七牛云 AI 大模型推理服务的官方接入文档给出的三项参数是:API 地址填 https://api.qnaigc.com/v1,模型 ID 需在控制台模型广场查询后填入「模型目录」,API Key 在控制台单独获取。这三项里最容易出错的是模型 ID——各平台的 ID 命名不一致,凭印象手写几乎必错,一定要从平台的模型列表里复制。
九、完整排查清单
按顺序走,命中即停:
十、几条能少走弯路的习惯
改任何 patch 之后都先 --dump-config 再启动。 这条命令只验证不启动,成本极低,却能把「配置写错」和「代码有 bug」这两类问题在启动前就分开。
开发期用 --patch overlay,不要直接改 profile 的文件。 overlay 在叠加顺序里排最后,改坏了删掉命令行参数即可复原,不会污染 profile。修改 patch 里某个插件的 config 后,「框架会卸载旧实例并加载新实例」,且「由于注册都属于 effect 并会自动清理,替换后不会保留旧实例的注册」——热替换是干净的,可以放心反复改。
写插件时把可调参数都放进 Config schema。 官方给的检验标准是一句话:「能否在 cordis.yml 中改变这个值,而不需要修改代码?」硬编码的超时、路径、开关,日后都会变成别人排查「插件不生效」时的黑箱。
别手写 profile manifest。 profile 的 package.json(含 dsh.profile.bundles 有序列表)和 cordis.patch.yml 由 dsh plugin add 自动生成和维护,官方明确说这两个文件「从不需要手写」。手动编辑最容易把 bundles 顺序搞乱,而顺序直接决定覆盖结果。
延伸阅读
打包与安装插件(含官方排查清单):https://deepseek-harness.github.io/deepseek-harness/develop/basic/publish
插件配置与 Schema 校验:https://deepseek-harness.github.io/deepseek-harness/develop/basic/config
配置自定义模型提供方:https://deepseek-harness.github.io/deepseek-harness/guide/providers
用户设置分层与 applies 语义:https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/settings
DeepSeek Harness 配置接入 AI 大模型推理:https://developer.qiniu.com/aitokenapi/13550/deepseek-harness-configuration-access-ai