oh-my-knowledge 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +596 -326
  2. package/README.zh.md +917 -0
  3. package/dist/src/analysis/failure-clusterer.d.ts +96 -0
  4. package/dist/src/analysis/failure-clusterer.d.ts.map +1 -0
  5. package/dist/src/analysis/failure-clusterer.js +298 -0
  6. package/dist/src/analysis/failure-clusterer.js.map +1 -0
  7. package/dist/src/analysis/sample-diagnostics.d.ts +78 -0
  8. package/dist/src/analysis/sample-diagnostics.d.ts.map +1 -0
  9. package/dist/src/analysis/sample-diagnostics.js +259 -0
  10. package/dist/src/analysis/sample-diagnostics.js.map +1 -0
  11. package/dist/src/analysis/saturation.d.ts +85 -0
  12. package/dist/src/analysis/saturation.d.ts.map +1 -0
  13. package/dist/src/analysis/saturation.js +174 -0
  14. package/dist/src/analysis/saturation.js.map +1 -0
  15. package/dist/src/cli.js +772 -18
  16. package/dist/src/cli.js.map +1 -1
  17. package/dist/src/eval-core/bootstrap.d.ts +72 -0
  18. package/dist/src/eval-core/bootstrap.d.ts.map +1 -0
  19. package/dist/src/eval-core/bootstrap.js +174 -0
  20. package/dist/src/eval-core/bootstrap.js.map +1 -0
  21. package/dist/src/eval-core/evaluation-execution.d.ts +15 -1
  22. package/dist/src/eval-core/evaluation-execution.d.ts.map +1 -1
  23. package/dist/src/eval-core/evaluation-execution.js +37 -3
  24. package/dist/src/eval-core/evaluation-execution.js.map +1 -1
  25. package/dist/src/eval-core/evaluation-job.d.ts +10 -2
  26. package/dist/src/eval-core/evaluation-job.d.ts.map +1 -1
  27. package/dist/src/eval-core/evaluation-job.js +9 -1
  28. package/dist/src/eval-core/evaluation-job.js.map +1 -1
  29. package/dist/src/eval-core/evaluation-reporting.d.ts.map +1 -1
  30. package/dist/src/eval-core/evaluation-reporting.js +90 -0
  31. package/dist/src/eval-core/evaluation-reporting.js.map +1 -1
  32. package/dist/src/eval-core/schema.d.ts.map +1 -1
  33. package/dist/src/eval-core/schema.js +69 -0
  34. package/dist/src/eval-core/schema.js.map +1 -1
  35. package/dist/src/eval-core/verdict.d.ts +74 -0
  36. package/dist/src/eval-core/verdict.d.ts.map +1 -0
  37. package/dist/src/eval-core/verdict.js +283 -0
  38. package/dist/src/eval-core/verdict.js.map +1 -0
  39. package/dist/src/eval-workflows/each-evaluation-workflow.d.ts +16 -3
  40. package/dist/src/eval-workflows/each-evaluation-workflow.d.ts.map +1 -1
  41. package/dist/src/eval-workflows/each-evaluation-workflow.js +10 -2
  42. package/dist/src/eval-workflows/each-evaluation-workflow.js.map +1 -1
  43. package/dist/src/eval-workflows/evaluation-pipeline.d.ts +17 -1
  44. package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +1 -1
  45. package/dist/src/eval-workflows/evaluation-pipeline.js +45 -3
  46. package/dist/src/eval-workflows/evaluation-pipeline.js.map +1 -1
  47. package/dist/src/eval-workflows/run-evaluation.d.ts +23 -2
  48. package/dist/src/eval-workflows/run-evaluation.d.ts.map +1 -1
  49. package/dist/src/eval-workflows/run-evaluation.js +104 -4
  50. package/dist/src/eval-workflows/run-evaluation.js.map +1 -1
  51. package/dist/src/grading/assertions.d.ts +16 -0
  52. package/dist/src/grading/assertions.d.ts.map +1 -1
  53. package/dist/src/grading/assertions.js +385 -111
  54. package/dist/src/grading/assertions.js.map +1 -1
  55. package/dist/src/grading/debias-validate.d.ts +84 -0
  56. package/dist/src/grading/debias-validate.d.ts.map +1 -0
  57. package/dist/src/grading/debias-validate.js +173 -0
  58. package/dist/src/grading/debias-validate.js.map +1 -0
  59. package/dist/src/grading/gold-cli.d.ts +88 -0
  60. package/dist/src/grading/gold-cli.d.ts.map +1 -0
  61. package/dist/src/grading/gold-cli.js +251 -0
  62. package/dist/src/grading/gold-cli.js.map +1 -0
  63. package/dist/src/grading/gold-dataset.d.ts +73 -0
  64. package/dist/src/grading/gold-dataset.d.ts.map +1 -0
  65. package/dist/src/grading/gold-dataset.js +161 -0
  66. package/dist/src/grading/gold-dataset.js.map +1 -0
  67. package/dist/src/grading/human-gold.d.ts +102 -0
  68. package/dist/src/grading/human-gold.d.ts.map +1 -0
  69. package/dist/src/grading/human-gold.js +188 -0
  70. package/dist/src/grading/human-gold.js.map +1 -0
  71. package/dist/src/grading/index.d.ts +27 -2
  72. package/dist/src/grading/index.d.ts.map +1 -1
  73. package/dist/src/grading/index.js +36 -18
  74. package/dist/src/grading/index.js.map +1 -1
  75. package/dist/src/grading/judge.d.ts +65 -2
  76. package/dist/src/grading/judge.d.ts.map +1 -1
  77. package/dist/src/grading/judge.js +280 -23
  78. package/dist/src/grading/judge.js.map +1 -1
  79. package/dist/src/inputs/eval-config.js +19 -0
  80. package/dist/src/inputs/eval-config.js.map +1 -1
  81. package/dist/src/observability/{production-analyzer.d.ts → skill-health-analyzer.d.ts} +24 -2
  82. package/dist/src/observability/skill-health-analyzer.d.ts.map +1 -0
  83. package/dist/src/observability/{production-analyzer.js → skill-health-analyzer.js} +61 -6
  84. package/dist/src/observability/skill-health-analyzer.js.map +1 -0
  85. package/dist/src/observability/trace-adapter.d.ts.map +1 -1
  86. package/dist/src/observability/trace-adapter.js +27 -1
  87. package/dist/src/observability/trace-adapter.js.map +1 -1
  88. package/dist/src/renderer/html-renderer.d.ts.map +1 -1
  89. package/dist/src/renderer/html-renderer.js +40 -6
  90. package/dist/src/renderer/html-renderer.js.map +1 -1
  91. package/dist/src/renderer/layout.d.ts.map +1 -1
  92. package/dist/src/renderer/layout.js +138 -4
  93. package/dist/src/renderer/layout.js.map +1 -1
  94. package/dist/src/renderer/skill-health-renderer.d.ts +2 -2
  95. package/dist/src/renderer/skill-health-renderer.d.ts.map +1 -1
  96. package/dist/src/renderer/skill-health-renderer.js +39 -4
  97. package/dist/src/renderer/skill-health-renderer.js.map +1 -1
  98. package/dist/src/renderer/summary.d.ts +28 -1
  99. package/dist/src/renderer/summary.d.ts.map +1 -1
  100. package/dist/src/renderer/summary.js +322 -8
  101. package/dist/src/renderer/summary.js.map +1 -1
  102. package/dist/src/renderer/table.d.ts.map +1 -1
  103. package/dist/src/renderer/table.js +63 -2
  104. package/dist/src/renderer/table.js.map +1 -1
  105. package/dist/src/server/report-server.d.ts +2 -1
  106. package/dist/src/server/report-server.d.ts.map +1 -1
  107. package/dist/src/server/report-server.js +397 -2
  108. package/dist/src/server/report-server.js.map +1 -1
  109. package/dist/src/types.d.ts +247 -0
  110. package/dist/src/types.d.ts.map +1 -1
  111. package/package.json +24 -6
  112. package/dist/src/observability/production-analyzer.d.ts.map +0 -1
  113. package/dist/src/observability/production-analyzer.js.map +0 -1
package/README.md CHANGED
@@ -1,103 +1,153 @@
1
1
  # oh-my-knowledge
2
2
 
