oh-my-knowledge 0.41.0 → 0.43.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 (67) hide show
  1. package/README.md +7 -2
  2. package/README.zh.md +7 -2
  3. package/dist/analysis/report-diagnostics.d.ts +8 -1
  4. package/dist/analysis/report-diagnostics.js +82 -1
  5. package/dist/artifact-graph/doctor.d.ts +21 -0
  6. package/dist/artifact-graph/doctor.js +569 -0
  7. package/dist/assets/agent-skills/omk/SKILL.md +9 -9
  8. package/dist/assets/agent-skills/omk/references/commands.md +2 -1
  9. package/dist/authoring/evolver.d.ts +3 -14
  10. package/dist/authoring/evolver.js +1 -52
  11. package/dist/authoring/generator.d.ts +24 -0
  12. package/dist/authoring/generator.js +33 -6
  13. package/dist/cli/commands/doctor.js +62 -61
  14. package/dist/cli/commands/eval/index.d.ts +1 -0
  15. package/dist/cli/commands/eval/index.js +60 -7
  16. package/dist/cli/commands/init.js +11 -7
  17. package/dist/cli/commands/observe/index.d.ts +2 -2
  18. package/dist/cli/commands/observe/index.js +8 -7
  19. package/dist/cli/commands/sample.js +22 -16
  20. package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
  21. package/dist/cli/lib/i18n-dict/common.js +8 -0
  22. package/dist/cli/lib/i18n-dict/help.js +12 -10
  23. package/dist/cli/lib/i18n-dict/init.d.ts +1 -1
  24. package/dist/cli/lib/i18n-dict/init.js +14 -11
  25. package/dist/cli/lib/i18n-dict/run.d.ts +1 -1
  26. package/dist/cli/lib/i18n-dict/run.js +4 -0
  27. package/dist/cli/lib/parse-run-config/samples-discovery.d.ts +5 -7
  28. package/dist/cli/lib/parse-run-config/samples-discovery.js +10 -32
  29. package/dist/cli/lib/parse-run-config.d.ts +3 -0
  30. package/dist/cli/lib/parse-run-config.js +4 -4
  31. package/dist/cli/lib/resolve-skill-input.js +10 -12
  32. package/dist/doctor/messages.js +2 -2
  33. package/dist/eval-core/artifact-file-names.d.ts +15 -0
  34. package/dist/eval-core/artifact-file-names.js +46 -0
  35. package/dist/eval-core/evaluation-job.d.ts +2 -1
  36. package/dist/eval-core/evaluation-job.js +2 -1
  37. package/dist/eval-core/evaluation-reporting.js +9 -4
  38. package/dist/eval-core/holdout.d.ts +66 -0
  39. package/dist/eval-core/holdout.js +118 -0
  40. package/dist/eval-core/measurement-dirs.js +13 -7
  41. package/dist/eval-core/report-file-migration.d.ts +10 -0
  42. package/dist/eval-core/report-file-migration.js +90 -0
  43. package/dist/eval-core/verdict.d.ts +44 -1
  44. package/dist/eval-core/verdict.js +175 -13
  45. package/dist/eval-workflows/evaluation-pipeline/report-finalize.js +19 -1
  46. package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts +2 -1
  47. package/dist/eval-workflows/evaluation-pipeline/run-state.js +2 -1
  48. package/dist/eval-workflows/evaluation-pipeline.d.ts +3 -1
  49. package/dist/eval-workflows/evaluation-pipeline.js +2 -1
  50. package/dist/eval-workflows/run-evaluation.d.ts +5 -2
  51. package/dist/eval-workflows/run-evaluation.js +8 -5
  52. package/dist/inputs/eval-config.js +6 -0
  53. package/dist/inputs/sample-locator.d.ts +23 -0
  54. package/dist/inputs/sample-locator.js +195 -0
  55. package/dist/inputs/skill-loader.js +7 -17
  56. package/dist/observability/inbox.js +7 -3
  57. package/dist/renderer/summary.js +36 -3
  58. package/dist/server/report-server.js +10 -4
  59. package/dist/server/report-store.js +17 -9
  60. package/dist/server/skill-index.js +16 -11
  61. package/dist/types/artifact-graph.d.ts +93 -0
  62. package/dist/types/artifact-graph.js +1 -0
  63. package/dist/types/eval.d.ts +7 -0
  64. package/dist/types/index.d.ts +1 -0
  65. package/dist/types/index.js +1 -0
  66. package/dist/types/report.d.ts +49 -0
  67. package/package.json +1 -1
