DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

CSlawyer1985 /

CSlawyer1985/dsh-jev-router

Verified

DSH 插件 · 用 Jev(TypeSafe System One)判定推理强度:默认只切思考强度(零缓存代价),自动模型路由出厂关闭 + 硬门禁 + 成本闸。作者 chenshi.ai

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHubProject homepage
READMESource: main@17541cfb

dsh-jev-router

License Version Tests Stars PRs Welcome Author
让 Jev 决定这次该用多强的推理
默认只切思考强度,绝不碰模型、绝不碰 prompt 前缀
语义判定 · 缓存安全 · 迟滞防抖 · 成本闸门 · 全链路可回滚


新用户? 从 安装 开始,然后到 配置项 填 TypeSafe API Key——两步就能用。

核心承诺:判定永不进入 prompt 前缀。 判定结果只走 agent/request 改写调用配置,不改写 messages、不注册 system prompt。这是本插件唯一不可妥协的设计,且有契约测试守着。

仓库:https://github.com/CSlawyer1985/dsh-jev-router · 作者:chenshi.ai · 决策模型:Jev(TypeSafe AI System One)


它解决什么问题

大模型每一次回答都要先决定"想多深"。想得深就贵、就慢;想得浅就快、就便宜。而用户其实已经把答案写在问题里了——"今天几号"和"帮我重构这个模块的架构"显然需要不同的推理强度,但传统 Harness 只能让用户手动切、或者一刀切用最高档。

Jev 正好补这个位置:它不聊天、不写代码,只在你给的可穷举选项里选一个,并附上概率与置信度。所以它能干的事是:

把"这句话该用多强的推理"从主观题变成客观题。

但这里有个所有同类插件都会踩的坑:换模型会破坏 prompt 缓存。本插件对这个代价做了量化,并把它变成了出厂关闭 + 硬门禁的可选能力。


运行效果

① 输入框徽章 —— 常驻显示当前生效的思考强度,点击循环切换(auto → off → low → high → max):

┌──────────────────────────────────────────────────────────────┐
│  描述你想要构建的内容, / 调用指令, @ 文件或对话              │
│                                                              │
│  ⚡ auto          DeepSeek-V41-Flash ▾        High ▾      ➤  │
└──────────────────────────────────────────────────────────────┘
     ↑ Jev 判定后的生效档位

② 设置页(设置 → 插件 → Jev 路由) —— 实时度量、密钥、三档开关、价目、署名:

区块 内容
Jev API Key 一行三按钮:密码输入框 + 保存 / 连通 / 清除。密钥永不回显,保存后输入框立即清空,只显示「已配置/未配置」
判定与时机 置信度门、riskCeiling、降档确认轮数、迟滞窗口、判定超时、回退档位、首步是否等待、徽章开关
模型路由参数 stickyRounds、切换冷却、单会话切换上限、命中率告警阈值
缓存与成本(实时) 累计命中率、近 50 步命中率、命中/未命中 tokens、缓存省下的钱、reasoning 占输出比
思考强度路由 总开关、生效档位 + Jev 判定与置信度、手动钉死档位(auto/off/low/high/max)
自动模型路由 开关(默认关)+ 三步确认流程 + 最近一次闸门裁决原因
价目 来源、抓取时间、当前 peak/off-peak 时段、三档单价、一键从官网刷新
署名 dsh-jev-router v0.1.0 · 作者 chenshi.ai

③ 命令 —— 判定与配置全部可在对话里完成:

/jev                 查看状态(等价于 /jev status)
/jev status          查看状态(档位、来源、置信度、命中率、节省)
/jev effort low      手动钉死档位(手动值永远压过自动判定)
/jev model auto on   开启自动模型路由(含缓存代价提示)
/jev why             解释本轮判定为什么是这个档位
/jev rollback        把模型回滚到切换前
/jev pricing refresh 从 DeepSeek 官网刷新价目
/jev about           插件与作者信息

④ 判定解释 —— /jev why 输出判定依据与策略裁决链:

来源:jev(jev-1.13.0)
档位:high(置信度 0.91)
错误代价(score 0-3):2.4
用户想要快答(noul 0-1):0.12
策略裁决:upgrade → high(已夹取:clamped(max->high))· 该模型支持 minimal/low/medium/high
模型闸门:cache-risk-not-acknowledged

原理:Jev 是什么

Jev 是 TypeSafe AI 的 System One 决策模型(作者 Diogo Almeida,前 ChatGPT 研究员)。它不做文本生成,只输出结构化判断。

三种判断原语

原语 用途 请求字段 返回字段
choice 从固定选项中选一个(最多 255 项) instructions + criteria(选项名→描述的映射) choice、probabilities、confidence
score 按有序等级打分(2–10 级) instructions + criteria(从低到高的等级描述数组) score、probabilities、confidence、legend
noul 判断是非 instructions(可选 criteria 说明 true/false 各指什么) noul(0–1 的"是"概率)

一次请求可以混用多种原语。问题字典的 key 不会发给模型——模型只看到 instructions 与 criteria,所以选项描述必须彼此可区分。

本插件的三个问题

{
  "state": { "user_message": "帮我重构这个模块的架构" },
  "model": "jev-latest",
  "questions": {
    // 主判定:该用多强的推理
    "effort": {
      "type": "choice",
      "instructions": "How much internal reasoning effort should the assistant spend on this request? …",
      "criteria": {
        "off":  "Trivial: greeting, thanks, plain lookup…",
        "low":  "Simple and short: a single-step answer…",
        "high": "Substantial: multi-step work, code changes, debugging…",
        "max":  "Exhaustive: the user explicitly asks for complete/thorough reasoning…"
      }
    },
    // 辅助:出错代价(用于解释与将来的策略扩展)
    "risk": {
      "type": "score",
      "instructions": "If the assistant answers this request incorrectly, how costly is the mistake?",
      "criteria": ["…4 级情境描述…"]
    },
    // 辅助:用户是否想要快答
    "urgent": {
      "type": "noul",
      "instructions": "Does the user want a fast answer rather than a thorough one?"
    }
  }
}

