delltig/dsh-multi-model-collab: Assign a task to multiple large models simultaneously

Introduction

When developing Agents using DSH (DeepSeek Harness), a common practice is to have a single model complete tasks serially. When encountering scenarios where answers might be unstable, require a second opinion, or need cross-validation, one must manually write orchestration logic in the outer layer: sending multiple requests, merging results, and handling partial failures. delltig/dsh-multi-model-collab encapsulates this action into a plugin: it registers a multi_model_run tool for the current Profile. When the main Agent invokes it, the plugin initiates multiple model calls in parallel (or based on primary/secondary roles) internally via the ctx.llm service, aggregates them according to the configured strategy, and returns “independent results for each model + aggregated summary”. Below, we introduce what it does, how to install it, and how to configure it.

Plugin Positioning

This is a DeepSeek Harness (dsh) plugin. The repository is delltig/dsh-multi-model-collab. The author field in package.json is applestore, version 0.1.0, under the MIT license. The README’s self-positioning is clear: it does not modify the DSH source code but is a pure Cordis plugin mount, relying on the ctx.tools.register and ctx.llm interfaces.

It solves the task of “letting N models answer a task once each”, rather than delegating to sub-Agents that come with tools, sessions, and multi-step processes. The plugin explicitly states that it does not hardcode any API keys, endpoints, or model lists; models are all routed using <provider>/<model> as reported by ctx.llm.listProviders(). A single model failure will not cause the entire tool call to fail; execute only throws an exception when all models fail. When the caller cancels, any ongoing model calls are cancelled as well.

Core Features

The main capability is registering the multi_model_run tool. When the main Agent invokes it, the plugin initiates multiple model calls internally based on the configuration, aggregates them by strategy, and returns the independent results of each model and the aggregated summary to the main Agent.

There are two execution modes:

  • parallel — Each model independently answers the same task, all running in parallel. Suitable for scenarios requiring a second opinion, cross-vendor consistency, or where answers might diverge.
  • primary_secondary — The primary model answers first; the remaining models receive “original task + primary draft + secondaryInstruction” to perform review, correction, and completion. Suitable for scenarios requiring a “reviewer” or where a single point of failure is risky. When the primary model fails, it automatically degrades to each secondary model answering independently rather than failing the entire operation.

There are three aggregation strategies:

  • all — Returns all results as-is without selection (the answer field is an empty string). This is the default value because divergence itself is often the information the main Agent needs.
  • vote — First normalizes the text (case, punctuation, whitespace, Markdown symbols) and then groups them; if there is a strict majority, the representative of that group is taken; if there is no strict majority, it falls back to the longest answer, and the summary explains the actual vote count and the reason for the fallback.
  • longest — Takes the longest successful answer as the answer.

