DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

j62268781-alt /

j62268781-alt/dsh-judgment

Verified

Structured judgment plugin for DeepSeek Harness: ask batched named questions (choice, score, yes-probability) and get calibrated probabilities from TypeSafe Jev, Luna, or any compatible endpoint. Strict validation, per-connection config, audit ledger, budget metering. It judges; your agent decides. Types-only contract at ./contract.

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@4de4f6fe

dsh-judgment · 决策

面向 DeepSeek Harness 的结构化判断插件:一次调用 = 一份状态 + 一批命名问题 → 一批概率答案。

语言: 中文(本文件) · English

它判断,消费者决定。 本插件只提供「批量概率判定」这一项能力,不提供循环—— 观察、寻址、阈值门控、缺答案时的取舍、loop 转移,全部留在消费者(你的 agent)那一侧。


这是什么

        ┌─────────────────────── dsh-judgment ───────────────────────┐
        │  ctx.judgment:多协议归一化 · 严格校验 · 凭据 · 审计 · 计量 · 指纹 │
        └───┬──────────────────┬───────────────────┬────────────────────────┘
            │ Service 直调      │ Service 直调       │
   ┌────────▼────────┐ ┌───────▼────────┐ ┌────────▼─────────┐
   │ dsh-sidebar-    │ │ LLM / 你自己的   │ │ 任何拿到 ctx 的   │
   │ mobile 的循环    │ │ agent 循环       │ │ 插件             │
   └─────────────────┘ └────────────────┘ └──────────────────┘

一次 JEV 调用 = 一份状态 + 一批命名问题 → 一批概率答案。理解这一点之后, 「把判断能力开放出去」就变成「如何把领域判断表达成批量概率问题」。

当前已实现的三种问题原语:

原语 语义 答案
noul 单个概率 p ∈ [0,1](是/否) { probability }
choice 2–255 个互斥候选单选 { optionId, probabilities }(完整分布,不只是 argmax)
score 2–10 个有序级别 { value, probabilities }(value 是级别位置,可为小数)

为什么必须批量:状态随每一批问题只发一次,批量相比逐个提问约 38 倍量级差异。 「一次问一个问题」是很自然的写法,但它会让成本高一个半数量级。


现在能用什么(诚实清单)

能力 状态
ctx.judgment Service · 两层配置(连接 × 用途)· 配置页(增删改 + 密钥 + 自检按钮) ✅ 可用
ask() 批量判断 · health() 远端自检 ✅ 已跑通(SystemOne,真端点实测 500ms 量级)
两套协议:system-one(noul)· openai-decisions(predicate) ✅ system-one 真网实测;openai-decisions 按两份独立公开文档逐条核对(并修掉 3 处错误假设),仍无真机
严格校验:答案集合一致 / 分布完整 / 归一化 0.02 / argmax 1e-6 / 模型名快照 ✅ 已实现,失败即拒不修复(两套协议共用同一份容差)
校验失败的结构化定位(规则 + 候选数 + Σp + 容差)· 并记进 ledger ✅ 已实现(消费者据此确定性决定缩多少候选重采)
审计 ledger(发包前落库)· 计量(token / 耗时 / request_id / 指纹) ✅ 已实现;落盘失败不阻断判断(只记日志)。这是对账凭证:账户后台只有汇总账单,没有"这笔钱对应哪一次请求"
本地配额(连接层 / 用途层,可选护栏) ✅ 已实现但默认不限(0):它是本地护栏,不是账户额度(见下方说明)
工具面(judgment/llm-face,可选组件) ⏳ 未实现(默认关)

⚠️ 插件更新后要重启 DSH:host 半是 Node 侧模块,进程启动时加载一次;客户端半改了刷新页面即生效。 自检若报 HTTP 404 / transport failure,基本就是 host 半还没重载。

关于配额的两个诚实说明(因此不做用量可视化):

  1. 本地配额 ≠ 账户额度。我们的计数只含本插件发出的调用;账户额度是所有用同一把 key 的 东西加起来的。所以页面上显示"12/60"时账户可能已经用了 90% —— 这种数字会给人虚假的安全感。 真正撞墙时服务端会返回 429,我们已经把它映射成类型化的 rate-limited(可重试)。
  2. 多实例共用同一账本时可能低估。账本是共享文件,但"这一小时用了几次"的计数在各进程内存里: 启动时读一次文件播种,之后只累加自己的请求。两个实例用同一连接名时,各自都以为只用了自己那份, 可能双双放行。所以本地配额只在"单实例 + 单写入者"下准确。

