DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

ZhengSJCode /

ZhengSJCode/jev-dsh

Topic repository only

System One (TypeSafe Jev) decision tool for DeepSeek Harness — bounded choice / noul / score judgments as an agent tool + skill.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@b6e4312d

jev-dsh — 第一层:把 System One(TypeSafe Jev)接成 DSH 的决策循环

给 DSH 一个 system_one 工具 + 一个技能:agent 可以对已有上下文做校准过的有界决策 (choice / noul / score),而不是把 Jev 当聊天模型塞进模型选择器。

已经可以用了,而且离线可验证(不需要真 API key)。

为什么是工具而不是模型

Jev 是 决策模型,不是 chat 模型:线协议只有 POST /v1/systemone,输入 state + 类型化 questions,输出带概率的 answers;不生成文本、不流式、不调工具。实测 /v1/chat/completions → 404、/v1/responses → 404、/v1/systemone → 403(存在、缺 key)。 DSH 的 llm-pi-ai 只认三种 chat 协议,所以这条路本来就不通(详见 research.md)。

生态里的正确形态就是"工具 + 技能"——pi-system-one 对 Pi 就是这么做的,本实现复用了它的 客户端与技能文本,只重写了 Pi 专属的外壳。

快速开始

# 装进默认 profile($DSH_HOME/profiles/web)
node install.mjs                          # 只写行,设置全部留给环境变量
node install.mjs --base-url https://api.typesafe.ai --model jev-latest
node install.mjs --anonymous              # 本地 Reflex:http://localhost:8008,不带凭据

# 只看会写什么 / 卸载
node install.mjs --print
node install.mjs --uninstall

install.mjs 往 profile 的 cordis.patch.yml 写一个受管块:工具行(绝对路径,因为 out-of-tree 插件不是按包名加载的)+ 一条 skill-filesystem 的 customSkillDirs 指向本包的 skills/。该文件被 hmr 行热监听,运行中的 profile 会自行重组,无需重启。

带任一设置参数时,安装器会明确写出 baseUrl: https://api.typesafe.ai、model: jev-latest、 apiKeyEnv: TYPESAFE_API_KEY;不带参数则一个字段都不写。

命名与凭据(都用 TypeSafe 自己的名字)

环境变量:TYPESAFE_BASE_URL、TYPESAFE_MODEL、TYPESAFE_API_KEY、TYPESAFE_TIMEOUT_MS、 TYPESAFE_PATH、TYPESAFE_API_KEY_ENV。这样已经有 TypeSafe SDK 变量的部署不用再导一套。

规则 说明
默认凭据引用 TYPESAFE_API_KEY;行里的 apiKeyEnv 或环境变量 TYPESAFE_API_KEY_ENV 可以换成别的引用
存储位置 $DSH_HOME/.credentials.yaml 的 refs:——就是 Models 页 credentials.set(ref, value) 写的那份文件,改了热生效、不用重启
查找顺序 先 ctx.credentials.resolve(ref)(其内部优先级是"启动环境 > 存储文件 > .env"),进程环境兜底
未点名 + 解析不到 不报错,不带凭据发请求:本地 Reflex 就该这么用,端点自己会回答它要不要 key
点名 + 解析不到 明确报错并指出引用名,不会静默发匿名请求(与 llm-pi-ai 对 apiKeyEnv 的处理一致)

最小配置:只把密钥写进凭据文件即可,不用改 profile。

# $DSH_HOME/.credentials.yaml
version: 1
refs:
  TYPESAFE_API_KEY: sk-…

注意:install.mjs 默认写的是真实 web profile。只想试的话用 DSH_PROFILE_DIR=<某测试 profile 目录> node install.mjs。 另外,hmr 行的 root 列表会被后安装的插件覆盖(手机插件也这么写, 它是另一个仓库),两个都装时只有最后安装的那个能热重载源码。

工具怎么用

{
  "state": "工单:我的打款连续三天失败,没人回复。客户等级:growth。",
  "questions": {
    "team":   { "type": "choice", "instructions": "哪个团队应该接手?",
                "criteria": { "billing": "打款、发票、退款", "technical": "缺陷、故障、集成" } },
    "urgent": { "type": "noul", "instructions": "这表达了紧急吗?" },
    "frustration": { "type": "score", "instructions": "客户有多烦躁?",
                     "criteria": ["平静", "烦躁", "非常生气"] }
  }
}

宽容的入参(会修复表示,修不了的报错点名到字段):

输入 处理
context / evidence state 的别名;同时给两个会被拒(只有调用方知道哪个是本意)
pick / select / yesno / rating / scale 类型别名 → choice / noul / score
省略 type 按 criteria 形状推断:数组→score,对象→choice,无→noul
question / prompt / ask;options / levels / labels instructions / criteria 的别名
"id": "问题文本" 裸字符串 → noul
数组形式 [{ "id": "team", ... }] 与 map 形式等价
criteria: ["a","b"](choice) 展开为 {a: null, b: null}
criteria: ["是","否"](noul) 展开为 {true: "是", false: "否"}

