DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-model-router

L

dsh-model-router

Discovered

DSH plugin: a decision model reads a new session's first message and picks the model that runs it, by walking an ordered chain of steps.

dsh-model-router

一个 DSH 插件:新建会话时,把用户的第一句 prompt 交给一个 SystemOne 决策模型,按一组决策流程逐级判断,再用选中的模型执行任务。

  • 决策模型说 SystemOne:本机 LM Studio(http://127.0.0.1:8848)或任何提供 /v1/systemone 的端点。
  • 决策流程(几级、每一级问什么、命中哪个选项走哪个模型、哪一级只在什么条件下才问)全部在设置页里配置,不用改代码。
  • 可以是"自动执行",也可以是"先问你":设置页有一个「允许自动选择模型并执行」开关。关掉它,判断完会先弹一张确认卡片,由你决定要不要切到它推荐的模型。
  • 没有保存过配置、或配置校验不通过时,插件完全跳过,不碰 DSH 原本选择的模型。

目录

dsh_jev_model_selector/          ← 项目目录(插件本身叫 dsh-model-router)
├── README.md                     ← 你正在读的总览
├── docs/
│   ├── 01-design.md              设计说明:为什么是「决策流程」而不是一次多选一
│   ├── 02-configuration.md       配置项完整参考
│   └── 03-verification.md        验证记录:测了什么、怎么测的、什么没测到
├── plugin/                       ← 可安装的 bundle(install_bundle 指向这里)
│   ├── package.json
│   ├── cordis.patch.yml
│   ├── index.js                  Host 半:会话钩子、决策模型调用、配置存储、设置页路由
│   ├── client.js                 浏览器半:设置页(表单 + JSON 原文 + 测试 + 最近决策)
│   ├── lib/core.js               纯逻辑:SystemOne 响应解析、决策流程引擎、校验
│   ├── lib/systemone.js          决策模型 HTTP 客户端(SystemOne 协议)
│   ├── lib/defaults.js           出厂默认配置
│   ├── locale/{zh,en}.json       插件卡片文案
│   ├── icon.svg
│   └── README.md
└── tools/                        ← 验证脚本(不是插件的一部分)
    ├── route-probe.mjs             对着真实决策模型跑路由自测
    ├── host-harness.mjs          Host 半驱动测试(含真实 HTTP)
    ├── client-smoke.mjs          设置页渲染冒烟测试
    └── inspect-session.mjs       直接解压读取会话日志

安装:plugin_manager 的 install_bundle,target 填 plugin/ 的绝对路径。 装完后在 设置 → 自动决策模型 里保存一次配置才会开始工作。


30 秒理解设计

一次「多选一」问不出正确的模型。实测(见 docs/03-verification.md)把两个互相独立的判断塞进同一个 choice,准确率明显低于拆成两段;拆开之后,每一层只回答一件事。

所以这个插件的模型是:一级步骤 = 一个问题 + 至少两个选项,每个选项自带它的结论。

{
  "question": "How large is this request?",
  "options": [
    { "key": "simple",  "description": "…short, single-step work…", "route": "lm-studio/qwen3.8-27b" },
    { "key": "complex", "description": "…long-horizon work…",       "route": "next" }
  ]
}

route 要么是一条具体的 provider/model,要么是 "next"(交给下一层继续判断)。链条:

逐层看:
  还没问过这一层            → 问它
  取概率最高的选项
  它是一条模型路由,且这一层够确定 → 定下来,结束
  否则                      → 交给下一层
所有层都没定下来             → 用兜底路由

顺序就是流程,这就是「先后顺序很重要」的答案:

  1. 优先级:排在前面的步骤先问。所以「一票否决」的判断排第一能省调用——被它拦下的 prompt 只花一次。
  2. 交给下一步:后面那一层之所以会被问到,是因为前面某个选项写了 "next"。想搞「二级/多级分类」,就让上一层的某个选项交给下一步,没有第二套条件语法。
  3. 最后一层不能交给下一步:它的每个选项都必须是具体路由(校验错误,不是警告)。所以链条一定会给出结论——某一层的答案,或者所有层都不够确定时的兜底路由。

阈值不是给你调的参数。 一个选项只有在它赢得的概率达到这一层的 threshold 时才让链条定下来,否则继续往下走(最后一层则落到兜底路由)。这条护栏管的是接近平分的情况:那时赢家往往只是"两个方向里稍微高一点的那个",不该由它决定。它留在文件里、可在 JSON 原文里微调,但不出现在表单上——写步骤的人只需要回答「问什么」和「每个答案意味着什么」。

实测里这条护栏抓到过它该抓的:当两个独立判断被挤进同一个分布时,一条本该走常规模型的请求得到 0.42 / 0.40——赢家只领先 0.02。阈值把它挡在了兜底路由上,而不是让一个掷硬币的结果决定。


它做了什么(Host 半)

挂钩几个扩展点,都在 plugin/index.js:

时机做什么
agent/created为这个 agent 注册下面几个监听器(agent 销毁时一起摘掉)
agent/inbox/inserted顺手记下第一句 prompt 的文本——再晚一点它就被这一轮认领走了
system-prompt/assemble(根 context,本会话第一次)跑决策流程 → 记住结论 → 把选择写进 harness 自己保存它的地方
agent/pre-step(仅第 1 轮第 1 步)可选地把一行说明注入这一步;也是没有组装步骤时的兜底
agent/request兜底:没有 session controller 的组合里直接改写调用配置
session/event用户手选模型时让位;把 request/header 与决策比对,写 verified / mismatch;sticky: false 时在下一轮开始前交还

几条刻意的选择:

  • 切换必须写进 harness 的选择状态,不能只改请求。 installModelSelection 在 Agent setup 阶段注册(早于 agent/created),所以它是 agent/request 瀑布里最外层的监听器:next() 返回之后,它用自己在组装时拍下的快照覆盖掉一切改写。只改请求的插件永远会被它覆盖。 这也是为什么组装监听器注册在根 context 上——插件加载比 Agent setup 更早,因此比 core 更外层,能在它拍快照之前把值放好。修好之前这个插件从未真正切换过任何模型,详见 docs/01-design.md 9.1。
  • 别相信自己的日志。 判定生效与否的唯一证据是会话里的 request/header(机器实际用的配置),所以每次请求之后都拿它和决策比一次并写 verified / mismatch。曾经的 applied 只记录了监听器返回什么。
  • 不新造会话事件类型。 本 harness 无法给 live append 打 ignorable 标记,写一个未知类型会让会话日志再也读不回来。所以结论只走 harness 已经认识的通道:一条 user/message(source.form = 'notice',和核心自己报告「模型切换」用的是同一形状),以及正常的 request/header。两处都可重放。
  • 声明的范围 = 决策的范围。 sticky: false 时选中的模型只用于判断出它的那一轮;notice 写「本轮」而不是「本会话」,提示里的身份也只在第一次组装里声明,下一轮开始前把选择交还。sticky: true(出厂默认)时三者都变成"整个会话"。
  • 不新造会话事件类型。 本 harness 无法给 live append 打 ignorable 标记,写一个未知类型会让会话日志再也读不回来。所以结论只走 harness 已经认识的通道:一条 user/message(source.form = 'notice',和核心自己报告「模型切换」用的是同一形状),以及正常的 request/header。两处都可重放。
  • 声明的范围 = 决策的范围。 sticky: false 时选中的模型只用于判断出它的那一轮;notice 写「本轮」而不是「本会话」,系统提示的身份也只在第一次组装里声明。sticky: true(出厂默认)时三者都变成"整个会话"。
  • 确认模式(autoApply: false)下,没回答就等于不同意。 确认卡片走 ctx.userQuestions:一张没有 callId 的阻塞式提问,渲染在该会话的输入框位置,用户答完才继续。取不到答案的所有情况都不切换——没有 questions 服务、没有连着的客户端、这一轮被取消、以及子会话(delegated child 没有人类可以问)。被推荐但没采纳这件事会写进会话里的 notice,因为卡片本身的问答不留日志痕迹。
  • 用户手选模型优先。 会话日志里出现 model/selection 就说明用户自己选过——路由让位。在保存配置之前就选中过模型的会话也会被跳过。
  • 失败即不干预。 决策模型连不上、超时、答不出选项,都只是记一条日志,DSH 用它原本要用的模型继续跑。没有回退模型——猜一个不如不猜。
  • 模型池不是配置项,而是读出来的。 每一级步骤直接写 provider/model;下拉选项来自当前 profile 实际注册的 provider / model,所以不存在一份会和 DSH 走偏的第二清单。决策命中的 provider 此刻没注册时,插件跳过切换并写明原因。
  • 配置是 Host 上的一个 JSON 文件($DSH_HOME/model-router.json),设置页通过 Host 注册的 HTTP 路由读写。API Key 只从 Host 进程的环境变量读,永远不下发到浏览器。
  • 不 import 任何 @deepseek-ai/* 包。 profile 里安装的 bundle 在 node_modules 里解析不到它们(已验证),所以 Host 半只用 node: 内置模块和相对路径模块——和 harness 自带的第三方 bundle(如 dsh-easy-balance)写法一致。

设置页(Client 半)

注册进 settings.section,所以在 设置 侧栏里是一个独立页面「自动决策模型」。

  • 表单:连接参数、DSH 当前提供的路由列表、任意多级步骤的增删改与上下排序。每个选项行里有:key、描述、执行操作(下拉框:某个模型 / 进入下一层步骤)、备注(可选)
  • 说明性文字都在 ! 后面:每个标签、每个区块标题后面一个 ! 圆点,hover(或键盘 focus)才弹出解释。表单页可见的注解行因此从 17 行降到 1 行(剩下那行是配置文件路径,是值不是解释)
  • JSON 原文:整份配置直接编辑——要看 threshold 之类的内部字段就在这里
  • 测试:用**当前表单里(哪怕还没保存)**的配置跑一次真实调用,逐层显示概率分布和最终选中
  • 最近决策:读回 Host 的 $DSH_HOME/model-router-decisions.jsonl,显示最近 20 次判定及其概率

样式只用 harness 的 --dsw-alias-* 主题变量,跟随明暗主题。


决策日志

每次判定都会追加一行 JSON 到 $DSH_HOME/model-router-decisions.jsonl(超过 512 KB 自动只留最近 1000 行):

{"at":"2026-10-11T09:06:01.000Z","event":"decision","sessionId":"session-…",
 "provider":"opencode-go","model":"deepseek-v4.1-flash","why":"长程 / 复杂任务",
 "elapsedMs":187,"trace":[{"step":"task-scale","key":"complex","probabilities":{"complex":0.9867,"simple":0.0133}},
                          {"step":"context-pressure","key":"broad","probabilities":{"broad":0.7275,"focused":0.2725}}]}

出厂阈值是起点,不是结论。给一个具体部署定阈值,唯一可靠的办法就是看它自己的流量产生什么分布——这份日志就是为此存在的。


快速上手

  1. 设置 → 自动决策模型 → 表单里核对地址(默认 http://127.0.0.1:8848)和决策模型名(默认 cloudflare-clef)

  2. 决定要不要自动执行:「允许自动选择模型并执行」默认打开。关掉它,每次新会话都会先弹一张确认卡片,你点了才切

  3. 切到测试页,随便输一句 prompt,点「运行测试」——应该看到逐级概率和结论

  4. 确认无误后回表单页点保存

  5. 新建一个会话,发一句 prompt。会话里会出现一行折叠的 context:

    [model-router: this session runs on lm-studio/qwen3.8-27b — 短程 / 一般复杂度]

    如果不需要这一行(它会占少量 token,并且对模型可见),把设置页的「在会话里加一行说明」关掉即可。

「先问我」模式长什么样

autoApply 关掉后,发出第一句 prompt 时输入框位置会变成一张卡片:

切换模型 / Switch model 决策模型建议用 opencode-go/deepseek-v4.1-flash 执行这次任务。要切换吗? 长程 / 复杂任务 task-type: simple=0.1659 complex=0.7745 [ 切换 / Switch ] [ 保持当前模型 / Keep current model ]

  • 选「切换」→ 用推荐模型执行,并留下一条 notice 说明原因
  • 选「保持当前模型」→ 用会话原本的模型执行,同样留下一条 notice(卡片本身的问答不进日志,所以这条 notice 是唯一的痕迹)
  • 直接取消这一轮 → 不切换
  • 在这张卡片上,没回答就等于不同意

已知边界

  • 决策模型只吃文本。 它会看第一句 prompt 的文本;图片、附件不参与判断。
  • 中文置信度偏低。 实测同一句话英文置信度 0.92、中文 0.66。设置页提供「先翻译成英文再判断」的开关,指定任意 chat 模型做预处理。
  • 决策模型必须是 decision 类型。 LM Studio 对普通 LLM 直接回 501 not_supported_error;GET /api/v1/models 的 type 字段就是答案。
  • 确认模式下子会话不会被切换。 ctx.userQuestions 只允许活的顶层 agent 提问(子 agent 抛 DELEGATED_CALLER)。所以 autoApply: false 时,delegate 出来的子会话连决策流程都不会跑——没人可以问,就不做没有授权的切换。这是刻意的取舍:宁可不切,也不替你点"同意"。
  • 选项描述值得认真写。 实测里「写 SQL 注入 PoC」在只给 false/true 两个裸选项时是 0.50 / 0.4992(等于掷硬币),补上描述后变成 complex=0.82 / simple=0.18。判断质量很大程度上就是你给的描述质量。
  • 路由下拉框依赖 Host 半返回的路由列表。 Host 半还没重启时列表是空的,此时路由字段会自动退化成手填文本框(并写明原因),而不是给你一个没有可选项的下拉框。重启后即为下拉框。
  • host 半的代码改动需要重启 dsh 才生效。 Loader 会保留第一次 import 的模块generation;plugin_manager 的禁用/启用不会重新 import。配置文件的改动则不需要重启(每次读取都比对 mtime)。
  • 一处未验证:设置页在真实浏览器里的视觉呈现。见 docs/03-verification.md。

Comments

Loading…

Similar plugins

dsh-model-router

by andrepontesmelo

DSH plugin: virtual model ids backed by pluggable routing algorithms (priority, round-robin) over real provider candidates, with transparent failover.

Tools & CapabilitiesDevelopment & InfrastructureManifest valid

★ 0

MIT

JavaScript

Oct 12, 2026

dsh plugin --profile web add @andrepontesmelo/dsh-model-router

DSH plugin: matches a model you declared on a custom gateway to the capability metadata it should have — context window, output cap, reasoning levels and image support — by looking the bare model name

Models & ProvidersManifest valid

★ 0

dsh plugin --profile web add dsh-model-metadata

by leolee9086

DSH plugin: route and rewrite outbound HTTP requests by host and path, with a native right-sidebar monitor

Manifest valid

★ 0

MIT

JavaScript

Sep 30, 2026

dsh plugin --profile web add @leolee9086/dsh-fetch-router

by IHS470

DSH plugin: one user-owned rule list, injected into every request system prompt, so the agent has to walk the rules before answering.

Development & InfrastructureManifest valid

★ 0

MIT

JavaScript

Oct 3, 2026

dsh plugin --profile web add dsh-global-rules

by zongzack

DSH plugin that uses the JEV model to score task complexity and switch to the appropriate model for each dispatch request.

Manifest valid

★ 0

Sep 24, 2026

dsh plugin --profile web add @zong/dsh-jev-router

by Yebeimu

A DSH plugin: collect pending ask_user_question prompts from other sessions and answer them without leaving the current one.

Manifest valid

★ 0

MIT

JavaScript

Oct 17, 2026

dsh plugin --profile web add dsh-sideask