为什么 instructions/criteria 全用英文:官方说明中日韩文字"可以处理但准确率较低"。所以中文原话放进 state,判定用的指令与选项描述统一用英文。这是刻意的工程取舍,不是随手写的。

为什么又快又便宜

它不逐 token 续写解释,一次前向就把状态压成判断。官方标称在适配任务上快 20–200 倍、便宜 40–400 倍;定价是输入 $0.042/百万 token、输出免费,速率上限 25 万 token/秒、1200 请求/分钟。

已知限制(决定了插件的降级策略)

限制 本插件的应对
英语优先,CJK 准确率较低 instructions/criteria 全英文;state 里放中文原话;置信度门 + 手动覆盖
只接受文本(无图/音/视频) extractUserText 只取文本块,忽略图片与文件附件
不解释理由 判定结果里带概率与置信度;/jev why 展示判定与裁决链
不可微调 领域知识只能靠 state 与 criteria 注入
上下文 64K(状态+最长单问 ≤32K) 只发用户原话,不发会话历史
置信度不是正确率 低于阈值一律弃权,沿用 harness 默认档

工作原理

架构总览

┌─────────────────────── Host 半(Node / Cordis 插件) ───────────────────────┐
│                                                                            │
│  agent/inbox/inserted ──► 取用户原话 ──► JevClient.ask() ──► 解析 ──► 存档  │
│       (用户消息进 inbox)                 api.typesafe.ai         │        │
│                                                                   │        │
│                                    置信度 < 阈值 → 弃权,沿用默认档        │
│                                                                   ↓        │
│  agent/request(waterfall)◄──────────────────────────────────────┘        │
│       │  ① 档位裁决:迟滞窗口 + 降档连续确认 + 手动覆盖优先                │
│       │  ② 档位夹取:向 llm 注册表问该模型支持哪些档位,夹到最近的         │
│       │  ③ [可选] 模型路由:五道闸 → 全过才改 provider/model               │
│       └──► 返回替换后的 LlmCallConfig                                      │
│            (只碰 reasoningEffort / model / provider)                      │
│                                                                            │
│  llm/stream(waterfall)──► 抓 usage ──► 度量累计(命中率/节省/占比)      │
│  agent/turn-stopping ──► 推进迟滞计数 ──► 命中率告警检查                    │
│                                                                            │
│  对外:commands(/jev)· webServer(4 条同源路由)· settings(设置页)      │
└────────────────────────────────────────────────────────────────────────────┘
                              ▲ 同源 HTTP(静态 client 模块没有 host.call)
┌─────────────────────────────┴──────── Client 半(静态 client 模块) ───────┐
│  settings.section          设置页:密钥 / 度量 / 三档开关 / 三步确认 / 署名 │
│  conversation.input.left   徽章:⚡ + 生效档位,点击循环切换                │
└────────────────────────────────────────────────────────────────────────────┘

一次消息的完整时序

用户: "仔细帮我查一下天气,如果你没查好我明天可能误飞机"
  │
  ├─ ① agent/inbox/inserted
  │     取纯文本 → POST api.typesafe.ai/v1/systemone
  │     questions: effort(choice) / risk(score) / urgent(noul)
  │     ◄── { effort:{choice:"high",confidence:0.91}, risk:{score:2.4}, urgent:{noul:0.12} }
  │     存 entry.decision = { effort:"high", source:"jev(jev-1.13.0)", confidence:0.91 }
  │        └─ 失败(无 Key / 超时 / 限流 / 429)→ 回落关键词表,source="heuristic"
  │
  ├─ ② agent/request(step=1,回合起点)
  │     const cfg = await next()                    // harness 原配置
  │     ├─ 档位裁决 decideEffort()
  │     │    · 手动 effort≠auto → 直接用(manual-override)
  │     │    · 置信度 < confidenceFloor → 弃权
  │     │    · 降档且「风险高 且 非用户明说」→ 才需连续 downgradeStreak 轮确认
  │     │    · 距上次换档 < hysteresisRounds → 不动
  │     ├─ 能力夹取 clampEffort()
  │     │    向 llm.resolveModelInfo(provider, model) 问 reasoning.efforts
  │     ├─ 模型路由(仅当 modelRouting=true)
  │     └─ 返回 { ...cfg, reasoningEffort }         // messages / system 原样透传
  │
  ├─ ③ 同一轮的 step 2..n
  │     沿用 entry.state.effort,不重新裁决(单轮内锁定)
  │
  ├─ ④ llm/stream
  │     抓 usage:{ inputTokens, cacheReadTokens, outputTokens, reasoningTokens }
  │       · purpose 标记(session-title / compaction)→ 只计 auxiliaryCalls
  │       · 其余 → 累计进度量
  │
  └─ ⑤ agent/turn-stopping
        迟滞计数 +1;窗口命中率跌破阈值 → 告警并建议 /jev rollback

挂载的 DSH 扩展点

扩展点 类型 用途
agent/inbox/inserted emit 拿到用户原话,喂给 Jev
agent/request waterfall ★ 核心:next() 给出 LlmCallConfig,返回替换值即切换
llm/stream waterfall 抓 usage,计算命中率与成本
agent/turn-stopping serial 推进迟滞计数、检查命中率告警
agent/disposed emit 清理会话状态
commands service 注册 /jev 命令族
webServer service 注册 4 条同源 HTTP 路由(前端半取数)
settings service Config schema → 自动生成设置页
credentials service 读写 TypeSafe API Key(分层凭据)
llm service 问模型能力(支持哪些推理档位)

缓存为什么不会被破坏

短答:因为 messages 的构造完全不引用 effort。

DSH 的 DeepSeek 适配器(@deepseek-ai/dsh-llm-deepseek)构造请求时是这样的:

const effort = options.purpose === "session-title"
  ? "off"
  : options.reasoningEffort ?? connection.defaults.reasoningEffort
    ?? (connection.defaults.thinking === "disabled" ? "off" : "high");

