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.
- package/README.md +596 -326
- package/README.zh.md +917 -0
- package/dist/src/analysis/failure-clusterer.d.ts +96 -0
- package/dist/src/analysis/failure-clusterer.d.ts.map +1 -0
- package/dist/src/analysis/failure-clusterer.js +298 -0
- package/dist/src/analysis/failure-clusterer.js.map +1 -0
- package/dist/src/analysis/sample-diagnostics.d.ts +78 -0
- package/dist/src/analysis/sample-diagnostics.d.ts.map +1 -0
- package/dist/src/analysis/sample-diagnostics.js +259 -0
- package/dist/src/analysis/sample-diagnostics.js.map +1 -0
- package/dist/src/analysis/saturation.d.ts +85 -0
- package/dist/src/analysis/saturation.d.ts.map +1 -0
- package/dist/src/analysis/saturation.js +174 -0
- package/dist/src/analysis/saturation.js.map +1 -0
- package/dist/src/cli.js +772 -18
- package/dist/src/cli.js.map +1 -1
- package/dist/src/eval-core/bootstrap.d.ts +72 -0
- package/dist/src/eval-core/bootstrap.d.ts.map +1 -0
- package/dist/src/eval-core/bootstrap.js +174 -0
- package/dist/src/eval-core/bootstrap.js.map +1 -0
- package/dist/src/eval-core/evaluation-execution.d.ts +15 -1
- package/dist/src/eval-core/evaluation-execution.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-execution.js +37 -3
- package/dist/src/eval-core/evaluation-execution.js.map +1 -1
- package/dist/src/eval-core/evaluation-job.d.ts +10 -2
- package/dist/src/eval-core/evaluation-job.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-job.js +9 -1
- package/dist/src/eval-core/evaluation-job.js.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.js +90 -0
- package/dist/src/eval-core/evaluation-reporting.js.map +1 -1
- package/dist/src/eval-core/schema.d.ts.map +1 -1
- package/dist/src/eval-core/schema.js +69 -0
- package/dist/src/eval-core/schema.js.map +1 -1
- package/dist/src/eval-core/verdict.d.ts +74 -0
- package/dist/src/eval-core/verdict.d.ts.map +1 -0
- package/dist/src/eval-core/verdict.js +283 -0
- package/dist/src/eval-core/verdict.js.map +1 -0
- package/dist/src/eval-workflows/each-evaluation-workflow.d.ts +16 -3
- package/dist/src/eval-workflows/each-evaluation-workflow.d.ts.map +1 -1
- package/dist/src/eval-workflows/each-evaluation-workflow.js +10 -2
- package/dist/src/eval-workflows/each-evaluation-workflow.js.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts +17 -1
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.js +45 -3
- package/dist/src/eval-workflows/evaluation-pipeline.js.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.d.ts +23 -2
- package/dist/src/eval-workflows/run-evaluation.d.ts.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.js +104 -4
- package/dist/src/eval-workflows/run-evaluation.js.map +1 -1
- package/dist/src/grading/assertions.d.ts +16 -0
- package/dist/src/grading/assertions.d.ts.map +1 -1
- package/dist/src/grading/assertions.js +385 -111
- package/dist/src/grading/assertions.js.map +1 -1
- package/dist/src/grading/debias-validate.d.ts +84 -0
- package/dist/src/grading/debias-validate.d.ts.map +1 -0
- package/dist/src/grading/debias-validate.js +173 -0
- package/dist/src/grading/debias-validate.js.map +1 -0
- package/dist/src/grading/gold-cli.d.ts +88 -0
- package/dist/src/grading/gold-cli.d.ts.map +1 -0
- package/dist/src/grading/gold-cli.js +251 -0
- package/dist/src/grading/gold-cli.js.map +1 -0
- package/dist/src/grading/gold-dataset.d.ts +73 -0
- package/dist/src/grading/gold-dataset.d.ts.map +1 -0
- package/dist/src/grading/gold-dataset.js +161 -0
- package/dist/src/grading/gold-dataset.js.map +1 -0
- package/dist/src/grading/human-gold.d.ts +102 -0
- package/dist/src/grading/human-gold.d.ts.map +1 -0
- package/dist/src/grading/human-gold.js +188 -0
- package/dist/src/grading/human-gold.js.map +1 -0
- package/dist/src/grading/index.d.ts +27 -2
- package/dist/src/grading/index.d.ts.map +1 -1
- package/dist/src/grading/index.js +36 -18
- package/dist/src/grading/index.js.map +1 -1
- package/dist/src/grading/judge.d.ts +65 -2
- package/dist/src/grading/judge.d.ts.map +1 -1
- package/dist/src/grading/judge.js +280 -23
- package/dist/src/grading/judge.js.map +1 -1
- package/dist/src/inputs/eval-config.js +19 -0
- package/dist/src/inputs/eval-config.js.map +1 -1
- package/dist/src/observability/{production-analyzer.d.ts → skill-health-analyzer.d.ts} +24 -2
- package/dist/src/observability/skill-health-analyzer.d.ts.map +1 -0
- package/dist/src/observability/{production-analyzer.js → skill-health-analyzer.js} +61 -6
- package/dist/src/observability/skill-health-analyzer.js.map +1 -0
- package/dist/src/observability/trace-adapter.d.ts.map +1 -1
- package/dist/src/observability/trace-adapter.js +27 -1
- package/dist/src/observability/trace-adapter.js.map +1 -1
- package/dist/src/renderer/html-renderer.d.ts.map +1 -1
- package/dist/src/renderer/html-renderer.js +40 -6
- package/dist/src/renderer/html-renderer.js.map +1 -1
- package/dist/src/renderer/layout.d.ts.map +1 -1
- package/dist/src/renderer/layout.js +138 -4
- package/dist/src/renderer/layout.js.map +1 -1
- package/dist/src/renderer/skill-health-renderer.d.ts +2 -2
- package/dist/src/renderer/skill-health-renderer.d.ts.map +1 -1
- package/dist/src/renderer/skill-health-renderer.js +39 -4
- package/dist/src/renderer/skill-health-renderer.js.map +1 -1
- package/dist/src/renderer/summary.d.ts +28 -1
- package/dist/src/renderer/summary.d.ts.map +1 -1
- package/dist/src/renderer/summary.js +322 -8
- package/dist/src/renderer/summary.js.map +1 -1
- package/dist/src/renderer/table.d.ts.map +1 -1
- package/dist/src/renderer/table.js +63 -2
- package/dist/src/renderer/table.js.map +1 -1
- package/dist/src/server/report-server.d.ts +2 -1
- package/dist/src/server/report-server.d.ts.map +1 -1
- package/dist/src/server/report-server.js +397 -2
- package/dist/src/server/report-server.js.map +1 -1
- package/dist/src/types.d.ts +247 -0
- package/dist/src/types.d.ts.map +1 -1
- package/package.json +24 -6
- package/dist/src/observability/production-analyzer.d.ts.map +0 -1
- 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
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/oh-my-knowledge)
|
|
4
|
+
[](https://github.com/lizhiyao/oh-my-knowledge/actions/workflows/ci.yml)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
[](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
|
-
|
|
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
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
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
|
-
#
|
|
71
|
+
# run the evaluation (auto-discovers everything under skills/)
|
|
30
72
|
omk bench run
|
|
31
73
|
```
|
|
32
74
|
|
|
33
|
-
##
|
|
75
|
+
## Use inside Claude Code
|
|
34
76
|
|
|
35
|
-
|
|
77
|
+
After installing omk, talk to it in natural language from Claude Code:
|
|
36
78
|
|
|
37
79
|
```
|
|
38
|
-
/omk eval #
|
|
39
|
-
/omk evolve #
|
|
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
|
-
|
|
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
|
-
| **
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
| **
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
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
|
-
|
|
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["
|
|
73
|
-
U["URL
|
|
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
|
|
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["
|
|
88
|
-
LS["LLM
|
|
89
|
-
CS["
|
|
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["
|
|
96
|
-
G["
|
|
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["
|
|
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
|
-
-
|
|
120
|
-
- **variant = artifact + runtime context
|
|
121
|
-
-
|
|
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
|
-
|
|
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": "
|
|
184
|
+
"rubric": "Should identify SQL injection risk and recommend parameterized queries",
|
|
135
185
|
"assertions": [
|
|
136
|
-
{ "type": "contains", "value": "SQL
|
|
137
|
-
{ "type": "contains", "value": "
|
|
138
|
-
{ "type": "not_contains", "value": "
|
|
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` |
|
|
155
|
-
| `rubric` | `string` |
|
|
156
|
-
| `assertions` | `array` |
|
|
157
|
-
| `assertions[].type` | `string` |
|
|
158
|
-
| `assertions[].value` | `string\|number` |
|
|
159
|
-
| `assertions[].values` | `array` |
|
|
160
|
-
| `assertions[].pattern` | `string` |
|
|
161
|
-
| `assertions[].flags` | `string` |
|
|
162
|
-
| `assertions[].schema` | `object` |
|
|
163
|
-
| `assertions[].reference` | `string` |
|
|
164
|
-
| `assertions[].threshold` | `number` |
|
|
165
|
-
| `assertions[].fn` | `string` |
|
|
166
|
-
| `assertions[].weight` | `number` |
|
|
167
|
-
| `dimensions` | `object` |
|
|
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`
|
|
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": "
|
|
226
|
+
"prompt": "Generate test cases from this PRD: https://wiki.example.com/prd/feature-x"
|
|
177
227
|
}
|
|
178
228
|
```
|
|
179
229
|
|
|
180
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
265
|
+
- Pass rate = sum of passed assertion weights / total weight (0–1)
|
|
266
|
+
- Score = 1 + pass_rate × 4 (mapped to 1–5)
|
|
267
|
+
- Example: 3 assertions (weight 1 each), 2 pass → pass rate 2/3 → score = 1 + 0.67 × 4 = **3.67**
|
|
218
268
|
|
|
219
|
-
#### 2. Rubric / Dimensions
|
|
269
|
+
#### 2. Rubric / Dimensions score
|
|
220
270
|
|
|
221
|
-
|
|
271
|
+
The judge model (default `haiku`) scores 1–5 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
|
-
|
|
|
228
|
-
|
|
|
229
|
-
|
|
|
230
|
-
|
|
|
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
|
-
|
|
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
|
-
| `
|
|
248
|
-
| `
|
|
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: '
|
|
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
|
-
|
|
|
266
|
-
|
|
|
267
|
-
|
|
|
268
|
-
|
|
|
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
|
|
279
|
-
--skill-dir
|
|
280
|
-
--
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
--
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
--
|
|
290
|
-
--
|
|
291
|
-
--
|
|
292
|
-
--
|
|
293
|
-
--
|
|
294
|
-
--
|
|
295
|
-
--
|
|
296
|
-
--
|
|
297
|
-
|
|
298
|
-
--
|
|
299
|
-
--
|
|
300
|
-
--
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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` →
|
|
322
|
-
- `{name}/SKILL.md` →
|
|
323
|
-
-
|
|
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
|
-
|
|
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
|
-
#
|
|
447
|
+
# generate for a specific artifact (writes eval-samples.json)
|
|
336
448
|
omk bench gen-samples skills/my-skill.md
|
|
337
449
|
|
|
338
|
-
#
|
|
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
|
|
349
|
-
--count <n>
|
|
350
|
-
--model
|
|
351
|
-
--skill-dir
|
|
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
|
-
|
|
468
|
+
Lets the AI iterate an artifact automatically: evaluate → analyze weak spots → LLM rewrites → evaluate again → keep if the score went up, drop otherwise → repeat.
|
|
357
469
|
|
|
358
470
|
```bash
|
|
359
|
-
#
|
|
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>
|
|
370
|
-
--target
|
|
371
|
-
--samples
|
|
372
|
-
--improve-model
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
428
|
-
-
|
|
429
|
-
- stdout
|
|
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
|
-
|
|
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 #
|
|
439
|
-
└── my-skill/ #
|
|
440
|
-
├── SKILL.md #
|
|
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`
|
|
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` |
|
|
452
|
-
| `baseline` |
|
|
453
|
-
| `project-env@/path/to/project` |
|
|
454
|
-
| `git:name` |
|
|
455
|
-
| `git:ref:name` |
|
|
456
|
-
| `./path/to/file.md` |
|
|
457
|
-
| `variant@/path/to/project` |
|
|
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
|
-
|
|
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
|
-
#
|
|
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 --
|
|
724
|
+
# compare empty artifact vs explicit artifact
|
|
725
|
+
omk bench run --control baseline --treatment my-skill
|
|
467
726
|
|
|
468
|
-
#
|
|
469
|
-
omk bench run --
|
|
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
|
-
#
|
|
472
|
-
omk bench run
|
|
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
|
-
#
|
|
475
|
-
omk bench run --
|
|
735
|
+
# before vs after (old version read from git history)
|
|
736
|
+
omk bench run --control git:my-skill --treatment my-skill
|
|
476
737
|
|
|
477
|
-
#
|
|
478
|
-
omk bench run --
|
|
738
|
+
# direct file paths
|
|
739
|
+
omk bench run --control ./old-skill.md --treatment ./new-skill.md
|
|
479
740
|
|
|
480
|
-
#
|
|
481
|
-
omk bench run --
|
|
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
|
|
487
|
-
- **claude-sdk
|
|
488
|
-
- **anthropic-api
|
|
489
|
-
- **openai
|
|
490
|
-
- **openai-api
|
|
491
|
-
- **gemini
|
|
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
|
|
754
|
+
### Agent evaluation and project-level runtime context
|
|
494
755
|
|
|
495
|
-
|
|
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
|
|
500
|
-
- `variant
|
|
501
|
-
- `runtime context
|
|
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
|
-
|
|
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
|
-
-
|
|
506
|
-
-
|
|
507
|
-
-
|
|
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
|
-
####
|
|
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.
|
|
788
|
+
**1. Bare-model baseline**
|
|
528
789
|
|
|
529
|
-
|
|
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
|
-
--
|
|
795
|
+
--control baseline \
|
|
796
|
+
--treatment my-skill
|
|
535
797
|
```
|
|
536
798
|
|
|
537
|
-
**2.
|
|
799
|
+
**2. Empty artifact + project-level runtime context**
|
|
538
800
|
|
|
539
|
-
|
|
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
|
-
--
|
|
806
|
+
--control baseline \
|
|
807
|
+
--treatment project-env@/path/to/target-project
|
|
545
808
|
```
|
|
546
809
|
|
|
547
|
-
**3.
|
|
810
|
+
**3. Explicit artifact injection**
|
|
548
811
|
|
|
549
|
-
|
|
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
|
-
--
|
|
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
|
-
|
|
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
|
-
--
|
|
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
|
-
--
|
|
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
|
-
-
|
|
580
|
-
-
|
|
581
|
-
-
|
|
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
|
-
|
|
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="
|
|
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="
|
|
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="
|
|
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
|
-
# Moonshot
|
|
607
|
-
export OPENAI_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`
|
|
623
|
-
- `--judge-executor`
|
|
624
|
-
-
|
|
625
|
-
-
|
|
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` |
|
|
632
|
-
| `OMK_BENCH_PORT` |
|
|
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
|
|
638
|
-
-
|
|
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
|
-
|
|
915
|
+
**Recommendations:**
|
|
643
916
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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
|
-
|
|
652
|
-
- 不要用不可信的第三方 eval-samples 文件
|
|
653
|
-
- 自定义断言有 30 秒执行超时,但无沙箱隔离
|
|
923
|
+
See [CHANGELOG](./CHANGELOG.md) for release notes. Contributions welcome — see [CONTRIBUTING](./CONTRIBUTING.md).
|