← 返回列表
⚠ 装前注意
dsh-jev-prune — 面向 DeepSeek Harness 的 Jev 判定式上下文压缩
基本兼容但装前注意:未发布到 npm registry,仅可从源码安装 · 最近上游提交 2026/9/24 · 已提供中文文档
DeepSeek Harness 中由 Jev 判定的上下文压缩:语义化工具结果剪枝 + 确定性回执压缩
综合分
38
GitHub 分
38
用户评分
—
★ Stars
3
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add yangyu666/dsh-jev-prune未发布到 npm registry,仅可从源码安装,改用 GitHub 源安装
信任档位:已验证本站已于 1 天前真实安装成功
- 是什么
- dsh 原生插件 · chat
- 装得上吗
- 本站已真实安装成功(非静态推断)
- 安全吗
- 本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
- 还在维护吗
- 活跃:最近一次提交在 1 天前
档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →
🟢实装验证通过· 2026/9/24
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/24(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查⚠ 装前注意
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-jev-prune(未发布到 npm,仅可源码安装)
✓Node 引擎要求 ^22.19.0 || >=24.0.0 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
未发布到 npm registry,仅可从源码安装
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/25 05:18:27
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-llm@deepseek-ai/dsh-tools@deepseek-ai/schemastery用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
由 DeepSeek 最新模型翻译生成dsh-jev-prune
dsh-jev-prune — 面向 DeepSeek Harness 的 Jev 判定式上下文压缩
面向 DeepSeek Harness 的 Jev 判定式上下文压缩。
来自 TypeSafe Jev 的结构化判定驱动 DSH 的两层上下文压缩。压缩算法本身未作改动;判定后端可插拔(Jev / 规则 / 自托管模型)。
English · 简体中文
license node dsh CI smoke checks
它解决的问题
DSH 内置的上下文回收是纯体积式的。一旦某个工具结果超过大小阈值,其中间部分就会被切掉,只保留头尾;而区域压缩则是让模型写一段摘要来替代旧历史。第一种方式无法区分“这个结果很大但我仍然需要它”和“这个已经用完了”,第二种方式则容易引发摘要幻觉。
本插件在这两处都用 Jev 的结构化输出(noul / choice,返回校准后的概率)来替代决策,遵循一条设计原则:
不应由模型生成的内容,就不由模型生成。 裁剪只决定保留还是丢弃;原始文本逐字保留。区域压缩注入的是由代码生成的确定性回执,其中不含任何模型推断。
两层机制
两层机制:结果裁剪与回执压缩
| 层级 | 拦截点 | DSH 默认行为 | 本插件 |
|---|---|---|---|
| 1 · 结果裁剪 | ctx.toolResultPruner.pruneSession | 一旦超过 thresholdChars 就切掉中间部分 | Jev 逐个工具结果判定其是否仍会被需要。仍需要的无论多大都绝不裁剪;已过期的无论多小都裁剪(除非短于 minCharsToPrune);无法获得判定时回退到 DSH 原有行为 |
| 2 · 回执压缩 | ctx.compaction.summarize + compactRegion | 模型读取原始历史并写出摘要 | 将已用尽的只读探测(完整的 tool-call + tool/result 对)移出表层,并注入确定性回执:工具名、命令、路径、字符数和 seq 全部由代码计算 |
第 2 层的回执如下所示:
[已压缩 · 确定性回执] 原历史 s25–s27 是 1 次工具调用(共约 16489 字符输出),
为释放上下文已移出。以下为事实清单(代码生成,无模型推断):
· s27 read:C:\Users\you\project\src\state.js → 16489 字符输出
原始事件仍完整保存在会话日志中(seqs 25–27)。需要内容时重跑相同命令/读取相同文件即可。
回执正文由插件自身的 JavaScript 发出,因此其措辞目前是中文——/jev 状态输出也是如此。jev_ 工具描述已经是英文。本地化运行时字符串是一项单独的改动。
门控(第 2 层)
将整个配对移出表面是破坏性的,因此默认策略刻意保守。以下每一项都必须成立:
- 两个轴的交集:result(内容是否仍被需要)和 effect(该调用是否改变了会话之外的状态)必须各自落在本次会话的尾部 compactQuantile 之内
- 该工具不在 neverCompactTools 中(写入类调用由硬性规则排除,绝不通过概率排除)
- 证据守卫:匹配 error / assert / fail / todo 及类似模式的结果绝不会被移出。如果第 1 层已经裁剪了某个结果,守卫会沿着 sourceEventSeqs 追踪并扫描原始事件
- 助手文本超过 maxStepTextChars,或其 reasoning 超过 maxStepReasoningChars 的步骤绝不会被移出。两者被有意分开度量:较长的 text 意味着该步骤正在给出值得保留的结论,而较长的 reasoning 只是草稿性工作——将它们合并为一个预算会让推理长度单独就悄无声息地关闭第 2 层
- 最近 compactPreserveRecent 个节点之内的任何内容都会被第 2 层跳过(第 1 层使用 preserveRecent)
- 范围的两端都必须满足 DSH 的工具配对平衡;该跨度必须至少节省 compactMinChars 个字符;并且回执必须保持在原始内容 token 数的 receiptMaxRatio 以下
概率被作为相对分位数使用,而绝不作为固定阈值:小型评判模型的输出分布很窄,只有在单个会话内的相对排序才携带稳定信息。
小样本下的降级。 在以写入/执行为主的会话中,只读工具往往只占少数(实测:六分之一),这可能使分位数样本量只有两三个条目——太少,无法让排序具有任何意义。该模式不会就此放弃,而是降级为绝对下限:两个轴都必须低于 floorThreshold(默认 0.2,比 compactThreshold 严格得多,以补偿缺失的相对信息)。如果样本量低于 minCandidatesForFloor(默认 2),则不会移出任何内容——单个样本不构成分布。默认值从 3 下调到 2,是为了让批量读取会话真正产生的双候选样本量不会被直接跳过;单个样本仍然绝不生效。降级始终会在报告和心跳中上报;它绝不会悄无声息地发生。
回执归属(围栏)
第 2 层通过一条短管道注入:插件渲染回执 → 将其存入 pendingReceipt → 调用 compactRegion → DSH 回调进入 summarize,回执即在此处被移交。
问题在于 compactRegion 是异步的,而 summarize 接收不到任何范围标识——它无法判断自己正在为哪一次压缩而被调用。因此,如果在我们等待期间有其他任何东西(比如 DSH 自身的自动压缩)启动了压缩,两者就会共享同一个以会话为键的槽位:那个其他 span 会被我们的回执替换掉,而我们本打算压缩的 span 则回退为模型摘要。 两份历史都被破坏,且双方都不会报告错误。
修复方案为每次压缩签发一个所有权令牌(fencing token),并附带三项约束:
| 机制 | 效果 |
|---|---|
| 所有权令牌 | 生产者递增 fenceCounter 并记录 activeFence;summarize 仅在 entry.fence === activeFence 时注入 |
| 一次性认领 | entry.claimed 会锁存,因此单次压缩内重复调用 summarize 不会重复注入 |
| 所有权检查 | compactRegion 返回后校验令牌;若已被接管,则设置 action.fenceLost = true,并绝不报告虚假的成功 |
finally 只清除我们自己的令牌——原始实现会无条件删除会话条目,从而抹掉另一次压缩的待处理回执。接管次数会计入 receiptFenceMisses,并在非零时呈现在状态报告中;在这种情况下,span 会回退为模型摘要,这是安全的方向。
要求
- Node ^22.19.0 || >=24.0.0
- dsh(@deepseek-ai/dsh),且基础 bundle 已加载到 profile 中(tool-result-pruner 和 compaction-basic 默认包含)
- 一个 TypeSafe API key(TYPESAFE_API_KEY 环境变量)
- 运行时对等依赖:@deepseek-ai/schemastery、@deepseek-ai/dsh-tools(由宿主提供)
- 可选动态依赖:来自 @deepseek-ai/dsh-llm 的 freezeMessage(缺失时回退为浅拷贝;插件仍可正常工作)
- 使用的宿主服务:toolResultPruner、compaction、tools、commands、llm,以及 tokenMeter。tokenMeter 被压力门控使用;如果你的宿主未注册它,基于比率的门控就无法比较任何东西——请将 softLimit / compactSoftLimit 设为绝对 token 数,或使用 judgeOn: 'always' / compactOn: 'always'(参见压力门控的失败方向)。缺失的计量器绝不会静默禁用某一层:该轮的门控会保持未武装状态,并以 warn 级别记录原因。
版本对齐:@deepseek-ai/dsh-tools 的对等版本范围是 ^0.1.5-rc.2——经测试的版本 0.1.5-rc.2 位于 npm 的 next 标签上,而非 latest。已提交 lockfile(devDependencies 固定了经测试的版本),因此 npm ci 可复现完全相同的测试条件。
兼容性:已针对 @deepseek-ai/dsh@0.1.5-rc.2 测试。DSH 0.1.x 是预发布线,事件形态和服务名称可能在不同 rc 版本之间发生变化。升级 DSH 后,请重新运行 npm run check 和冒烟测试,并在真实会话中调用一次 jev_probe_shapes 以验证字段假设。
项目文档:贡献指南、架构、移植契约 和 配置示例。
安装
Install from a local directory (the repo directory name matches the package name)
dsh plugin --profile web add link:/absolute/path/to/dsh-jev-prune
Confirm it made it into the config tree
dsh --profile web --dump-config | grep jev-prune
在没有 pnpm 的机器上,等效的手动接线(幂等)为:
node wire_profile.mjs
配置
| 键 | 默认值 | 描述 |
|---|---|---|
| enabled | true | 总开关 |
| model | jev-latest | 评判模型 |
| keepMode | budget | 第 1 层决策规则。budget:裁剪多少由压力差比率决定,哪些结果由 Jev 的排名决定(见下文)。absolute:旧版固定阈值行为 |
| keepThreshold | 0.5 | 第 1 层:在 absolute 模式下,P(keep) ≥ 此值表示不裁剪;在 budget 模式下它仅是一个保护上限(达到或超过此值的结果永远不会进入候选池) |
| alwaysTrimRatio | 0.5 | 第 1 层:固定裁剪比率,仅在 judgeOn: 'always' 下使用(该模式没有可用于推导比率的压力信号)。预算为候选池总字符增益的这一比例。pressure 模式根据差值计算比率并忽略此键 |
| volumeBudgetThresholdChars | 8192 | ⚠️ 已弃用(仅为兼容性保留):早期的 budget 模式将预算锚定在容量规则上;现在改用压力差比率,因此此键不再生效 |
| keepFloorThreshold / minCandidatesForBudget | 0.2 / 4 | budget 模式的小样本回退:当被评判的候选少于 4 个时,只有 P(keep) = 2 永远不为真) |
请注意,0 对大多数键来说是一个合法值(headChars = 0 不保留头部;preserveRecent = 0 不保护任何内容)——它不会被当作“未设置”。
会话内用法
| 入口点 | 用途 |
|---|---|
| /jev、jev_prune_status | 两层的账本:缓存的判定、累计节省量、接管状态、工具名索引 |
| jev_prune_now | 强制执行一次第 1 层修剪 |
| jev_compact_now(支持 dryRun) | 强制执行一次第 2 层收据压缩,并列出每个门控的排除计数;在 dryRun 下它还会打印完整的收据文本 |
| jev_restore | 安全阀:取回检查点移出的原始文本(只读) |
| jev_probe_shapes | 打印真实的事件形态和解析后的工具名,以便适配不同的 DSH 版本 |
在正常操作中,两层都由上下文压力自动驱动;无需手动步骤。
设计说明
- 读取概率,从不读取生成的文本:判定始终从结构化响应中读取 answers[id].noul
- 状态携带任务目标:“这是否仍然有用”实际上意味着“相对于目标是否有用”,因此状态头部携带最近的用户指令
- 结构由代码决定,语义由模型决定:写入型调用和近期窗口由硬规则保证,绝不交给概率
- 影子价格协议逐字对齐 DSH 的 compaction/prune + surfaceOp: replace,因此纯消费者可以复用相同的 token 核算
- 按 Unicode 码点切片,绝不拆分代理对;token 估算使用逐词校正算法,适用于中英文混合文本
- 钩子顺序至关重要:判定钩子被 prepend(ctx.on(..., true)),因此它会在基础包的 compaction-basic 之前运行。该包是 pruner.pruneSession 的唯一调用者(两个调用点都在其中——:888 用于上下文溢出,:902 用于压力),因此它也是第 1 层判定结果被消费的唯一位置。如果不使用 prepend 注册,修剪将读取上一轮的判定结果,每个新结果都会回退到大小规则——第 1 层静默失效,任何地方都不会报错。smoke_apply.mjs 的 M 块通过在调用 pruneSession 的那一刻观察判定计数器来固定这一点。
测试
npm install # 拉取对等依赖(已提交 lockfile;CI 使用 npm ci)
npm run check # 纯函数自检:token 估算 / 状态组装 / 候选选择 / 两层的决策 / 打包完整性
冒烟测试(不需要完整的 DSH 依赖树,约 4 秒)。注意,插件入口静态导入了两个 peer 依赖,因此在干净目录中请先安装它们(失败信息中也会这样提示):
npm install @deepseek-ai/schemastery @deepseek-ai/dsh-tools
cp {index,jev,state,prune,receipt}.js package.json /node_modules/dsh-jev-prune/
cp smoke_apply.mjs / && cd / && node smoke_apply.mjs
测试脚本和辅助工具(check.js / smoke_apply.mjs / inspect_session.mjs / verify_real_shapes.mjs / wire_profile.mjs)都随 npm 包一起发布,因此在已安装的副本中直接运行 npm run check 即可。CI(.github/workflows/ci.yml)运行两个任务:一个仅依赖 peer 依赖的快速冒烟任务,以及一个基于完整 DSH 依赖树的集成任务。
Coverage: the takeover of both interception points, the full decision path of both layers, the append protocol, receipt injection and its ownership (fence), concurrent-compaction races, every gating branch (with counterfactual controls), the text/reasoning split, small-population degradation, out-of-range config clamping, judge retries and per-batch isolation (including per-pass vs lifetime counter semantics), non-duplicated batch accounting, pressure gates failing closed in the same direction while still acting when the threshold is an absolute count, token-estimate calibration against a holdout set, the compaction quota, alwaysTrimRatio actually moving the budget (with a precondition assert that the run took the budget path and not the small-population fallback), a missing session exiting gracefully instead of throwing, the judge hook being prepended ahead of the base bundle's compaction-basic (observed at the exact moment pruneSession is called), a skipped layer-2 pass recording why* it was skipped (the blocked reason plus the per-reason exclusion counts — previously only the success path wrote a note, so the one path you actually need to debug was the one that stayed silent), the degraded-mode floor pinned at both readings (the mechanism, with the floor passed explicitly, and the default — the exported constant is now the single source of truth, so it can no longer diverge from computeEligibleSeqs's own defaults), and shell-type tools being excluded by default (the pwsh Remove-Item regression case).
The fake ctx in smoke_apply.mjs mirrors cordis's listener model, not just its method names: multiple listeners per event, prepend, and waterfall ordering — where a listener that never calls next() vetoes the rest of the chain, including the host's built-in behaviour. Modelling it as a one-handler-per-event map hid the ordering contract completely: two listeners silently overwrote each other, and the prepend flag was ignored.
Test boundaries (what CI actually verifies): pure-function logic, takeover and the append protocol under a fake ctx, plus — in the integration job — "the plugin module loads against the real dependency tree and freezeMessage is available". Not covered by CI: service takeover inside a live DSH host and event-shape drift between rc versions — verify those with jev_probe_shapes in a real session.
Repository layout
├── index.js # Plugin entry: config, both interception points, pre-step orchestration, commands and tools
├── prune.js # Layer 1: code-point slicing, per-node decisions, shadow-price protocol
├── receipt.js # Layer 2: tool-pairing balance, range selection, evidence guard, receipt rendering
├── state.js # DSH events → judge state: goal extraction, the two question axes, event-shape probes
├── jev.js # Jev client (structured noul batch API) + token estimation
├── check.js # Pure-function self-checks
├── smoke_apply.mjs # Smoke test (real apply on a fake ctx)
├── wire_profile.mjs # Manual install path for machines without pnpm
├── inspect_session.mjs # Offline inspector for session logs (multi-frame zstd JSONL)
├── verify_real_shapes.mjs # Offline regression of tool-name resolution against real session logs
├── assets/ # Banner and diagrams (SVG sources + rendered PNGs)
└── cordis.patch.yml # Install contract
Privacy
The plugin sends session history text — including file paths, code snippets and command output — to the TypeSafe API for judgment. Assess this yourself before working on sensitive code. If data must not leave the machine, swap the judgment backend for a self-hosted model: the judgment and the compaction machinery are decoupled, and the replacement points are jev.js and state.js.
License
MIT
English · 简体中文