因此本地配额的价值只有一个:多个消费者共用一把 key 时,别让其中一个(比如写错的循环)把预算烧穿。

score 的语义是实测校准过的:它返回的是级别分布的期望值(连续位置),不是整数下标 —— 3 级时 0.84(与 Σ i·p_i 完全一致)、4 级时 1.42(与 Σ i·p_i = 1.41 误差 ≤0.01, 模型只给两位小数)。所以需要离散级别时请取分布的众数,provider 绝不替你四舍五入。


安装

# 从 npm(推荐;当前 0.1.0)
dsh plugin --profile <profile> add dsh-judgment@0.1.0

# 或从 GitHub 装(仓库根带 cordis.patch.yml,可直接作为插件包安装)
dsh plugin --profile <profile> add github:j62268781-alt/dsh-judgment

# 或本地路径(开发用;profile 名见 ~/.dsh/profiles)
dsh plugin --profile <profile> add /绝对路径/dsh-judgment

装完需要重启 DSH(host 半是 Node 侧模块,只在进程启动时加载)。 重启后在 设置 → 决策 里配置连接;自检按钮在卡片右上角的 ⋯ 菜单里。

装完在 设置 → 决策 里配置。本插件零工具:它不会往模型面前塞任何工具, 只注册一个 Service(工具面是另一个可选组件,默认关)。


配置:连接层 × 用途层

配置页两个页签,字段也可以直接手写在 profile 的 cordis.patch.yml:

- id: judgment/provider
  name: dsh-judgment
  config:
    connections:                    # 连接层:一个 (协议, 地址, 模型, 凭据) 元组 = 一条连接
      typesafe-main:
        protocol: system-one        # system-one | openai-decisions(协议身份显式,不由 URL 推断)
        baseUrl: https://api.typesafe.ai/v1/systemone
        model: typesafe/jev-1.13    # 请 pin 到具体版本,别用会漂移的别名
        credentialRef: JUDGMENT_API_KEY_1
        timeoutMs: 10000
    purposes:                       # 用途层:消费者只报用途名
      browser.action: { connection: typesafe-main }
      compaction.retain: { connection: typesafe-main }
    defaultConnection: ''           # 调用没给用途名时用它;留空且只配了一条连接时用那一条

「用途」是干什么的:它是给消费者的稳定名字,把「我要这类判断」和 「这判断具体走哪条连接 / 哪个模型 / 哪把密钥」解耦开。代码里写 browser.action, 换厂商、换模型、换密钥都只改配置,不动消费者一行代码。 未知用途名直接报错,绝不静默换一条连接。 多个用途可以共用一条连接;一个用途只能指向一条连接。

第二套协议 openai-decisions 长这样(端点是 /v1/decisions,模型 gpt-6-luna; 只按输入计费 $0.10 / 1M、输出免费,所以 usage.outputTokens 恒为 0 不必奇怪):

    connections:
      openai-decisions:
        protocol: openai-decisions          # 协议身份显式;不由 URL 推断
        baseUrl: https://api.openai.com/v1/decisions
        model: gpt-6-luna
        credentialRef: JUDGMENT_API_KEY_2

两套协议的 score 语义是同一个:0 基级别的概率加权均值(可落在两级之间), 所以消费者换协议不用改读数方式。

已知不支持的子集:openai-decisions 官方允许 input 传消息数组(含 inline base64 图片, 最多 128 张),而本插件的 state 是 JSON、input 只会以字符串发出 —— 所以不支持图像输入。 需要视觉判断时请走主模型侧;这里只做纯文本的结构化判断。


密钥怎么给

密钥永远不写进配置。凭据引用名本身就是环境变量名(DSH 凭据服务的设计), 所以两条路都行:

方式 怎么做 页面表现
页面上填 设置 → 决策 → 连接 → 密钥(密码框,保存后不回显) 显示「已配置 · 来源 file」
环境变量 启动 DSH 之前设 JUDGMENT_API_KEY_1=sk-xxx(或写进 dotenv) 显示「由启动环境提供(只读)」,输入框锁住