package/README.md CHANGED
@@ -23,11 +23,15 @@ omk init demo && cd demo
23
23
  omk eval --control code-review-v1 --treatment code-review-v2
24
24
  ```
25
25
 
26
- That's it — no editing required. `omk init` scaffolds two skill variants and three sample cases; `omk eval` runs the controlled A/B and opens an HTML report with a one-line verdict in about five minutes.
26
+ Runs out of the box — no edits needed first. `omk init` scaffolds two skill variants and three sample cases; `omk eval` runs the controlled A/B and opens an HTML report with a one-line verdict in about five minutes. Once it runs, swap in your own skills and cases.
27
+
28
+ Prerequisite: the default executor and judge use the `claude` CLI — install and log in first (see [Requirements](#requirements)); to use another model or run offline (no API key) see [executors](docs/reference/executors.md).
29
+
30
+ > The first run has only 3 cases, so the verdict will usually be `UNDERPOWERED` (insufficient data) — that's a normal starting point, not an error; grow to ~20+ cases before trusting a ship/no-ship call.
27
31
 
28
32
  > The CLI notifies you when a newer version is available (at most once per 20h); set `OMK_SKIP_UPDATE_CHECK=1` to silence it permanently.
29
33
 
30
- Walkthrough: [5-minute quickstart guide](docs/quickstart-skill-eval.md) (recommended for first-time users).
34
+ Walkthrough: [5-minute quickstart guide](docs/quickstart-skill-eval.md) (recommended for first-time users). More runnable examples (Skill Map, A/B, offline executor, agent runtime, RAG) live in the repo's [example gallery](https://github.com/lizhiyao/oh-my-knowledge/tree/main/examples).
31
35
 
32
36
  Deeper: [who omk is for](docs/explanation/who-omk-is-for.md) · [CLI reference](docs/reference/cli.md) · [how it works](docs/explanation/architecture.md) · [eval sample format](docs/reference/eval-sample-format.md) · [executors](docs/reference/executors.md) · [artifact layout](docs/reference/artifact-layout.md)
33
37
 
@@ -122,6 +126,7 @@ The full docs are published at **[oh-my-knowledge.pages.dev](https://oh-my-knowl
122
126
  - **[Executors](docs/reference/executors.md)** & **[artifact layout](docs/reference/artifact-layout.md)** — built-in / custom executors; how `variant` resolves to an artifact + runtime context
123
127
  - **[How-to guides](docs/guides/agent-eval.md)** — [evaluate an agent](docs/guides/agent-eval.md) (project runtime context) and [use non-Claude models](docs/guides/non-claude-models.md) (GLM / Qwen / DeepSeek / Moonshot / Ollama)
124
128
  - **[Quickstart](docs/quickstart-skill-eval.md)** — first-time five-minute walkthrough
129
+ - **[Example gallery](https://github.com/lizhiyao/oh-my-knowledge/tree/main/examples)** — a set of runnable examples in the repo, arranged simplest-to-richest
125
130
  - **[Sample design spec](docs/specs/sample-design-spec.md)** — capability / construct / provenance metadata; industry-gap mapping
126
131
  - **[Statistical rigor](docs/explanation/statistical-rigor.md)** — why bootstrap CI / α / length-debias / saturation matter
127
132
  - **[Comparison with 7 tools](docs/reference/comparison.md)** — 25+ dimensions across promptfoo / DeepEval / RAGAS / OpenAI Evals / LangSmith / lm-eval-harness / inspect-ai
package/README.zh.md CHANGED
@@ -23,11 +23,15 @@ omk init demo && cd demo
23
23
  omk eval --control code-review-v1 --treatment code-review-v2
24
24
  ```
25
25
 