3
- 知识载体评测工具 — 用客观数据衡量你的 artifact 质量。
3
+ [![npm version](https://img.shields.io/npm/v/oh-my-knowledge.svg)](https://www.npmjs.com/package/oh-my-knowledge)
4
+ [![CI](https://github.com/lizhiyao/oh-my-knowledge/actions/workflows/ci.yml/badge.svg)](https://github.com/lizhiyao/oh-my-knowledge/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
6
+ [![Node.js Version](https://img.shields.io/node/v/oh-my-knowledge.svg)](https://nodejs.org)
4
7
 
5
- **固定模型,只变知识载体,数据说话。**
8
+ **English** | [简体中文](./README.zh.md)
6
9
 
7
- ## 为什么需要这个工具
10
+ **omk** — LLM evaluation framework with built-in statistical rigor. Bootstrap CI / Krippendorff α / length-debias / saturation curves out of the box. Native support for Claude Code skills, prompts, agents, and RAG.
8
11
 
9
- 做知识工程的团队会产出大量知识载体(当前常见是 skill,也包括 prompt、agent、workflow 等)。当被问到"v2 v1 好在哪"时,需要客观数据而非主观判断。`oh-my-knowledge` 通过控制变量实验解决这个问题:相同模型、相同测试样本,只改变知识载体。
12
+ **Fix the model, vary the knowledge artifact, let the data speak.**
10
13
 
11
- ## 快速开始
14
+ ## Why this tool
15
+
16
+ Teams doing knowledge engineering produce lots of knowledge artifacts (skills today, but also prompts, agents, workflows…). When someone asks "why is v2 better than v1", you need objective data instead of gut feeling. `oh-my-knowledge` solves this with controlled experiments: **same model, same test samples, only the knowledge artifact changes.**
17
+
18
+ ## Key features
19
+
20
+ - **Controlled-variable offline bench** — fix the model and samples, vary only the artifact; works with Claude Code skills, CLAUDE.md prompts, RAG knowledge bases, or any markdown-based instruction
21
+ - **Six-dimension scoring** — separate signals for Fact / Behavior / LLM-judge / Cost / Efficiency / Stability, so a regression in one axis isn't hidden by gains in another
22
+ - **Production session observability** — parse Claude Code session JSONL traces, measure per-skill failure rate, latency, token cost, and knowledge-gap signals on real user sessions
23
+ - **Knowledge-gap detection** — severity-weighted signals (explicit markers / failed searches / hedging language / repeated failures) quantify risk exposure instead of claiming completeness
24
+ - **Pre-merge CI gate** — `omk bench ci` enforces three-layer all-pass (fact + behavior + llm-judge) semantics, catching single-layer regressions a composite score would hide
25
+ - **One-line ship/no-ship verdict** — `omk bench verdict <reportId>` aggregates bootstrap CI / three-layer ci-gate / saturation / human α into a six-tier verdict (PROGRESS / CAUTIOUS / REGRESS / NOISE / UNDERPOWERED / SOLO) plus an action recommendation; the exit code reflects whether to ship
26
+
27
+ ### Statistical rigor
28
+
29
+ The biggest LLM-eval failure mode is "confident bias" — narrow CIs around the wrong answer. omk's statistical layer ships four pieces so conclusions can be externally audited:
30
+
31
+ - **Bootstrap CI** (`--bootstrap`) — distribution-free confidence intervals. The t-test breaks on ordinal LLM scores; bootstrap resamples raw observations and stays valid at small N (< 30) and on skewed data. Pairwise diff CI not crossing 0 = significant.
32
+ - **Human Gold + Krippendorff α** (`--gold-dir`) — bring an external annotation as anchor. CI tells you "is the judge stable", α tells you "is the judge correct" — two complementary axes. omk warns when the gold annotator and the judge are the same model (would inflate α).
33
+ - **Length-controlled judge prompt** (default ON) — research shows LLM judges over-weight verbosity. omk's judge prompt explicitly states "length is not a quality signal"; template hash is `v3-cot-length` so older reports (with the legacy hash) are visibly different. `omk bench debias-validate length <reportId>` re-judges with the opposite setting and reports the score shift.
34
+ - **Saturation curve** — answers "have I run enough samples?". With `--repeat ≥ 5` we accumulate cumulative N → bootstrap CI; when CI shrink rate stays under 5% across 3 windows, more samples buy nothing. The HTML report inlines the SVG curve plus a verdict.
35
+
36
+ ## Why omk over alternatives
37
+
38
+ | | omk | promptfoo | DeepEval | RAGAS | LangSmith |
39
+ |--|--|--|--|--|--|
40
+ | Bootstrap CI | ✓ | ✗ | ✗ | ✗ | ✗ |
41
+ | Krippendorff α (judge ↔ human) | ✓ | ✗ | ✗ | ✗ | ✗ |
42
+ | Length-debias judge prompt | ✓ default | ✗ | ✗ | ✗ | ✗ |
43
+ | Saturation curve | ✓ | ✗ | ✗ | ✗ | ✗ |
44
+ | Three-layer scoring isolation | ✓ | ✗ | partial | ✗ | ✗ |
45
+ | Native Claude Code skill | ✓ | ✗ | ✗ | ✗ | ✗ |
46
+ | Full Chinese docs | ✓ | ✗ | ✗ | ✗ | ✗ |
47
+ | Hosted SaaS dashboard | ✗ | ✗ | ✓ | ✗ | ✓ |
48
+
49
+ omk's moat is **statistical rigor** — every conclusion is auditable by a researcher. If you need a hosted SaaS dashboard, choose LangSmith. If you want quick local prompt iteration without statistics, choose promptfoo. **If you ship to production and someone will ask "why should I trust this number?", choose omk**.
50
+
51
+ Full comparison with 7 tools across 25+ dimensions: [docs/comparison.md](docs/comparison.md)
52
+
53
+ ## Quick start
12
54
 
13
55
  ```bash
14
- # 安装
56
+ # install
15
57
  npm i oh-my-knowledge -g
16
58
 
17
- # 生成评测项目脚手架
59
+ # scaffold an eval project
18
60
  omk bench init my-eval
19
61
  cd my-eval
20
62
 
21
- # 把要对比的 artifact 放到 skills/ 目录
22
- # 方式一:直接放 .md 文件(skills/v1.md, skills/v2.md
23
- # 方式二:放完整 artifact 目录(skills/my-skill-v1/SKILL.md, ...)
24
- # 只放一个 artifact 也行,会自动加 baseline 对照
63
+ # drop the artifacts you want to compare into skills/
64
+ # option 1: plain .md files (skills/v1.md, skills/v2.md)
65
+ # option 2: full artifact dirs (skills/my-skill-v1/SKILL.md, ...)
66
+ # a single artifact also works — baseline is auto-added as control
25
67
 
26
- # 预览评测计划
68
+ # preview the plan
27
69
  omk bench run --dry-run
28
70
 
29
- # 运行评测(自动发现 skills/ 目录下的所有 artifact)
71
+ # run the evaluation (auto-discovers everything under skills/)
30
72
  omk bench run
31
73
  ```
32
74
 
33
- ## Claude Code 中使用
75
+ ## Use inside Claude Code
34
76
 
35
- 安装 omk 后,在 Claude Code 中直接用自然语言交互:
77
+ After installing omk, talk to it in natural language from Claude Code:
36
78
 
37
79
  ```
38
- /omk eval # 评测当前项目的 artifact
39
- /omk evolve # 自动迭代改进 artifact
40
- /omk gen-samples # 生成测试用例
80
+ /omk eval # evaluate the artifact(s) in the current project
81
+ /omk evolve # auto-iterate to improve an artifact
82
+ /omk gen-samples # generate test cases
41
83
  ```
42
84
 
43
- 或直接说"帮我评测 v1 v2 的差异""改进一下这个 artifact"omk 会自动理解意图并调用对应命令。
85
+ You can also just say "compare v1 vs v2 for me" or "improve this artifact"omk picks the right command.
44
86
 
45
- ## 特性
87
+ ## Features
46
88
 
47
- | 特性 | 说明 |
48
- |------|------|
49
- | **18 种断言** | 包含子串、正则、JSON Schema、语义相似度、自定义函数等 |
50
- | **四维评估** | 质量、成本、效率、稳定性四个维度对比 |
51
- | **多执行器** | 支持 Claude CLI / Claude SDK / OpenAI / Gemini 及自定义命令 |
52
- | **MCP URL 获取** | 通过 MCP Server 获取私有文档 URL 内容(SSO 保护的知识库等) |
53
- | **盲测 A/B** | `--blind` 隐藏变体名称,HTML 报告有揭晓按钮 |
54
- | **并行执行** | `--concurrency N` 并行 N 个任务 |
55
- | **多轮方差分析** | `--repeat N` 重复 N 次,计算均值/标准差/置信区间/t 检验 |
56
- | **自动分析** | 检测低区分度断言、均匀分数、全通过/全失败、高成本样本 |
57
- | **可追溯性** | 报告含 CLI 版本、Node 版本、artifact 哈希 |
58
- | **中英切换** | HTML 报告右上角一键切换语言 |
89
+ | Feature | What it does |
90
+ |---|---|
91
+ | **21+ assertion types** | substring, regex, JSON Schema, ROUGE/BLEU/Levenshtein similarity, agent tool-call assertions, semantic similarity, custom JS, and more |
92
+ | **Assertion negation + composition** | universal `not: true` field + `assert-set` (any/all) with arbitrary nesting |
93
+ | **Six-dim evaluation** | Fact / Behavior / LLM-judge / Cost / Efficiency / Stability shown independently |
94
+ | **Statistical rigor** | Bootstrap CI / Krippendorff α / length-debias / saturation curve |
95
+ | **One-line verdict** | `omk bench verdict <id>` six-tier verdict + ship recommendation + exit-code routing; HTML pill shares the same rules |
96
+ | **RAG metrics** | `faithfulness` / `answer_relevancy` / `context_recall` anti-hallucination + answer relevance + context coverage; auto-inherits length-debias |
97
+ | **Sample diagnostics** | `omk bench diagnose <id>` 7 issue kinds (low discrimination / duplicates / ambiguous rubric / cost outliers / etc.) + 0-100 healthScore |
98
+ | **Failure clustering** | `omk bench failures <id>` — single LLM call clusters failed samples and emits per-cluster fixes |
99
+ | **Hard budget caps** | `--budget-usd / --budget-per-sample-usd / --budget-per-sample-ms` — abort on total-cost overrun, flag per-sample overruns; partial report persisted |
100
+ | **Multi-executor** | Claude CLI / Claude SDK / OpenAI / Gemini / any custom command |
101
+ | **Multi-judge ensemble** | `--judge-models claude:opus,openai:gpt-4o` cross-vendor scoring + agreement metrics |
102
+ | **MCP URL fetching** | pull content from private-doc URLs via an MCP server (SSO-protected knowledge bases, etc.) |
103
+ | **Blind A/B** | `--blind` hides variant names; HTML report has a reveal button |
104
+ | **Parallel execution** | `--concurrency N` runs N tasks at once |
105
+ | **Multi-run variance** | `--repeat N` repeats the eval and computes mean / SD / CI / t-test |
106
+ | **Auto analysis** | detects low-discrimination assertions, flat scores, all-pass / all-fail, expensive samples |
107
+ | **Traceability** | reports carry CLI version, Node version, artifact version fingerprint, judge prompt hash |
108
+ | **EN / ZH switch** | one-click language toggle in the HTML report |
59
109
 
60
- ## 工作原理
110
+ ## How it works
61
111
 
62
- 核心思路:**固定模型 + 固定样本,只变 artifact runtime context**,通过交错调度消除时间漂移,用断言 + LLM 评委双通道评分,再叠加知识缺口信号量化风险敞口。
112
+ Core idea: **fix the model and the samples, vary only the artifact and runtime context**, use interleaved scheduling to cancel time drift, score via assertions + LLM judge (dual channel), then layer on knowledge-gap signals to quantify risk exposure.
63
113
 
64
114
  ```mermaid
65
115
  flowchart TD
66
- subgraph Input["① 输入"]
116
+ subgraph Input["① Input"]
67
117
  S["eval-samples<br/>(JSON / YAML)"]
68
118
  A["artifacts<br/>skills/*.md · SKILL.md<br/>baseline · git:name · @cwd"]
69
119
  end
70
120
 
71
- subgraph Prep["② 预处理(解析与抓取)"]
72
- V["变体解析<br/>variant → artifact + runtime context<br/>(cwd / 项目级 CLAUDE.md / 本地 skills)"]
73
- U["URL 抓取<br/>prompt / context 中的 URL<br/>MCP Server(私有文档) → HTTP"]
121
+ subgraph Prep["② Preprocess (resolve & fetch)"]
122
+ V["variant resolution<br/>variant → artifact + runtime context<br/>(cwd / project CLAUDE.md / local skills)"]
123
+ U["URL fetching<br/>URLs in prompt / context<br/>MCP Server(private docs) → HTTP"]
74
124
  end
75
125
 
76
- subgraph Schedule["③ 交错调度 + 并发"]
126
+ subgraph Schedule["③ Interleaved + concurrent scheduling"]
77
127
  Q["s1-v1 → s1-v2 → s2-v1 → s2-v2 …<br/>--concurrency N · --repeat N"]
78
128
  end
79
129
 
80
- subgraph Exec["④ 执行器(固定模型)"]
81
- E["claude / claude-sdk / openai / gemini<br/>anthropic-api / openai-api / 自定义命令"]
82
- T["claude-sdk 抽取<br/>turns / toolCalls trace"]
130
+ subgraph Exec["④ Executor (fixed model)"]
131
+ E["claude / claude-sdk / openai / gemini<br/>anthropic-api / openai-api / custom"]
132
+ T["claude-sdk extracts<br/>turns / toolCalls trace"]
83
133
  E -.-> T
84
134
  end
85
135
 
86
- subgraph Score["⑤ 双通道评分"]
87
- AS["断言(18 )<br/>内容 / 结构 / 成本 / 延迟<br/>agent: tools_called · turns_min …"]
88
- LS["LLM 评委<br/>rubric · dimensions(多维独立打分)"]
89
- CS["综合分数<br/>断言 & LLM 有则均值"]
136
+ subgraph Score["⑤ Dual-channel scoring"]
137
+ AS["assertions (18 types)<br/>content / structure / cost / latency<br/>agent: tools_called · turns_min …"]
138
+ LS["LLM judge<br/>rubric · dimensions (independent per-dim scores)"]
139
+ CS["composite score<br/>mean of assertion & LLM when both present"]
90
140
  AS --> CS
91
141
  LS --> CS
92
142
  end
93
143
 
94
- subgraph Analyze["⑥ 自动分析 + 知识缺口"]
95
- D["低区分度断言 / 均匀分 / 全通过全失败<br/>高成本样本 · 方差 · t 检验"]
96
- G["知识缺口信号<br/>(风险敞口量化, 不证明完备)"]
144
+ subgraph Analyze["⑥ Auto analysis + knowledge gaps"]
145
+ D["low-discrimination / flat scores / all-pass or all-fail<br/>expensive samples · variance · t-test"]
146
+ G["knowledge-gap signals<br/>(quantify risk exposure, not completeness proof)"]
97
147
  end
98
148
 
99
- subgraph Report["⑦ 报告"]
100
- R["四维: 质量 / 成本 / 效率 / 稳定性<br/>JSON + HTML · 盲测揭晓<br/>CLI/Node/artifact 哈希可追溯"]
149
+ subgraph Report["⑦ Report"]
150
+ R["Six dims: Fact / Behavior / LLM-judge / Cost / Efficiency / Stability<br/>JSON + HTML · top verdict pill · blind reveal<br/>CLI/Node/version fingerprint traceable"]
101
151
  end
102
152
 
103
153
  S --> U
@@ -114,72 +164,72 @@ flowchart TD
114
164
  G --> R
115
165
  ```
116
166
 
117
- **关键设计:**
167
+ **Key design choices:**
118
168
 
119
- - **交错调度**消除时间漂移:同一样本的不同 variant 交替发出,而非 v1 全跑完再跑 v2,避免模型负载/网络波动被错误归因给 artifact
120
- - **variant = artifact + runtime context**:`name@cwd` 让对照组可以显式声明"项目目录"这个隐性输入,把"项目级沉淀""显式 artifact 注入"拆开测。
121
- - **双通道评分互补**:断言抓确定性缺陷(必须调用某工具/必须包含某字段),LLM 评委抓主观质量(可读性/完整性),两者都存在时取均值。
122
- - **知识缺口信号**不是评分的一部分,而是一个独立追踪项:它告诉你"这次评测覆盖了多少风险敞口",用于追踪收敛,而非断言知识"完备"。
169
+ - **Interleaved scheduling** removes time drift: different variants of the same sample are dispatched alternately rather than "all of v1 then all of v2", so model load / network jitter can't be mis-attributed to the artifact.
170
+ - **variant = artifact + runtime context**: `name@cwd` lets control groups explicitly declare the "project directory" input, separating "project-level accumulated knowledge" from "explicit artifact injection".
171
+ - **Dual-channel scoring is complementary**: assertions catch deterministic defects (must call tool X, must contain field Y); the LLM judge catches subjective quality (readability, completeness). Mean is taken when both are present.
172
+ - **Knowledge-gap signals** are not part of the score — they are an independent tracking channel that tells you "how much risk exposure this evaluation covered", for convergence tracking, not as a completeness proof.
123
173
 
124
- ## 评测样本格式
174
+ ## Eval sample format
125
175
 
126
- 支持 JSON YAML(`eval-samples.json`、`eval-samples.yaml`、`eval-samples.yml`)。
176
+ Supports JSON and YAML (`eval-samples.json`, `eval-samples.yaml`, `eval-samples.yml`).
127
177
 
128
178
  ```json
129
179
  [
130
180
  {
131
181
  "sample_id": "s001",
132
- "prompt": "审查这段代码的安全性",
182
+ "prompt": "Review this code for security issues",
133
183
  "context": "function auth(u, p) { db.query('SELECT * FROM users WHERE name=' + u); }",
134
- "rubric": "应识别 SQL 注入风险并建议参数化查询",
184
+ "rubric": "Should identify SQL injection risk and recommend parameterized queries",
135
185
  "assertions": [
136
- { "type": "contains", "value": "SQL 注入", "weight": 1 },
137
- { "type": "contains", "value": "参数化", "weight": 1 },
138
- { "type": "not_contains", "value": "没有问题", "weight": 0.5 }
186
+ { "type": "contains", "value": "SQL injection", "weight": 1 },
187
+ { "type": "contains", "value": "parameterized", "weight": 1 },
188
+ { "type": "not_contains", "value": "looks fine", "weight": 0.5 }
139
189
  ],
140
190
  "dimensions": {
141
- "security": "是否识别出注入漏洞",
142
- "actionability": "是否给出可直接使用的修复代码"
191
+ "security": "did it identify the injection vulnerability?",
192
+ "actionability": "did it give directly usable fix code?"
143
193
  }
144
194
  }
145
195
  ]
146
196
  ```
147
197
 
148
- ### 字段说明
198
+ ### Fields
149
199
 
150
- | 字段 | 类型 | 必填 | 说明 |
151
- |------|------|------|------|
152
- | `sample_id` | `string` | **是** | 样本唯一标识 |
153
- | `prompt` | `string` | **是** | 发送给模型的用户提示词 |
154
- | `context` | `string` | | 附加上下文(代码片段等),会被包裹在代码块中拼接到 prompt 后。也支持 URL,运行时自动抓取内容 |
155
- | `rubric` | `string` | | LLM 评委的评分标准(1-5 分) |
156
- | `assertions` | `array` | | 断言检查列表,详见[断言类型](#断言类型) |
157
- | `assertions[].type` | `string` | **是** | 断言类型 |
158
- | `assertions[].value` | `string\|number` | 视类型 | 检查值(`contains`、`min_length`、`cost_max` 等必填) |
159
- | `assertions[].values` | `array` | 视类型 | 字符串数组(`contains_all`、`contains_any` 必填) |
160
- | `assertions[].pattern` | `string` | 视类型 | 正则表达式(`regex` 必填) |
161
- | `assertions[].flags` | `string` | | 正则标志(默认 `"i"`) |
162
- | `assertions[].schema` | `object` | 视类型 | JSON Schema 对象(`json_schema` 必填,基于 [ajv](https://ajv.js.org/) |
163
- | `assertions[].reference` | `string` | 视类型 | 参考文本(`semantic_similarity` 必填) |
164
- | `assertions[].threshold` | `number` | | 语义相似度通过阈值(默认 3 |
165
- | `assertions[].fn` | `string` | 视类型 | 自定义断言 JS 文件路径(`custom` 必填) |
166
- | `assertions[].weight` | `number` | | 权重(默认 1 |
167
- | `dimensions` | `object` | | 多维度评分,key 为维度名,value 为评分标准文本 |
200
+ | Field | Type | Required | Description |
201
+ |---|---|---|---|
202
+ | `sample_id` | `string` | **yes** | Unique sample ID |
203
+ | `prompt` | `string` | **yes** | User prompt sent to the model |
204
+ | `context` | `string` | no | Extra context (e.g. code). Wrapped in a code block and appended to the prompt. URLs are auto-fetched at runtime. |
205
+ | `rubric` | `string` | no | Scoring guideline for the LLM judge (1-5 scale) |
206
+ | `assertions` | `array` | no | Assertion checks; see [assertion types](#assertion-types) |
207
+ | `assertions[].type` | `string` | **yes** | Assertion type |
208
+ | `assertions[].value` | `string\|number` | depends | Check value (required for `contains`, `min_length`, `cost_max`, etc.) |
209
+ | `assertions[].values` | `array` | depends | String array (required for `contains_all`, `contains_any`) |
210
+ | `assertions[].pattern` | `string` | depends | Regex pattern (required for `regex`) |
211
+ | `assertions[].flags` | `string` | no | Regex flags (default `"i"`) |
212
+ | `assertions[].schema` | `object` | depends | JSON Schema object (required for `json_schema`, via [ajv](https://ajv.js.org/)) |
213
+ | `assertions[].reference` | `string` | depends | Reference text (required for `semantic_similarity`) |
214
+ | `assertions[].threshold` | `number` | no | Pass threshold for semantic similarity (default 3) |
215
+ | `assertions[].fn` | `string` | depends | Path to a custom assertion JS file (required for `custom`) |
216
+ | `assertions[].weight` | `number` | no | Weight (default 1) |
217
+ | `dimensions` | `object` | no | Multi-dimension scoring; key = dimension name, value = scoring guideline |
168
218
 
169
- ### URL 自动抓取
219
+ ### URL auto-fetching
170
220
 
171
- `prompt` `context` 中的 URL 会在评测前自动抓取内容并内联到文本中。适用于引用在线文档、API 文档等场景:
221
+ URLs in `prompt` and `context` are auto-fetched before evaluation and inlined into the text. Useful when referencing online docs, API references, etc.:
172
222
 
173
223
  ```json
174
224
  {
175
225
  "sample_id": "s001",
176
- "prompt": "请根据以下 PRD 文档生成测试用例:https://wiki.example.com/prd/feature-x"
226
+ "prompt": "Generate test cases from this PRD: https://wiki.example.com/prd/feature-x"
177
227
  }
178
228
  ```
179
229
 
180
- 运行时,URL 会被替换为实际文档内容。获取顺序:先通过 MCP Server 获取匹配的 URL(如 SSO 保护的私有文档),再通过 HTTP 获取剩余 URL。MCP 已成功的 URL 不会重复 HTTP 抓取。
230
+ At runtime, URLs are replaced with the actual content. Fetch order: MCP Server first for matching URLs (e.g. SSO-protected private docs), then plain HTTP for the rest. URLs already resolved by MCP are not re-fetched via HTTP.
181
231
 
182
- **私有文档 URL**:在项目目录放一个 `.mcp.json` 配置文件,或通过 `--mcp-config` 指定路径:
232
+ **Private-doc URLs**: drop a `.mcp.json` config file into the project dir, or pass `--mcp-config <path>`:
183
233
 
184
234
  ```json
185
235
  {
@@ -202,452 +252,672 @@ flowchart TD
202
252
  }
203
253
  ```
204
254
 
205
- **公网 URL**:直接 HTTP 获取,如果需要认证请确保命令行环境已配置好网络访问(VPN、代理等)。
255
+ **Public URLs**: fetched via plain HTTP. If they require auth, make sure the shell already has network access configured (VPN, proxy, etc.).
206
256
 
207
- ### 评分策略
257
+ ### Scoring strategy
208
258
 
209
- #### 1. 断言评分
259
+ #### 1. Assertion score
210
260
 
211
- 基于规则的本地检查,每个断言产生通过/失败结果。
261
+ Rule-based local checks; each assertion yields pass/fail.
212
262
 
213
- **计算方式:**
263
+ **Formula:**
214
264
 
215
- - 通过率 = 通过断言的权重之和 / 总权重(0~1
216
- - 分数 = 1 + 通过率 × 4(映射到 1~5 分)
217
- - 示例:3 个断言(权重各 1),2 个通过通过率 = 2/3 → 分数 = 1 + 0.67 × 4 = **3.67**
265
+ - Pass rate = sum of passed assertion weights / total weight (01)
266
+ - Score = 1 + pass_rate × 4 (mapped to 15)
267
+ - Example: 3 assertions (weight 1 each), 2 passpass rate 2/3 → score = 1 + 0.67 × 4 = **3.67**
218
268
 
219
- #### 2. Rubric / Dimensions 评分
269
+ #### 2. Rubric / Dimensions score
220
270
 
221
- 评委模型(默认 `haiku`)按标准打 1-5 分。`dimensions` 模式下各维度独立评分后取平均。
271
+ The judge model (default `haiku`) scores 15 against the rubric. In `dimensions` mode, each dimension is scored independently and then averaged.
222
272
 
223
- #### 3. 综合分数
273
+ #### 3. Composite score
224
274
 
225
- | 条件 | 公式 |
226
- |------|------|
227
- | 仅断言 | `assertionScore` |
228
- | LLM | `llmScore` |
229
- | 两者都有 | `(assertionScore + llmScore) / 2` |
230
- | 都没有 | `0` |
275
+ | Condition | Formula |
276
+ |---|---|
277
+ | Only assertions | `assertionScore` |
278
+ | Only LLM judge | `llmScore` |
279
+ | Both present | `(assertionScore + llmScore) / 2` |
280
+ | Neither | `0` |
231
281
 
232
- ### 断言类型
282
+ ### Assertion types
233
283
 
234
- **确定性断言(18 种):**
284
+ **Deterministic assertions (21+ total):**
235
285
 
236
- | 类型 | 说明 |
237
- |------|------|
238
- | `contains` / `not_contains` | 包含/不包含子串 |
239
- | `regex` | 正则匹配 |
240
- | `min_length` / `max_length` | 长度范围 |
241
- | `json_valid` / `json_schema` | JSON 校验 |
242
- | `starts_with` / `ends_with` | 前缀/后缀匹配 |
243
- | `equals` / `not_equals` | 精确匹配 |
244
- | `word_count_min` / `word_count_max` | 词数范围 |
245
- | `contains_all` / `contains_any` | 多值匹配 |
246
- | `cost_max` / `latency_max` | 成本/延迟限制 |
247
- | `semantic_similarity` | LLM 语义相似度 |
248
- | `custom` | 自定义 JS 函数(30s 超时) |
286
+ | Type | Description |
287
+ |---|---|
288
+ | `contains` / `not_contains` | substring must / must-not appear |
289
+ | `regex` | regex match |
290
+ | `min_length` / `max_length` | length bounds |
291
+ | `json_valid` / `json_schema` | JSON validation |
292
+ | `starts_with` / `ends_with` | prefix / suffix |
293
+ | `equals` / `not_equals` | exact match |
294
+ | `word_count_min` / `word_count_max` | word-count bounds |
295
+ | `contains_all` / `contains_any` | multi-value match |
296
+ | `cost_max` / `latency_max` | cost / latency caps |
297
+ | `tools_called` / `tools_not_called` / `tools_count_min` / `tools_count_max` | agent tool-call assertions |
298
+ | `tool_output_contains` / `tool_input_contains` | match content of a tool's input or output |
299
+ | `turns_min` / `turns_max` | conversation-turn bounds |
300
+ | `rouge_n_min` | ROUGE-N recall ≥ threshold (`reference` field holds the gold text; `n` defaults to 1; `threshold` defaults to 0.5) |
301
+ | `levenshtein_max` | edit distance ≤ value (for "output should be near-identical to reference") |
302
+ | `bleu_min` | BLEU-4 ≥ threshold (unsmoothed; degenerates to 0 on short text) |
303
+ | `faithfulness` | output stays grounded in `sample.context` (anti-hallucination); LLM judge 1-5; threshold defaults to 3 |
304
+ | `answer_relevancy` | output directly answers `sample.prompt`; catches dodging, topic drift, verbosity; threshold defaults to 3 |
305
+ | `context_recall` | gold facts in `sample.context` are actually used in the output; `reference` may explicitly enumerate gold facts; threshold defaults to 3 |
306
+ | `semantic_similarity` | LLM-based holistic semantic similarity (complementary to the three RAG metrics above) |
307
+ | `custom` | custom JS function (30 s timeout) |
249
308
 
250
- ### 自定义断言
309
+ **Universal modifier:**
310
+
311
+ Any assertion takes `not: true` to invert (replaces paired `not_contains` / `not_equals` etc; legacy types remain as aliases):
312
+
313
+ ```yaml
314
+ - type: regex
315
+ pattern: "TODO|FIXME"
316
+ not: true # output must NOT contain TODO/FIXME
317
+ ```
318
+
319
+ **Composition (assert-set):**
320
+
321
+ `assert-set` combines child assertions with `any` (OR) or `all` (AND) and supports nesting:
322
+
323
+ ```yaml
324
+ - type: assert-set
325
+ mode: any # at least one child must pass (mode: 'all' = all must pass)
326
+ children:
327
+ - { type: contains, value: "parameterized" }
328
+ - { type: contains, value: "prepared statement" }
329
+ - { type: regex, pattern: "bind\\(.*\\?" }
330
+ ```
331
+
332
+ Children can independently use `not: true`; nested `assert-set`s can express any boolean shape.
333
+
334
+ ### Custom assertion
251
335
 
252
336
  ```js
253
337
  // my-assertion.mjs
254
338
  export default function(output, { sample, assertion }) {
255
- return { pass: output.includes('SQL'), message: '检查了 SQL 关键字' };
339
+ return { pass: output.includes('SQL'), message: 'checked for SQL keyword' };
256
340
  }
257
341
  ```
258
342
 
259
- ## 四维评估指标
343
+ ## Six-dim evaluation
260
344
 
261
- 评测报告从四个维度展示结果:
345
+ Reports display results across six independent dimensions. The three scoring layers — Fact / Behavior / LLM-judge — are shown separately so you see **which layer regressed** instead of a single composite number:
262
346
 
263
- | 维度 | 指标 | 说明 |
264
- |------|------|------|
265
- | 📊 **质量** | 综合分数、断言分、LLM 评分、min/max | 基于断言和 LLM 评委的综合评分 |
266
- | 💰 **成本** | 总成本、输入/输出 Token | 基于 Token 消耗和模型定价的 API 费用 |
267
- | **效率** | 平均延迟 (ms) | 从发送请求到收到完整响应的端到端耗时 |
268
- | 🛡️ **稳定性** | 成功率 (%) | 模型调用成功率,失败包括超时、API 错误等 |
347
+ | Dimension | Metric | Description |
348
+ |---|---|---|
349
+ | 📋 **Fact** | fact-assertion pass rate | rule-verifiable assertions like `contains` / `json_schema` / `fact_check`, mapped to 1-5 |
350
+ | 🛠️ **Behavior** | behavior-assertion pass rate | execution-compliance assertions like `tools_called` / `tool_output_contains` / `turns_max` |
351
+ | 💬 **LLM-judge** | rubric score | 1-5 scored by the judge model against a predefined rubric; subjective, catches what rules miss |
352
+ | 💰 **Cost** | total cost, input/output tokens | API cost based on token usage and model pricing |
353
+ | ⚡ **Efficiency** | average latency (ms) | end-to-end latency from request to full response |
354
+ | 🛡️ **Stability** | CV (coefficient of variation) | score consistency across repeated runs (`--repeat ≥ 2`); single-run shows `—`, **honestly acknowledging what can't be measured** |
269
355
 
270
- ## CLI 参考
356
+ ## CLI reference
271
357
 
272
358
  ### `omk bench run`
273
359
 
274
360
  ```bash
275
- omk bench run [选项]
276
-
277
- 选项:
278
- --samples <路径> 样本文件(默认:eval-samples.json,自动检测 .yaml/.yml
279
- --skill-dir <路径> artifact 目录(参数名沿用历史写法,默认:skills
280
- --variants <a,b> 变体名称,不指定时自动从 artifact 目录发现
281
- 只有一个 artifact 时自动加 baseline 对照
282
- 特殊值:baseline(空 artifact)、git:name(git 历史版本)、
283
- git:ref:name(指定 commit)、含 / 的路径(直接读取文件)
284
- --model <名称> 被测模型(默认:sonnet)
285
- --judge-model <名称> 评委模型(默认:haiku)
286
- --output-dir <路径> 输出目录(默认:~/.oh-my-knowledge/reports/)
287
- --no-judge 跳过 LLM 评分
288
- --no-cache 禁用结果缓存(默认开启,相同输入自动复用)
289
- --dry-run 仅预览
290
- --blind 盲测模式
291
- --concurrency <n> 并行任务数(默认:1)
292
- --timeout <秒> 单个任务的执行器超时时间(默认:120)
293
- --repeat <n> 重复 N 次做方差分析(默认:1)
294
- --executor <名称> 执行器(默认:claude),支持自定义命令
295
- --skip-preflight 跳过评测前的模型连通性检查
296
- --mcp-config <路径> MCP 配置文件,用于通过 MCP Server 获取私有文档 URL 内容
297
- (默认:当前目录的 .mcp.json)
298
- --no-serve 评测完成后不自动启动报告服务
299
- --verbose 打印每个样本的详细执行结果(耗时、tokens、输出预览)
300
- --each 批量评测:每个 artifact 独立和 baseline 对比
301
- 需要每个 artifact 配对 {name}.eval-samples.json
302
- ```
303
-
304
- ### `omk bench run --each`(批量评测)
305
-
306
- skills/ 下放了多个**独立的** artifact 时,使用 `--each` 逐个评测,每个 artifact 独立和 baseline 对比,生成一份合并报告。
361
+ omk bench run [options]
362
+
363
+ options:
364
+ --samples <path> sample file (default: eval-samples.json, also detects .yaml/.yml)
365
+ --skill-dir <path> artifact dir (default: skills)
366
+ --control <expr> control-group variant expression (experiment role = control)
367
+ --treatment <v1,v2> treatment-group variant expressions, comma-separated
368
+ at least one of --control / --treatment is required
369
+ (unless you use --config or --each)
370
+ special values: baseline (empty artifact), git:name (git HEAD),
371
+ git:ref:name (specific commit), path with "/" (read file directly)
372
+ --config <path> YAML/JSON config file (evaluation-as-code); declares
373
+ samples + variants + model + executor in one file; CLI
374
+ flags override config fields when both are provided
375
+ --model <name> model under test (default: sonnet)
376
+ --judge-model <name> judge model (default: haiku)
377
+ --output-dir <path> output dir (default: ~/.oh-my-knowledge/reports/)
378
+ --no-judge skip the LLM judge
379
+ --no-cache disable result cache (on by default; identical inputs reuse)
380
+ --dry-run preview only
381
+ --blind blind mode
382
+ --concurrency <n> parallel tasks (default: 1)
383
+ --timeout <sec> per-task executor timeout (default: 120)
384
+ --repeat <n> repeat N times for variance analysis (default: 1)
385
+ --executor <name> executor (default: claude); supports custom commands
386
+ --skip-preflight skip evaluation model reachability check
387
+ --mcp-config <path> MCP config for fetching private-doc URLs via MCP Server
388
+ (default: .mcp.json in cwd)
389
+ --no-serve don't auto-start the report server after the run
390
+ --verbose print per-sample details (duration, tokens, output preview)
391
+ --each batch mode: evaluate each artifact independently vs baseline
392
+ requires {name}.eval-samples.json paired with each artifact
393
+ --judge-repeat <n> run the LLM judge N times per (sample × dimension) and report stddev
394
+ --judge-models <list> multi-judge ensemble: "executor1:model1,executor2:model2"
395
+ ≥ 2 judges enables ensemble + inter-judge agreement output
396
+ --bootstrap enable distribution-free CIs: bootstrap CI per variant +
397
+ pairwise diff CI (CI containing 0 = not significant)
398
+ --bootstrap-samples N bootstrap resample count (default 1000)
399
+ --gold-dir <path> after the run, compare scores against the gold dataset
400
+ (Krippendorff α / κ / Pearson). Result is written to
401
+ report.meta.humanAgreement and shown in the HTML report
402
+ --no-debias-length revert to legacy v2-cot judge prompt (no "length is not
403
+ a quality signal" paragraph) — for byte-compat with
404
+ legacy reports whose hash predates v3-cot-length
405
+ --budget-usd <num> total cost cap (USD); on overrun the run aborts and
406
+ a partial report is persisted (`report.meta.budgetExhausted = true`)
407
+ --budget-per-sample-usd <num> per-sample cost cap; offending samples fail individually,
408
+ the run continues
409
+ --budget-per-sample-ms <num> per-sample latency cap (ms); same semantics as cost cap
410
+ ```
411
+
412
+ **eval.yaml budget**: declare `budget: { totalUSD?, perSampleUSD?, perSampleMs? }` (all optional, must be ≥ 0). CLI flags of the same name override the config values.
413
+
414
+ **Difference from `cost_max` / `latency_max` assertions**: assertions are **per-sample scoring rules** (exceeding the cap fails that one assertion, the run continues); budget caps are **workflow-level hard limits** (`totalUSD` overrun aborts the run and persists a partial report; per-sample overruns fail the offending sample but the run continues). Assertions answer "is quality acceptable?"; budgets answer "are cost/time within the envelope?".
415
+
416
+ ### `omk bench run --each` (batch mode)
417
+
418
+ When `skills/` contains several **independent** artifacts, use `--each` to evaluate each one against baseline and produce a merged report.
307
419
 
308
420
  ```
309
421
  skills/
310
- ├── asset.md ← artifact 文件
311
- ├── asset.eval-samples.json ← 配对的测试集
422
+ ├── asset.md ← artifact file
423
+ ├── asset.eval-samples.json ← paired samples
312
424
  ├── home.md
313
425
  ├── home.eval-samples.json
314
- └── product/ ← 目录格式也支持
426
+ └── product/ ← directory format also supported
315
427
  ├── SKILL.md
316
428
  └── eval-samples.json
317
429
  ```
318
430
 
319
- 配对规则:
431
+ Pairing rules:
320
432
 
321
- - `{name}.md` → 查找同目录下的 `{name}.eval-samples.json`
322
- - `{name}/SKILL.md` → 查找 `{name}/eval-samples.json`
323
- - 没有配对 eval-samples artifact 会被跳过并打印警告
433
+ - `{name}.md` → looks for `{name}.eval-samples.json` in the same dir
434
+ - `{name}/SKILL.md` → looks for `{name}/eval-samples.json`
435
+ - artifacts without paired samples are skipped with a warning
324
436
 
325
437
  ```bash
326
438
  omk bench run --each
327
439
  omk bench run --each --dry-run
328
440
  ```
329
441
 
330
- ### `omk bench gen-samples`(生成测评用例)
442
+ ### `omk bench gen-samples` (generate test cases)
331
443
 
332
- 读取 artifact 内容,通过 LLM 自动生成 eval-samples。生成后请审查编辑再跑评测。
444
+ Reads an artifact's content and uses an LLM to auto-generate eval-samples. Review and edit them before running eval.
333
445
 
334
446
  ```bash
335
- # 为指定 artifact 生成测试集(输出到 eval-samples.json
447
+ # generate for a specific artifact (writes eval-samples.json)
336
448
  omk bench gen-samples skills/my-skill.md
337
449
 
338
- # skills/ 下所有缺少测试集的 artifact 批量生成
450
+ # batch-generate for every artifact under skills/ that lacks samples
339
451
  omk bench gen-samples --each
340
452
 
341
- # 指定生成数量
453
+ # specify sample count
342
454
  omk bench gen-samples skills/my-skill.md --count 10
343
455
  ```
344
456
 
345
- 选项:
457
+ Options:
346
458
 
347
459
  ```
348
- --each 为所有缺少 eval-samples artifact 批量生成
349
- --count <n> 每个 artifact 生成的样本数(默认:5
350
- --model <名称> 生成用的模型(默认:sonnet
351
- --skill-dir <路径> artifact 目录(参数名沿用历史写法,默认:skills),配合 --each 使用
460
+ --each batch-generate for every artifact missing samples
461
+ --count <n> samples per artifact (default: 5)
462
+ --model <name> model used for generation (default: sonnet)
463
+ --skill-dir <path> artifact dir (default: skills), used with --each
352
464
  ```
353
465
 
354
- ### `omk bench evolve`(自我循环改进)
466
+ ### `omk bench evolve` (self-iterating improvement)
355
467
 
356
- AI 自动迭代 artifact:评测分析弱点 → LLM 改进再评测分数涨了留、没涨扔重复。
468
+ Lets the AI iterate an artifact automatically: evaluate analyze weak spots → LLM rewritesevaluate again keep if the score went up, drop otherwise repeat.
357
469
 
358
470
  ```bash
359
- # 基本用法:迭代 5
471
+ # basic: iterate 5 rounds
360
472
  omk bench evolve skills/my-skill.md
361
473
 
362
- # 指定轮数和目标分数
474
+ # set rounds and target score
363
475
  omk bench evolve skills/my-skill.md --rounds 10 --target 4.5
364
476
  ```
365
477
 
366
- 选项:
478
+ Options:
367
479
 
368
480
  ```
369
- --rounds <n> 最大迭代轮数(默认:5
370
- --target <分数> 目标分数,达到即停
371
- --samples <路径> 样本文件(默认:eval-samples.json
372
- --improve-model <名称> 改进用模型(默认:sonnet
481
+ --rounds <n> max iteration rounds (default: 5)
482
+ --target <score> stop early when the score reaches this threshold
483
+ --samples <path> sample file (default: eval-samples.json)
484
+ --improve-model <name> model used for rewrites (default: sonnet)
373
485
  ```
374
486
 
375
- 每轮产出保存在 `skills/evolve/` 目录(`my-skill.r0.md`、`my-skill.r1.md`...),可以 diff 查看 AI 改了什么。最佳版本自动写回原始文件。
487
+ Each round's output is saved under `skills/evolve/` (`my-skill.r0.md`, `my-skill.r1.md`…), so you can `diff` to see what the AI changed. The best round is written back to the original file.
376
488
 
377
489
  ### `omk bench ci`
378
490
 
379
- 在自动化流水线中运行评测。评分达标则退出码为 0(通过),否则为 1(失败),可直接用于卡点判断。
491
+ Run the evaluation inside CI. Exit code 0 on pass, 1 on fail — can be wired into gates directly.
492
+
493
+ The gate is **three-layer all-pass**: `avgFactScore >= threshold AND avgBehaviorScore >= threshold AND avgJudgeScore >= threshold`. Any layer below threshold is FAIL, and the output shows which layer broke. This stops cases like `fact 4.5→2.5 but judge 3→5` from passing via composite averaging — if one layer regresses, the gate catches it.
380
494
 
381
495
  ```bash
382
- omk bench ci [选项]
383
- --threshold <数值> 达标的最低综合分数(默认:3.5
496
+ omk bench ci [options]
497
+ --threshold <number> per-layer minimum score (default: 3.5); applied
498
+ independently to fact / behavior / judge
384
499
  ```
385
500
 
386
501
  ### `omk bench report`
387
502
 
388
- 启动报告服务,浏览历史报告、提交反馈、删除报告。
503
+ Start the report server to browse historical reports, submit feedback, and delete reports.
389
504
 
390
505
  ```bash
391
- omk bench report [选项]
392
- --port <端口号> 服务端口(默认:7799
506
+ omk bench report [options]
507
+ --port <number> server port (default: 7799)
393
508
  ```
394
509
 
395
510
  ### `omk bench init`
396
511
 
397
512
  ```bash
398
- omk bench init [目录] # 生成评测项目脚手架
513
+ omk bench init [dir] # scaffold an eval project
514
+ ```
515
+
516
+ ### `omk bench gold` (human gold anchor)
517
+
518
+ Bring a human (or stronger-model proxy) annotation as an external anchor and compute Krippendorff α / weighted κ / Pearson against the LLM judge. Answers "is the judge correct?", complementary to Bootstrap CI's "is the judge stable?".
519
+
520
+ ```bash
521
+ omk bench gold init [--out <dir>] [--annotator <id>] # scaffold a dataset template
522
+ omk bench gold validate <dir> # check schema (annotator / date / version / score range)
523
+ omk bench gold compare <reportId> --gold-dir <dir> # compare against an existing report; prints α/κ/r + verdict
524
+ ```
525
+
526
+ Dataset layout:
527
+
528
+ ```
529
+ gold-dir/
530
+ ├── metadata.yaml # annotator (must NOT match the omk judge model — would trigger contamination warning) + date + version
531
+ └── annotations.yaml # [{ sample_id, score, reason? }] concatenated by sample_id
532
+ ```
533
+
534
+ α thresholds follow Krippendorff (2011): ≥ 0.80 strong agreement; [0.67, 0.80) acceptable; < 0.40 large divergence — investigate rubric / prompt.
535
+
536
+ Full demo: [examples/gold-dataset/](examples/gold-dataset/)
537
+
538
+ ### `omk bench debias-validate length` (judge length-bias check)
539
+
540
+ Re-judges every (sample × variant) of an existing report with the OPPOSITE length-debias setting (v3-cot-length ↔ v2-cot) and bootstraps the CI on the score difference. A significant shift = the judge is sensitive to the length-debias instruction (indirect evidence of length bias).
541
+
542
+ ```bash
543
+ omk bench debias-validate length <reportId> [options]
544
+ --variant <name> check a single variant only
545
+ --judge-model <id> override the report's judge model
546
+ --bootstrap-samples N bootstrap iterations (default 1000)
547
+ --seed N deterministic seed
399
548
  ```
400
549
 
401
- ## 执行器
550
+ Verdict bucket: none / weak / medium (|0.2-0.5|) / strong (≥ 0.5). Re-judge cost roughly doubles vs the original judge pass.
402
551
 
403
- ### 内置执行器
552
+ ### `omk bench saturation` (saturation curve)
404
553
 
405
- | 执行器 | 适用场景 | 说明 |
406
- |--------|----------|------|
407
- | `claude` | 默认 | 通过 `claude -p` 调用 Claude CLI |
408
- | `claude-sdk` | 结构化输出 | 通过 Claude Agent SDK 调用,无 stdout 解析,避免 buffer 截断 |
409
- | `openai` | 跨厂商对比 | 通过 `openai api` CLI 调用 |
410
- | `gemini` | 跨厂商对比 | 通过 `gemini` CLI 调用 |
411
- | `anthropic-api` | 无需 CLI | 直接调用 Anthropic HTTP API(需 `ANTHROPIC_API_KEY`) |
412
- | `openai-api` | 无需 CLI | 直接调用 OpenAI HTTP API(需 `OPENAI_API_KEY`) |
554
+ Answers "have I run enough samples?". Reads the saturation trace from an existing report (no re-run). Verdicts only emit when the original run used `--repeat ≥ 5`; below that, the curve is plotted but no verdict is computed.
413
555
 
414
- API 直调执行器支持通过环境变量自定义 Base URL:`ANTHROPIC_BASE_URL`、`OPENAI_BASE_URL`。
556
+ ```bash
557
+ omk bench saturation <reportId> [options]
558
+ --variant <name> single-variant view
559
+ --method <m> slope | bootstrap-ci-width (default) | plateau-height
560
+ --threshold <num> method-specific cutoff (defaults match the method)
561
+ --window <num> consecutive windows that must satisfy the threshold (default 3)
562
+ ```
415
563
 
416
- ### 自定义执行器
564
+ The HTML report inlines an SVG curve (cumulative N on X, mean ± 95% CI shading on Y, one curve per variant) automatically.
417
565
 
418
- 任何 shell 命令都可以作为执行器,通过 stdin/stdout JSON 协议通信:
566
+ ### `omk bench verdict` (one-line ship/no-ship verdict)
567
+
568
+ Aggregates bootstrap CI / three-layer ci-gate / saturation / human α into one of six verdicts: **PROGRESS** (significant improvement, all three layers pass → exit 0), **CAUTIOUS** (real gain but with a warning — broken gate / trivially small / control regressed → exit 1), **REGRESS** (significant negative shift → exit 1), **NOISE** (CI spans 0, undecidable → exit 1), **UNDERPOWERED** (sample size too small → exit 1), **SOLO** (single variant; exit 0 only if its own three-layer gate passes).
569
+
570
+ ```bash
571
+ omk bench verdict <reportId> [options]
572
+ --threshold <num> three-layer gate threshold (default 3.5, matches `omk bench ci`)
573
+ --trivial-diff <num> "practically tiny" cutoff (default 0.1)
574
+ --verbose expand per-pair detail
575
+ ```
576
+
577
+ Shares its rule module with the HTML report's verdict pill — CLI and UI cannot disagree.
578
+
579
+ ### `omk bench diagnose` (sample quality diagnostics)
580
+
581
+ Answers "is the conclusion polluted by bad samples?". Diagnoses 7 sample-quality issues: `flat_scores` (low discrimination), `all_pass` (too easy), `all_fail` (broken — error severity), `near_duplicate` (prompt ROUGE-1 ≥ threshold), `ambiguous_rubric` (high judge stddev across `--judge-repeat ≥ 2`), `cost_outlier` (≥ k× median), `latency_outlier` (≥ k× median), `error_prone` (executor failure).
582
+
583
+ ```bash
584
+ omk bench diagnose <reportId> [options]
585
+ --top <n> show top N per kind (default 10, 0 = all)
586
+ --duplicate-rouge <num> near-duplicate ROUGE-1 threshold (default 0.7)
587
+ --ambiguous-stddev <num> judge-stddev threshold (default 1.0)
588
+ --cost-k <num> cost-outlier multiplier vs median (default 3)
589
+ --latency-k <num> latency-outlier multiplier vs median (default 3)
590
+ --flat <num> flat_scores spread threshold (default 0.5)
591
+ ```
592
+
593
+ Output includes a healthScore (0-100, formula `100 - normalized × 20` where `normalized = (errors×8 + warnings×3 + infos×1) / N`). Exit code is 0 only when `healthScore ≥ 70` AND no error-severity issue — CI-friendly.
594
+
595
+ ### `omk bench failures` (failure case LLM clustering)
596
+
597
+ When 14 of 50 samples failed, reading them one by one is slow. This command sends failed samples to a single LLM call, clusters them into ≤ N groups, and emits per-cluster root cause + fix. "Failed" = `compositeScore < threshold` OR `ok = false`.
598
+
599
+ ```bash
600
+ omk bench failures <reportId> [options]
601
+ --judge-executor <name> executor (default: claude)
602
+ --judge-model <id> clustering model (default: from report.meta.judgeModel)
603
+ --max-clusters <n> maximum clusters (default 5)
604
+ --threshold <num> failure score threshold (default 3)
605
+ --max-feed <n> max failures fed to LLM (default 50; takes the worst)
606
+ ```
607
+
608
+ Tolerant: ```json``` markdown fences, `"sample_id@variant"` string member form, hallucinated members are dropped, single-failure case skips the LLM call, executor errors degrade to unclassified.
609
+
610
+ ### `omk bench diff` (report comparison — single / dual mode)
611
+
612
+ **Single-arg mode** (within-report sample-level): `omk bench diff <reportId>` — within one report, drill down per-sample comparing `variants[0]` against `variants[1]` (or `--variant <name>`).
613
+
614
+ **Dual-arg mode** (cross-report variant-level): `omk bench diff <reportId1> <reportId2>` — compare the same variant across two reports (legacy behavior preserved).
615
+
616
+ ```bash
617
+ omk bench diff <reportId> [--variant <name>] [--regressions-only] [--threshold 0] [--top N]
618
+ omk bench diff <reportId1> <reportId2> [--regressions-only] [--threshold 0]
619
+ ```
620
+
621
+ Single-arg output sorts by |Δ| desc; rows where Δ < threshold are highlighted as regressions. `--top N` caps row count, `--regressions-only` shows only negative Δ samples.
622
+
623
+ ## `omk analyze` — production observability
624
+
625
+ `omk bench run` is **offline evaluation** (fixed controls, repeatable, scored). Production is different — no control group, no ground truth, no repetition, so scoring isn't valid there. `omk analyze` turns existing Claude Code session traces into **skill-health reports** (coverage, gap signals, execution stability, tokens/latency per skill). It gives you clues about **which skill is worth re-evaluating offline**, not a production score.
626
+
627
+ ```bash
628
+ # analyze all cc sessions of the current project (auto-infers kb from the trace)
629
+ omk analyze ~/.claude/projects/-Users-you-Documents-my-project
630
+
631
+ # restrict to the last 7 days / 24 hours / 30 minutes
632
+ omk analyze ~/.claude/projects/my-project --last 7d
633
+
634
+ # absolute time window
635
+ omk analyze ~/.claude/projects/my-project --from 2026-04-01T00:00:00Z --to 2026-04-15T23:59:59Z
636
+
637
+ # whitelist specific skills
638
+ omk analyze ~/.claude/projects/my-project --skills audit,polish
639
+
640
+ # override the inferred knowledge-base root
641
+ omk analyze ~/.claude/projects/my-project --kb /path/to/project
642
+ ```
643
+
644
+ The command writes `~/.oh-my-knowledge/analyses/<timestamp>-skill-health.json`. Browse results alongside bench reports with `omk bench report` — the homepage has a "📊 Skill Health Reports" link, and each skill card also has a "trend →" link to its time-series view. For two reports side-by-side, use the compare selector on `/analyses`.
645
+
646
+ **What you get per skill:**
647
+
648
+ - **Knowledge usage** — which KB files this skill actually read (coverage %)
649
+ - **Knowledge gaps** — four weighted signals (failed search / model-flagged gap / hedging / repeated miss); hedging goes through an LLM-assisted classifier to filter out business-possibility hedging vs genuine knowledge uncertainty
650
+ - **Execution stability** — tool-failure rate; a skill with > 20% failures gets a warning that its gap signals may be environmental noise rather than real knowledge gaps
651
+ - **Usage cost** — billable tokens (input+output) separate from cached tokens, total duration
652
+
653
+ **What this is NOT:**
654
+
655
+ - Not a general APM (request/response/latency tracing is Langfuse / Datadog territory)
656
+ - Not streaming / alerting (batch only — run on a cron if you want periodic snapshots)
657
+ - Not a production score (no control group, no ground truth — use `omk bench run` for scoring)
658
+
659
+ ## Executors
660
+
661
+ ### Built-in executors
662
+
663
+ | Executor | When to use | Description |
664
+ |---|---|---|
665
+ | `claude` | default | invokes `claude -p` via Claude CLI |
666
+ | `claude-sdk` | structured output | uses Claude Agent SDK — no stdout parsing, avoids buffer truncation |
667
+ | `openai` | cross-vendor comparison | invokes `openai api` CLI |
668
+ | `gemini` | cross-vendor comparison | invokes `gemini` CLI |
669
+ | `anthropic-api` | no CLI needed | calls Anthropic HTTP API directly (needs `ANTHROPIC_API_KEY`) |
670
+ | `openai-api` | no CLI needed | calls OpenAI HTTP API directly (needs `OPENAI_API_KEY`) |
671
+
672
+ API-direct executors support custom base URLs via env: `ANTHROPIC_BASE_URL`, `OPENAI_BASE_URL`.
673
+
674
+ ### Custom executor
675
+
676
+ Any shell command can serve as an executor, communicating via stdin/stdout JSON:
419
677
 
420
678
  ```bash
421
679
  omk bench run --executor "python my_provider.py"
422
680
  omk bench run --executor "./my-executor.sh"
423
681
  ```
424
682
 
425
- **协议约定:**
683
+ **Protocol:**
426
684
 
427
- - **输入**(stdin):JSON `{"model":"...","system":"...","prompt":"..."}`
428
- - **输出**(stdout):JSON `{"output":"模型回复","inputTokens":0,"outputTokens":0,"costUSD":0}`
429
- - stdout 中只需返回有值的字段,其余默认为 0;也可以直接输出纯文本(不解析 token/成本)
430
- - 非零退出码视为执行失败
685
+ - **input** (stdin): JSON `{"model":"...","system":"...","prompt":"..."}`
686
+ - **output** (stdout): JSON `{"output":"model reply","inputTokens":0,"outputTokens":0,"costUSD":0}`
687
+ - stdout only needs to return the fields you care about; others default to 0. Plain-text output (no tokens/cost parsing) is also fine.
688
+ - non-zero exit code counts as failure
431
689
 
432
- ### Artifact 目录结构
690
+ ### Artifact directory layout
433
691
 
434
- 默认执行器(claude/openai/gemini)支持两种 artifact 布局,同一次评测中可混用:
692
+ The built-in executors (claude / openai / gemini) support two artifact layouts, mixable in the same run:
435
693
 
436
694
  ```
437
695
  skills/
438
- ├── v1.md # 方式一:直接放 .md 文件
439
- └── my-skill/ # 方式二:完整 artifact 目录
440
- ├── SKILL.md # 工具自动读取此文件作为 system prompt
441
- ├── config.json # 其他文件不参与评测,仅保留完整性
696
+ ├── v1.md # option 1: plain .md file
697
+ └── my-skill/ # option 2: full artifact dir
698
+ ├── SKILL.md # this file is auto-loaded as system prompt
699
+ ├── config.json # other files don't participate in eval, kept for completeness
442
700
  └── scripts/
443
701
  ```
444
702
 
445
- **Variant 解析规则:**
703
+ **Variant resolution rules:**
446
704
 
447
- `variant` 是实验分组表达式。解析之后,OMK 会得到一个 `artifact` 与可选的 `runtime context`(当前主要是 `cwd`)。
705
+ `variant` is the experiment-group expression. After resolution, OMK produces an `artifact` plus an optional `runtime context` (currently mainly `cwd`).
448
706
 
449
- | 格式 | 含义 |
450
- |------|------|
451
- | `name` | artifact 目录查找 `name.md` `name/SKILL.md`,解析为一个 artifact |
452
- | `baseline` | artifact,不使用 system prompt;可直接理解为“什么都没有” |
453
- | `project-env@/path/to/project` | artifact,但在指定项目目录运行,用于单独观察项目级 runtime context |
454
- | `git:name` | git HEAD 读取一个 artifact 的上次提交版本 |
455
- | `git:ref:name` | git 指定 commit 读取一个 artifact |
456
- | `./path/to/file.md` | `/` 的路径,直接读取文件作为 artifact |
457
- | `variant@/path/to/project` | 给任意变体附加运行目录,支持 `name@cwd`、`git:name@cwd`、`/file.md@cwd` |
707
+ | Format | Meaning |
708
+ |---|---|
709
+ | `name` | looks up `name.md` or `name/SKILL.md` in the artifact dir, resolves to one artifact |
710
+ | `baseline` | empty artifact, no system prompt — think "nothing at all" |
711
+ | `project-env@/path/to/project` | empty artifact, but run in the specified project dir — observe project-level runtime context alone |
712
+ | `git:name` | reads the last-committed version of an artifact from git HEAD |
713
+ | `git:ref:name` | reads an artifact from a specific commit |
714
+ | `./path/to/file.md` | path with `/`: read the file directly as an artifact |
715
+ | `variant@/path/to/project` | attach a run dir to any variant; supports `name@cwd`, `git:name@cwd`, `/file.md@cwd` |
458
716
 
459
- 不指定 `--variants` 时,自动扫描 artifact 目录下的所有 `.md` 文件和含 `SKILL.md` 的子目录。只有一个 artifact 时自动加 `baseline` 作为对照。
717
+ When both `--control` and `--treatment` are omitted, use `--config eval.yaml` or `--each`. With `--each`, `baseline` is auto-added as control and every discovered artifact becomes a treatment.
460
718
 
461
719
  ```bash
462
- # 自动发现 skills/ 下所有 artifact
463
- omk bench run
720
+ # explicit: one control, one or more treatments
721
+ omk bench run --control v1 --treatment v2
722
+ omk bench run --control baseline --treatment v1,v2,v3
464
723
 
465
- # 显式指定两个变体
466
- omk bench run --variants v1,v2
724
+ # compare empty artifact vs explicit artifact
725
+ omk bench run --control baseline --treatment my-skill
467
726
 
468
- # 对比空 artifact 和显式 artifact 的效果差异
469
- omk bench run --variants baseline,my-skill
727
+ # observe project-level runtime context in isolation (use a self-describing label)
728
+ omk bench run --control baseline --treatment project-env@/path/to/target-project
470
729
 
471
- # 推荐用自描述标签单独观察项目级 runtime context 的影响
472
- omk bench run --variants project-env@/path/to/target-project
730
+ # compare "project-level runtime context" vs "explicit artifact injection"
731
+ omk bench run \
732
+ --control project-env@/path/to/target-project \
733
+ --treatment /path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
473
734
 
474
- # 对比“项目级 runtime context”与“显式 artifact 注入”
475
- omk bench run --variants project-env@/path/to/target-project,/path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
735
+ # before vs after (old version read from git history)
736
+ omk bench run --control git:my-skill --treatment my-skill
476
737
 
477
- # 对比修改前后(旧版本从 git 历史读取)
478
- omk bench run --variants git:my-skill,my-skill
738
+ # direct file paths
739
+ omk bench run --control ./old-skill.md --treatment ./new-skill.md
479
740
 
480
- # 直接指定文件路径
481
- omk bench run --variants ./old-skill.md,./new-skill.md
741
+ # config-file driven (evaluation-as-code)
742
+ omk bench run --config eval.yaml
482
743
  ```
483
744
 
484
- **前置要求:**
745
+ **Prerequisites:**
485
746
 
486
- - **claude**:安装 [Claude Code](https://claude.ai/code) 并认证
487
- - **claude-sdk**:安装 [Claude Code](https://claude.ai/code) 并认证(使用 Agent SDK,无需 CLI stdout 解析)
488
- - **anthropic-api**:设置 `ANTHROPIC_API_KEY` 环境变量
489
- - **openai**:`pip install openai` 并设置 `OPENAI_API_KEY`
490
- - **openai-api**:设置 `OPENAI_API_KEY` 环境变量
491
- - **gemini**:`npm i -g @google/gemini-cli` 并认证
747
+ - **claude**: install [Claude Code](https://claude.ai/code) and authenticate
748
+ - **claude-sdk**: install [Claude Code](https://claude.ai/code) and authenticate (uses Agent SDK, no CLI stdout parsing)
749
+ - **anthropic-api**: set the `ANTHROPIC_API_KEY` env var
750
+ - **openai**: `pip install openai` and set `OPENAI_API_KEY`
751
+ - **openai-api**: set the `OPENAI_API_KEY` env var
752
+ - **gemini**: `npm i -g @google/gemini-cli` and authenticate
492
753
 
493
- ### Agent 评测与项目级 Runtime Context
754
+ ### Agent evaluation and project-level runtime context
494
755
 
495
- 当执行器使用 `claude-sdk` 时,OMK 现在已经支持第一版 agent-aware evaluation
756
+ When the executor is `claude-sdk`, OMK supports a first pass of agent-aware evaluation.
496
757
 
497
- 这里建议把几个概念分开理解:
758
+ A few concepts worth keeping separate:
498
759
 
499
- - `artifact`:被评测对象,例如 baselineskillpromptagent
500
- - `variant`:CLI 里的实验分组表达式
501
- - `runtime context`:运行时上下文,当前主要是 `cwd`;在项目型 agent 场景下,它就包含项目目录、`CLAUDE.md`、本地 skills 等会影响行为的环境因素
760
+ - `artifact`: the thing being evaluated — baseline, skill, prompt, agent
761
+ - `variant`: the CLI expression for an experiment group
762
+ - `runtime context`: the runtime environment; currently mainly `cwd`. In project-type agent scenarios it includes the project dir, its `CLAUDE.md`, local skills, and any other environmental factors that affect behavior
502
763
 
503
- OMK 里,`agent` 不是所有对象的总称,`skill` 也不是所有对象的总称。更稳妥的说法是:你在比较不同 artifact 在不同 runtime context 下的表现。
764
+ In OMK, `agent` is not a catch-all term and neither is `skill`. A cleaner phrasing: **you are comparing how different artifacts behave under different runtime contexts.**
504
765
 
505
- - 自动抽取 turns / toolCalls trace
506
- - 支持基于工具调用行为的断言
507
- - 支持在指定 `cwd` 下运行,让 Claude Code 自动加载项目内的 `CLAUDE.md`、skills 和本地 runtime context
766
+ - auto-extracts turns / toolCalls traces
767
+ - supports assertions on tool-call behavior
768
+ - supports running under a specified `cwd`, so Claude Code auto-loads the project's `CLAUDE.md`, skills, and local runtime context
508
769
 
509
- #### 推荐执行器
770
+ #### Recommended executor
510
771
 
511
772
  ```bash
512
773
  omk bench run --executor claude-sdk
513
774
  ```
514
775
 
515
- #### 支持的 agent 相关断言
776
+ #### Agent-related assertions
516
777
 
517
- | 断言 | 含义 |
518
- |------|------|
519
- | `tools_called` | 必须调用指定工具 |
520
- | `tools_not_called` | 禁止调用指定工具 |
521
- | `tools_count_min` / `tools_count_max` | 工具调用次数上下界 |
522
- | `tool_output_contains` | 指定工具输出必须包含关键内容 |
523
- | `turns_min` / `turns_max` | 交互轮次上下界 |
778
+ | Assertion | Meaning |
779
+ |---|---|
780
+ | `tools_called` | must call the specified tool(s) |
781
+ | `tools_not_called` | must not call the specified tool(s) |
782
+ | `tools_count_min` / `tools_count_max` | tool-call-count bounds |
783
+ | `tool_output_contains` | output of a specific tool must contain given content |
784
+ | `turns_min` / `turns_max` | turn-count bounds |
524
785
 
525
- #### 三种常见对照组
786
+ #### Three common control setups
526
787
 
527
- **1. 裸模型 baseline**
788
+ **1. Bare-model baseline**
528
789
 
529
- 不注入 system prompt,也不进入带知识的项目目录。
790
+ No system prompt and no knowledge-carrying project dir. Requires at least one treatment to compare against:
530
791
 
531
792
  ```bash
532
793
  omk bench run \
533
794
  --executor claude-sdk \
534
- --variants baseline
795
+ --control baseline \
796
+ --treatment my-skill
535
797
  ```
536
798
 
537
- **2. artifact + 项目级 runtime context**
799
+ **2. Empty artifact + project-level runtime context**
538
800
 
539
- 不注入 system prompt,但在项目目录运行。它不是严格意义上的“裸 baseline”,而是“空 artifact + 项目级 runtime context”。
801
+ No system prompt, but runs inside a project dir. This is **not** a strict "bare baseline" — it is "empty artifact + project-level runtime context".
540
802
 
541
803
  ```bash
542
804
  omk bench run \
543
805
  --executor claude-sdk \
544
- --variants project-env@/path/to/target-project
806
+ --control baseline \
807
+ --treatment project-env@/path/to/target-project
545
808
  ```
546
809
 
547
- **3. 显式 artifact 注入**
810
+ **3. Explicit artifact injection**
548
811
 
549
- 直接把某个外部 `SKILL.md` 作为 artifact 注入,同时保留项目目录上下文。适合对比“项目级 runtime context”与“显式单 artifact 注入”之间的差异。
812
+ Inject an external `SKILL.md` as the artifact while also keeping the project dir. Good for contrasting "project-level runtime context" vs "explicit single-artifact injection".
550
813
 
551
814
  ```bash
552
815
  omk bench run \
553
816
  --executor claude-sdk \
554
- --variants /path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
817
+ --control project-env@/path/to/target-project \
818
+ --treatment /path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
555
819
  ```
556
820
 
557
- #### 推荐的第一轮对照设计
821
+ #### Recommended first-round design
558
822
 
559
- 对于 PRD / 复杂业务知识场景,建议先从下面两组开始:
823
+ For PRD / complex business-knowledge scenarios, start with:
560
824
 
561
825
  ```bash
562
826
  omk bench run \
563
827
  --executor claude-sdk \
564
828
  --samples skills/evaluate-review/eval-samples.yaml \
565
- --variants baseline,/path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
829
+ --control baseline \
830
+ --treatment /path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
566
831
  ```
567
832
 
568
- 如果你想证明“项目目录中的知识沉淀本身”是否有效,再加第三组:
833
+ If you want to prove whether "the knowledge sitting inside the project directory" is effective on its own, add a second treatment:
569
834
 
570
835
  ```bash
571
836
  omk bench run \
572
837
  --executor claude-sdk \
573
838
  --samples skills/evaluate-review/eval-samples.yaml \
574
- --variants baseline,project-env@/path/to/target-project,/path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
839
+ --control baseline \
840
+ --treatment project-env@/path/to/target-project,/path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
575
841
  ```
576
842
 
577
- #### 设计建议
843
+ #### Design tips
578
844
 
579
- - **先用 `--dry-run`**:确认样本、variant `cwd` 被正确解析
580
- - **项目级对照必须区分 `cwd`**:相同 prompt 在不同项目目录下会走不同 runtime context
581
- - **优先先跑 PRD 场景**:相比 Coding,更容易验证知识完整性、影响面识别和业务正确性
845
+ - **Always start with `--dry-run`** to confirm samples, variants, and `cwd` are parsed correctly
846
+ - **Project-level controls must differ in `cwd`**: the same prompt under different project dirs hits different runtime contexts
847
+ - **Try PRD scenarios first**: compared to pure coding, they make it easier to validate knowledge completeness, impact-area detection, and business correctness
582
848
 
583
- ### 常见模型配置示例
849
+ ### Common model configurations
584
850
 
585
- **没有 Claude?** 大多数国产模型(GLM、通义千问、MoonshotDeepSeek 等)都兼容 OpenAI API 格式,可以直接使用 `openai-api` 执行器:
851
+ **Don't have Claude?** Most Chinese LLMs (GLM, Qwen, Moonshot, DeepSeek, etc.) are OpenAI-API compatible — use the `openai-api` executor directly:
586
852
 
587
853
  ```bash
588
- # GLM(智谱)
589
- export OPENAI_API_KEY="你的智谱 API Key"
854
+ # GLM (Zhipu)
855
+ export OPENAI_API_KEY="your Zhipu API key"
590
856
  export OPENAI_BASE_URL="https://open.bigmodel.cn/api/paas/v4"
591
857
  omk bench run --executor openai-api --model glm-4-plus \
592
858
  --judge-model glm-4-plus --no-cache
593
859
 
594
- # 通义千问
595
- export OPENAI_API_KEY="你的通义 API Key"
860
+ # Qwen (Alibaba)
861
+ export OPENAI_API_KEY="your Qwen API key"
596
862
  export OPENAI_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
597
863
  omk bench run --executor openai-api --model qwen-plus \
598
864
  --judge-model qwen-plus
599
865
 
600
866
  # DeepSeek
601
- export OPENAI_API_KEY="你的 DeepSeek API Key"
867
+ export OPENAI_API_KEY="your DeepSeek API key"
602
868
  export OPENAI_BASE_URL="https://api.deepseek.com"
603
869
  omk bench run --executor openai-api --model deepseek-chat \
604
870
  --judge-model deepseek-chat
605
871
 
606
- # MoonshotKimi
607
- export OPENAI_API_KEY="你的 Moonshot API Key"
872
+ # Moonshot (Kimi)
873
+ export OPENAI_API_KEY="your Moonshot API key"
608
874
  export OPENAI_BASE_URL="https://api.moonshot.cn/v1"
609
875
  omk bench run --executor openai-api --model moonshot-v1-8k \
610
876
  --judge-model moonshot-v1-8k
611
877
  ```
612
878
 
613
- **Ollama 本地模型:**
879
+ **Ollama local model:**
614
880
 
615
881
  ```bash
616
882
  omk bench run --executor "python examples/custom-executor/ollama-executor.py" \
617
883
  --model llama3 --no-judge
618
884
  ```
619
885
 
620
- **关于评委模型:**
886
+ **About the judge model:**
621
887
 
622
- - `--judge-model` 指定 LLM 评委使用的模型,默认 `haiku`
623
- - `--judge-executor` 指定评委使用的执行器(默认与 `--executor` 相同)
624
- - 如果你没有 Claude,用 `--judge-executor` `--judge-model` 指向你可用的模型
625
- - `--no-judge` 可跳过 LLM 评委,仅使用断言评分
888
+ - `--judge-model` picks the model used by the LLM judge (default `haiku`)
889
+ - `--judge-executor` picks the executor the judge uses (defaults to `--executor`)
890
+ - If you don't have Claude, point `--judge-executor` and `--judge-model` at whatever model you have
891
+ - Add `--no-judge` to skip the LLM judge and rely on assertions alone
626
892
 
627
- ## 环境变量
893
+ ## Environment variables
628
894
 
629
- | 变量 | 说明 |
630
- |------|------|
631
- | `CCV_PROXY_URL` | 将请求代理到 cc-viewer,实时可视化评测流量 |
632
- | `OMK_BENCH_PORT` | 报告服务端口(默认:7799 |
895
+ | Variable | Description |
896
+ |---|---|
897
+ | `CCV_PROXY_URL` | proxy requests through cc-viewer for live eval-traffic visualization |
898
+ | `OMK_BENCH_PORT` | report server port (default: 7799) |
633
899
 
634
- ## 系统要求
900
+ ## Requirements
635
901
 
636
902
  - Node.js >= 20
637
- - `claude` CLI(用于默认执行器和 LLM 评委,安装方式见 [Claude Code](https://claude.ai/code)
638
- - 使用其他执行器(openai/gemini)且加 `--no-judge` 时可不装
903
+ - `claude` CLI (for the default executor and LLM judge; see [Claude Code](https://claude.ai/code))
904
+ - not needed if you use other executors (openai / gemini) with `--no-judge`
905
+
906
+ ## Security notice
907
+
908
+ This tool is designed for **local trusted environments** (dev machines, CI pipelines). The following features execute local code — make sure inputs come from a trusted source:
639
909
 
640
- ## 安全说明
910
+ | Feature | Risk | Scope |
911
+ |---|---|---|
912
+ | **Custom assertions** (`custom`) | dynamically loads and executes user-specified `.mjs` files | only use assertion files you authored or reviewed |
913
+ | **eval-samples.json** | assertion configs can reference external file paths | don't use sample files from untrusted sources |
641
914
 
642
- 本工具设计用于**本地可信环境**(开发机、CI 流水线)。以下功能会执行本地代码,请确保输入来源可信:
915
+ **Recommendations:**
643
916
 
644
- | 功能 | 风险说明 | 适用范围 |
645
- |------|----------|----------|
646
- | **自定义断言** (`custom`) | 动态加载并执行用户指定的 `.mjs` 文件 | 仅使用自己编写或审查过的断言文件 |
647
- | **eval-samples.json** | 断言配置中可引用外部文件路径 | 不要使用不可信来源的样本文件 |
917
+ - Do not expose `omk bench report` on the public internet (no auth)
918
+ - Don't use third-party eval-samples you haven't vetted
919
+ - Custom assertions have a 30-second timeout but no sandbox isolation
648
920
 
649
- **建议:**
921
+ ---
650
922
 
651
- - 不要在公网服务中暴露 `omk bench report` 服务(无认证)
652
- - 不要用不可信的第三方 eval-samples 文件
653
- - 自定义断言有 30 秒执行超时,但无沙箱隔离
923
+ See [CHANGELOG](./CHANGELOG.md) for release notes. Contributions welcome — see [CONTRIBUTING](./CONTRIBUTING.md).