# dsh-set-model
简体中文 ·
English
DeepSeek Harness(DSH / cordis plugin)插件:通过 Jev 或工具调用,让 Agent 根据任务场景自主切换模型预设和思考深度。
- **自动换档(Jev)**:接入 TypeSafe Jev 决策模型,每轮对话开局读一次你的最新指令判断任务类型,自动在声明的档位间换档——排查疑难升到高推理档位,改文案、跑测试回到日常档位,不用手动干预、也不必在提问时交代"用深度思考";档位只声明思考强度时,换档就是**在同一个模型上自动调节思考深度**;
- **自由调模工具**:Agent 用 `set_model` / `list_models` 按任务难度自主切换 provider、model 与 reasoningEffort;
- **档位工具**:部署声明一组「模型 + 思考强度」档位,Agent 只能在档位间切换(`switch_preset` / `list_presets`);档位可以只声明思考强度而不绑定模型(`provider` / `model` 留空 = 沿用当前模型),从而只调节思考深度;未声明 `presets` 时内置 `low` / `medium` / `high` 三档;
- 自动换档与两个工具面开关**彼此独立**,可任意组合(只开 Jev、只开工具、都开、都关);
- 可配置一段**策略提示词**:同一份文本既告诉 Agent、也告诉 Jev 各阶段该用哪个档位;
- 插件把当前模型与档位注入运行时上下文,并随 Plan 模式自动升降级。
攻坚疑难问题时升到高推理档位,日常执行保持高性价比档位——开启 Jev 后这条策略每轮自动执行,不再需要用户手动改配置。