拒绝并给出可操作信息的情形:缺 state、同时给两个 state 别名、缺/空 questions、未知类型、 choice 没有 criteria、score 少于两级或多于十级、choice 超过 255 项、缺 instructions、 重复 id、数组形式缺 id、state 超过 128KB(提示裁剪,服务端上限 32k token)。

返回给模型的是分布而不只是赢家:

system-one · jev-1.13.0 · 5ms · in 128 / out 16 tok
[choice] team → billing  (confidence 0.7)
  billing 0.7 · technical 0.3
[noul]   urgent → 0.9
[score]  frustration → 1  (confidence 0.8)
  Calm 0.1 · Frustrated 0.8 · Very angry 0.1

验证(全部离线可跑)

层 命令 结果
入参修复/拒绝 node test/normalize.test.mjs 30 项断言通过
渲染 node test/render.test.mjs 6 项通过
工具 schema 过 harness 真校验器 ./test/schema.test.sh 13 项通过:parameters/output 被 assertSupportedJsonSchema 接受;TYPESAFE_ 命名、环境变量回退、以及 install.mjs --print 写出的 apiKeyEnv: TYPESAFE_API_KEY / --anonymous 形态都与运行时一致
真实 HTTP 执行(含错误路径) node test/execute.e2e.mjs 19 项通过:自起 mock/mock-systemone.mjs,断言线上 body、Bearer 头、403、未点名凭据匿名发请求、点名凭据缺失报错、未配置端点、TYPESAFE_ 环境变量
决策循环驱动手机 node test/adapter.e2e.mjs 14 项通过:观察步不打扰模型、refs 永远来自最新一屏、重复 tap 被挡下
长候选表(翻译 App 选语言那一类) node test/adapter-candidates.e2e.mjs 8 项通过:40 行候选 + 目标概率 0.2 默认照常动手;设了 gate.candidates 之后被拒的是那一行,tap 仍在菜单里
重放一次录下来的会话 node test/replay-session.test.mjs 8 项通过:goal/历史从转录里读出来、世界按转录回答、换了选择报 DIFFERENT、转录没问过的报 unrecorded
决策循环本身 cd kernel && npm test loop.e2e.mjs 九条终止路径 + candidates.test.mjs 20 项(低目标概率、坏分布 fail-closed、候选下限、左中文右英文整条跑通)
真实 DSH 全链路 见下 插件挂载 → 工具注册(tools=25)→ 模型调用 → 真实 HTTP → 结果以 role:"tool" 回传 → 技能进入会话目录
凭据库分支(和 OpenAI 同一条路) 见下 密钥只放在 $DSH_HOME/.credentials.yaml 的 refs: { TYPESAFE_API_KEY: … },环境变量不设 → 请求带上正确 Bearer;把该文件移走 → 立即报 no credential for TYPESAFE_API_KEY(反向对照,证明来源就是存储文件)

上表里驱动手机的两条(adapter.e2e.mjs、adapter-candidates.e2e.mjs)是唯一跨到另一个仓库 的测试:它们要真的 mobile_test_* 工具,才能证明"先观察后决策""refs 来自最新一屏"。 本仓库对手机没有运行时依赖——profile 用路径点名 capabilities——所以手机不在场时这两条 跳过而不是失败(test/phone-core.mjs 按 DSH_PHONE_CORE → 同级的 phone-core/ 或 dsh-phone/phone-core/ 顺序找)。单独 clone 本仓库 npm test 依然全绿。

全链路那条的做法:测试 profile 里用 install.mjs 装本插件,再用 e2e/llm-overlay.yml 把 agent 自己的模型指向 e2e/mock-llm.mjs(一个只会返回 system_one 工具调用的假 chat 端点), 于是整条链路不需要任何真实 key:

node mock/mock-systemone.mjs &          # :8940,带 key 校验
node e2e/mock-llm.mjs &                 # :8942,会调用 system_one
DSH_PROFILE_DIR=<测试 profile> node install.mjs --base-url http://127.0.0.1:8940 --model jev-latest
# 凭据走存储文件(不设 TYPESAFE_API_KEY 环境变量):
printf 'version: 1\nrefs:\n  TYPESAFE_API_KEY: mock-key\n' > <测试 home>/.credentials.yaml
DSH_HOME=<测试 home> MOCK_CHAT_API_KEY=mock-key \
  node <checkout>/apps/cli/lib/bin.js --profile headless \
  --patch e2e/llm-overlay.yml "Judge this ticket"
# → TOOL-LOOP-OK tools=25 result=system-one · jev-1.13.0 · 5ms | [choice] team → billing …

目录结构