优先级:启动环境 > 存储文件 > 项目 dotenv > 用户 dotenv。 启动环境那一层只读且不可覆盖;存储文件永远不覆盖启动环境。

引用名由插件自动分配(JUDGMENT_API_KEY_1、_2…,取最小空号),你不需要记它。 删除连接时会先清除它的密钥——序号会被回收,否则新连接会读到上一把遗留的 key。

⚠️ 环境变量与 dotenv 都是启动时取的快照:运行中新增 / 修改变量不会被读到, 必须在启动 DSH 之前设好。


消费者怎么用

现在真正在用的消费方:dsh-agent-browser —— 它的「决策」开关 就是这套接口的实战用法(ctx.get('judgment') + apiVersion + status().purposes[].usable 门控 + 批量 ask + 把 invalid-response 当新采样)。接口清单与跨仓库约定见 DESIGN.md §四「消费方」。它也 import type { Judge } from 'dsh-judgment/contract', 所以契约一升版它就会编译报错——那正是我们想要的强制点。

import type { ApiVersion, Judge } from 'dsh-judgment/contract'

const EXPECTED: ApiVersion = 1                         // 版本契约就是本地这个常量
const judge = ctx.get('judgment') as Judge | undefined  // 可选依赖:缺失返回 undefined,不抛错
if (!judge || judge.apiVersion !== EXPECTED) return useFallbackJudge()

const state = { page: 'a11y 摘要……' }                    // 不透明载荷,形状由你定
const questions = {
  action: {
    kind: 'choice',
    prompt: '哪一步最能推进当前任务?',
    options: { e0: '点击「搜索」', e1: '在输入框输入' },
  },
  goal_done: { kind: 'noul', prompt: '任务目标已经达成' },
  retain: { kind: 'score', prompt: '这段输出要逐字保留多少?', levels: ['丢弃', '截断', '保留'] },
}

const result = await judge.ask(state, questions, { purpose: 'browser.action' })

for (const [id, answer] of Object.entries(result.answers)) {
  // 没有判断的问题以 abstain 出现,**不是缺键**:该保留还是该中止由你的领域决定
  if (answer.kind === 'abstain') continue
  if (answer.kind === 'choice') console.log(id, answer.optionId, answer.probabilities)
}

// 不变量:地址只对产生它的快照有效。执行前用指纹比对,不一致就丢弃。
const now = judge.fingerprint(state, questions, { purpose: 'browser.action' })
if (now !== result.fingerprint) return 'stale_state'

结果里带齐溯源:answers · fingerprint · usage(token / 耗时 / 估算成本 / 是否完整)· provider(协议身份)· connection · model(响应返回的模型快照)。

契约入口 dsh-judgment/contract 只出类型:零 import、零运行时值, import type 之后编译期就被擦除,运行期不引入本插件的任何代码。 它固定了 Judge 接口、三种问题原语、答案形状、校验容差与 JudgmentError 的形状—— provider 实现、第三方替换、HostModelJudge 兜底都按它对齐。

用途预设:browser.action

"动作域"消费者的推荐配置(预设名来自 dsh-agent-browser 的实测校准):

purposes:
  browser.action: { connection: typesafe-main }

候选数建议 ≤ 16。同一目标各采样 6 次(decision-model-preview,紧凑状态 ~2.9k tokens):

候选上限 命中率 严格校验拒绝率 tokens/次
10 5/5 0/6 2,879
16 4/5 0/6 3,026
40 3/5 6/6 3,689

模型在候选多时系统性欠归一(实测 Σp = 0.95–0.98,与候选数正相关),会撞上 distribution-normalized。这不是放宽容差的理由(放宽等于"修复答案"、等于用猜测 覆盖证据),而是消费者该缩候选的理由。延迟参考:单次 ask 210–350 ms。

校验失败长什么样(供消费者确定性决策)

try {
  await judge.ask(state, questions, { purpose: 'browser.action' })
} catch (error) {
  if (error instanceof JudgmentError && error.code === 'invalid-response') {
    const { rule, questionId, optionCount, sum, tolerance } = error.validation ?? {}
    // 例:缩到一半重采。重采 = 新采样,是消费者的决定;provider 不重试。
    const nextCap = optionCount === undefined ? 16 : Math.max(4, Math.floor(optionCount / 2))
  }
}