## 三个独立开关
| 开关 | 默认 | 作用 |
|---|---|---|
| `enableModelTools` | `true` | 注入 `set_model` / `list_models`,Agent 可切换到任意已注册 provider / model / 思考强度 |
| `enablePresetTools` | `false` | 注入 `switch_preset` / `list_presets`,Agent 只能在 `presets` 声明的档位间切换(默认三档只调思考强度) |
| `jevEnabled` | `false` | Jev 每轮自动决策档位,**不依赖上面两个工具**,也不受它们影响 |
> 档位工具约束的是 **Agent 的工具面**;你在 GUI 模型选择器里的手动切换不受限制(DSH 未提供拦截用户切模的插件接口),可作为人工兜底通道。三个开关都不影响 GUI 的既有行为。
## 策略提示词
`policyPrompt` 是这一份策略的**唯一出处**,有两个读者:
| 读者 | 何时读到 | 读到什么 |
|---|---|---|
| Agent | 两个工具开关至少开一个时 | 作为 `## Model policy` 段落写进系统提示词 |
| Jev | `jevEnabled` 打开时 | 随每轮决策请求发出(`state.policy`),与当前指令、上一轮回复一同进入判定 |
两个读者都不在时(工具全关、Jev 也关)这段配置不被读取,插件只在日志里给一条 warning。
```yaml
policyPrompt: |
规划与疑难排查一律先切 deep;日常实现、改文件、跑测试用 daily。
```
开启档位工具后 Agent 实际看到的(档位清单由 `presets` 的 `id`/`name`/`when` 自动生成,无需手写):
```
## Model policy
规划与疑难排查一律先切 deep;日常实现、改文件、跑测试用 daily。
本部署只允许在下列模型档位之间切换;进入对应阶段时调用 switch_preset 换档:
- **daily(日常执行)** — deepseek/deepseek-chat · reasoning low — 适用:常规实现、修改、测试
- **deep(深度攻坚)** — deepseek/deepseek-reasoner · reasoning max — 适用:疑难排查、架构设计
```
档位的 `when` 同时也是 Jev 的判定依据(档位名与思考强度附在其后)。Jev 开启时缺 `when` 的档位只剩一个名字可判,插件会就此给一条 warning。
未配置任何策略时段落为空,不会在提示词里留下痕迹。
## 模型看到什么
插件通过 `systemPrompt.context` 把当前活跃模型注册进每个 step 组装的 Runtime Context 快照(该快照仅在文本变化时才追加新的一条,内容相同则复用上一条,因此稳定轮次里只落一条 cache-safe 追加;模型随时自查、无需调用工具):
```
[Current active model: deepseek/deepseek-chat · reasoning: low · preset: daily]
```
处于 Plan 模式时附带状态标记:
```
[Current active model: deepseek/deepseek-reasoner · reasoning: max · preset: deep] [Plan Mode Active]
```
`preset: xx` 仅在当前模型命中某个档位时出现。
## 工具
### set_model(自由调模工具)
`set_model({ provider?, model?, reasoningEffort?, reason })` —— `reason` 必填(每次切换都会使会话 KV cache 失效,工具说明要求 Agent 给出理由、避免频繁切换):
```json
{ "model": "deepseek-reasoner", "reasoningEffort": "high", "reason": "疑难并发问题排查,需要深度推理" }
```
### list_models(自由调模工具)
`list_models({ provider? })` 列出已注册 Providers 与模型能力(推理档位、上下文窗口):
```
# Available Providers & Models:
## Provider: deepseek
- **deepseek-reasoner** (DeepSeek Reasoner) | Reasoning: [low, high, max] | Context: 128000
- **deepseek-chat** (DeepSeek Chat) | Context: 128000
```
### switch_preset(档位工具)
`switch_preset({ preset, reason })` —— `preset` 是配置里的档位 id,`reason` 必填;id 不存在时返回可用档位清单:
```
Model preset switched to deep (深度攻坚) — deepseek/deepseek-reasoner · reasoning: max. Effective from the next step.
Reason: 疑难并发问题排查,需要深度推理
```
### list_presets(档位工具)
`list_presets({})` 列出允许的档位、各自适用阶段与当前生效档位:
```
# Model presets
Mode: preset · Plan Mode active
Current: deepseek/deepseek-reasoner · reasoning max · preset deep
- **daily(日常执行)** — deepseek/deepseek-chat · reasoning low — 适用:常规实现、修改、测试
- **deep(深度攻坚)** — deepseek/deepseek-reasoner · reasoning max — 适用:疑难排查、架构设计 ← current
```
切换自下一个 step 生效:插件向会话写入 `model/selection` 事件(与 GUI 模型选择器同一机制),Web UI 状态同步。
## Plan 模式自动升降级
插件监听 `agent/pre-step` 的 Plan 投影状态变迁,零人工干预:
- **进入 Plan 模式**(`/plan on`):暂存当前模型并自动切换——设置了 `planPreset` 时切到该档位(优先),否则切到 `planModel` 指定的模型;
- **退出 Plan 模式**(`exit_plan_mode` 批准 / `/plan off`):自动恢复进入前的模型(`autoRestorePlanModel`,默认开启);
- 自动切换失败仅 warn 降级,绝不打断 step。
## Jev 自动模型切换
开启 `jevEnabled` 后,插件在每轮对话的首个 step 问一次 **TypeSafe Jev 决策模型**:这条指令属于什么任务类型,该用哪个档位。判定过程如下:
1. 取当前回合最新的用户指令(尚未提交进会话也取得到),拼上上一轮助手回复作为背景,尾部对齐截断到 2000 字符;
2. 把 `presets` 里每个档位(`id` / `name` / `when`)连同 `keep`(保持现状)一起作为候选项,由 Jev 给出最佳档位与置信度;
3. 置信度低于 `jevConfidenceThreshold`(默认 0.7)就不动,避免无谓换档;
4. 命中则写入 `model/selection` 事件切换模型,与 GUI 手动切模同一机制,Web UI 状态同步;切换只影响下一个 step 起的新请求。
"改一下这个按钮的颜色" 会留在日常档位;"线上订单偶发丢单,帮我定位" 会切到深度攻坚档位。命中时宿主日志里能看到实际判定:
```
dsh-set-model: [Jev] auto-switched session to preset "deep" (deepseek/deepseek-reasoner · reasoning: max) with confidence 0.87
```
档位只声明 `reasoningEffort`(`provider` / `model` 留空)时,换档表现为**在同一个模型上自动调整思考深度**——例如日常 `low`、疑难 `max`,模型不变、思考预算自动变。
- **极低成本与高速度**:基于 OpenRouter Decisions API,单次判定约 200~300ms,成本仅约 $0.000015 / 次;
- **通用凭据体系**:设置页配置 `jevApiKey` / `jevBaseUrl` / `jevModel`,或走环境变量 `JEV_API_KEY` / `JEV_BASE_URL` / `JEV_MODEL`;不绑定任何特定平台或本机密码本(api-vault),Base URL 与 API Key 由用户自主配置;
- **稳健性保障**:3 秒熔断超时、generation 竞态淘汰(新一轮指令作废在途判定)、Plan 模式期间主动避让不抢档;任何失败只写 warn 日志并保持当前档位,绝不阻塞本轮对话;
- **与工具开关解耦**:`jevEnabled` 独立于 `enableModelTools` / `enablePresetTools`,只开 Jev、不给 Agent 调模工具也能用。
## 安全护栏
- **Provider 白名单**:`allowedProviders` 非空时,白名单外的切换请求直接拒绝;它同时约束档位(白名单外的档位会在加载时被丢弃并记录错误日志);
- **上下文窗口护栏**:当前会话 token 水位超过目标模型 contextWindow 时拒绝切换,并提示先压缩上下文(如 clear_mind):
```
Current session token pressure (70000 tokens) exceeds target model context window (65536 tokens).
Please compact context (e.g. clear_mind) before switching to this model.
```
- **路由校验与思考深度自适应**:支持完整的 7 级思考深度(`off | minimal | low | medium | high | xhigh | max`);当目标模型不支持所选档位时自动沿阶梯就近钳制(如 `xhigh`/`max` → `high`),非推理模型自动剥离思考深度,再经 DSH 核心 `ctx.llm.resolveCallConfig` 校验路由;
- **配置诊断不致命**:重复档位 id、缺 provider/model、`planPreset` 指向不存在的档位等会写入宿主日志(`dsh-set-model: ...`)并跳过该条,插件照常加载——配置错误不会拖垮整棵插件树;
- **仅根 Agent**:工具只注册给根 Agent(subagent 不提供自主调模);把两个工具开关都关掉即可整体关闭。
## 设置界面
插件自带 Web 设置页「动态模型」(设置 → 动态模型),可可视化维护:
- 档位表增删排序,provider / model / 思考强度均为下拉选择(数据取自当前模型目录),附 `id`、显示名、适用阶段;
- 策略提示词;
- Agent 调模能力:两个工具开关与 `allowedProviders`;
- Plan 目标(档位或模型)与 `autoRestorePlanModel`;
- Jev 自动换档开关与高级选项(API Key / Base URL / 模型 / 置信度阈值),附「测试连接」按钮,探测 Decisions API 的可用性与时延。
保存后宿主即时重新应用插件配置,无需重启。字段需标记为 `volatile` 才会出现在设置表单里(DSH 设置系统的约定)。
## 配置
Settings 命名空间为插件在 profile 里的 loader entry id:**`set-model`**(Web Settings UI 可视化配置,改动运行时动态生效)。
```yaml
# profile 的 cordis.patch.yml(或设置页写入的用户层)
- id: set-model
name: dsh-set-model
config:
enableModelTools: true # 注入自由调模工具 set_model / list_models(默认 true)
enablePresetTools: false # 注入档位工具 switch_preset / list_presets(默认 false)
jevEnabled: false # Jev 自动切档位(默认 false,独立于上面两个开关)
presets: # 缺省为内置三档 low / medium / high(均不绑定模型)
- id: daily # 稳定标识,Agent 用它切换
name: 日常执行 # 展示名,缺省用 id
when: 常规实现、修改、测试 # 适用阶段:写进策略清单,也是 Jev 的判定依据
provider: deepseek # provider 与 model 同时留空 = 沿用当前模型,只调思考强度
model: deepseek-chat
reasoningEffort: low # off | minimal | low | medium | high | xhigh | max,缺省跟随模型默认
- id: deep
name: 深度攻坚
when: 疑难排查、架构设计
provider: deepseek
model: deepseek-reasoner
reasoningEffort: max
policyPrompt: | # 可选:策略文本,Agent(有工具时)与 Jev 共用
线上事故排查一律先切 deep。
planPreset: deep # 进入 Plan 模式使用的档位 id(优先)
planModel: # 未设 planPreset 时,进入 Plan 模式使用的模型
provider: deepseek
model: deepseek-reasoner
reasoningEffort: high
autoRestorePlanModel: true # 退出 Plan 模式自动恢复(默认 true)
allowedProviders: [] # 可选:可切换 Provider 白名单(空 = 不限制)
jevApiKey: "" # 可选:Jev API Key(缺省走 JEV_API_KEY 环境变量)
jevBaseUrl: "" # 可选:Jev Decisions API URL(缺省走 JEV_BASE_URL 或默认官方端点)
jevModel: "" # 可选:Jev 决策模型(缺省走 JEV_MODEL 或 typesafe/jev-1.13)
jevConfidenceThreshold: 0.7 # Jev 切换置信度阈值(0.0 ~ 1.0,默认 0.7)
```
## 安装
```bash
dsh plugin --profile web add dsh-set-model
```
安装后无需手动改配置,插件自带的 `cordis.patch.yml` 自动挂载生效。
从 GitHub 直装(源码安装,pnpm ≥10 需允许构建脚本):
```bash
dsh plugin --profile web add github:john-walks-slow/dsh-set-model
# 首次 add 会被 pnpm 拦截:把 pnpm 提示的包名加入
# ~/.dsh/profiles/web/pnpm-workspace.yaml 的 allowBuilds 后重跑
```
## 权限与兼容
- **零直接副作用(常规路径)与轻量决策网络(Jev 路径)**:常规路径无外部服务、无插件自身网络请求(模型路由与能力查询均经由 DSH 核心 `llm` 服务)、无直接文件系统写入;启用 Jev 自动切模后,仅在回合首步向用户配置的 Jev Decisions API 发送轻量任务摘要(当前指令与上一轮助手回复的尾部 2000 字符、策略提示词、档位清单,带 3 秒熔断超时保护,超时或网络异常仅 warn 降级,绝不阻塞主会话正常进行)。仅通过会话事件 API 写入 `model/selection` 事件(由 DSH 会话系统落盘)并注入一行 Runtime Context 与一段系统提示词
- **浏览器半**:设置页以 `lib/client.js`(构建产物)注册 `settings.section`,仅调用 DSH 的 `remote.settings` / `remote.session` RPC,不发起自有网络请求
- **宿主版本要求**:DSH `^0.1.7-rc.2 || ^0.2.0-rc.1`(0.1.2~0.1.6 会得到明确的"插件与 dsh 版本不兼容"安装提示,请升级 DSH)
- **依赖声明**:DSH 运行时包(`cordis`、`dsh-agent`、`dsh-llm`、`dsh-session`、`dsh-tools`,以及可选的 `dsh-settings`)全部声明为 `peerDependencies`——DSH 的模块解析层据此把插件内的 `@deepseek-ai/*` import 路由到**宿主自带副本**,插件绝不携带、也不 hoist 这些包的副本;唯一运行时依赖是纯 schema 库 `@deepseek-ai/schemastery`(`react` / `esbuild` / `@types/react` 只用于构建浏览器半,运行时由 Web 壳提供 React)。Node ≥ 22.5
- **失败降级**:Plan 模式自动切换失败只 warn 不打断 step;工具校验失败向 Agent 返回明确错误,不污染会话状态;设置页挂载失败只 console.error,不拖垮 GUI
## 本地开发
```bash
npm install
npm run check # tsc(宿主) + tsc(浏览器半)
npm test # tsc(含 test) + node --test dist/test/*.test.js
npm run build # tsc → dist/src,esbuild → lib/client.js
npm run build:client # 只重建浏览器半
```
需要图形界面验证时用 dsh-e2e 最小实例(见 `scripts/dev-worktree.sh`):
```bash
dsh-e2e start --wait-ready --patch e2e/fixture/cordis.patch.yml
dsh-e2e run e2e/verify-policy-page.mjs
```
e2e 的种子配置在 `e2e/fixture/cordis.patch.yml`(档位、Plan 目标与策略提示词的样例值),启动时通过 `--patch` 注入;playwright / camoufox 路径可用 `DSH_E2E_PLAYWRIGHT` / `DSH_E2E_BROWSER` 覆盖。
浏览器半改完务必重建 `lib/client.js` 并跑一次 `node --check lib/client.js`(构建脚本内已内置)。
## License
MIT
## 发新版
改动入库后一条命令完成测试、版本号、打包(`npm version` 会自动 commit 并打 tag):
```bash
npm run release # patch;较大更新改用:npm version minor 或 major
```
然后指纹发布并推送:
```bash
node ~/.agents/skills/npm-publish/scripts/publish-webauthn.cjs /tmp/dsh-set-model-<新版>.tgz
git push --follow-tags
```
发布后 `npm view dsh-set-model version` 复验。批量发多个包时,在指纹页勾选“5 分钟内同 IP 不再挑战”,一次指纹即可连发。
> **发布后的暂存/扫描窗口(2026-10 起 npm 行为)**:`PUBLISHED_OK` 只是 PUT 被接受,版本随后进入暂存/扫描态,
> 期间 `npm view` 仍显示旧版本、`//` 与 tarball URL 都是 404,对**同一版本**重发会报
> 409 `Cannot publish over previously staged version`。实测约 5 分钟后自动 promote(`0.2.0`:PUT 15:27:43Z → 上线 15:32:33Z)。
> 遇到 409 不要改版本号,等待并复查 registry 即可。落库判定用 `npm view dsh-set-model@ dist.shasum`
> 与本地 tgz 的 `sha1sum` 比对:一致即上线产物与本地产物字节相同。
## 社区
- 感谢 [LINUX DO](https://linux.do/) 社区提供的交流环境。