if (!["off","low","high","max"].includes(effort)
    || connection.defaults.thinking === "disabled" && effort !== "off")
  throw new LlmError(`DeepSeek Messages does not support reasoning effort ${effort}`,
                     "UNSUPPORTED_REASONING_EFFORT");

return {
  model: options.model,
  stream: true,
  messages,                                   // ← 由会话历史独立构造,与 effort 无关
  max_tokens: options.maxTokens ?? model?.maxTokens ?? connection.maxTokens,
  thinking: { type: effort === "off" ? "disabled" : "enabled" },
  ...effort === "off" ? {} : { output_config: { effort } },  // ← 只影响这两个顶层字段
  ...system.length === 0 ? {} : { system },
};

三个可验证的结论:

  1. messages 的构造不引用 effort —— 前缀字节不变,DeepSeek 的自动前缀缓存照常命中。
  2. 档位白名单就是 off | low | high | max —— 且不支持的值会在发网络请求之前抛 UNSUPPORTED_REASONING_EFFORT(写错立刻报错,不会静默烧钱)。
  3. purpose === "session-title" 的请求被强制 off —— 说明"按用途改档"是框架既有做法,本插件只是把它扩展到用户 prompt 上。

三种真正会破坏缓存的操作

操作 机制 本插件的态度
切模型 适配器回放历史时,readReplay 的注释写着 cross-model signatures are not portable——非本源模型的 thinking 签名会被丢弃,整段已缓存前缀被重写 Tier B,出厂关闭 + 硬门禁 + 成本闸
把判定写进 prompt 头部 每轮前缀都变 → 缓存归零 绝不:判定只存在于 LlmCallConfig,契约测试禁止出现 systemPrompt
provider 把 effort 纳入 cache identity Anthropic/OpenAI 路径上,thinking 开关会改变历史形态(思考块增删)→ 重写前缀。生态里有对应缺陷报告(zeroclaw#10786、zeroclaw#10777)与修复(deepagents#6196,标题明确写 for OpenAI and Anthropic,不含 DeepSeek) 当前走 DeepSeek Messages 路径,此条不成立;一旦切到 Anthropic/OpenAI 应调大 hysteresisRounds 或关闭自动改档(详见 缓存安全)

策略选择

这一节是本插件的全部判断力所在。每一条都对应一个具体的失败模式。

三档功能模型

档 做什么 默认 缓存代价 为什么这样定
Tier A 自动思考强度(off/low/high/max),模型锁定不变 开 无——只改 reasoningEffort 唯一能做到"零缓存代价"的优化,所以默认开
Tier B 自动模型路由(可跨 provider) 关 高——跨模型无缓存复用 收益常为负(见下方盈亏平衡),所以出厂关闭 + 硬门禁
Tier C skill 路由 关 中 首版仅占位,未实现

档位裁决:迟滞 + 不对称

档位切换不是"判定什么就用什么",中间有三道过滤:

规则 配置项 默认 防的是什么
置信度门 confidenceFloor 0.5 Jev 不确定时不猜,沿用 harness 默认档
迟滞窗口 hysteresisRounds 2 防止在档位间高频横跳——那会让每一轮都像首次请求
降档连续确认 downgradeStreak 2 防止一次误判把推理强度砍掉
手动覆盖 effort auto 手动值永远压过自动判定
单轮锁定 — 内置 同一轮的多步(工具循环)复用同一档位,不重复裁决
关键词豁免 — 内置 关键词回退是用户自己写下的字面指令,对它再要求连续确认会让回退路径价值减半,故立即生效

降档为什么不对称:升档的最坏后果是多花点钱,降档的最坏后果是答案质量塌掉。两者不该用同一套阈值。

档位夹取:跨模型的能力对齐

DSH 对不支持的推理强度是硬拒绝——llm 服务的 resolveCallConfig 文档明确写着 no clamping or aliasing is performed。而各家档位命名并不一致:

来源 档位
DeepSeek off low high max
OpenAI 风格 minimal low medium high
Anthropic 风格 low medium high xhigh max

所以每次裁决后都要夹取到该模型真正支持的档位。档位名先对齐到一条统一强度轴(off/none/disabled=0,minimal/low=1,medium=1.5,high=2,xhigh/max=3):

情形 行为 理由
精确命中 原样使用 —
低于该模型下界 取最省的那一档 用户要的是省,不擅自升级
高于上界 取最强的那一档 不能因为夹取而掉质量
区间内等距 取更强的一侧 同上,宁可多花
模型无推理元数据 一个档位都不设 设了必然被硬拒绝
发生夹取 写日志 + /jev why 显示 实际档位与判定不一致必须可见

模型路由五道闸

按下列顺序求值——先可行性,后平滑:

序 闸 拒绝原因 理由
0 用户已确认缓存风险 cache-risk-not-acknowledged 硬门禁在代码里,不靠文档提醒
1 必须是回合起点 not-turn-boundary 绝不在回合中途换模型
2 候选必须能解析 unresolvable-candidate 切过去只会得到一个必然失败的请求
3 成本比较 not-worth-it / no-pricing 硬可行性过滤;刻意排在粘滞之前,不可行的切换不该累积粘滞计数
4 粘滞(连续胜出) sticky-pending(n/N) 防抖:候选要连续 stickyRounds 轮胜出
5 冷却与预算 cooldown / switch-budget-exhausted 单会话切换次数上限 + 最小间隔轮数

盈亏平衡:为什么破缓存几乎不划算

设前缀 $N$ 个 token,降档每轮省下 $\Delta_{out}$ 个输出 token:

$$\underbrace{N,(P_{miss}-P_{hit})}{\text{破缓存代价}} ;<; \underbrace{\Delta{out}\cdot P_{out}}_{\text{降档收益}}$$

代入 DeepSeek Pro 峰值价(命中 $0.044 / 未命中 $1.32 / 输出 $3.96,每百万 token),即 $P_{out}=3P_{miss}$、$P_{hit}=P_{miss}/30$:

$$\Delta_{out}\cdot 3P_{miss} ;>; 0.967,N,P_{miss} ;\Longrightarrow; \boxed{\Delta_{out} > 0.32,N}$$

每破一次缓存,你得省下超过「前缀长度 1/3」的输出 token 才不亏。 50K 前缀意味着要省 16K 输出 token——而典型思考预算在 1–8K 量级。

这正是闸 3 在每一轮真实计算的东西,也是 Tier B 出厂关闭的量化依据。

实测基线(为什么"保护缓存"优先于"省思考 token")

从本机 ~/.dsh/sessions/**/session.jsonl.zstd 解压统计(1935 个 step):

未命中输入 :   1,311,751
缓存命中   : 532,722,304      ← 5.3 亿
命中率     :     99.75%
输出总计   :   1,140,708   (其中 reasoning 462,480 = 40.5%)

两个结论:

  1. 命中率已经接近榨干。任何把命中率打成 0 的操作都是灾难级。
  2. reasoning 占了全部输出 token 的 40.5%——思考强度确实是个大钱袋,值得调,但必须在不破坏缓存的前提下调。

命中率的时间形状(同一会话内连续 step)也值得注意:会话开头 1–2 步是冷启动(20% → 0%),随后爬到 97%+。真正的缓存损失在会话边界与前缀重写,不在档位切换。

同一会话的独立核实(直接从 session.v4.jsonl.zstd 的 439 个 usage 样本统计):

cacheReadTokens  = 172,263,296
inputTokens      =   1,047,071
outputTokens     =     532,830
命中率           =      99.40%

关于 reasoning 这一行的口径:上面的数字来自会话日志(跨多个 DSH build)。 但当前 DSH build 里没有任何适配器填充 reasoningTokens——DeepSeek、pi-ai、Anthropic/OpenAI 都不填(该字段只在 token-meter 类型与 UI 里被消费,没有生产者)。 所以插件在设置页显示 「未上报」而不是 0%:provider 不上报不等于没有思考。 这也意味着 Tier B 的成本闸在当前 build 上拿不到「预测节省」,会一律以 not-worth-it 拒绝——这是保守方向,符合「不确定就不动」。

缓存安全红线

红线 实现 回归测试
判定不进 prompt 前缀 只经 agent/request 返回替换的 LlmCallConfig 禁止出现 .messages = / systemPrompt;只允许赋值 reasoningEffort/model/provider
provider 只能与 model 成对切换 同一处赋值 契约测试断言成对出现
模型默认锁死 Tier B 默认关闭 + acknowledgeCacheRisk 硬门禁 闸 0 两个用例
不破缓存地降档 Tier A 只改 reasoningEffort 策略层 15 项用例
不在回合中途换模型 atTurnStart = step <= 1 闸 1 用例
破缓存要有净收益 成本闸 Δ_out > 0.32N(按目标路由价目算) 闸 3 用例

配置项

全部配置都在「设置 → 插件 → Jev 路由」里,DSH 由 Config schema 自动生成表单。带 .volatile() 的字段改动即时生效,不需要重启。

Tier A · 思考强度

字段 默认 说明
enabled true 总开关
effort auto 手动钉死档位:auto/off/low/high/max。手动值永远压过自动判定
confidenceFloor 0.5 低于此置信度弃权,沿用 harness 默认档
fallbackEffort high Jev 不可用时的关键词回退默认档
hysteresisRounds 2 迟滞窗口(轮)
downgradeStreak 2 降档需连续确认轮数。只作用于高错误代价的降档;低代价与关键词回退立即生效
riskCeiling 0.6 「低错误代价」上界(Jev 的 risk 分 0–3)。risk 不超过此值的降档立即生效
offFloorChars 280 off 地板的字符阈值:消息不短于此长度时不允许降到 off(0 = 关闭)。off 的语义是"琐碎到不需要思考",需要"短消息"这个正面证据;Jev 对中文准确率官方说明较低
syncSessionEffort true 把生效档位同步进会话配置(写 model/selection),让 DSH 原生的「模型后面的思考程度」跟随。关掉则原生界面永远停在会话设置上

全部可调项都在插件的设置页上(设置 → 插件 → Jev 路由),不必去 DSH 的通用设置里找。status.config 会公开全部可调字段的已解析值,所以新增配置项会自动出现在页面上。 | timeoutMs | 4000 | 单次判定超时;超时即回退,绝不阻塞用户请求。不要按热调用中位数设——实测冷启动 1367–1463ms、热调用 358–548ms,卡在中间会导致重启后首次判定必然超时 | | blockOnDecision | true | 首步是否等待判定结果;关闭后用上一轮判定,延迟更低 |

Tier B · 自动模型路由

字段 默认 说明
modelRouting false 自动模型路由总开关
acknowledgeCacheRisk false 硬门禁:不勾选则策略层拒绝一切模型切换
modelSwitchMode turn-boundary 只支持回合起点切换
modelAllowlist [] 候选模型。裸 id = 当前 provider;provider::model = 跨 provider(用双冒号,因为真实模型 id 里就有斜杠与冒号)
modelNotes [] "模型id: 说明",作为 Jev 的 criteria 描述
customPricing [] 非 DeepSeek 模型的自备价目:"provider::model=命中,未命中,输出"(USD/1M)
contextSafetyMargin 0.05 切换模型前的上下文窗口安全余量(比例)。比较「当前请求的未命中 inputTokens」与「目标模型窗口 ×(1−余量)」,装不下就拒绝;热缓存的 cacheReadTokens 由 provider 缓存路径单独处理。0 = 不留余量
stickyRounds 3 候选需连续胜出轮数
switchCooldown 2 两次切换最小间隔轮数
maxSwitchesPerSession 2 单会话切换上限
hitRateAlert 0.8 窗口命中率告警阈值

Tier C · 预留

字段 默认 说明
skillRouting false 首版未实现,仅占位

凭据与价格

字段 默认 说明
apiKeyRef TYPESAFE_API_KEY 存放 Key 的凭据名。密钥本体进 DSH 凭据存储,不写进 profile 配置
pricingAutoRefreshHours 24 价目刷新间隔(0 = 只在启动与手动触发时获取)。改动需重启
pricingCachePath '' 默认 ~/.dsh/jev-router/pricing.json。改动需重启
holidays [] 中国法定节假日(YYYY-MM-DD),用于 peak/off-peak 判定

界面

字段 默认 说明
showBadge true 输入框工具行的档位徽章。建议关闭:原生「模型后面的思考程度」是唯一真相,两个指示器重复(见下方说明)
namespace jev-router 设置 namespace(= loader entry id)。改动需重启

Key 的解析顺序(DSH 原生分层):继承的进程环境变量(只读,最高)> $DSH_HOME/.credentials.yaml(可写,设置页输入框写这里)> <cwd>/.env > $DSH_HOME/.env。 如果解析到的来源是进程环境变量,设置页的写入会被拒绝(只读层遮蔽可写层),此时会返回明确错误。


安装

本插件是 DSH bundle:同时提供 Host 半(lib/)与静态 Client 半(client/),并在 package.json 里声明 dsh.bundle.patch 与 dsh.client。

方式 A:link: 安装(推荐用于开发)

# 1) 把包链接进 profile。编辑 ~/.dsh/profiles/<profile>/package.json:
#      "dependencies": { "dsh-jev-router": "link:/path/to/dsh-jev-router" }
#      "dsh": { "profile": { "bundles": [ ..., "dsh-jev-router" ] } }

# 2) 安装并重启
cd ~/.dsh/profiles/<profile> && pnpm install

为什么是 link: 而不是 file::pnpm 对 file: 依赖使用硬链接——编辑源码会替换 inode 从而断开链接,运行时读到的是旧代码。link: 建立符号链接,源码改动即时可见,开发循环才成立。

方式 B:作为 bundle 安装(推荐用于使用)

# 从 GitHub 安装
npm install --no-save CSlawyer1985/dsh-jev-router
# 或本地路径
dsh plugin install /path/to/dsh-jev-router

或直接把包放进 profile 的 node_modules,并在 cordis.patch.yml 里插入:

- insert:
    - id: jev-router
      name: 'dsh-jev-router'

id 必须与 lib/namespace.js 的 DEFAULT_NAMESPACE 一致(契约测试守着这一点),否则 /jev 写的配置与挂载的 entry 对不上。

首次启用后必须重启一次

Host 半在插件管理操作后可能热生效,但前端半需要重启——client 模块清单在启动时就写进了首页的 __DSH_BOOT__。重启后浏览器再刷新一次页面即可看到徽章与设置页。


使用

  1. 配 Key:设置 → 插件 → Jev 路由 → 「Jev API Key」粘贴 TypeSafe API Key → 保存。保存后下一次对话即生效,不需要重启。密钥只写入 DSH 凭据存储,不会回显,也不进配置文件。

  2. 测连通:点连通——插件会用一句固定的探针消息真的打一次 Jev,然后告诉你结果。成功会显示版本号、判定档位、置信度与耗时:

    已接通 · jev-1.13.0 · off(置信度 1.00) · 430ms
    

    失败会明确说原因(未配置 / 请求失败 / 响应无法解析),不会假装成功。这个按钮让你不必靠"用一次看看"来判断是否配好。

两个指示器,该信哪个?

指示器 数据源 是否准
DSH 原生「模型后面的思考程度」 会话配置(model/selection) 准(同步不变量保证它等于实际请求档位)
本插件的输入框徽章(showBadge) 请求侧状态 准,但与原生重复

两者现在由同一条不变量保证一致。建议关掉插件徽章(设置页 → 输入框档位徽章 → 关), 让原生指示器成为唯一真相——它在模型名后面,更显眼,也不会多一个需要维护的一致性问题。

  1. 确认生效:发 /jev status 或 /jev why,看判定的来源——jev(jev-1.13.0) 表示走真 Jev;heuristic 表示没配 Key 或调用失败(超时/限流)。
  2. 日常使用:什么都不用做。Tier A 默认开启,Jev 会按每条消息的难度自动升降档位。
  3. 手动干预:点徽章循环切档,或 /jev effort high 钉死;想交回自动用 /jev effort auto。
  4. 开 Tier B(可选):设置页打开「自动模型路由」→ 三步确认 → 第二步会拿你自己的实测命中率与前缀长度算出盈亏平衡点,勾选风险确认后才能保存。

目录结构

dsh-jev-router/
├── lib/
│   ├── index.js         # Host 半入口:扩展点接线 + 运行时接口(配置/凭据/回滚/快照)
│   ├── classify.js      # Jev 问题构造 / 响应解析 / 关键词回退表
│   ├── jev-client.js    # TypeSafe HTTP 客户端(超时 + 429 退避 + 静默降级)
│   ├── policy.js        # 缓存安全策略(迟滞 / 五道闸 / 盈亏平衡)—— 纯函数
│   ├── metrics.js       # usage 累计、命中率、节省估算 —— 纯函数
│   ├── pricing.js       # 官网价目抓取解析 + 自备价目 —— 纯函数 + 磁盘缓存
│   ├── model-caps.js    # 模型能力查询缓存 + 档位夹取 + 白名单解析 —— 纯函数
│   ├── config.js        # 配置解析层(volatile 拆包)+ DEFAULTS 唯一真源
│   ├── routes.js        # 同源 HTTP 路由(前端半取数)
│   ├── commands.js      # /jev 命令族
│   ├── settings.js      # Config schema(schemastery)+ 设置页接线
│   └── namespace.js     # 共享常量
├── client/
│   └── client.js        # 静态 Client 模块(__ModuleLoader__.load):设置页 + 徽章
├── scripts/
│   ├── verify-install.sh    # 安装自检(13 项,含前端资产交付)
│   ├── probe-frontend.mjs   # 前端启动探针(CDP + 无头 Chrome,覆盖"是否激活")
│   ├── try-jev.mjs          # Jev 判定评测:同一批消息喂给 Jev 与关键词表做对比
│   └── rollback.sh          # 一键回滚(禁用插件 + 从备份恢复 patch)
├── docs/
│   ├── CACHE_SAFETY.md      # 缓存安全设计:实测基线、源码证据、盈亏平衡推导、A/B 方案
│   └── DEVELOPMENT_PLAN.md  # 开发规划、逐条缺陷记录、验证方法
├── test/                    # 201 项测试(11 个文件,分六层)
├── cordis.patch.yml         # bundle patch(loader 挂载行)
├── package.json             # dsh.bundle.patch + dsh.client 声明
└── README.md

测试与验证

node --test test/*.test.js      # 201 项

# 拿真实消息测 Jev 的判定质量(需要已配置 Key)
node scripts/try-jev.mjs                    # 内置样例集
node scripts/try-jev.mjs "你的消息" ...     # 自己的消息
node scripts/try-jev.mjs --json             # 输出 JSON

try-jev.mjs 会把同一批消息同时喂给 Jev 与关键词表,把差异直接摆出来。实测样例(8 条,实际版本 jev-1.13.0,中位耗时 426ms):

消息 Jev 关键词表
你好 off (1.00) off
今天几号? off (0.67) high
把这段 JSON 格式化成两空格缩进 low (0.84) high
帮我彻底重构这个模块的架构… high (0.65) high
这段代码线上偶发超时…帮我定位根因 high (0.99) high
ultrathink 一下这个一致性协议有没有漏洞 max (0.64) max
不要完整全量思考,简单说就行 low (0.99) max
别 ultrathink,我只要一个是或否 low (0.76) max

不一致 4/8,且每一条都是 Jev 对、关键词表错。最后两条是否定句——关键词表看到「完整全量思考」「ultrathink」就判最高档,而用户的意思正好相反。这就是「语义判定」与「关键词匹配」的差别。

测试分层

层 文件 覆盖
单元 policy / pricing / classify / metrics / routes 盈亏平衡数学、迟滞与降档确认、五道闸(含硬门禁)、价目解析(含 rowspan 错位与结构不识别)、peak 时段判定、同源校验
能力适配 model-caps 强度轴对齐、夹取规则(上下界/等距/无元数据)、provider::model 解析(含 ollama 那种带斜杠冒号的 id)、能力缓存的正/负 TTL
配置解析 config volatile 引用拆包、缺省回退、整份配置都是引用时 resolveConfig 必须给出裸布尔/数字/字符串
会话生命周期 session-store 同一会话只创建一次、TTL 过期回收、LRU 容量淘汰、disposal 只计数不删状态、create() 返回值不得被额外包装
前端半 client 用桩 React + 仿真语义的槽位桩(未声明就抛、inject 挂起等待)真正渲染设置页与徽章;覆盖"槽位未声明时 apply 绝不能抛"这条事故回归
集成 integration 假 Cordis 上下文把插件真正 apply() 起来,端到端跑 inbox → Jev → request 改写、同轮多 step 复用、llm/stream 计量、四条路由、命令族、Tier B 硬门禁与放行、跨 provider 路由
契约 contract 禁止改写 messages、禁止注册 systemPrompt、只允许改 reasoningEffort/model/provider、provider 与 model 成对切换、volatile 字段约定、默认值单一真源

隔离实例验证(不碰在用的实例)

不必重启正在使用的 DSH 也能做完整实机验证:

# 1) 装一份与运行时同版本的 dsh
npm install --prefix /tmp/jev-verify/pkg @deepseek-ai/dsh@<版本>

# 2) 造独立 HOME + profile(bundles 里加 dsh-jev-router,node_modules 里 link 过去)

# 3) 启动(--profile 是全局选项,必须写在其他选项之前)
DSH_HOME=/tmp/jev-verify/home/.dsh \
  node /tmp/jev-verify/pkg/node_modules/@deepseek-ai/dsh/lib/bin.js \
  --profile jevtest --port 19488 --no-open

# 4) Host 侧自检(13 项)
bash scripts/verify-install.sh http://127.0.0.1:19488 "$TOKEN"

# 5) 前端启动探针(Host 检查覆盖不到这一层,必须单独跑)
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless=new --disable-gpu \
  --remote-debugging-port=9222 --user-data-dir=/tmp/jev-chrome about:blank &
node scripts/probe-frontend.mjs "http://127.0.0.1:19488/?token=$TOKEN" 15000

前端探针为什么必须有

因为前端半是唯一在 Node 侧完全测不到的代码,而它的失败是致命的——client fiber 一旦 FAILED,前端启动检查会判定:

web boot: 1 entry did not activate
dsh-jev-router: failed

然后 DSH 直接无法启动,用户只能用「禁用第三方插件、备份 profile patch 并重启」的安全模式自救。

本插件确实发生过这样一次事故:client.js 的 apply() 直接调 slots.register(…),而槽位由属主插件声明、顺序没有保证;槽位未声明时 register 抛 is not declared → fiber FAILED → 应用起不来。

关键在于:当时 __DSH_BOOT__ 里有它、合并包能下载、包内确实有我的代码——三件事都成立,应用照样起不来。 因为缺的这层验证是"模块是否激活",而不是"资产是否交付"。scripts/probe-frontend.mjs 就是补这一层的。


三个必须遵守的 DSH 约定(都是实机启动才暴露的踩坑记录)

1. 前端半的 apply() 绝不能抛

见上一节。两条硬性做法:

  • 槽位注册必须走 slots.inject(slotName, () => slots.register(...))——它天然等待声明完成(已发布的 dshmarket 插件用的就是这个模式);
  • 整个 apply 外面再包一层 try/catch,任何界面问题只降级为控制台告警。

2. .volatile() 字段在插件里是引用对象,不是裸值

DSH 把可运行时修改的配置字段交给插件时包了一层(来自 @deepseek-ai/cosmokit):

Object.freeze({ get: () => current, [write]: (v) => { current = v } })

后果:config.modelRouting === true 恒为 false(Tier B 永远打不开)、config.confidenceFloor 参与比较得到 NaN(置信度门失效)、config.timeoutMs 进 setTimeout 变成 NaN。

本插件通过 lib/config.js 的 readConfig() / resolveConfig() 统一拆包,策略层只接收解析后的裸值,并有契约测试禁止任何 live.<field> 直读。

3. client 模块是合并包,没有单独 URL

前端半被打进 plugins/??<idA>/client.js,<idB>/client.js…&rev=…,要读首页 __DSH_BOOT__ 图谱才知道真实 URL。所以自检脚本是从图谱里取 URL 再验证,而不是硬拼 /plugins/<id>/client.js。


设计原则

  1. 判定不进前缀——判定结果只存在于调用配置里。这是唯一不可妥协的一条,有契约测试守着。
  2. 不确定就不动——置信度不足弃权,模型能力解析不了就不设档位,候选模型解析不了就拒绝切换。宁可不动,也不猜。
  3. 代价必须量化——"换模型便宜"这种直觉不构成理由。破缓存的代价按目标路由的真实价目逐轮计算,算不过就不做。
  4. 门禁在代码里,不在文档里——acknowledgeCacheRisk 未勾选时策略层直接拒绝,不依赖用户读文档。
  5. 实际行为必须可见——夹取写日志、闸门拒绝写日志、/jev why 展示完整裁决链。静默降级是可维护性的敌人。
  6. 纯函数优先——策略、度量、价目解析、能力夹取全部是纯函数,能单测就不用集成测试兜。
  7. 界面故障不得阻断核心——前端半整体包 try/catch;插件最坏情况是"没有界面",绝不是"起不来"。
  8. 默认值只有一份真源——lib/config.js 的 DEFAULTS 同时供 schema 与解析层使用,契约测试禁止 schema 里出现硬编码默认值。

迭代日志

日期 版本 类型 要点
2026-09-25 v0.1.0 初始发布 Tier A 思考强度路由(迟滞 + 置信度门 + 关键词回退)+ Tier B 自动模型路由(五道闸 + 硬门禁 + 三步确认 + 回滚)+ /jev 命令族 + 设置页 + 徽章 + 署名
2026-09-25 v0.1.0 修复 同步条件从「裁决 changed」改为不变量「会话配置 == 实际请求档位」:早先只在裁决变化时写 model/selection,而同轮后续 step 走另一条路径改请求档位却不写,于是原生指示器会停在旧值上、两个指示器各说各话。现在逐次比对,任何路径下都收敛且不产生重复事件
2026-09-25 v0.1.0 修复 showBadge 是个死开关:徽章无条件注册,该配置只在设置页的开关上被读、从未参与判断,所以"关掉徽章"无效。改为注册一个自取状态的包装组件,showBadge === false 时返回 null,设置一改立即生效
2026-09-25 v0.1.0 修复 弃权会保留上一次的降档 → 真实任务在 thinking: disabled 下运行(质量事故):先发「你好」立即降到 off,接着发一条几百字的批改任务,Jev 判 low 但置信度 0.45 < 门槛 → 弃权 → 档位留在 off。改为:降档必须由「对当前这条消息的判定」持续支撑,弃权时撤销上一次的降档、回退到 harness 默认(并绕过迟滞,因为这是安全方向的修正)
2026-09-25 v0.1.0 修复 新增 offFloorChars(默认 280):长消息不得被判为「琐碎」。针对 Jev 官方声明的 CJK 准确率较低——长中文请求被判 off 是已知高风险组合
2026-09-25 v0.1.0 更正 GPT-5.6 的 1.05M 与 Codex 的 272K 不是同一个概念:同一 pi-ai 目录里,openai-codex/openai 条目写 272K,而 OpenRouter / Vercel / Azure / Bedrock / Copilot 等条目写 1.05M;外部资料也明确 272K 是长上下文计价分界。此前把 272K 误称为模型硬上限,现已更正
2026-09-25 v0.1.0 修复 模型路由的上下文窗口闸(context-too-large):原先五道闸只算缓存代价,完全没检查「目标模型装不装得下当前会话」。实测事故——一个已累积 556K token 的会话切到窗口 272K 的模型,请求被 provider 直接拒绝(CONTEXT_WINDOW_EXCEEDED)。现在从 llm.resolveModelInfo 读 context.contextWindow,与当前会话已用 token 比较,超出窗口×(1−contextSafetyMargin) 即拒绝。这道闸排在成本闸之前:缓存代价是花钱,窗口不够是请求必然失败
2026-09-25 v0.1.0 修复 判定缺失时会绕过降档确认:早先 no-decision 沿用上一轮判定,carry-downgrade 会直接降档——它比 low-confidence(Jev 至少还答了)更不确定却更激进。改为与弃权一致:判定缺失只撤销未被支撑的降档,绝不据此降档;顺带删掉只写不读的死代码 streak/lastDecided
2026-09-25 v0.1.0 修复 inbox hook 把合成消息也发给 Jev:agent/inbox/inserted 对每一条消息触发,实测会混入 goal 轮次、审批变更、tool-jobs 通知等(整段会话 inserted 的 source.kind 分布:user 30 / goal 3 / user-approval 1 / tool-jobs 1 / tool-goal 1),它们既浪费 Jev 调用,又与用户消息竞态覆盖决策。改为只分类 source.kind === 'user' 的真实用户输入(source 缺失时保守不跳过),新增 inboxStats 诊断
2026-09-25 v0.1.0 修复 setConfig 切断 volatile 引用:Object.assign(live, patch) 把 Object.freeze({get,[write]}) 引用替换成普通值,之后用户在 DSH 通用设置页改同一字段插件读到旧值。移除该赋值(settings.update 已通过 write 符号写回,readConfig 每次 .get() 即最新)
2026-09-25 v0.1.0 修复 DSH 原生档位指示器永不跟随:Tier A 只改单次调用的 reasoningEffort,而原生「模型后面的思考程度」读的是会话配置——所以界面永远停在会话设置上,用户完全看不到插件在工作。新增 syncSessionEffort(默认开):档位变化时追加 model/selection(内置选择器走的同一条通道,字段规范 disposition(["provider","model"], ["reasoningEffort"])),原生界面随之更新。同步失败会记录到诊断,不静默
2026-09-25 v0.1.0 修复 占位徽章有写全局配置的副作用:会话未就绪时显示 ⚡ ? 的那个按钮,其 onClick 会把全局手动档位从 auto 改成下一档(通常是 off)。点一下就让所有会话进入 manual-override、Jev 判定被完全绕过,现象是「思考强度完全不变化」。占位符改为 disabled 且绝不带 onClick
2026-09-25 v0.1.0 体验 手动档位时徽章用 🔒 区分(自动为 ⚡),并在 title 里说明「Jev 自动判定已被绕过;点一下回到 auto」;点击语义改为手动时一次点击直接回 auto,不再继续往下降档
2026-09-25 v0.1.0 修复 多会话下徽章显示别的会话的档位:状态接口只按 agents.list()[0] 或"最近活跃会话"定位,开了两个会话时会串号(用户在 A 会话看到 B 会话的 off,以为功能坏了)。改为前端传 ?session=<id> 精确取数(槽位契约的 standardProps 提供 sessionId),并在拿不到会话时宁可不显示也不显示错的值
2026-09-25 v0.1.0 修复 指定了不存在的会话时绝不用别的会话顶替(返回 scope: none);新增 statusRequests 诊断统计,用于事后确认徽章是否真的带上了会话 id
2026-09-25 v0.1.0 修复 判定超时默认值过紧导致重启后首次判定静默降级:实测冷启动 1367–1463ms、热调用 358–548ms,而默认 timeoutMs 是 1500ms——正好卡在两者之间,于是每次重启后第一条消息都超时走关键词回退。默认改为 4000ms
2026-09-25 v0.1.0 修复 判定失败是静默的(无日志、无痕迹)→ 新增 jevFailure 诊断字段与告警日志,并区分 not-configured / timeout / http / network / parse,界面能直接告诉你该去配 Key 还是调超时
2026-09-25 v0.1.0 修复 设置页只有 4 个可调项(其余配置项在插件页里根本不存在)→ status.config 改为公开全部可调字段的已解析值;设置页补齐判定时机 / 模型路由参数 / 回退档位等全部调参项
2026-09-25 v0.1.0 体验 Key 区块简化为一行三按钮(输入框 + 保存 / 连通 / 清除),密钥永不回显;新增 POST /jev-router/test 与「连通」按钮,用固定探针消息真的打一次 Jev 并报告版本 / 档位 / 置信度 / 耗时
2026-09-25 v0.1.0 体验 降档改为「每条消息立即生效」:新增 riskCeiling,用 Jev 自己的错误代价分做豁免——低代价降档不再等连续确认,高代价降档的保护完整保留;另新增「判定缺失时沿用上一轮意图」
2026-09-25 v0.1.0 修复 agent/request 里的 async 未 await,导致状态路由返回 {}(JSON.stringify(Promise))
2026-09-25 v0.1.0 修复 闸门顺序:成本闸前移到粘滞闸之前——不可行的切换不该累积粘滞计数
2026-09-25 v0.1.0 修复 配置字段未标 .volatile() 导致设置写入被拒(has no volatile fields)
2026-09-25 v0.1.0 修复 Jev 响应里两个 model 混淆(顶层是作答版本号,路由目标在 answers.model.choice)→ Tier B 永远不生效
2026-09-25 v0.1.0 能力 新增 model-caps.js:向 llm 注册表问模型支持的推理档位并夹取,修复硬编码档位集合导致非 DeepSeek 模型请求失败
2026-09-25 v0.1.0 能力 跨 provider 路由:provider::model 条目、候选可解析校验、成本闸改用目标路由价目、新增 customPricing
2026-09-25 v0.1.0 修复 前端半导致 DSH 无法启动(槽位声明竞态 → client fiber FAILED → web boot: 1 entry did not activate)→ 改走 slots.inject + apply 整体 try/catch,新增前端探针
2026-09-25 v0.1.0 能力 新增设置页密钥输入框 + POST /jev-router/credential,密钥经 DSH 凭据存储写入(不进 profile 配置),保存后下一次判定即生效
2026-09-25 v0.1.0 能力 新增 scripts/rollback.sh:一键禁用插件并从备份恢复 profile patch(不需要 DSH 在运行)
2026-09-25 v0.1.0 重构 会话状态生命周期收敛到 lib/session-store.js:按会话 id 保留(而非 agent 实例),TTL 30 分钟 + LRU 32 个淘汰,取代原来的无上限 Map;agent/disposed 只计数不删状态
2026-09-25 v0.1.0 修复 快照在两轮之间丢失会话信息(agents.list()[0] 拿不到 live agent 时为空)→ 新增 sessions.mostRecent() 回落到最近活跃会话
2026-09-25 v0.1.0 更正 一次误诊的更正:曾把"连发两条简单消息仍是 downgrade-pending"归因于 agent/disposed 清空状态。加诊断后反证——disposedSignals 始终为 0,且会话日志显示两次观察之间应用被重启过(request/header.reason 从 series 变 resume)。真实原因是进程重启清空内存态,非该事件。详见 开发规划 第 10 条
2026-09-25 v0.1.0 能力 新增诊断字段(sessionsCreated / sessionsEvicted / disposedSignals / lowStreak / 最近 8 次裁决)——这类不报错的失效只能靠可观测性发现
2026-09-25 v0.1.0 能力 新增 scripts/try-jev.mjs 判定评测;修正 reasoningTokens 计量口径(当前 build 无人上报该字段 → 显示「未上报」而非假 0%)。已用会话日志核实:439 个 usage 样本的字段集合为 cacheReadTokens / cacheWriteTokens / inputTokens / outputTokens / totalTokens,无 reasoningTokens

许可证

MIT © chenshi.ai

Jev 是 TypeSafe AI 的云端服务,本插件只是它的客户端。使用 Jev 需遵守 TypeSafe 的服务条款;本插件不包含任何模型权重。

—/ 5

No ratings yet

Verified DSH bundle

Commit 17541cfbbda3

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout