Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions external_plugins/jev-sim/.zcode-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "jev-sim",
"version": "0.2.0",
"description": "Jev-style decision simulation on the session model: Choice/Noul/Score typed decisions with rotation debiasing, an explicit NONE outlet, three-state output, and temperature-scaling calibration",
"description_i18n": {
"en": "Jev-style decision simulation on the session model: Choice/Noul/Score typed decisions with rotation debiasing, an explicit NONE outlet, three-state output, and temperature-scaling calibration",
"zh-CN": "用会话内大模型模拟 Jev 决策模型:Choice/Noul/Score 类型化决策,轮转去偏差、NONE 出口、三态输出、温度缩放校准"
},
"author": { "name": "forrestneo" },
"skills": "./skills"
}
103 changes: 103 additions & 0 deletions external_plugins/jev-sim/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# jev-sim: an in-session decision model

Simulates [Jev](https://typesafe.ai)-style typed decisions with the LLM (GLM) already running in your ZCode session. Turns judgment questions into probabilistic, machine-readable decisions — no external decision-model API, no API key, no data leaves your machine.

## Three primitives

| Primitive | Input | Output | Typical use |
|---|---|---|---|
| Choice | state + question + options | probability per option + NONE outlet | classification, routing, branching |
| Noul | state + proposition | P(true) in 0-1 | boolean judgment, risk screening |
| Score | state + rubric | level distribution + expected value | ranking, quality rating |

## How it works

Jev scores each candidate via token probabilities from sentence completion. Session-model APIs expose no token probabilities (the GLM API has no logprobs), so the protocol compensates with mechanisms validated in the open decision-model community:

1. **Letter readout** — each judge subagent outputs exactly one JSON line (options encoded A-Z)
2. **Option rotation** (cyclic shift, AnyJev's L0 layer) — ask the same question K=3 times with rotated option order, removing position bias; vote disagreement is itself signal
3. **Symmetric framings** (Noul) — positive / reversed / evidence-first views, removing framing bias
4. **Frequency as probability** — K votes, smoothed and normalized into a raw distribution
5. **Temperature-scaling calibration** — fit temperature T on labeled items, `p_cal ∝ p_raw^(1/T)`; instruction models are overconfident, and this step translates "vote unanimity" into "how often this much confidence was historically right"

## Output contract

```json
{
"primitive": "choice",
"protocol": "jev-sim/0.2",
"status": "answer",
"question": "Which team should handle this email?",
"decision": { "type": "choice", "choice": "billing-support", "probability": 0.72 },
"probabilities": [
{ "option": "billing-support", "probability": 0.72 },
{ "option": "legal", "probability": 0.14 },
{ "option": "sales", "probability": 0.14 }
],
"none_probability": 0.02,
"confidence": 0.72,
"threshold": 0.6,
"abstained": false,
"calibrated": true
}
```

`status` is one of: **answer** (normal decision), **none** (NONE votes won — no option holds, refuse to force a pick), **clarify** (top probability below threshold; ask for more context or escalate). Harness-level errors are reported as errors, never disguised as decisions.

## Usage

After installing, start a new task, select the jev-sim skill in the composer, and hand it a judgment question:

> State: customer email — broken chair on arrival, no support response for three days, threatens a consumer-complaint hotline and a lawyer.
> Question: which team should handle this? Options: marketing / billing-support / legal / sales

**Use for**: classification, routing, boolean judgment, rubric scoring, risk screening.
**Do not use for**: open-ended writing, exact symbol/string lookups, arithmetic, or anything expressible as a deterministic rule — just do those directly.

Calibration (recommended once before first use): `python scripts/calibrate.py --results eval/your-results.jsonl`. It writes `scripts/calibration.json`, applied automatically afterwards. `eval/choice-eval.jsonl` ships a 12-item seed set; see the "calibration workflow" section in SKILL.md.

## Side effects and dependencies

- **Network access: none.** All sampling happens inside the current ZCode session; no external API is called, no data is sent anywhere.
- **Model dependency:** uses the session's built-in LLM (judge subagents via the Agent tool); no extra account or key.
- **Command execution:** runs `python scripts/aggregate.py` and `scripts/calibrate.py` (Python stdlib only) for local aggregation and calibration.
- **File writes:** normal use only prints results to the conversation. Only an explicitly run calibration writes `calibration.json` under the plugin's `scripts/`; the aggregator only reads the votes file you specify.
- **No hooks, no MCP server, no background process, no credentials or user-data collection.**

## Third-party sources and license

- The Choice/Noul/Score primitive taxonomy references public Jev (System One, TypeSafe AI) concepts; this plugin is not affiliated with it.
- The rotation-debiasing idea references the AnyJev paper (Nokia Applied Research, Apache-2.0); no code was copied.
- All plugin code is original. MIT License.

## Test results (2026-10-14, GLM-5.3 as judge)

**Unit tests** (synthetic data, verifying the math): letter→option mapping, smoothing, and gray-zone abstention all correct; the calibrator reduced ECE from 0.125 to 0.042 on synthetic error data (T=1.45, correct softening direction); a golden-section boundary-update bug was found and fixed during testing. The 0.2.0 NONE outlet and status states passed a nine-path smoke test (majority-NONE correctly triggers `none`, vote splits trigger `clarify`, option renormalization correct).

**Live-model evaluation** (12 easy + 8 trap items, 3 rotations each, 60 votes total): 60/60 correct, zero vote splits, self-reported judge confidence 72-100 (mean ~97). Position-bias check: across all three rotations judges answered by option content, not position. Consequence: 20/20 correct degenerates temperature fitting ("all-correct data proves nothing"), so **no calibration.json ships**; output honestly reports `calibrated: false` until you calibrate on your own labeled items.

**Primitive demos**: Choice (email routing, 4 options × 3 rotations) 3/3 unanimous → 0.928; Noul (contract clause, three framings) 0.95/0.98/0.96 → p_true 0.963; Score (5-level rubric) 3/3 → level 4, expected value 3.88.

**Honest conclusion**: GLM-5.3 as judge is highly consistent on well-posed items; frequency probabilities often hit 1.0 (~0.93 after smoothing); abstention and NONE only fire when judges genuinely split. To profile real failure rates, recalibrate on items your business actually gets wrong.

## Known limitations

- Each decision spawns K judge subagents — slower and more session-token-hungry than calling a dedicated decision-model API (you save API cost and privacy)
- Probabilities come from sampling frequency, not token logits; resolution is capped by K (at K=3, non-NONE votes only give 0/⅓/⅔/1)
- Letter readout supports ≤26 options; more requires grouped elimination (see SKILL.md)
- Until calibrated, output reports `calibrated: false` and probabilities skew confident

## Layout

```
.zcode-plugin/plugin.json plugin manifest
skills/jev-sim/SKILL.md decision protocol (core)
scripts/aggregate.py vote aggregation + temperature scaling + NONE/status
scripts/calibrate.py ECE computation + temperature fitting
eval/choice-eval.jsonl 12-item seed evaluation set
```

## Versions

- 0.2.0 — explicit NONE outlet and three-state `status`; option-cap note with grouped sampling; threshold statement (0.6 default is a starting point); audit fields (protocol/threshold/n_samples/none_probability); EN/CN READMEs
- 0.1.0 — Choice/Noul/Score primitives, rotation sampling, temperature-scaling calibration, 20-item evaluation (60/60 votes correct)
103 changes: 103 additions & 0 deletions external_plugins/jev-sim/README_CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# jev-sim:会话内决策模型

用当前 ZCode 会话里的大模型(GLM)模拟 [Jev](https://typesafe.ai) 式决策模型。把"判断题"变成带概率的类型化决策——不调外部决策模型 API、不需要 API key、数据不出本机。

## 三个原语

| 原语 | 输入 | 输出 | 典型用途 |
|---|---|---|---|
| Choice | 背景 + 问题 + 选项 | 每个选项的概率 + NONE 出口 | 分类、路由、分支 |
| Noul | 背景 + 命题 | 命题为真的概率 0-1 | 布尔判断、风险筛查 |
| Score | 背景 + 量规 | 等级分布 + 期望分值 | 排序、质量评估 |

## 工作原理

Jev 的底层是把决策变成句子填空,用 token 概率给每个候选打分。会话模型拿不到 token 概率(GLM API 不开放 logprobs),所以用替代机制,全部来自开源决策模型社区的已验证做法:

1. **字母作答**(letter readout)——判官子代理只回一行 JSON(选项编成 A-Z)
2. **选项轮转**(cyclic shift,AnyJev 的 L0 层)——同一问题换选项顺序问 K=3 遍,消除位置偏差,选票分歧本身就是信息
3. **对称问句**(Noul 专用)——正问、反问、证据式三视角,消除框架偏差
4. **频率即概率**——K 张选票平滑归一化成原始概率分布
5. **温度缩放校准**——拿带标准答案的题拟合温度 T,`p_cal ∝ p_raw^(1/T)`;指示模型普遍过自信,这步把"投票一致性"翻译成"历史上这种把握实际对多少"

## 输出契约

```json
{
"primitive": "choice",
"protocol": "jev-sim/0.2",
"status": "answer",
"question": "这封邮件路由给谁?",
"decision": { "type": "choice", "choice": "客服-退款", "probability": 0.72 },
"probabilities": [
{ "option": "客服-退款", "probability": 0.72 },
{ "option": "法务", "probability": 0.14 },
{ "option": "销售", "probability": 0.14 }
],
"none_probability": 0.02,
"confidence": 0.72,
"threshold": 0.6,
"abstained": false,
"calibrated": true
}
```

`status` 三态:**answer**(正常决策)、**none**(NONE 票占多数——没有选项成立,不硬选)、**clarify**(最高概率低于阈值,建议补背景重问或人工处理)。采样/脚本层面的错误按错误原样上报,不伪装成决策。

## 使用

安装后新建任务,在 composer 里选中 jev-sim 技能,直接给判断题:

> 背景:发件人李某,正文:你们平台 2 号下单的椅子到货就是断腿的,联系客服三天没人理,今天不退款就投诉……
> 问题:这封邮件路由给哪个团队?选项:市场 / 客服-退款 / 法务 / 销售

**适用**:分类、路由、布尔判断、按量规评分、风险筛查。
**不适用**:开放式写作、精确符号/字符串查找、算术、能写成确定性规则的判断——这些直接做,不过决策协议。

校准(建议首次使用前跑一次):`python scripts/calibrate.py --results eval/your-results.jsonl`,生成 `scripts/calibration.json` 后所有决策自动套用。自带 `eval/choice-eval.jsonl` 是 12 题种子评测集,方法见 SKILL.md"校准流程"。

## 副作用与依赖声明

- **网络访问:无。** 全部采样发生在当前 ZCode 会话内,不请求任何外部 API,不发送任何数据。
- **模型依赖:** 使用会话内置大模型(通过 Agent 工具派出判官子代理);无需额外账号或密钥。
- **命令执行:** 运行 `python scripts/aggregate.py`、`scripts/calibrate.py`(仅 Python 标准库)做本地聚合与校准。
- **文件写入:** 常规使用只把结果输出到对话。仅当主动运行校准时会在插件 `scripts/` 下写 `calibration.json`;聚合脚本只读指定的 votes 文件。
- **无 hooks、无 MCP server、无后台进程,不收集凭据或用户数据。**

## 第三方来源与许可

- Choice/Noul/Score 原语分类参考 Jev(System One,TypeSafe AI)的公开概念;本插件与其无关联。
- 选项轮转去偏差思路参考 AnyJev 论文(Nokia Applied Research,Apache-2.0)的 cyclic shift,未复制其代码。
- 本插件全部代码原创,MIT License。

## 测试结果(2026-10-14,GLM-5.3 判官)

**单元测试**(合成数据,验证脚本数学):Choice 字母→选项映射、平滑、灰区弃答全部正确;校准器在合成误差数据上把 ECE 从 0.125 降到 0.042(T=1.45,正确软化方向);期间发现并修复了黄金分割搜索的边界更新 bug。0.2.0 新增的 NONE 出口与 status 三态通过九条路径冒烟测试(多数 NONE 正确触发 none、平票触发 clarify、选项概率重归一正确)。

**真模型评测**(12 道易题 + 8 道陷阱题,各 3 轮转,共 60 张选票):60/60 全对,零选票分裂,判官自报置信度 72-100(均值约 97)。位置偏差检验:三个轮转下判官始终按选项内容作答,未观察到位置偏差。后果:20 题全对使温度拟合退化("全对数据证明不了什么"),**本插件不预置 calibration.json**,输出如实标注 `calibrated: false`,直到用自己的标注题跑过校准。

**三原语功能演示**:Choice(邮件路由,4 选项 3 轮转)3/3 一致 → 0.928;Noul(合同条款,三视角)0.95/0.98/0.96 → p_true 0.963;Score(5 级量规)3/3 一致 → 4=满意,期望分值 3.88。

**诚实结论**:GLM-5.3 当判官在明确题上高度一致,频率概率经常是 1.0(平滑后约 0.93);弃答与 NONE 机制只在判官分裂(真分歧/真难题)时触发。要刻画真实失败率,需要用你自己业务里"会错"的题重新跑校准。

## 已知边界

- 每次决策要派 K 个判官子代理,比直接调专用决策模型 API 慢、费会话 token(省的是 API 费和隐私)
- 概率来自采样频率而非 token logit,分辨率受轮次 K 限制(K=3 时非 NONE 票只有 0/⅓/⅔/1 四档)
- 字母作答支持 ≤26 个选项,更多需分组初筛(见 SKILL.md)
- 校准没跑过之前,输出标 `calibrated: false`,概率偏自信

## 目录

```
.zcode-plugin/plugin.json 插件清单
skills/jev-sim/SKILL.md 决策协议(核心)
scripts/aggregate.py 选票聚合 + 温度缩放 + NONE/status
scripts/calibrate.py ECE 计算 + 温度拟合
eval/choice-eval.jsonl 12 题种子评测集
```

## 版本

- 0.2.0 —— NONE 显式出口与 `status` 三态输出;选项上限与分组采样说明;阈值声明(默认 0.6 仅为起点);审计字段(protocol/threshold/n_samples/none_probability);中英 README
- 0.1.0 —— Choice/Noul/Score 三原语、轮转采样、温度缩放校准、20 题评测(60/60 票正确)
12 changes: 12 additions & 0 deletions external_plugins/jev-sim/eval/choice-eval.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{"id": "ev01", "question": "这封邮件应该路由给哪个团队?", "state": "发件人:李某。正文:你们平台2号下单的椅子到货就是断腿的,联系客服三天没人理。今天不给我退款,我就去12315和黑猫投诉,并且找律师发函。订单号SF88231。", "options": ["客服-退款", "法务", "销售", "市场"], "correct": "客服-退款"}
{"id": "ev02", "question": "这段代码是什么语言?", "state": "def flatten(xs):\n out = []\n for x in xs:\n if isinstance(x, list):\n out.extend(flatten(x))\n else:\n out.append(x)\n return out", "options": ["Python", "JavaScript", "Go", "Ruby"], "correct": "Python"}
{"id": "ev03", "question": "这条用户评论的情感倾向是?", "state": "评论原文:真是'太棒了',买来第二天屏幕就绿了,客服还教我重启大法,学到了学到了。下次一定还来(不会)。", "options": ["正面", "负面", "中性", "无法判断"], "correct": "负面"}
{"id": "ev04", "question": "这个故障该定什么优先级?", "state": "监控告警:23:47 主数据库实例无响应,订单服务全量报错,支付成功率跌到0,持续12分钟,影响所有线上交易。", "options": ["P0-立即响应", "P1-高优处理", "P2-工作时间内处理", "P3-排期处理"], "correct": "P0-立即响应"}
{"id": "ev05", "question": "这段文字是什么语言?", "state": "Trotz der Herausforderungen in den vergangenen Monaten hat das Team eine bemerkenswerte Leistung erbracht, die Anerkennung verdient.", "options": ["德语", "荷兰语", "瑞典语", "丹麦语"], "correct": "德语"}
{"id": "ev06", "question": "这份简历的技术背景最贴近哪个岗位方向?", "state": "技能:Docker、Kubernetes、Terraform、Prometheus、Grafana、Shell、Go 基础。项目:搭建过 200 节点 K8s 集群,落地 CI/CD 流水线,负责服务网格治理。", "options": ["DevOps工程师", "前端工程师", "算法工程师", "数据分析师"], "correct": "DevOps工程师"}
{"id": "ev07", "question": "这段新闻属于哪个频道?", "state": "摘要:某研究团队宣布在超导量子比特的纠错码距离上取得新纪录,逻辑错误率随码距增加实现了指数级下降,相关论文发表于 Nature。", "options": ["科技", "财经", "体育", "娱乐"], "correct": "科技"}
{"id": "ev08", "question": "这段代码的主要缺陷类型是?", "state": "function total(items) {\n let sum = 0;\n for (let i = 0; i <= items.length; i++) {\n sum += items[i].price;\n }\n return sum;\n}", "options": ["边界错误off-by-one", "空指针风险", "死循环", "类型错误"], "correct": "边界错误off-by-one"}
{"id": "ev09", "question": "这封邮件的核心意图是什么?", "state": "发件人:某公司采购部王某。正文:看到贵司展会的工业相机方案,我们要采购30台用于产线质检,请报含税单价、货期和批量折扣,发到这个邮箱即可。", "options": ["销售-询价", "客服-售后", "人事-招聘", "合作-商务"], "correct": "销售-询价"}
{"id": "ev10", "question": "下列哪种动物是哺乳动物?", "state": "(常识判断)", "options": ["海豚", "鲨鱼", "企鹅", "鳄鱼"], "correct": "海豚"}
{"id": "ev11", "question": "这条消息应该路由给哪个处理组?", "state": "用户App内消息:上周买的落地灯罩运损裂了一条缝,灯体没事。不用退货吧?能不能只补发一个灯罩,或者我留着用你们补我差价也行。", "options": ["售后-补发换货", "客服-退款", "物流-理赔", "销售-续购"], "correct": "售后-补发换货"}
{"id": "ev12", "question": "光年是什么类型的单位?", "state": "(常识判断)", "options": ["距离单位", "时间单位", "速度单位", "亮度单位"], "correct": "距离单位"}
Loading