前言¶
在 DSH(DeepSeek Harness)上写 Agent 时,常见的做法是让一个模型串行地把活干完。遇到答案可能不稳定、需要第二个意见,或者需要交叉验证的场景,就得自己在外层写编排逻辑:发多次请求、合并结果、处理部分失败。delltig/dsh-multi-model-collab 把这个动作收进了一个插件:它向当前 Profile 注册一个 multi_model_run 工具,主 Agent 调用时,插件内部通过 ctx.llm 服务并行(或按主/副角色)发起多次模型调用,再按配置的策略聚合,返回「每个模型的独立结果 + 聚合摘要」。下面介绍它做了什么、怎么装、怎么配。
插件定位¶
这是一个 DeepSeek Harness(dsh)插件,仓库为 delltig/dsh-multi-model-collab,package.json 里 author 字段为 applestore,版本 0.1.0,MIT 许可证。README 的自我定位很明确:不修改 dsh 源码,是纯 Cordis 插件挂载,靠 ctx.tools.register + ctx.llm 两个接口工作。
它解决的是「一个任务让 N 个模型各答一遍」这件事,而不是带工具、带会话、多步的子 Agent 委派。插件明确写了它不硬编码任何 API Key、端点或模型清单,模型一律用 ctx.llm.listProviders() 报告的 <provider>/<model> 路由;单个模型失败不会让整个工具调用失败,只有全部模型失败时 execute 才抛异常;调用方取消时,仍在进行的模型调用一并取消。
核心功能¶
主要能力是注册 multi_model_run 工具。主 Agent 调用它时,插件内部按配置发起多次模型调用,再按策略聚合,把每个模型的独立结果和聚合摘要返回给主 Agent。
执行模式有两种:
parallel——每个模型独立回答同一个任务,全部并行。适合要第二意见、要跨供应商一致性、答案可能有分歧的场合。primary_secondary——主模型先答;其余模型拿到「原始任务 + 主模型草稿 +secondaryInstruction」,做审阅、纠错与补全。适合需要一个”审稿人”、单点判断风险高的场合。主模型失败时自动降级为各副模型独立作答,而不是整体失败。
聚合策略有三种:
all——原样返回全部结果,不做选择(answer为空串)。这是默认值,理由是分歧本身往往是主 Agent 需要的信息。vote——先把文本归一化(大小写、标点、空白、Markdown 符号)再分组;严格多数则取该组代表,无严格多数则回退到最长答案,并在summary里说明实际票数与回退原因。longest——取最长的成功答案作为answer。
render() 把这些渲染成一段 Markdown(每个模型一节 + ## aggregate 摘要 + 可选 ## selected answer),主 Agent 读到的就是这段文本。
安装与启用¶
方式 A:安装到 Profile(长期生效)。在插件包目录内(即本仓库根目录)执行:
dsh plugin --profile desktop add .
该命令把本包装进 ~/.dsh/profiles/<profile>/node_modules,package.json 的 dsh.bundle.patch 指向的 cordis.patch.yml 会作为 bundle patch 层生效。也可以用 dsh 的插件管理器,在 dsh 会话内调用:
plugin_manager(action="install_bundle", target="<本目录绝对路径>")
方式 B:--patch 覆盖层,不安装直接试。cordis.patch.yml 里插入行的 name 是相对路径(./lib/index.js),Loader 会把它锚定在该 patch 文件旁边,所以无需安装即可挂载:
dsh web --patch ./cordis.patch.yml
# 若用 pnpm 管理 dsh:
pnpm dsh web --patch ./cordis.patch.yml
# 不抢端口、不自动开浏览器:
dsh web --patch ./cordis.patch.yml --no-open --port 0
也可以一次性 headless 验证:
dsh --profile <headless-profile> --patch ./cordis.patch.yml "用 multi_model_run 跑一个任务"
这里有一个坑:headless 模板默认把 credentials 行设为 disabled: true;如果 Profile 没有其它层重新启用它,API Key 会查找失败(MISSING_CREDENTIAL)。Web / Desktop Profile 本来就启用了该行,无需处理。
改源码后重新构建:
npm install
npm run build # tsc -> lib/,并 node --check 校验产物
npm test # 离线行为测试(假 ctx.llm,不发网络请求)
配置¶
所有配置都在 Loader 行(cordis.yml / cordis.patch.yml)的 config: 下,改值不需要改代码。字段与默认值如下,与 src/index.ts 的 Config 一致。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
models |
string[] |
["deepseek-official/deepseek-flash", "deepseek-official/deepseek-v4-pro"] |
默认模型组合,格式 <provider>/<model>。primary_secondary 模式下第一个是主模型。 |
mode |
parallel / primary_secondary |
parallel |
执行模式。 |
aggregate |
all / vote / longest |
all |
聚合策略。 |
defaultProvider |
string |
"" |
模型标识不带 provider 前缀时使用哪个 provider;空则按各 provider 的目录自动匹配。 |
system |
string |
"" |
发给每个模型的 system prompt;空则不发。 |
temperature |
number |
-1 |
采样温度;负数表示不发该字段(用适配器默认)。 |
maxTokens |
number |
0 |
单次调用的输出 token 上限;0 表示不限制(用适配器默认)。 |
perModelTimeoutMs |
number |
180000 |
单个模型调用的取消预算。 |
toolTimeoutMs |
number |
900000 |
整个工具调用的协作预算;0 表示交给注册表默认。 |
maxConcurrency |
number |
4 |
同时在飞的模型调用数上限。 |
clipChars |
number |
12000 |
每个模型返回文本的截断长度;0 表示不截断。未截断长度仍记录在 chars。 |
includeUsage |
boolean |
true |
是否上报每个模型的 token 用量(适配器提供时)。 |
secondaryInstruction |
string |
审阅指令(中英双语) | primary_secondary 模式下要求副模型做什么。 |
models 的默认值是示例组合,实际跑什么模型取决于当前 Profile 里 listProviders() 报告了哪些路由,所以默认值未必都能解析。
工具用法¶
工具名 multi_model_run。入参如下。
| 参数 | 必填 | 说明 |
|---|---|---|
task |
是 | 要执行的任务/问题。每个模型只看到这段文本,看不到当前会话,必须自带上下文。 |
models |
否 | 本次使用的模型列表,覆盖配置的 models。 |
mode |
否 | 本次执行模式,覆盖配置。 |
aggregate |
否 | 本次聚合策略,覆盖配置。 |
system |
否 | 本次 system prompt,覆盖配置。 |
task 是唯一必填项,这意味着调用前要在 task 里把上下文写全。返回结构如下(节选):
{
"task": "…",
"mode": "parallel",
"aggregate": "all",
"models": ["deepseek-official/deepseek-flash", "deepseek-official/deepseek-v4-pro"],
"succeeded": 2,
"failed": 0,
"results": [
{
"provider": "deepseek-official",
"model": "deepseek-flash",
"id": "deepseek-official/deepseek-flash",
"role": "peer",
"status": "ok",
"text": "…",
"clipped": false,
"chars": 12,
"durationMs": 1840,
"error": null,
"usage": { "inputTokens": 10, "outputTokens": 5, "totalTokens": 15 }
}
],
"answer": "",
"summary": "mode=parallel aggregate=all models=2 ok=2 failed=0 elapsed=2140ms\n- OK …"
}
失败与取消的边界:
- 单个模型失败(适配器报错、抛异常、无终止 chunk、超时)→ 该条
status: "failed"+error.code,其余模型继续;工具整体仍然成功。 - 全部模型失败 →
execute抛错,错误信息里带每个模型的code: message,并带code: "ALL_MODELS_FAILED"。 exec.signal触发 → 已发出的模型调用被取消;若一个都没成功则抛ABORTED。- 模型引用无法解析(例如只写了
deepseek-flash但目录里匹配不到、又没配defaultProvider)→ 抛错并列出当前所有可用的provider/model路由。
验证¶
离线行为测试不发网络请求,共 21 个用例:
npm test
覆盖插件契约、defineTool 参数/输出 schema 编译、并行执行、失败隔离、全失败抛错、vote/longest 聚合、primary_secondary 的审阅 prompt 与主模型失败降级、模型引用解析(含目录查找与报错文案)、文本截断、取消、缺终止 chunk。
端到端测试(真实模型,需要已配置的供应商):
npm run e2e # 4 个真实场景,约 2 分钟,会消耗真实 token
它会自建/复用临时 headless profile(默认 mmcollab),挂 ./cordis.patch.yml 让真实 Agent 调用 multi_model_run,并断言四件事:并行双模型都返回、单模型失败被隔离、primary_secondary 的副模型真的审阅了草稿、全部失败时工具调用本身报错。
手工单跑的等价命令(需要先造临时 headless profile):
node "<dsh>/lib/bin.js" --profile mmcollab --from-default-profile headless --dump-config
node "<dsh>/lib/bin.js" --profile mmcollab \
--patch ./verify/patch-forms/enable-credentials.yml \
--patch ./cordis.patch.yml --json \
"Call the multi_model_run tool exactly once with task='Reply with exactly the single word PONG and nothing else.' and omit every other argument. Then print the tool result verbatim."
这里有个容易漏的点:只看退出码不够。Loader 对没能激活的插件行只打印一条 stderr 警告,进程照样 exit 0。验收必须断言 --json 输出里出现 tool_call / tool_result。
适用场景与注意¶
适合的场景:需要第二意见、需要跨供应商对同一个问题的答案做对比,或者想让一个模型产出草稿、另一个模型审校的场合。它不承担带工具、多步的委派任务——那应该用子 Agent 机制。README 在设计说明里明确写了,这个插件走 ctx.llm.stream() 而不是复用子 Agent,因为前者的每条调用只有一个模型请求,没有会话、工具集和上下文;后者的每个子 Agent 有自己的 loop 和上下文,适合需要模型自己查资料再回答的任务。
使用前要注意几点:
- 插件以当前 dsh 进程的权限运行,安装前应检查源码与许可证(MIT)。
- 这些调用不是完整 Agent 回合,不会写入 session,也不会被按会话计量的插件(如 cost-meter)统计,因为
GenerateOptions.sessionId没有设置。每个模型的实际用量看返回里的usage字段(includeUsage: true时)。 - 没有工具循环,模型只能拿到
task文本,不能自己检索或执行命令。 vote是文本归一化后的严格多数,不是语义投票。开放式答案很少逐字一致,通常会回退到最长答案,并在summary里写明。- 默认每个模型最多 12000 字符乘以模型数,模型多或答案长时调小
clipChars。 - README 写明实测于 dsh
0.2.1(CLI rc.2 / 桌面打包运行时 rc.3),package.json声明兼容性为>=0.2.0-rc.1 <0.3.0-0。 - GitHub 上存在一个同名但无关的项目
zanapeng/dsh-multi-model-collab(2026-08 创建),它把固定的四段流水线写死;本插件不是流水线,而是一个通用工具,选型时别装错。
结尾¶
这个插件的价值在于把”一个任务问多个模型”从一个需要自己搭的编排动作,收敛成 Profile 里一个配置项加一次工具调用,同时把部分失败、取消、聚合策略这些边界情况处理掉了。如果你已经在 DSH 上做模型对比或交叉验证,可以先按方式 B 挂 --patch 试一次,再决定要不要装到 Profile。
目录页:https://www.skillhub.cn/plugins/delltig/dsh-multi-model-collab
GitHub:https://github.com/delltig/dsh-multi-model-collab