jev-dsh/
├── package.json            插件包(纯 ESM JS,无构建步骤)
├── install.mjs             写/删 profile 里的受管块(--print / --uninstall / --base-url …)
├── src/
│   ├── tools.js            插件入口:system_one 的定义与注册(apply / createToolDefinitions)
│   ├── normalize.js        入参修复与拒绝(纯函数,可单测)
│   ├── client.js           端点与凭据绑定,system-one-core 的 HttpSystemOneProvider
│   ├── credential.js       凭据引用怎么解析(存储文件 / 环境变量)
│   ├── checkout.js         DSH checkout 在哪(`DSH_CHECKOUT` → 逐级向上 → 两个惯例位置)
│   └── render.js           答案 → 模型可读文本
├── adapter/                同一条决策循环,但挂成 harness 的 LlmAdapter(`llm-systemone` 行)
│                           由安装器写行:install.mjs 之外还有 adapter/install.mjs
├── preset/install.mjs      装「Jev」模式(模式 = persona + systemone 路由 + 一个工具行)
├── kernel/                移植自 SystemOneHarness 的 loop:observe → encode → ask → gate → execute
├── skills/system-one/      SKILL.md + references/use-cases.md(源自 pi-system-one,改了 2 处)
├── tools/store-key.mjs     把密钥写进 $DSH_HOME/.credentials.yaml 的 refs:,不经过 shell 与屏幕
├── mock/mock-systemone.mjs 脚本化 /v1/systemone 端点
├── e2e/                    mock-llm.mjs、llm-overlay.yml、测试 profile
├── test/                   normalize / render / say / space / decide / adapter / 执行与 schema
├── notes/                  research.md(调研与实测)、USAGE.md(当前怎么用)
└── vendor/                 第三方快照:system-one-core(运行时唯一依赖)、pi-system-one
                            出处与许可见 vendor/README.md

把一次真实会话重放一遍

改门、改菜单、改候选表之后,"这次是不是好点了"不该靠感觉。tools/replay-session.mjs 把一次录下来的 会话按当前代码再问一遍:同一句 goal、同一屏、同一段历史,逐条对照当时做了什么。

# 真端点(凭据取 TYPESAFE_API_KEY 或 $DSH_HOME/.credentials.yaml)
node tools/replay-session.mjs --session 9057753a --turn 2

# 没有 key 时用脚本答案,链路与测试同一套
node tools/replay-session.mjs --session 9057753a --turn 2 --script /tmp/steps.json

# 全部轮次
node tools/replay-session.mjs --session 9057753a --all --steps 8

每步标 same / DIFFERENT / unrecorded,退出码非 0 表示有对不上的地方。三条规矩:

  • 世界只从转录来:循环问什么,就用当时录下的那条结果回答,顺序对得上才用;对不上就不编。
  • 同名不同 kind 不算"改了选择":把 tap 的结果喂给 launch 会让循环基于一个没发生过的世界推理, 所以那种情况记 unrecorded,而不是拿去对齐。
  • 多帧 zstd 必须整份解:DSH 每 flush 一次追加一帧,Node 自带的解码器只解第一帧。截断的转录 比拒绝更危险,所以这里优先用 zstd CLI,只有它缺席且解出来不完整时才明确报错。

几个实现决定

  • system-one-core 是 vendored(vendor/system-one-core)而不是 npm 依赖:out-of-tree 插件 按绝对路径加载、裸模块名会按 profile 目录解析,那里没有包。相对路径导入是确定性的,也就不需要 在宿主机上装任何东西。
  • 不导出 Config schema:声明 schemastery schema 需要 import @deepseek-ai/schemastery, 同样在 profile 目录不可解析。改用 loader 行 config + 环境变量,行为一致。
  • 原始 JSON-Schema 定义而非 defineTool(...):理由同上(@deepseek-ai/dsh-tools 不可导入); 入参检查因此由本包自己负责,normalize.js 就是那份检查,schema.test.sh 借 harness 的校验器 补上类型层面。
  • 工具始终注册:端点没配时报一条可操作的错误(no endpoint configured; set TYPESAFE_BASE_URL …), 比静默不注册更容易发现和排查。
  • 门的两个问题分开判:动作的 confidence 与设计者声明的参数按风险阈值取最弱值;from: 参数 (选项由环境这一步枚举)改成判"分布是否自洽 + 选中是否 argmax",绝对下限是 gate.candidates (默认 0)。理由与代价写在 kernel/README.md:把候选概率折进风险最小值,会让 120 行的语言 选择器永远无法行动——那是"该点哪一个"的问题,不是"要不要点"的问题。
  • refusal 缩到出问题的那一层:动作弱就从菜单里拿掉动作(withoutChoices),目标弱就从名单里 拿掉那一项(withoutCandidate),而不是一律删动作。否则一个控件没把握的代价是这一回合不能再 点任何东西,而屏幕本身并没有变得更难。窄化同时进 next_action.criteria 的校验,所以被拿掉的 选项即使被重新选中也会被拒("没被展示的就不能选"要能扛住模型无视菜单)。

没做的

  • pi-system-one 的 /so config、/judge 命令面:那是 Pi 的斜杠命令与 UI API (ExtensionAPI:session_start、resources_discover、ui.notify),DSH 没有对应物。
  • UHP:SystemOneHarness(Python)已经是 UHP Core 合规的服务,DSH 侧要做客户端才行,属于另一件事。
  • 真模型复测:本机没有 TYPESAFE_API_KEY,也没有本地 Reflex 端点,所以延迟/定价/真答案分布 都还是文档值。拿到 key 后把 TYPESAFE_BASE_URL 指向真端点跑同一条全链路即可。
—/ 5

No ratings yet

Manifest verification required

Commit b6e4312dcc9d

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