rule 是七条校验规则之一;optionCount / sum / tolerance 是数字 (能确定时才给,不知道就不编——distribution-complete 不含 sum)。 所以消费者不必从 message 里正则抠数字。容差请运行期读 tolerance,不要写死: 它是契约定稿,但契约升版时可能变。

这些数值也会记进审计 ledger(validationSum / validationOptionCount), 于是"候选越多、Σp 越欠归一"这类校准可以从账本复算,不靠手记。

关于 HostModelJudge:为什么本插件不内置

契约里描述了它(实现同一个 Judge 的"宿主模型兜底",没有 Jev 连接时降级)。 它归属消费者侧,不在 provider 内,理由两条:

  1. 它消耗主模型 token,会改变本插件"判断层不花主模型 token"的定位,必须是消费者显式 opt-in;
  2. 它需要 ctx.llm——provider 一旦依赖主模型,就不再是"零工具的纯能力提供者"。

所以做法是:消费者自己写一个约百行的 Judge(ask 里调主模型,把分布喂给同一套校验语义, 失败即拒),ProtocolId 里已留 'host-model' 以便账本溯源。需要复用校验核心的话, src/provider/wire.ts(MIT)可以直接 vendored 过去。 如果确实想要官方维护,那也应该是一个单独的 opt-in 包,而不是塞进本插件。


开发

📄 DESIGN.md —— 设计与实现的差异记录:哪些最初调研的结论被实测或工程现实改掉了、 为什么、证据在哪(score 语义、凭据命名、工具面、HostModelJudge、Remote 通路、配额语义、 usable 假否定、peer 兼容事故、第二协议的三处修正)。与旧调研文档冲突时以它为准。

pnpm install
pnpm build            # Host(tsc) + 客户端(esbuild → lib/client.js)
pnpm test             # Service / 契约 / 客户端交接 / 纯逻辑 / react-dom 渲染
pnpm typecheck        # 含契约的 exactOptionalPropertyTypes + noUncheckedIndexedAccess
pnpm verify:contract  # 强制契约零 import、零运行时值
pnpm verify:served    # 问运行中的 DSH:本插件的客户端半是否已被 composer 并生效
pnpm probe            # 真网探针:用协议模块打一次真实端点(直接读本机 profile 与凭据)
pnpm live             # 真网集成:boot 最小服务,走完整 Service 调 ask() / health()
pnpm verify:remote    # 验证设置页「自检」走的 Remote 通路(两端都用本插件产物 + 交叉校验线契约)

目录:

src/contract/index.ts   唯一冻结面(仅类型,零依赖)
src/index.ts            入口 ".":注册 ctx.judgment
src/config.ts           两层配置(schemastery)
src/provider/           错误类型 · 指纹
client/                 配置页面(统一 JSX,esbuild 打成 window.__ModuleLoader__ 单文件)
tests/                  服务 / 契约 / 客户端交接 / 纯逻辑 / 渲染

设计要点(都写在契约的 TSDoc 里):校验失败即拒、绝不"修复"——"修复"一个不自洽的 概率分布等于用猜测覆盖证据;provider 永不返回默认值,没有判断就是 abstain; loop 必须留在消费者——同一协议的安全方向按领域相反(裁剪域缺答案 = 全保留, 动作域缺答案 = 中止)。


下一步(路线图)

  1. openai-decisions 的真网校准:协议已实现,并按两份独立公开文档逐条核对过 (promptfoo 的 openai:decisions provider 文档 + 一个真调两家 API 的对比项目), 本轮据此修掉三处错误假设(score 是 0 基级别的概率加权均值、levels.label 是级别名、 分布项的键字段与 label 都要宽容读)。仍缺真机——手上没有那条连接与密钥。 剩下只有三处要真机确认:分布项键名是 value 还是 index、非拒答答案是否带 type、 响应是否回显 model(代码对三者都取"宽容读取、语义照严")。
  2. 工具面(judgment/llm-face,可选组件):3 个语义窄工具 + 1 份 skill,默认关, 绝不暴露原始 state/questions。

已从路线图移除:

  • 配额可视化 —— 理由见上文「关于配额的两个诚实说明」:真实上限在账户/服务端,本地计数只覆盖本插件, 做出来会给人虚假的安全感。撞墙时服务端 429 已映射成类型化 rate-limited,信号是清楚的。

许可

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit 4de4f6fe58c3

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