26
- 不用改任何文件 —— `omk init` 帮你脚手架两版 skill 和三条评测用例;`omk eval` 跑控制变量 A/B5 分钟内出 HTML 报告 + 一行 verdict
26
+ 开箱即跑:`omk init` 脚手架好两版 skill 和三条评测用例,不用先改任何文件,`omk eval` 跑控制变量 A/B,约 5 分钟出 HTML 报告 + 一行 verdict;跑通后再把 skill 和用例换成你自己的。
27
+
28
+ 前置:默认执行器与评委用 `claude` CLI,需先安装并登录(见[系统要求](#系统要求));想用别的模型或离线跑(无需 API key)见[执行器](docs/zh/reference/executors.md)。
29
+
30
+ > 首跑只有 3 条用例,verdict 多半是「数据不足(UNDERPOWERED)」——这是正常起点而非出错;把用例加到约 20 条以上,再看「可发布」结论。
27
31
 
28
32
  > 命令行有新版本时会自动提示(每 20 小时最多一次);想永久关闭该提醒,设环境变量 `OMK_SKIP_UPDATE_CHECK=1` 即可。
29
33
 
30
- 手把手教程:[5 分钟快速上手](docs/zh/quickstart-skill-eval.md)(推荐第一次跑评测的用户)。
34
+ 手把手教程:[5 分钟快速上手](docs/zh/quickstart-skill-eval.md)(推荐第一次跑评测的用户)。更多可跑示例(Skill Map、A/B、离线执行器、agent runtime、RAG)见仓库的[示例画廊](https://github.com/lizhiyao/oh-my-knowledge/tree/main/examples)。
31
35
 
32
36
  深入:[为谁、解决什么](docs/zh/explanation/who-omk-is-for.md) · [CLI 参考](docs/zh/reference/cli.md) · [工作原理](docs/zh/explanation/architecture.md) · [评测用例格式](docs/zh/reference/eval-sample-format.md) · [执行器](docs/zh/reference/executors.md) · [artifact 布局](docs/zh/reference/artifact-layout.md)
33
37
 
@@ -122,6 +126,7 @@ RAG 专项评测请看 RAGAS(独立 niche,跟 omk 互补)。完整对比
122
126
  - **[执行器](docs/zh/reference/executors.md)** & **[artifact 布局](docs/zh/reference/artifact-layout.md)** —— 内置 / 自定义执行器;variant 如何解析为 artifact + runtime context
123
127
  - **[操作指南](docs/zh/guides/agent-eval.md)** —— [评测 agent](docs/zh/guides/agent-eval.md)(项目 runtime context)与[使用非 Claude 模型](docs/zh/guides/non-claude-models.md)(GLM / 通义 / DeepSeek / Moonshot / Ollama)
124
128
  - **[快速上手](docs/zh/quickstart-skill-eval.md)** —— 第一次跑评测的 5 分钟教程
129
+ - **[示例画廊](https://github.com/lizhiyao/oh-my-knowledge/tree/main/examples)** —— 仓库里一组可直接跑的示例,按由简到全排成上手路径
125
130
  - **[用例设计规范](docs/zh/specs/sample-design-spec.md)** —— capability / construct / provenance 元数据;行业 gap 映射
126
131
  - **[统计严谨性](docs/zh/explanation/statistical-rigor.md)** —— 为什么 Bootstrap CI / α / 长度去偏 / 饱和曲线重要
127
132
  - **[7 工具对比](docs/zh/reference/comparison.md)** —— promptfoo / DeepEval / RAGAS / OpenAI Evals / LangSmith / lm-eval-harness / inspect-ai 等 25+ 维度横评
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Auto-analysis: detect patterns and generate insights from evaluation results.
3
3
  */
4
- import type { Report, AnalysisResult, Sample, SampleQualityAggregate, Lang } from '../types/index.js';
4
+ import type { Report, AnalysisResult, Sample, SampleQualityAggregate, Representativeness, Lang } from '../types/index.js';
5
5
  /** opts for `analyzeResults`. Optional because most older callers don't have
6
6
  * samples in scope; new callers (evaluation-pipeline / evolver) pass them in to
7
7
  * populate `analysis.sampleQuality`. */
@@ -30,4 +30,11 @@ export declare function analyzeResults(report: Report, opts?: AnalyzeResultsOpti
30
30
  * participate in grading / judge / verdict. See docs/specs/sample-design-spec.md.
31
31
  */
32
32
  export declare function buildSampleQualityAggregate(samples: Sample[]): SampleQualityAggregate;
33
+ /**
34
+ * Relative-balance / skew of the sample set. Pure function of the aggregate's
35
+ * distributions — no external "expected" denominator exists (capabilities are
36
+ * free-form), so it reports concentration (dominant bucket share) + the dominant
37
+ * label per dimension, not absolute coverage. Diagnostic only.
38
+ */
39
+ export declare function buildRepresentativeness(aggregate: SampleQualityAggregate): Representativeness;
33
40
  export declare function generateAnalysisSummary(report: Report, lang?: Lang): string | undefined;
@@ -144,7 +144,7 @@ export function buildSampleQualityAggregate(samples) {
144
144
  rubricCount++;
145
145
  }
146
146
  }
147
- return {
147
+ const aggregate = {
148
148
  capabilityCoverage,
149
149
  difficultyDistribution,
150
150
  constructDistribution,
@@ -155,6 +155,56 @@ export function buildSampleQualityAggregate(samples) {
155
155
  sampleCountWithConstruct: withConstruct,
156
156
  sampleCountWithProvenance: withProvenance,
157
157
  };
158
+ aggregate.representativeness = buildRepresentativeness(aggregate);
159
+ return aggregate;
160
+ }
161
+ /** Largest entry of a `label → count` map, as `[label, share]` over a given total.
162
+ * Returns `[undefined, 0]` when the map is empty or total ≤ 0. */
163
+ function dominantShare(counts, total) {
164
+ let label;
165
+ let max = 0;
166
+ for (const [k, v] of Object.entries(counts)) {
167
+ if (v > max) {
168
+ max = v;
169
+ label = k;
170
+ }
171
+ }
172
+ return total > 0 ? [label, max / total] : [undefined, 0];
173
+ }
174
+ /**
175
+ * Relative-balance / skew of the sample set. Pure function of the aggregate's
176
+ * distributions — no external "expected" denominator exists (capabilities are
177
+ * free-form), so it reports concentration (dominant bucket share) + the dominant
178
+ * label per dimension, not absolute coverage. Diagnostic only.
179
+ */
180
+ export function buildRepresentativeness(aggregate) {
181
+ const capTotal = Object.values(aggregate.capabilityCoverage).reduce((a, b) => a + b, 0);
182
+ const [dominantCapability, capabilityConcentration] = dominantShare(aggregate.capabilityCoverage, capTotal);
183
+ // difficulty / construct concentration over *declared* samples only — `unspecified`
184
+ // is "didn't say", not a bucket the set is skewed toward.
185
+ const declaredDifficulty = {
186
+ easy: aggregate.difficultyDistribution.easy,
187
+ medium: aggregate.difficultyDistribution.medium,
188
+ hard: aggregate.difficultyDistribution.hard,
189
+ };
190
+ const diffTotal = declaredDifficulty.easy + declaredDifficulty.medium + declaredDifficulty.hard;
191
+ const [dominantDifficulty, difficultyConcentration] = dominantShare(declaredDifficulty, diffTotal);
192
+ const declaredConstruct = {};
193
+ for (const [k, v] of Object.entries(aggregate.constructDistribution)) {
194
+ if (k !== 'unspecified')
195
+ declaredConstruct[k] = v;
196
+ }
197
+ const consTotal = Object.values(declaredConstruct).reduce((a, b) => a + b, 0);
198
+ const [dominantConstruct, constructConcentration] = dominantShare(declaredConstruct, consTotal);
199
+ return {
200
+ capabilityCount: Object.keys(aggregate.capabilityCoverage).length,
201
+ capabilityConcentration,
202
+ ...(dominantCapability ? { dominantCapability } : {}),
203
+ difficultyConcentration,
204
+ ...(dominantDifficulty ? { dominantDifficulty: dominantDifficulty } : {}),
205
+ constructConcentration,
206
+ ...(dominantConstruct ? { dominantConstruct } : {}),
207
+ };
158
208
  }
159
209
  export function generateAnalysisSummary(report, lang = 'zh') {
160
210
  const variants = report.meta?.variants || [];
@@ -383,12 +433,43 @@ export function generateAnalysisSummary(report, lang = 'zh') {
383
433
  ? `【综合洞察】${synthesis.join(';')}。`
384
434
  : `【Synthesis】${synthesis.join('; ')}.`);
385
435
  }
436
+ // ── Sample-composition skew ── flags over-representation ("70% are easy") so the
437
+ // reader knows the set may not be representative of the real task distribution.
438
+ // Diagnostic only — never gates the verdict. N≥10 guard mirrors capability_thin.
439
+ const rep = report.analysis?.sampleQuality?.representativeness;
440
+ const sampleCount = report.meta?.sampleCount ?? 0;
441
+ if (rep && sampleCount >= 10) {
442
+ const skews = [];
443
+ if (rep.difficultyConcentration > SKEW_CONCENTRATION_BAND && rep.dominantDifficulty) {
444
+ skews.push(lang === 'zh'
445
+ ? `难度 ${(rep.difficultyConcentration * 100).toFixed(0)}% 集中在 ${rep.dominantDifficulty}`
446
+ : `${(rep.difficultyConcentration * 100).toFixed(0)}% of declared difficulty is ${rep.dominantDifficulty}`);
447
+ }
448
+ if (rep.capabilityConcentration > SKEW_CONCENTRATION_BAND && rep.dominantCapability) {
449
+ skews.push(lang === 'zh'
450
+ ? `能力标签 ${(rep.capabilityConcentration * 100).toFixed(0)}% 集中在 ${rep.dominantCapability}`
451
+ : `${(rep.capabilityConcentration * 100).toFixed(0)}% of capability tags are ${rep.dominantCapability}`);
452
+ }
453
+ if (rep.constructConcentration > SKEW_CONCENTRATION_BAND && rep.dominantConstruct) {
454
+ skews.push(lang === 'zh'
455
+ ? `construct ${(rep.constructConcentration * 100).toFixed(0)}% 集中在 ${rep.dominantConstruct}`
456
+ : `${(rep.constructConcentration * 100).toFixed(0)}% of declared construct is ${rep.dominantConstruct}`);
457
+ }
458
+ if (skews.length > 0) {
459
+ lines.push(lang === 'zh'
460
+ ? `【用例构成】偏斜:${skews.join(';')}——补充其它维度可提升代表性(构成提示,不影响 verdict)。`
461
+ : `【Sample composition】Skewed: ${skews.join('; ')} — adding other dimensions improves representativeness (informational; does not affect the verdict).`);
462
+ }
463
+ }
386
464
  // Caveats and recommendations are handled by the issues table below,
387
465
  // so the summary focuses only on verdict + differentiators + synthesis.
388
466
  if (lines.length === 0)
389
467
  return undefined;
390
468
  return lines.join('\n');
391
469
  }
470
+ /** Dominant-bucket share above which the sample set is flagged as skewed. Pragmatic
471
+ * default — 60% of declared samples in one bucket is a clear over-representation. */
472
+ const SKEW_CONCENTRATION_BAND = 0.6;
392
473
  const AGENT_ASSERTION_TYPES = new Set([
393
474
  'tools_called',
394
475
  'tools_not_called',
@@ -0,0 +1,21 @@
1
+ import type { ArtifactGraphDocument, DoctorReport, DoctorSkillReport, Lang } from '../types/index.js';
2
+ export interface BuildDoctorGraphOptions {
3
+ report: DoctorReport;
4
+ skill: DoctorSkillReport;
5
+ sourcePath: string;
6
+ generatedAt?: string;
7
+ }
8
+ export interface PersistDoctorGraphOptions extends BuildDoctorGraphOptions {
9
+ outputDir: string;
10
+ fileStem: string;
11
+ lang: Lang;
12
+ }
13
+ export interface PersistDoctorGraphResult {
14
+ graphPath: string;
15
+ evidenceCardPath: string;
16
+ }
17
+ export declare function doctorGraphDirForDoctorOutput(doctorOutputDir: string): string;
18
+ export declare function buildDoctorArtifactGraph(options: BuildDoctorGraphOptions): ArtifactGraphDocument;
19
+ export declare function renderDoctorEvidenceCard(graph: ArtifactGraphDocument, skill: DoctorSkillReport, lang: Lang): string;
20
+ export declare function persistDoctorGraphSidecars(options: PersistDoctorGraphOptions): PersistDoctorGraphResult;
21
+ export declare function removeDoctorGraphSidecars(doctorOutputDir: string, fileStem: string): void;