为 DSH 提供模型分档路由:按会话把辅助请求与子任务分流到轻量模型,主对话与复杂任务走强档,跨 provider 生效。
ⓘ 此插件是大仓库 biociao/dsh-science 的子包,星数与活跃度统计的是整个仓库。
- 语言
- JavaScript
- License
- MIT
- 分支
- main
安装
$ dsh plugin --profile web add dsh-model-tier在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程
对话式安装
帮我安装 DeepSeek Harness 插件 biociao/dsh-science/packages/dsh-model-tier:先查看仓库 https://github.com/biociao/dsh-science.git 确认安全性,然后执行安装命令并验证插件加载成功。
把这段指令粘贴给 DSH Web GUI 里的助手,由它代你完成安装与验证。
一句话定位
为 DeepSeek Harness 提供的会话级模型分档路由插件:把会话里的辅助请求(标题/压缩)和子任务自动分流到轻量模型,把复杂任务升级到强档模型,主对话按档位方案走,每个档位可以指向不同 provider。
核心能力
- 在会话模型选择器里注册虚拟 provider「智能分档」,把多个分档方案变成可选"模型",按会话 opt-in 启用路由,未选中的会话完全不受影响
- 按请求类型自动决策档位:辅助请求(会话标题、压缩)走轻档,子任务走轻档,主对话走主力档
- 按规则升级到强档:可选「最近用户消息超过 N 字符」「子代理链路深度 ≥ N」触发强档,把长输入与深链推理自动交给更强的模型
- 可选 LLM 前置分类器:用一个小模型在结构规则之上把任务判为 light/default/strong,按 (sessionId, 消息哈希) 缓存,每轮只分类一次,失败/超时自动回落结构档位
- 多方案预设 + 热重载:设置页可保存多套分档方案,配置文件按 mtime 热加载,无需重启 profile
- 跨 provider 路由:三档可分别指向不同 provider/model,档位未配置时按 default → light → strong 顺序回落,再不行透传全局默认模型
技术实现
- 语言: JavaScript(ESM,Node 内置模块)
- 关键依赖: 零第三方依赖;仅用
node:fs、node:path、node:os、node:url - 架构模式: 通过
ctx.llm.registerAdapter(["model-tier"], ...)注册进程级虚拟 LLM 适配器;订阅agent/request事件做全局默认模型回退守卫;客户端通过slots.inject注入「设置页 section」与「聊天页底部 dock」 - 入口文件:
engines/model-tier.mjs(路由引擎,导出name = "dsh-model-tier"、inject = ["llm"])+engines/model-tier-ui.mjs(设置页 HTTP 路由)+client/model-tier-ui/src/index.js(设置页 + 路由读数 UI)
适用场景
希望同时使用不同 provider 的强模型(如 DeepSeek、智谱)和便宜/本地轻模型的用户,希望在同一个会话里自动按请求类型分流模型以节省 token 成本与延迟。适合多模型协作(如主力档用旗舰、辅助档用本地小模型、复杂档用推理强模型)的开发、研究与自动化工作流场景。Claude Code 用户迁移到 DSH 后可以无缝复用「小模型处理辅助请求」的分级思路。
前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| Node | >= 18 | package.json 中 engines.node 声明 |
| DSH | 未声明 | package.json 中无 dsh 版本字段,按当前发布的 DSH 宿主运行 |
| 平台 | 跨平台 | 服务端无平台相关原生模块;客户端声明 dsh.client.platform: "web" |
| 原生模块 | 无 | 仅依赖 Node 内置模块(fs/path/os/url) |
安装方式
dsh plugin --profile web add dsh-model-tier
配置项
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
tiers.strong | 对象 | 强档 {provider, model, reasoningEffort?},用于复杂任务(深链子任务 / 超长输入) | 未配置 → 回落主力档 |
tiers.default | 对象 | 主力档,被选中会话的主对话走此档 | 未配置 → 回落轻档/强档 |
tiers.light | 对象 | 轻档,用于辅助请求(标题/压缩)+ 子任务 | 未配置 → 回落主力档 |
routing.auxiliary | 字符串数组 | 视为辅助请求的 purpose 列表,自动走轻档 | ["session-title", "compaction"] |
routing.subagents | 字符串 | "light" 表示子任务路由到轻档;其它值不路由 | "light" |
routing.subagentDepthStrong | 数字/null | 子代理 delegationDepth ≥ N 时升级到强档 | null(关闭) |
routing.escalateOnChars | 数字/null | 最近一条用户消息文本长度 ≥ N(剥离 harness 注入的 <system-reminder> 段后)时升级到强档 | null(关闭) |
routing.classify | 对象/null | LLM 前置分类器:true/{} 启用(缺省用轻档模型分类),或 {provider, model, timeoutMs?, maxChars?, maxTokens?} 显式指定;建议选非 thinking 模型 | null(关闭) |
enabled | 布尔 | 总开关:关闭后模型选择器不再出现「智能分档」 | true |
三档全部未配置时,模型选择器不出现任何分档方案,因此示例配置可安全随包发布,不会污染不期望 provider 的环境。
常见问题
Q: 这个插件和 Claude Code 的 Opus/Sonnet/Haiku 策略是什么关系?
A: 是同一思路的 DSH 落地版:在同一会话内自动把辅助请求(标题/压缩)和子任务分流到轻量档,主对话走主力档,复杂任务按规则升级到强档,对应到 Claude Code 的 smallModel/--model-small 与任务复杂度判断。
Q: 安装后所有会话都会被自动分档吗?
A: 不会。插件只在会话模型选择器里注册一个虚拟 provider「智能分档」,需要在每个会话里手动选中「智能分档 / 某方案」才会启用路由,未选中的会话完全不受影响(按会话 opt-in,无全局路由)。
Q: 配置文件存在哪里?改了需要重启吗?
A: 写入 $DSH_HOME/model-tier.json(默认 ~/.dsh/model-tier.json)。路由引擎按文件 mtime 缓存,改动即热生效,无需重启 profile 或重开会话。
Q: LLM 前置分类器 (routing.classify) 是必须的吗?
A: 不是。它是可选增强项:开启后,每次用户提问或子任务派发前会用一个小模型把任务判为 light/default/strong,覆盖结构档位。缺省用轻档模型分类,失败/超时/乱答自动回落到结构档位(不会阻塞主请求)。
Q: 分类器目标能用 thinking 模型吗?
A: 不建议。thinking 模型的推理内容同样消耗 maxTokens 额度(默认 512,可用 maxTokens 调大),实测曾出现 thinking 占满额度、输出空字符串拿不到分类词的情况,README 明确建议选非 thinking 模型。
Q: 和 dsh-science 是什么关系?
A: dsh-model-tier 是 dsh-science 的配套包。安装 dsh-science 会自动带上 dsh-model-tier 作为依赖;但它本身完全独立,可在任何 profile 单独 dsh plugin add dsh-model-tier 使用。
上手难度
入门 — 配置只涉及三档 provider/model 的 YAML,写入 yaml 后即可在模型选择器看到「智能分档」分组并在会话里启用;进阶功能(分类器、深链升级)有清晰开关和默认值。
已知问题与限制
- 选择窗口竞态(已知限制):由于平台会把「会话模型选择」持久化为全局默认模型,引擎用
agent/request监听做 best-effort 回退;但在「选中方案」到「下一次请求」之间的极短时间窗口内新建的会话仍会继承分档方案(来源:README.zh.md:48-49、engines/model-tier.mjs:651-681) - 跨档位 thinking 兼容:当历史中混入其它档位(非 thinking / 无推理捕获)产生的工具调用助手消息,而目标档位是 DeepSeek 这类要求回传
reasoning_content的 thinking 模型时,会触发 400 INVALID_REQUEST。引擎会自动给缺失推理块的消息补占位推理内容并透明重试一次(产生 warn 日志,但只重试一次,不重放已产出的内容;来源:engines/model-tier.mjs:344-369) - 分类器缓存上限 200 项:按 (sessionId, 消息哈希) 缓存分类结果,超过上限按插入顺序淘汰旧项,长会话密集场景下偶发重复分类属正常(来源:
engines/model-tier.mjs:318-323) - 轻档模型推理等级:档位显式
reasoningEffort会原样转发;选择器的推理等级仅在目标模型声明支持时才转发,否则会被宿主以UNSUPPORTED_REASONING_EFFORT硬失败,轻档模型通常不支持推理,标题/压缩等辅助请求会随之静默回退(来源:engines/model-tier.mjs:622-639) - 未配置档位的方案不上线:若设置页保存的方案三档都未配置完整 provider/model,对应档位会按 TIER_FALLBACK 顺序回落,最终仍无目标时会抛错「无可用模型」(来源:
engines/model-tier.mjs:294-303、engines/model-tier.mjs:597-606)
A Claude Science–style research workbench for DeepSeek Harness — for genomics / pathogens / human health / bioinformatics projects.
One-liner: dsh-science — Claude Science-style research workbench for DSH: ReAct research-loop engine (research_* tools), versioned artifacts with provenance (artifact_* tools), an SSH remote-compute engine (remote_* tools, mirroring Claude Science's Computer / Remote compute clusters), and 11 science skills for genomics / pathogens / bioinformatics.
- ReAct research loop engine —
research_init/research_state/research_hypothesis/research_experiment/research_findings/research_phase/research_review/research_report, persisted in aresearch-manifest.jsonstate machine (Question → Hypothesis → Experiment → Observe → Analyze → Conclude → Next Question). - Versioned artifacts with provenance —
artifact_save/artifact_list/artifact_show/artifact_diff/artifact_verify/artifact_deprecate/artifact_reproduce: every result saved asartifacts/<name>/v<N>/with per-file SHA-256,artifact.jsonprovenance (command / inputs / environment / envFile) and an append-onlyprovenance.md. - Remote compute engine (SSH / HPC clusters) — 16 tools:
remote_host_add/remote_host_probe/remote_host_notes/remote_run/remote_status/remote_logs/remote_pull/remote_cancel/remote_execetc. Connect lab workstations or HPC clusters via~/.ssh/configaliases (nothing installed on the host, zero third-party deps). Long bioinformatics jobs run as detached processes on workstations or viasbatchon SLURM — they survive connection loss; submission asks for approval by default;remote_statusbatch-monitors and auto-transitions state (running → succeeded/failed/killed);remote_pullfetches outputs back (files over the size threshold stay on the host with their paths recorded). - Remote Hosts config UI (bundle/profile-level) — a Settings > 远程主机 page (the analog of Claude Science's Settings > Compute > SSH hosts): list/add/probe/edit/remove hosts, plus each project's access allowlist and job summary. Host-side REST API (
webServerroute/dsh-science/remote-hosts/*,engines/remote-hosts-ui.mjs) + client bundle (client/remote-hosts-ui/, built byscripts/build-client-bundle.mjs) sharing the same data files as the remote engine. Requires a web-process restart to activate (see docs/remote-hosts-ui.md). - Model Tier router (tiered, cross-provider) — via the companion bundle
dsh-model-tier: within one session, automatically routes auxiliary requests (session titles, compaction summaries) and subagent/background tasks to a light tier, keeps the main conversation on the default tier, and escalates complex work (deep subagent chains, very long inputs) to a strong tier — each tier may point at a different provider (e.g. strong GLM-5.3 / default deepseek-v4-flash / light minimax-M3), mirroring Claude Code's Opus/Sonnet/Haiku strategy. Built on DSH's nativeagent/request+llm/streamwaterfall extension points; a no-op when the tier's provider is unregistered. Installed automatically with dsh-science, but also standalone-installable into any profile (dsh plugin add dsh-model-tier). - 11 science skills — research-loop, science-project-setup, artifact-provenance, scientific-reviewer, literature-connector, parallel-delegation, manuscript-writing, bioinformatics-toolkit, conda-environments, data-inventory, remote-compute.
All in-repo engine plugins are zero-dependency (Node built-ins + the system OpenSSH binaries, sharing engines/core.mjs) and register plain cordis tools; the companion dsh-model-tier router is likewise zero-dependency. Installable either as a profile bundle (dsh plugin add) or as an agent preset (科学模式).
v0.2.0: Model Tier router (new, companion bundle)
Mirrors Claude Code's Opus/Sonnet/Haiku tiering: within one session, auxiliary requests (purpose ∈ {session-title, compaction}) and subagents (session.meta.origin === 'subagent') are routed to the light tier; the main conversation keeps its own per-session model selection (never overridden); deep subagent chains (delegationDepth ≥ subagentDepthStrong) and very long inputs (escalateOnChars, opt-in) escalate to the strong tier. An optional LLM pre-classifier (routing.classify) grades each user prompt / subtask dispatch by complexity (light / default / strong) before routing. Each tier is {provider, model, reasoningEffort?} and may span providers.
Ships as the standalone bundle dsh-model-tier — dsh-science depends on it and mounts it in its cordis.patch.yml, but it can equally be installed on its own into any profile (dsh plugin add dsh-model-tier):
- id: model-tier
name: dsh-model-tier
config:
tiers:
strong: { provider: zai-coding-cn, model: glm-5.3 }
default: { provider: deepseek-official, model: deepseek-v4-flash }
light: { provider: opencode-go, model: minimax-m2.7 }
routing:
auxiliary: [session-title, compaction]
subagents: light
subagentDepthStrong: 3
- Host plane — mounted in the profile bundle (
cordis.patch.yml), not the agent preset, so it applies to every session and subagent on the profile. - Safety rails — no
tiersconfigured → inert no-op; target provider unregistered → no routing; a failing light-tier call automatically falls back to the original route (auxiliary features never break). - Verified —
node packages/dsh-model-tier/test/model-tier.test.mjs(zero-dependency unit matrix) +bash packages/dsh-model-tier/scripts/test-model-tier.sh(E2E: light tier pointed at a local mock LLM; asserts the title request is actually routed).
v0.2.0: Remote compute (new)
Mirrors Claude Science's Remote compute clusters / Computer capability, following its documented mechanism:
- Host registration + read-only probe —
remote_host_addtakes a~/.ssh/configalias (oruser@host; ProxyJump etc. handled by OpenSSH), with optional port/identityFile overrides; probing records CPUs, memory, GPUs, CUDA driver, conda/module/Apptainer presence, scratch dirs,sbatchand SLURM partitions (remote_host_probere-runs it). Host registry:$DSH_HOME/remotes/hosts.json. - Job submission —
remote_runcopies script + inputs into<scratch>/<jobId>/(default~/dsh-scratch); workstations run it as a detachednohup+setsidprocess (connection-loss safe), SLURM clusters getsbatch(with--time); default job timeout 30 min; submission asks for approval by default (the analog of Claude Science's "Run this job on?" card). - Monitoring & reaction —
remote_statusbatch-probes (ps / squeue+sacct / done+exitcode markers) and auto-transitions state;remote_logstails logs;remote_pullfetches outputs and writespulled-manifest.json(files > 100 MB stay on the host with recorded paths);remote_cancelkills (process group / scancel). Job registry:<project>/.dsh/remotes/jobs.json, persists across sessions. - Host Details document —
remote_host_notesmaintains per-host notes (environment activation, partitions/account, conventions) that the model reads before submitting jobs. - Per-project access allowlist (allowed servers, isolated per project) — every host-connecting action (add/probe,
remote_host_probe,remote_exec,remote_run) requires the host to be in the project's allowlist (.dsh/remotes/allowlist.json) by default; first use pops an approval dialog and, on approval, persists the grant at project scope (the analog of Claude Science's "This project" approval scope). The project root resolves by priority:research-manifest.json(research project) →.dsh(workspace) →.git→ session cwd — multiple research projects in one workspace keep separate allowlists; grants never leak across projects (authorization paths fail closed when no session cwd is available). Review withremote_host_allowlist, revoke withremote_host_revoke, pre-grant withremote_host_allow(approval-gated); disable withrequireHostAccess: falsefor unattended runs.
v0.1.1 hardening (robustness update)
- Concurrency-safe state: all manifest/artifact writes go through a lightweight file lock (O_EXCL + stale reclaim) and atomic tmp+rename — parallel subagents can no longer corrupt or lose updates on
research-manifest.json/artifacts.json. - Structured error codes (
ERR_NOT_INIT/ERR_NOT_FOUND/ERR_VALIDATION/ERR_PATH/ERR_QUOTA/ERR_LOCK_TIMEOUT/ERR_IO) instead of opaque strings. - Hypothesis state machine (proposed → testing → supported/refuted/inconclusive) and forward-only phase transitions (rewind requires config).
- manifest ↔ artifacts linked:
research_statemerges the artifact index;artifact_savewrites back to the manifest. - Manifest schema v1→v2 migration on load, persisted on next write.
- Artifact upgrades: streaming SHA-256 (big files), identical-content dedup via hardlink,
artifact_diff/artifact_verify/artifact_deprecate, envFile + input hashes in provenance. - Structured JSON outputs (
research_report,artifact_diff,artifact_verify) and an audit log at<root>/.science.log.
Install
Option A — profile bundle (community standard)
dsh plugin --profile web add dsh-science # after npm publish
# or straight from GitHub:
dsh plugin --profile web add "github:biociao/dsh-science"
Restart the profile (or refresh the Web GUI). The bundle inserts the three engines
into the profile layer stack; the research_* / artifact_* / remote_* tools
become available to every agent on that profile.
Option B — agent preset (full 科学模式 experience, per-agent)
git clone https://github.com/biociao/dsh-science ~/.dsh/.agent-presets/science
# or from a local checkout:
bash scripts/install.sh # copy (or: bash scripts/install.sh link)
Then create a session in the DSH Web GUI and pick the 科学模式 preset — the preset carries the research persona + engines with per-agent scoping.
Skills
The 11 skills are discovered automatically from a project's .dsh/skills/
(drop this repo's skills/ into your project), or install them machine-wide:
bash scripts/install-skills.sh # -> ~/.dsh/skills (respects $DSH_HOME)
Quick start (first session)
research_init— createresearch-manifest.json+ the project skeleton (experiments/ literature/ artifacts/ analyses/ figures/ manuscript/ reviews/ data/ envs/).- Read
research_stateat the start of every session; the loop state persists across sessions. - Run the loop:
research_hypothesis(H1/H2/…) →research_experiment(E01/…, createsexperiments/<id>/{design.md,log.md,code/,results/}) → run code →research_findings(appends to log.md, updates hypothesis status, advances the loop) →artifact_savefor anything worth citing or reproducing. - When GPU/cluster/specialized environments are needed:
remote_host_addthe host →remote_runa background job (approval required) → pollremote_status/remote_logs→remote_pulloutputs when done →artifact_saveto archive. See docs/remote-compute.md and theremote-computeskill. - For key claims: extract the claim, have a review subagent check it against the
execution records (see the
scientific-reviewerskill), archive withresearch_review(writesreviews/R0n/report.md).
Repository layout
dsh-science/
├── package.json # dsh.bundle.patch -> ./cordis.patch.yml (+ dsh.client + exports)
├── cordis.patch.yml # bundle patch: inserts the engines by subpath export + mounts dsh-model-tier
├── packages/
│ └── dsh-model-tier/ # 配套独立 bundle:模型分档路由(可单独 dsh plugin add)
├── engines/ # canonical engine sources (bundle form)
│ ├── core.mjs # shared core: locks, atomic writes, error codes, streaming sha256, structured tools, audit
│ ├── research-loop.mjs
│ ├── artifact-registry.mjs
│ ├── remote-compute.mjs# SSH/local transports, host registry + probe, job submit/monitor/pull/cancel
│ └── remote-hosts-ui.mjs# Remote Hosts 设置页的宿主 REST API(webServer 路由)
├── client/ # client 插件(设置页 UI,bundle/profile 级)
│ └── remote-hosts-ui/ # src/index.js 源码 · lib/client.js 打包产物(build-client-bundle.mjs)
├── preset/ # agent-preset form (mirrors engines/ via sync-engines.sh)
│ ├── agent.cordis.yml # references ./engines/*.mjs (relative, preset mount)
│ ├── preset.yml
│ └── engines/ # mirror — keep in sync: bash scripts/sync-engines.sh
├── skills/ # 11 SKILL.md skills
├── scripts/
│ ├── install.sh # install preset -> ~/.dsh/.agent-presets/science
│ ├── install-skills.sh # install skills -> ~/.dsh/skills
│ ├── sync-engines.sh # mirror engines/ -> preset/engines/
│ ├── init-project.sh # project skeleton without a science session
│ ├── build-client-bundle.mjs # wrap client src -> __ModuleLoader__ bundle (lib/client.js)
│ ├── smoke-test.mjs # 125 checks against a temp workspace (node >= 18)
│ └── stability-test.mjs# 25 concurrency/atomicity/stress checks (locks, lost-update, soak, migration)
└── test/verify-bundle.sh # isolated end-to-end bundle install + boot + client scan check
Verification
node scripts/smoke-test.mjs # engine logic + end-to-end loop + error codes + migration
node scripts/stability-test.mjs # concurrency / atomicity / lock / stress stability checks
bash test/verify-bundle.sh # pnpm pack -> isolated profile -> install -> boot check
All are part of the release checklist and are safe to run in CI (both test scripts
write only to a temp workspace; the bundle test uses an isolated $DSH_HOME).
FAQ
Why subpath exports and not relative paths in the bundle?
dsh plugin add installs the package into the profile and its cordis.patch.yml
rows join the profile composition. The profile loader resolves a row name
relative to the profile directory (not the package), so ./engines/x.mjs
fails with ERR_MODULE_NOT_FOUND. Referencing dsh-science/engines/x.mjs
(subpath export, exports in package.json) resolves from the profile's
node_modules and works — verified experimentally on dsh 0.1.0-rc.6.
The agent-preset mount, by contrast, resolves relative names from the preset
directory, which is why preset/agent.cordis.yml can use ./engines/*.mjs.
Bundle or preset — which should I use?
- Bundle: tools available to every agent on the profile; one command to install.
- Preset: the full 科学模式 experience (research persona, per-agent scoping).
The persona row in
cordis.patch.ymlis commented out because a profile-wide persona would apply to all agents — uncomment it before publishing if that is what you want.
Where do the skills come from?
A project's .dsh/skills/ is auto-discovered; scripts/install-skills.sh puts
them machine-wide in ~/.dsh/skills (respecting $DSH_HOME).
Development
Branching model & release workflow (main = release, dev = integration, feat/* = features,
tag-triggered npm publish + GitHub Release via Actions): see
docs/branching.md.
bash scripts/sync-engines.sh # after editing engines/*.mjs — keeps preset/engines in sync
node scripts/smoke-test.mjs # logic + static package checks
node scripts/stability-test.mjs # concurrency / atomicity / lock stability checks
bash test/verify-bundle.sh # end-to-end bundle install + boot
Workspace-isolation patch — DSH's New Session used to fall back to the most
recently used Workspace, letting automation spawn sessions into unrelated
projects. scripts/patch-session-isolation.mjs applies the one-line guard
(idempotent, backs up first; re-apply after every dsh upgrade). See
docs/workspace-isolation.md.
node scripts/patch-session-isolation.mjs apply # idempotent, backs up first
node scripts/patch-session-isolation.mjs status # check current state
node scripts/patch-session-isolation.mjs revert # restore pristine file
Community
- Topic: github.com/topics/dsh-plugin
- Curated lists: awesome-dsh-plugin · awesome-deepseek-harness
License
MIT — see LICENSE.
收录徽章
[](https://deepseek-plugin.org/plugins/biociao/dsh-science/packages/dsh-model-tier)把这段 markdown 粘贴到你的 GitHub README,链接回本插件详情页。徽章只声明已被本站收录,不代表安全认证。