dsh-effort-router
DSH 模型分流插件:按每一轮请求的难度自动选模型与思考强度——简单问题走便宜快模型,难题才叫强模型。零 token 规则判定,只在判不准的灰区问一次小模型。发送前把本轮计划同步到输入框的模型框,座位=实际;不改你的默认模型。
- Stars
- 0
- Language
- JavaScript
- Created
- Sep 10, 2026
- Updated
- Sep 10, 2026
Introduction
🧭 dsh-effort-router
给 DeepSeek Harness 装一个"按题目难度自动换模型"的路由器。 简单问题走便宜快模型,难题才叫强模型;规则判定 零 token,只有判不准的灰区才问一次小模型。 发送前会把本轮计划同步到输入框那个模型框,所以座位显示的就是这一轮真正会用的模型。
English summary ↓ · 更新日志 · License: MIT
这是什么?
这是一个 DSH 插件(DeepSeek Harness plugin),包名 dsh-effort-router。DSH 的插件就是普通的 npm 包,通过 package.json 里的 dsh 字段向宿主声明自己:
{
"name": "dsh-effort-router",
"dsh": {
"bundle": { "patch": "./cordis.patch.yml" }, // 往宿主插件树里插一行:{ id: model-router, name: dsh-effort-router }
"client": { "platform": "web", "inject": [...] } // 另半个自己在浏览器里跑(输入框开关、设置面板)
}
}
安装到某个 profile 之后,它会挂在 agent/request 这条请求水路上,在每次真正调用模型之前决定这一轮用哪个 provider / 模型 / 思考强度。
关于名字(重要)
这个插件叫 dsh-effort-router,中文名仍是「模型分流」。原因很直白:npm 上的 dsh-model-router 是别人的包(thedeveloper256,已发到 0.7.0,是「角色制路由」,与本项目无关)。npm 不允许重名发布,所以:
- 不要执行
dsh plugin add dsh-model-router—— 那会装上别人的插件; - 用 tarball(
dsh plugin add /path/to/dsh-effort-router-0.3.0.tgz)或从本仓库安装; - GitHub 仓库名保留
dsh-model-router(改名可选,不影响使用)。
本插件的内部标识保持原样:HTTP 前缀 /api/model-router、插件树 entry id model-router、设置页 tab id。只有包名变了。
它解决什么问题
| 你的痛点 | 手动做法 | 这个插件 |
|---|---|---|
| 问个「1+1」也烧着最贵的模型 | 每次手动切 | 自动判 simple → 便宜快模型 + 低思考强度 |
| 全程用便宜模型,难题质量掉 | 猜什么时候该切 | 自动判 hard → 强模型 + MAX 思考强度 |
| 贴一张报错截图,模型不会看图 | 先手动换 vision 模型 | 图片档独立成档,思考强度按难度给(low/high/max) |
| 切来切去很烦,还怕改了自己的设置 | — | 每轮请求级覆盖:不动你的会话选择、不动你的全局默认模型 |
一句话:把"该用多贵的脑子"这件事自动化,且不留下副作用。
30 秒上手
# 从 tarball 安装(把路径换成你下载到的文件)
dsh plugin --profile web add /path/to/dsh-effort-router-0.3.0.tgz
# 或从源码目录安装
dsh plugin --profile web add /path/to/dsh-effort-router
然后 重启宿主(host 半边是 Node 进程加载的,必须重启;client 半边只需刷新页面)。
开机后你会看到:
- 输入框右侧多了一个开关:
🧭 分流:开 / 关—— 点一下即时生效,不用进设置、不用找保存按钮; - 输入框下方多了一行读数带:
🧭 本轮实际 deepseek-v4-flash@high · 档位 standard; - 设置 → 插件 → 🧭 模型分流 里有完整面板:分档映射、图片档强度、灰区判定、分类测试、最近决策。
工作原理
四个档位
| 档位 | 何时命中 | 默认目标(按你的模型目录自动选) |
|---|---|---|
| ① simple | 闲聊、单点问答、小改动 | 便宜快模型 @ low |
| ② standard | 常规问答(规则判不准时交给小模型裁定) | 便宜快模型 @ high |
| ③ hard | 原理/机制/架构/多步/长文/代码块 | 强模型 @ max |
| ④ vision | 本轮带图 | 能收图的模型,强度按难度 low / high / max |
档位映射可以完全手写覆盖(面板下拉选),留空即用"自动推荐"。
判定规则(零 token)
加分(越难越高)
| 信号 | 权重 |
|---|---|
为什么 / 原理 / 本质 / 深层 / 底层 / 机制 / 论证 / 证明 | +2 |
架构 / 设计 / 重构 / 迁移 / 优化 / 调优 / 方案 / 对比 / 取舍 / 选型 | +2 |
并发 / 分布式 / 一致性 / 事务 / 缓存 / 索引 / 性能 / 安全 / 漏洞 | +2 |
| 代码块 ``` | +2 |
帮我做 / 实现一个 / 开发一个 / 搭建 / 集成 / 部署一整套 | +2 |
多个 / 分步 / 先…再…最后 / 步骤 / 流程 / 全流程 / 端到端 | +1 |
| 文本 >800 字 → +2;>300 字 → +1 | — |
减分(越像小事越低):以 你好/在吗/谢谢/好的/ok/hi 开头 −3;是什么/多少/几/谁/哪里/哪年 −1;改一下/换成/删掉/加上/重命名 −1;短于 30 字 −1;≤16 字且非疑问句再 −1。
图片信号:带图 + 配文含 报错/日志/代码/修复/排查/定位/500 等 +2;多张图 +1;只有图没配文 +1(不会再被当成"小事")。
合计 ≥3 → hard,≤−2 → simple,其余 standard。
灰区判定(判不准时才花一次小调用)
规则落在 standard 这个最不可靠的中间带时,插件会把这一轮交给一个便宜模型判一次难度(maxTokens: 8、4 秒超时、失败即退回规则)。明显简单和明显困难的轮次一次调用都不花——所以像「为什么这段会内存泄漏?setInterval(()=>{},100)」这种"差 1 分"的句子,也能自己走到 MAX,不需要你维护任何关键词表。
每轮请求级覆盖
你发消息 ──> 客户端(开关 / 计划同步 / 读数带)
│
▼
宿主 agent/request 水路
│ ① 分类:规则 →(灰区)小模型
│ ② 记忆:手选?我们设过的?会话开局?
│ ③ 覆盖这一次请求的 provider/model/reasoningEffort
▼
实际模型调用(你的会话选择不变)
开关与取舍(重要,请先读这段)
| 开关 | 默认 | 作用 | 代价 |
|---|---|---|---|
| 启用自动分流 | 开 | 总开关。关掉后宿主直接放行,完全不碰任何东西 | — |
| 尊重手动选择 | 开 | 你手动在座位上选过的模型,插件让开(决策日志里记 skip:manual) | — |
| 记录决策日志 | 开 | 每次判定写一行 JSONL | 极小磁盘 |
| 灰区判定 | 开 | 见上,让"不靠关键词"成为可能 | standard 轮多一次小调用(约 8 token 输出) |
| 座位同步 | 无开关,始终生效 | 发送前把本轮计划写进会话选择:座位框显示的就是本轮真正要用的模型,带图轮自动落到图片档(所以文本模型会话也能直接发图) | 每轮一次会话选择写入——座位会跟着计划走,这正是设计目标;全局默认模型先读走再写回,不会被动 |
几个开关点一下立即生效(立即写盘 + 让宿主的计划缓存失效),不需要滚到面板底部再点保存。
座位 = 本轮实际(这是设计,不是巧合)
模型座位渲染的是会话选择,而分流是每轮请求级决定的。为了不让两者打架,插件在每次发送前多做一步:
你按下发送
│
├─ POST /api/model-router/preview 本轮的文字 + 图片张数(与正式判定同一套代码路径)
│ 返回:目标模型@强度,以及"这是不是你自己手选过的"
├─ 你没手选过 → 把目标写进会话选择(座位随之刷新)
│ 写之前先读走你的全局默认模型,写之后立刻还回去
└─ 真正发送(宿主的请求水路再确认一次同样的目标)
三条性质因此同时成立:
- 座位 = 本轮实际:输入框里显示什么模型,这一轮就真的用什么模型;
- 带图轮自动落到图片档:计划本身就是 vision 目标,harness 的图片准入检查自然通过(不再需要单独的"带图自动切"开关);
- 你手选的模型绝不会被覆盖:预览返回
manual标志时客户端原地不动,宿主也在同一轮跳过它,并在决策日志里记skip:manual。
输入框下方那行读数带保留——它额外显示档位(simple/standard/hard/vision),与座位同源,不会再互相矛盾。
面板一览
设置 → 插件 → 🧭 模型分流
- 📡 最近一次实际请求:档位 / 难度 / 领域 / 实际模型 / 你的输入(每 5 秒刷新,只读)
- 开关:启用 / 尊重手动选择 / 记录日志 / 带图自动切模型 / 灰区判定
- 分档映射:四个档位分别用哪个厂商、哪个模型、什么思考强度(留空=自动推荐)
- 图片档的思考强度:简单 / 常规 / 困难 三档分别给 low / high / max
- 强制关键词(可选,默认空):命中即强制某档,完全绕开打分
- 分类测试:输入一句话 + 选配图张数(0/1/2),立刻看到判定与目标模型——不用真传图
- 最近决策:时间 / 档位 / 难度·领域 / 判定依据 / 路由(
from → to)/ 你的输入
HTTP API(面板用的那几个,可脚本调用)
curl -s localhost:3080/api/model-router/state # 配置 + 目录 + 上次决策 + 运行中代码指纹
curl -s -X POST localhost:3080/api/model-router/preview \
-H 'content-type: application/json' \
-d '{"text":"这个报错怎么解决","imageCount":1}' # 只判定,不调用模型
curl -s localhost:3080/api/model-router/log?limit=30 # 最近决策
curl -s -X POST localhost:3080/api/model-router/config \
-H 'content-type: application/json' -d '{"enabled":false}' # 即时改配置
排查:这次改动到底要重启还是刷新?
插件分两半,规则完全不同:
| 改的地方 | 生效方式 |
|---|---|
lib/*.js(host 半边:路由、分类、API) | 必须重启宿主(Node 进程内存里的旧代码) |
lib/client.js(client 半边:开关、面板、读数带) | 刷新浏览器页面即可(服务端每次从磁盘读、no-cache) |
storages/dsh-effort-router/config.json(档位、开关) | 都不用,点面板即存即生效 |
自带一个自检脚本,让运行中的宿主自己报出它加载的是哪份代码:
node tools/check-reload.mjs # 默认检查 web profile 的安装目录
node tools/check-reload.mjs --src . # 顺便比一下源码与安装副本是否一致
退出码:0 什么都不用做 · 1 需要重启宿主 · 2 安装目录不对 · 3 连不上宿主 · 4 源码还没装进 profile。
开发
npm run build # 用 esbuild 把 src/client/index.jsx 打成 lib/client.js(host 半边不需要构建)
npm test # 27 个用例:判定规则 / 图片阶梯 / 灰区判定 / 开关与手选 / 图片守卫 / 座位同步 / HTTP API
目录:
lib/ host 半边(Node):index.js 挂水路,router.js 分档,classify.js 规则,
judge.js 灰区小模型,config.js 配置,api.js HTTP 面板接口
src/client/ client 半边源码(React 设置面板 + 输入框开关 + 读数带)
lib/client.js 上面这份的构建产物(浏览器加载)
tools/check-reload 「要不要重启」自检
cordis.patch.yml 往宿主插件树插入自己那一行
已知限制
- 每轮只判一次:判定发生在该轮第一个请求上,之后同轮不会重判。
- 只看本轮文本:不读历史,所以"接着上面那个问题"这类指代型追问可能只拿到 standard——补一句带信号的说明即可。
- 带图自动切模型默认关,原因见上文取舍表。
- 座位同步会让座位随每轮计划移动;若你希望座位固定在自己选的模型上,在座位上手动选一次即可——之后插件会让开(
skip:manual)。
English summary
dsh-effort-router is a DeepSeek Harness plugin that routes each request (not the session) to the cheapest model that can still do the job:
- Zero-token rule classifier with four tiers —
simple / standard / hard / vision. - Gray-zone judge: only when the rules land on the ambiguous middle band, one 8-token call to a cheap model decides — no keyword whitelists to maintain.
- Vision tier ladder: one image model, effort scaled by difficulty (low/high/max), so a holiday photo and a stack-trace screenshot stop sharing one level.
- The composer seat equals reality: before every send the client applies the turn plan to the session selection — reading and restoring your global default model around it — so the model box shows what the turn will really use, and image turns reach the vision model without a separate guard. A model you picked by hand is never overridden (logged as
skip:manual). - Instant switches: an on/off button right in the composer, applied on the very next turn.
Install: dsh plugin --profile web add dsh-effort-router-0.3.0.tgz, then restart the host.
MIT licensed.