Bundle
dsh-auto-model-router
DSH 插件:按任务复杂度在 deepseek-v4-flash 与 deepseek-v4-pro 之间自动路由(只升不降)
- Source
- JianTG
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 5 days ago
Readme
# dsh-auto-model-router
DSH(DeepSeek Harness)cordis 插件:按任务复杂度在 `deepseek-v4-flash`(基线)与 `deepseek-v4-pro`(升级)之间**自动路由**,只升不降,绝不回合中途切换模型。
## 效果
- 简单问题(寒暄/单点问答/短消息)→ **flash**(便宜、快)
- 复杂任务(代码/架构/重构/长任务/关键词命中)→ **自动升 pro**(不干砸重活)
- 拿不准的"中间态"消息 → 让 flash 分类器确认一次(3s 超时兜底,失败=不升级)
- 你手动 `/model` 选 pro → **永远终局**,插件绝不覆盖
- 路由决策全自动,回合内锁定,重启/恢复后可重放
## 安装
```bash
# 1. 装进 web profile(自动注册,无需改任何配置文件)
dsh plugin --profile web add <本目录路径或 git 地址>
# 2. 激活前置:把默认模型改成 flash(任选其一)
# a) 界面上用 /model 选一次 DeepSeek-V4-Flash
# b) 或编辑 ~/.dsh/settings.yaml:
agent-default-model:
provider: deepseek-official
model: deepseek-v4-flash
reasoningEffort: off
# 3. 重启 dsh web
```
卸载/更新:`dsh plugin --profile web remove|update dsh-auto-model-router`。
> 本机注意:npm/pnpm 联网需 `NODE_OPTIONS=--use-system-ca`(本机 Node 自带 CA 证书过旧)。
## 配置(`~/.dsh/settings.yaml` 的 `auto-model-router` 段,热更新)
```yaml
auto-model-router:
enabled: true # 总开关
baseProvider: deepseek-official
baseModel: deepseek-v4-flash # 基线模型(插件只管理这一对的升级/回落)
proModel: deepseek-v4-pro # 升级目标
upgradeReasoningEffort: high # 升级到 pro 时的推理强度(off/high/max)
classifierEnabled: true # 中间态是否调用 flash 分类器
classifierTimeoutMs: 3000
lengthStrong: 400 # 去空白长度 ≥ 此值 → pro
lengthWeak: 40 # 去空白长度 ≤ 此值 → flash
keywordsStrong: [重构, 架构, 设计, 排查, 分析, 审计, 优化, 测试, 实现, 开发, 修复, bug, 多文件, 批量, review, 迁移, 方案, implement, debug, refactor, migrate, migration, optimize, architecture, deploy, plugin]
```
## 原理(摘要)
两个 prepend 瀑布监听器 + 自有 sidecar(完整设计见 `DESIGN.md`,红队评审留痕见 `docs/red-team-review.md`):
```
agent/pre-step(每回合第一步)→ 启发式分类 → 中间态问 flash → 决策落盘 sidecar
agent/request(prepend 最外层)→ 读决策:pro 且基线 flash → 升级;flash 且上回合系插件升级 → 回落
```
关键语义:
- **排序**:插件以 `{ prepend: true }` 注册在最外层;内置模型选择层(`installModelSelection`)恒在内层——插件返回值生效,且绝不覆盖你的手动选择(只认"默认模型=flash"时回落)。
- **痕迹 sidecar**:`~/.dsh/plugins/auto-model-router/<sessionId>.jsonl`(`withFileLock` 串行写 + 原子替换)。**绝不写 session 日志**——DSH 的事件类型是封闭白名单,写未知类型会让会话重启后无法加载。
- **可重放**:决策按 (sessionId, turn) 幂等重算,HMR/重启后回合内决策不变。
- **失败安全**:插件任何异常 → 透传原配置,主请求照常;下游异常原样传播。
## 已知边界(文档化)
1. `agentDefaultModel` 服务缺失的 profile(理论上)→ 只升级不回落。
2. 手动 `/model` 选 pro 时若"保存为默认"失败(低概率)→ 下一回合可能被回落覆盖;重启后此边界消失。
3. 分类器调用是**真实计费**但不进界面 token 统计(meter 只统计会话事件);分类调用账本 = sidecar 中 `source: classifier` 的记录。
4. subagent/后台任务同样按各自消息自动路由(重活自动上 pro)。
5. 启发式是**强信号优先**:消息里带代码块(```)或命中强关键词就会判 pro,哪怕正文只是一句查询——这是有意为之(重活宁上 pro,不干砸),规则见 `lib/classify.js`。
## 验证与审计
```bash
node --test # 单元测试(28 条)
node scripts/experiment.mjs <会话日志> # 用真实消息评估启发式命中率
node scripts/inspect-session.mjs <日志> # 解码会话日志,按回合打印实际模型
node scripts/summarize.mjs [sidecar目录] # 汇总 sidecar:pro/flash 占比、分类器调用次数
node scripts/e2e-web.mjs <url> <cwd> # 全自动 web 端到端(需测试实例)
node scripts/e2e-restart.mjs prepare|verify … # 跨重启回落验收
```
**路由审计**:sidecar 每条记录含 `{sessionId, turn, decision, source}`——对照 `inspect-session.mjs` 的每回合实际模型,即可人工复核每一次自动路由是否正确;误判的规则可随时在 settings 里调整(改完热更新生效)。
## 开发
```bash
pnpm install # 本机需 NODE_OPTIONS=--use-system-ca
node --test
```
提交信息遵循项目仓库规范(中文、单变更集)。插件以 `link:` 方式装进 profile,改代码后重启 `dsh web` 即生效。
Install
dsh plugin --profile web add github:JianTG/dsh-auto-model-router
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-auto-model-router from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.