render() renders these into a Markdown block (one section per model + ## aggregate summary + optional ## selected answer), which is the text read by the main Agent.

Installation and Activation

Method A: Install to Profile (long-term effect). Execute the following inside the plugin package directory (i.e., the root directory of this repository):

dsh plugin --profile desktop add .

This command installs this package into ~/.dsh/profiles/<profile>/node_modules, and the cordis.patch.yml pointed to by dsh.bundle.patch in package.json will take effect as a bundle patch layer. You can also use the DSH plugin manager within a DSH session:

plugin_manager(action="install_bundle", target="<absolute path of this directory>")

Method B: --patch overlay, try without installing. The name of the inserted line in cordis.patch.yml is a relative path (./lib/index.js), and the Loader anchors it next to this patch file, so no installation is required to mount it:

dsh web --patch ./cordis.patch.yml
# If managing dsh with pnpm:
pnpm dsh web --patch ./cordis.patch.yml
# Do not take a port or open browser automatically:
dsh web --patch ./cordis.patch.yml --no-open --port 0

You can also verify it in one go with headless mode:

dsh --profile <headless-profile> --patch ./cordis.patch.yml "Run a task using multi_model_run"

Here is a pitfall: The headless template defaults the credentials line to disabled: true; if no other layer in the Profile re-enables it, the API key lookup will fail (MISSING_CREDENTIAL). The Web/Desktop Profile already enables this line, so no handling is needed.

Rebuild after modifying source code:

npm install
npm run build   # tsc -> lib/, and node --check to verify the build
npm test        # Offline behavior tests (mock ctx.llm, no network requests)

Configuration

All configurations are under the config: key in the Loader line (cordis.yml / cordis.patch.yml), and changing values does not require modifying the code. The fields and default values are as follows, consistent with the Config in src/index.ts.

Field Type Default Description
models string[] ["deepseek-official/deepseek-flash", "deepseek-official/deepseek-v4-pro"] Default model combination, format <provider>/<model>. In primary_secondary mode, the first one is the primary model.
mode parallel / primary_secondary parallel Execution mode.
aggregate all / vote / longest all Aggregation strategy.
defaultProvider string "" Which provider to use when model identifiers do not include a provider prefix; empty means auto-match by provider directory.
system string "" System prompt sent to each model; empty means not sent.
temperature number -1 Sampling temperature; negative values indicate not sending this field (use adapter default).
maxTokens number 0 Upper limit of output tokens for a single call; 0 means no limit (use adapter default).
perModelTimeoutMs number 180000 Cancellation budget for a single model call.
toolTimeoutMs number 900000 Collaboration budget for the entire tool call; 0 means use the registry default.
maxConcurrency number 4 Upper limit on the number of model calls running simultaneously.
clipChars number 12000 Truncation length for text returned by each model; 0 means no truncation. The untruncated length is still recorded in chars.
includeUsage boolean true Whether to report token usage for each model (when provided by the adapter).
secondaryInstruction string Review instruction (bilingual) What to ask secondary models to do in primary_secondary mode.

The default value for models is an example combination. The actual models run depend on which routes listProviders() reports in the current Profile, so the defaults may not all be resolvable.

Tool Usage

The tool name is multi_model_run. The input parameters are as follows.

Parameter Required Description
task Yes The task/question to execute. Each model only sees this text and not the current session; it must provide its own context.
models No The list of models to use for this call, overriding the configured models.
mode No The execution mode for this call, overriding the configuration.
aggregate No The aggregation strategy for this call, overriding the configuration.
system No The system prompt for this call, overriding the configuration.

task is the only required parameter, meaning you must write the full context into task before calling. The return structure is as follows (excerpt):

{
  "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   …"
}

Boundaries for failure and cancellation:

  • A single model fails (adapter error, exception thrown, no termination chunk, timeout) → That entry has status: "failed" + error.code; other models continue; the tool call as a whole is still successful.
  • All models fail → execute throws an error, with code: message for each model in the error message, and code: "ALL_MODELS_FAILED".
  • exec.signal is triggered → Any initiated model calls are cancelled; if none succeed, ABORTED is thrown.
  • Model reference cannot be resolved (e.g., only wrote deepseek-flash but no match in directory, and defaultProvider is not configured) → Throws an error listing all currently available provider/model routes.

Verification

Offline behavior tests do not send network requests and consist of 21 test cases:

npm test

Coverage includes plugin contracts, defineTool parameter/output schema compilation, parallel execution, failure isolation, throwing on all failures, vote/longest aggregation, review prompts and primary model failure fallback in primary_secondary, model reference resolution (including directory lookup and error messages), text truncation, cancellation, and missing termination chunks.

End-to-end tests (real models, requiring configured vendors):

npm run e2e        # 4 real scenarios, approx 2 minutes, consumes real tokens

It builds or reuses a temporary headless profile (default mmcollab), mounts ./cordis.patch.yml to let the real Agent call multi_model_run, and asserts four things: both parallel dual models return, single model failure is isolated, the secondary models in primary_secondary truly reviewed the draft, and the tool call itself throws an error when all fail.

Equivalent command for manual single run (requires creating a temporary headless profile first):

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."

Here is a point often missed: checking the exit code alone is not enough. The Loader only prints a single stderr warning for lines that failed to activate the plugin, but the process will still exit 0. Verification must assert the appearance of tool_call / tool_result in the --json output.

Use Cases and Notes

Suitable scenarios: requiring a second opinion, needing to compare answers from different vendors for the same question, or wanting one model to produce a draft while another reviews and proofreads it. It does not handle delegation tasks that involve tools and multi-steps — that should be handled by the sub-Agent mechanism. The README explicitly states in the design notes that this plugin uses ctx.llm.stream() instead of reusing sub-Agents, because the former only has one model request per call without a session, toolset, or context; while the latter has its own loop and context for each sub-Agent, making it suitable for tasks requiring the model to research and then answer.

Points to note before use:

  1. The plugin runs with the permissions of the current DSH process; check the source code and license (MIT) before installing.
  2. These calls are not complete Agent turns; they do not write to the session, and will not be counted by plugins that track by session (e.g., cost-meter), because GenerateOptions.sessionId is not set. The actual usage for each model can be seen in the usage field in the return (when includeUsage: true).
  3. There is no tool loop; models can only get the task text and cannot search for information or execute commands themselves.
  4. vote is a strict majority after text normalization, not semantic voting. Open-ended answers rarely match word-for-word, so it usually falls back to the longest answer, which is explained in summary.
  5. By default, the total length is at most 12,000 characters multiplied by the number of models. If there are many models or long answers, reduce clipChars.
  6. The README specifies testing on dsh 0.2.1 (CLI rc.2 / Desktop packaged runtime rc.3), and the package.json compatibility is declared as >=0.2.0-rc.1 <0.3.0-0.
  7. There is a project with the same name but unrelated to this one on GitHub (zanapeng/dsh-multi-model-collab, created in August 2026); it hardcodes a fixed four-stage pipeline. This plugin is not a pipeline but a general-purpose tool; do not install the wrong one when selecting.

Conclusion

The value of this plugin lies in transforming “asking multiple models for one task” from a manual orchestration effort into a configuration item plus a single tool call within the Profile, while handling edge cases such as partial failures, cancellations, and aggregation strategies. If you are already doing model comparisons or cross-validation on DSH, you can first try mounting it via Method B using --patch, and then decide whether to install it to the Profile.

Directory: https://www.skillhub.cn/plugins/delltig/dsh-multi-model-collab
GitHub: https://github.com/delltig/dsh-multi-model-collab

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye