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.zh.md ADDED
@@ -0,0 +1,917 @@
1
+ # oh-my-knowledge
2
+
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)
7
+
8
+ [English](./README.md) | **简体中文**
9
+
10
+ **omk** — 内置统计严谨性的 LLM 评测框架。Bootstrap CI / Krippendorff α / 长度偏差校正 / 饱和曲线开箱即用。原生支持 Claude Code skill、prompt、agent、RAG。
11
+
12
+ **固定模型,只变知识载体,数据说话。**
13
+
14
+ ## 为什么需要这个工具
15
+
16
+ 做知识工程的团队会产出大量知识载体(当前常见是 skill,也包括 prompt、agent、workflow 等)。当被问到"v2 比 v1 好在哪"时,需要客观数据而非主观判断。`oh-my-knowledge` 通过控制变量实验解决这个问题:相同模型、相同测试样本,只改变知识载体。
17
+
18
+ ## 核心能力
19
+
20
+ - **控制变量离线评测** — 固定模型和样本,只变知识载体;兼容 Claude Code skill、CLAUDE.md prompt、RAG 知识库等任何 markdown 形式的指令
21
+ - **六维独立打分** — Fact / Behavior / LLM-judge / Cost / Efficiency / Stability 分别出信号,单一维度的回退不会被其他维度的收益掩盖
22
+ - **线上 session 观测** — 解析 Claude Code session JSONL,在真实用户会话上测量各 skill 的失败率、耗时、token 成本和知识缺口信号
23
+ - **知识缺口识别** — 严重度加权的信号(显式标记 / 搜索失败 / hedging 用语 / 反复失败)量化风险敞口,不宣称完备性
24
+ - **合并前 CI 门** — `omk bench ci` 强制三层 all-pass(fact + behavior + llm-judge),抓复合分掩盖的单层回退
25
+ - **一行 ship/no-ship 结论** — `omk bench verdict <reportId>` 聚合 bootstrap CI / 三层 ci-gate / saturation / human α,给六档 verdict(PROGRESS / CAUTIOUS / REGRESS / NOISE / UNDERPOWERED / SOLO)+ 行动建议;exit code 反映是否可 ship
26
+
27
+ ### 统计严谨性
28
+ LLM 评测最容易踩的坑是"自信的偏差"——CI 很窄但结论错。omk 的统计层做四件事让结论可被外部审计:
29
+
30
+ - **Bootstrap CI** (`--bootstrap`) — 不假设分布的置信区间。t 检验在 LLM 序数评分上失效,bootstrap 直接重采样原始数据,对小 N(< 30)和偏态分布都稳。pairwise diff CI 不含 0 = 显著差异。
31
+ - **Human Gold + Krippendorff α** (`--gold-dir`) — 引入外部标注作为锚点。CI 解决"评委稳不稳",α 解决"评委对不对"——两个维度互补。omk 自动检测污染(gold annotator 与 judge 同模型时警告)。
32
+ - **Length-controlled judge prompt** (默认开启) — 研究证实 LLM 评委隐性偏向更长的回答。omk 的 judge prompt 加显式段落"长度不是质量信号",template hash 为 v3-cot-length,跟旧版本的报告 hash 肉眼可辨。`omk bench debias-validate length <reportId>` 重判检测偏差幅度。
33
+ - **Saturation curve** — 回答"我跑够样本了吗"。`--repeat ≥ 5` 时累积 N → 均值 + bootstrap CI 序列,CI 宽度衰减率 < 5% 持续 3 个窗口判定饱和——再多样本对结论无实质收益。HTML 报告内联 SVG 曲线 + verdict。
34
+
35
+ ## 为什么选 omk
36
+
37
+ | | omk | promptfoo | DeepEval | RAGAS | LangSmith |
38
+ |--|--|--|--|--|--|
39
+ | Bootstrap CI | ✓ | ✗ | ✗ | ✗ | ✗ |
40
+ | Krippendorff α(评委 ↔ 人工锚点) | ✓ | ✗ | ✗ | ✗ | ✗ |
41
+ | Length-debias 评委 prompt | ✓ 默认 | ✗ | ✗ | ✗ | ✗ |
42
+ | 饱和曲线 | ✓ | ✗ | ✗ | ✗ | ✗ |
43
+ | 三层独立评分 | ✓ | ✗ | 部分 | ✗ | ✗ |
44
+ | 原生 Claude Code skill | ✓ | ✗ | ✗ | ✗ | ✗ |
45
+ | 完整中文文档 | ✓ | ✗ | ✗ | ✗ | ✗ |
46
+ | 托管 SaaS 看板 | ✗ | ✗ | ✓ | ✗ | ✓ |
47
+
48
+ omk 的护城河是**统计严谨性** — 每条结论都能被研究者审计。需要托管 SaaS 看板?选 LangSmith。要本地快速 prompt 迭代不要统计层?选 promptfoo。**要 ship 到生产且会被问"为什么应该相信这个数字"?选 omk**。
49
+
50
+ 完整对比(7 个工具 × 25+ 维度): [docs/zh/comparison.md](docs/zh/comparison.md)
51
+
52
+ ## 快速开始
53
+
54
+ ```bash
55
+ # 安装
56
+ npm i oh-my-knowledge -g
57
+
58
+ # 生成评测项目脚手架
59
+ omk bench init my-eval
60
+ cd my-eval
61
+
62
+ # 把要对比的 artifact 放到 skills/ 目录
63
+ # 方式一:直接放 .md 文件(skills/v1.md, skills/v2.md)
64
+ # 方式二:放完整 artifact 目录(skills/my-skill-v1/SKILL.md, ...)
65
+ # 只放一个 artifact 也行,会自动加 baseline 对照
66
+
67
+ # 预览评测计划
68
+ omk bench run --dry-run
69
+
70
+ # 运行评测(自动发现 skills/ 目录下的所有 artifact)
71
+ omk bench run
72
+ ```
73
+
74
+ ## 在 Claude Code 中使用
75
+
76
+ 安装 omk 后,在 Claude Code 中直接用自然语言交互:
77
+
78
+ ```
79
+ /omk eval # 评测当前项目的 artifact
80
+ /omk evolve # 自动迭代改进 artifact
81
+ /omk gen-samples # 生成测试用例
82
+ ```
83
+
84
+ 或直接说"帮我评测 v1 和 v2 的差异"、"改进一下这个 artifact",omk 会自动理解意图并调用对应命令。
85
+
86
+ ## 特性
87
+
88
+ | 特性 | 说明 |
89
+ |------|------|
90
+ | **21+ 种断言** | 包含子串、正则、JSON Schema、ROUGE/BLEU/Levenshtein 相似度、Agent 工具调用、语义相似度、自定义函数等 |
91
+ | **断言取反 + 组合** | 通用 `not: true` 字段 + `assert-set` (any/all) 任意嵌套 |
92
+ | **六维评估** | 事实 / 行为 / LLM 评价 / 成本 / 效率 / 稳定性独立展示 |
93
+ | **统计严谨性** | Bootstrap CI / Krippendorff α / Length-debias / Saturation curve |
94
+ | **Verdict 一行结论** | `omk bench verdict <id>` 六档判定 + ship 建议 + exit code 路由,与 HTML 报告 verdict pill 共享规则 |
95
+ | **RAG metrics** | `faithfulness` / `answer_relevancy` / `context_recall` 三 metric — 反幻觉 + 切题度 + context 覆盖,自动继承 length-debias |
96
+ | **样本质量诊断** | `omk bench diagnose <id>` 7 类 issue(区分度低 / 重复 / 歧义 / 成本异常 / 全 fail 等)+ healthScore 0-100 |
97
+ | **失败聚类 + 根因** | `omk bench failures <id>` 单 LLM 调用聚类失败样本 + 每 cluster 给修复建议 |
98
+ | **预算硬阈值** | `--budget-usd / --budget-per-sample-usd / --budget-per-sample-ms` 总成本 + 单样本成本/耗时上限,超出中止保留 partial report |
99
+ | **多执行器** | 支持 Claude CLI / Claude SDK / OpenAI / Gemini 及自定义命令 |
100
+ | **多评委 ensemble** | `--judge-models claude:opus,openai:gpt-4o` 跨厂商评分 + agreement 度量 |
101
+ | **MCP URL 获取** | 通过 MCP Server 获取私有文档 URL 内容(SSO 保护的知识库等) |
102
+ | **盲测 A/B** | `--blind` 隐藏变体名称,HTML 报告有揭晓按钮 |
103
+ | **并行执行** | `--concurrency N` 并行 N 个任务 |
104
+ | **多轮方差分析** | `--repeat N` 重复 N 次,计算均值/标准差/置信区间/t 检验 |
105
+ | **自动分析** | 检测低区分度断言、均匀分数、全通过/全失败、高成本样本 |
106
+ | **可追溯性** | 报告含 CLI 版本、Node 版本、artifact 版本指纹、judge prompt hash |
107
+ | **中英切换** | HTML 报告右上角一键切换语言 |
108
+
109
+ ## 工作原理
110
+
111
+ 核心思路:**固定模型 + 固定样本,只变 artifact 和 runtime context**,通过交错调度消除时间漂移,用断言 + LLM 评委双通道评分,再叠加知识缺口信号量化风险敞口。
112
+
113
+ ```mermaid
114
+ flowchart TD
115
+ subgraph Input["① 输入"]
116
+ S["eval-samples<br/>(JSON / YAML)"]
117
+ A["artifacts<br/>skills/*.md · SKILL.md<br/>baseline · git:name · @cwd"]
118
+ end
119
+
120
+ subgraph Prep["② 预处理(解析与抓取)"]
121
+ V["变体解析<br/>variant → artifact + runtime context<br/>(cwd / 项目级 CLAUDE.md / 本地 skills)"]
122
+ U["URL 抓取<br/>prompt / context 中的 URL<br/>MCP Server(私有文档) → HTTP"]
123
+ end
124
+
125
+ subgraph Schedule["③ 交错调度 + 并发"]
126
+ Q["s1-v1 → s1-v2 → s2-v1 → s2-v2 …<br/>--concurrency N · --repeat N"]
127
+ end
128
+
129
+ subgraph Exec["④ 执行器(固定模型)"]
130
+ E["claude / claude-sdk / openai / gemini<br/>anthropic-api / openai-api / 自定义命令"]
131
+ T["claude-sdk 抽取<br/>turns / toolCalls trace"]
132
+ E -.-> T
133
+ end
134
+
135
+ subgraph Score["⑤ 双通道评分"]
136
+ AS["断言(18 种)<br/>内容 / 结构 / 成本 / 延迟<br/>agent: tools_called · turns_min …"]
137
+ LS["LLM 评委<br/>rubric · dimensions(多维独立打分)"]
138
+ CS["综合分数<br/>断言 & LLM 有则均值"]
139
+ AS --> CS
140
+ LS --> CS
141
+ end
142
+
143
+ subgraph Analyze["⑥ 自动分析 + 知识缺口"]
144
+ D["低区分度断言 / 均匀分 / 全通过全失败<br/>高成本样本 · 方差 · t 检验"]
145
+ G["知识缺口信号<br/>(风险敞口量化, 不证明完备)"]
146
+ end
147
+
148
+ subgraph Report["⑦ 报告"]
149
+ R["六维: 事实 / 行为 / LLM 评价 / 成本 / 效率 / 稳定性<br/>JSON + HTML · 顶部 verdict pill · 盲测揭晓<br/>CLI/Node/版本指纹可追溯"]
150
+ end
151
+
152
+ S --> U
153
+ A --> V
154
+ V --> Q
155
+ U --> Q
156
+ Q --> E
157
+ T --> AS
158
+ E --> AS
159
+ E --> LS
160
+ CS --> D
161
+ CS --> G
162
+ D --> R
163
+ G --> R
164
+ ```
165
+
166
+ **关键设计:**
167
+
168
+ - **交错调度**消除时间漂移:同一样本的不同 variant 交替发出,而非 v1 全跑完再跑 v2,避免模型负载/网络波动被错误归因给 artifact。
169
+ - **variant = artifact + runtime context**:`name@cwd` 让对照组可以显式声明"项目目录"这个隐性输入,把"项目级沉淀"和"显式 artifact 注入"拆开测。
170
+ - **双通道评分互补**:断言抓确定性缺陷(必须调用某工具/必须包含某字段),LLM 评委抓主观质量(可读性/完整性),两者都存在时取均值。
171
+ - **知识缺口信号**不是评分的一部分,而是一个独立追踪项:它告诉你"这次评测覆盖了多少风险敞口",用于追踪收敛,而非断言知识"完备"。
172
+
173
+ ## 评测样本格式
174
+
175
+ 支持 JSON 和 YAML(`eval-samples.json`、`eval-samples.yaml`、`eval-samples.yml`)。
176
+
177
+ ```json
178
+ [
179
+ {
180
+ "sample_id": "s001",
181
+ "prompt": "审查这段代码的安全性",
182
+ "context": "function auth(u, p) { db.query('SELECT * FROM users WHERE name=' + u); }",
183
+ "rubric": "应识别 SQL 注入风险并建议参数化查询",
184
+ "assertions": [
185
+ { "type": "contains", "value": "SQL 注入", "weight": 1 },
186
+ { "type": "contains", "value": "参数化", "weight": 1 },
187
+ { "type": "not_contains", "value": "没有问题", "weight": 0.5 }
188
+ ],
189
+ "dimensions": {
190
+ "security": "是否识别出注入漏洞",
191
+ "actionability": "是否给出可直接使用的修复代码"
192
+ }
193
+ }
194
+ ]
195
+ ```
196
+
197
+ ### 字段说明
198
+
199
+ | 字段 | 类型 | 必填 | 说明 |
200
+ |------|------|------|------|
201
+ | `sample_id` | `string` | **是** | 样本唯一标识 |
202
+ | `prompt` | `string` | **是** | 发送给模型的用户提示词 |
203
+ | `context` | `string` | 否 | 附加上下文(代码片段等),会被包裹在代码块中拼接到 prompt 后。也支持 URL,运行时自动抓取内容 |
204
+ | `rubric` | `string` | 否 | LLM 评委的评分标准(1-5 分) |
205
+ | `assertions` | `array` | 否 | 断言检查列表,详见[断言类型](#断言类型) |
206
+ | `assertions[].type` | `string` | **是** | 断言类型 |
207
+ | `assertions[].value` | `string\|number` | 视类型 | 检查值(`contains`、`min_length`、`cost_max` 等必填) |
208
+ | `assertions[].values` | `array` | 视类型 | 字符串数组(`contains_all`、`contains_any` 必填) |
209
+ | `assertions[].pattern` | `string` | 视类型 | 正则表达式(`regex` 必填) |
210
+ | `assertions[].flags` | `string` | 否 | 正则标志(默认 `"i"`) |
211
+ | `assertions[].schema` | `object` | 视类型 | JSON Schema 对象(`json_schema` 必填,基于 [ajv](https://ajv.js.org/)) |
212
+ | `assertions[].reference` | `string` | 视类型 | 参考文本(`semantic_similarity` 必填) |
213
+ | `assertions[].threshold` | `number` | 否 | 语义相似度通过阈值(默认 3) |
214
+ | `assertions[].fn` | `string` | 视类型 | 自定义断言 JS 文件路径(`custom` 必填) |
215
+ | `assertions[].weight` | `number` | 否 | 权重(默认 1) |
216
+ | `dimensions` | `object` | 否 | 多维度评分,key 为维度名,value 为评分标准文本 |
217
+
218
+ ### URL 自动抓取
219
+
220
+ `prompt` 和 `context` 中的 URL 会在评测前自动抓取内容并内联到文本中。适用于引用在线文档、API 文档等场景:
221
+
222
+ ```json
223
+ {
224
+ "sample_id": "s001",
225
+ "prompt": "请根据以下 PRD 文档生成测试用例:https://wiki.example.com/prd/feature-x"
226
+ }
227
+ ```
228
+
229
+ 运行时,URL 会被替换为实际文档内容。获取顺序:先通过 MCP Server 获取匹配的 URL(如 SSO 保护的私有文档),再通过 HTTP 获取剩余 URL。MCP 已成功的 URL 不会重复 HTTP 抓取。
230
+
231
+ **私有文档 URL**:在项目目录放一个 `.mcp.json` 配置文件,或通过 `--mcp-config` 指定路径:
232
+
233
+ ```json
234
+ {
235
+ "mcpServers": {
236
+ "docs": {
237
+ "command": "npx",
238
+ "args": ["@example/docs-mcp-server"],
239
+ "env": { "DOCS_API_TOKEN": "xxx" },
240
+ "urlPatterns": ["docs.example.com"],
241
+ "fetchTool": {
242
+ "name": "fetch_doc",
243
+ "urlTransform": {
244
+ "regex": "docs\\.example\\.com/([^/]+/[^/]+)/([^/?#]+)",
245
+ "params": { "namespace": "$1", "slug": "$2" }
246
+ },
247
+ "contentExtract": "data.body"
248
+ }
249
+ }
250
+ }
251
+ }
252
+ ```
253
+
254
+ **公网 URL**:直接 HTTP 获取,如果需要认证请确保命令行环境已配置好网络访问(VPN、代理等)。
255
+
256
+ ### 评分策略
257
+
258
+ #### 1. 断言评分
259
+
260
+ 基于规则的本地检查,每个断言产生通过/失败结果。
261
+
262
+ **计算方式:**
263
+
264
+ - 通过率 = 通过断言的权重之和 / 总权重(0~1)
265
+ - 分数 = 1 + 通过率 × 4(映射到 1~5 分)
266
+ - 示例:3 个断言(权重各 1),2 个通过 → 通过率 = 2/3 → 分数 = 1 + 0.67 × 4 = **3.67**
267
+
268
+ #### 2. Rubric / Dimensions 评分
269
+
270
+ 评委模型(默认 `haiku`)按标准打 1-5 分。`dimensions` 模式下各维度独立评分后取平均。
271
+
272
+ #### 3. 综合分数
273
+
274
+ | 条件 | 公式 |
275
+ |------|------|
276
+ | 仅断言 | `assertionScore` |
277
+ | 仅 LLM | `llmScore` |
278
+ | 两者都有 | `(assertionScore + llmScore) / 2` |
279
+ | 都没有 | `0` |
280
+
281
+ ### 断言类型
282
+
283
+ **确定性断言(21+ 种):**
284
+
285
+ | 类型 | 说明 |
286
+ |------|------|
287
+ | `contains` / `not_contains` | 包含/不包含子串 |
288
+ | `regex` | 正则匹配 |
289
+ | `min_length` / `max_length` | 长度范围 |
290
+ | `json_valid` / `json_schema` | JSON 校验 |
291
+ | `starts_with` / `ends_with` | 前缀/后缀匹配 |
292
+ | `equals` / `not_equals` | 精确匹配 |
293
+ | `word_count_min` / `word_count_max` | 词数范围 |
294
+ | `contains_all` / `contains_any` | 多值匹配 |
295
+ | `cost_max` / `latency_max` | 成本/延迟限制 |
296
+ | `tools_called` / `tools_not_called` / `tools_count_min` / `tools_count_max` | Agent 工具调用断言 |
297
+ | `tool_output_contains` / `tool_input_contains` | 工具输入/输出内容匹配 |
298
+ | `turns_min` / `turns_max` | 多轮对话轮数限制 |
299
+ | `rouge_n_min` | ROUGE-N recall ≥ threshold(`reference` 字段填参考答案,`n` 默认 1,`threshold` 默认 0.5) |
300
+ | `levenshtein_max` | 编辑距离 ≤ value(用于"输出跟参考几乎一致"场景) |
301
+ | `bleu_min` | BLEU-4 ≥ threshold(unsmoothed,短文本会塌陷到 0) |
302
+ | `faithfulness` | 输出是否被 `sample.context` 支持(反幻觉);LLM judge 1-5 评分,threshold 默认 3 |
303
+ | `answer_relevancy` | 输出是否切题回答 `sample.prompt`;能抓住跑题、回避、冗余;threshold 默认 3 |
304
+ | `context_recall` | `sample.context` 关键事实在输出中的覆盖率;`reference` 可显式指定 gold facts;threshold 默认 3 |
305
+ | `semantic_similarity` | LLM 语义相似度(与 reference 的整体相似度,与 RAG 三 metric 互补) |
306
+ | `custom` | 自定义 JS 函数(30s 超时) |
307
+
308
+ **通用修饰:**
309
+
310
+ 任何断言加 `not: true` 即反向(替代 `not_contains` / `not_equals` 等成对类型;老类型保留作 alias):
311
+
312
+ ```yaml
313
+ - type: regex
314
+ pattern: "TODO|FIXME"
315
+ not: true # 必须不含 TODO/FIXME
316
+ ```
317
+
318
+ **断言组合(— assert-set):**
319
+
320
+ `assert-set` 类型让多个断言以 `any`(OR)或 `all`(AND)逻辑组合,可嵌套:
321
+
322
+ ```yaml
323
+ - type: assert-set
324
+ mode: any # 任一通过即过 (mode: 'all' 则需全部通过)
325
+ children:
326
+ - { type: contains, value: "参数化" }
327
+ - { type: contains, value: "prepared statement" }
328
+ - { type: regex, pattern: "bind\\(.*\\?" }
329
+ ```
330
+
331
+ 子断言可独立带 `not: true`;嵌套 assert-set 可表达任意布尔逻辑。
332
+
333
+ ### 自定义断言
334
+
335
+ ```js
336
+ // my-assertion.mjs
337
+ export default function(output, { sample, assertion }) {
338
+ return { pass: output.includes('SQL'), message: '检查了 SQL 关键字' };
339
+ }
340
+ ```
341
+
342
+ ## 六维评估指标
343
+
344
+ 评测报告从六个维度独立展示结果。其中评分三层(事实 / 行为 / LLM 评价)分开展示,让你看到**是哪一层拉胯**,而不是只看到一个合成分:
345
+
346
+ | 维度 | 指标 | 说明 |
347
+ |------|------|------|
348
+ | 📋 **事实** | 事实类断言通过率 | `contains` / `json_schema` / `fact_check` 等规则可验证断言的 1-5 分映射 |
349
+ | 🛠️ **行为** | 行为类断言通过率 | `tools_called` / `tool_output_contains` / `turns_max` 等执行合规类断言 |
350
+ | 💬 **LLM 评价** | rubric 评分 | 由评委模型按预先写好的评分规则(rubric)打的 1-5 分,主观但能抓规则断言之外的"整体好不好" |
351
+ | 💰 **成本** | 总成本、输入/输出 Token 数 | 基于 Token 消耗和模型定价的 API 费用 |
352
+ | ⚡ **效率** | 平均延迟 (ms) | 从发送请求到收到完整响应的端到端耗时 |
353
+ | 🛡️ **稳定性** | CV(变异系数) | 跨重复运行(`--repeat ≥ 2`)分数一致性;单轮评测显示 `—`,**诚实交代测不到什么** |
354
+
355
+ ## CLI 参考
356
+
357
+ ### `omk bench run`
358
+
359
+ ```bash
360
+ omk bench run [选项]
361
+
362
+ 选项:
363
+ --samples <路径> 样本文件(默认:eval-samples.json,自动检测 .yaml/.yml)
364
+ --skill-dir <路径> artifact 目录(默认:skills)
365
+ --control <expr> 对照组变体表达式(experiment role = control)
366
+ --treatment <v1,v2> 实验组变体表达式,逗号分隔
367
+ 除非用 --config 或 --each,--control / --treatment 两者至少传一个
368
+ 特殊值:baseline(空 artifact)、git:name(git 历史版本)、
369
+ git:ref:name(指定 commit)、含 / 的路径(直接读取文件)
370
+ --config <路径> YAML/JSON 配置文件(evaluation-as-code);在一个文件里声明
371
+ samples + variants + model + executor;CLI 参数会覆盖 config
372
+ --model <名称> 被测模型(默认:sonnet)
373
+ --judge-model <名称> 评委模型(默认:haiku)
374
+ --output-dir <路径> 输出目录(默认:~/.oh-my-knowledge/reports/)
375
+ --no-judge 跳过 LLM 评分
376
+ --no-cache 禁用结果缓存(默认开启,相同输入自动复用)
377
+ --dry-run 仅预览
378
+ --blind 盲测模式
379
+ --concurrency <n> 并行任务数(默认:1)
380
+ --timeout <秒> 单个任务的执行器超时时间(默认:120)
381
+ --repeat <n> 重复 N 次做方差分析(默认:1)
382
+ --executor <名称> 执行器(默认:claude),支持自定义命令
383
+ --skip-preflight 跳过评测前的模型连通性检查
384
+ --mcp-config <路径> MCP 配置文件,用于通过 MCP Server 获取私有文档 URL 内容
385
+ (默认:当前目录的 .mcp.json)
386
+ --no-serve 评测完成后不自动启动报告服务
387
+ --verbose 打印每个样本的详细执行结果(耗时、tokens、输出预览)
388
+ --each 批量评测:每个 artifact 独立和 baseline 对比
389
+ 需要每个 artifact 配对 {name}.eval-samples.json
390
+ --judge-repeat <n> 每条 sample × dimension 跑 LLM 评委 N 次,输出 stddev (评委自一致性)
391
+ --judge-models <list> 多评委 ensemble: "executor1:model1,executor2:model2"
392
+ ≥ 2 个 judge 触发 ensemble + inter-judge agreement 输出
393
+ --bootstrap 启用 distribution-free CI:每个 variant 加 bootstrap CI,
394
+ pairwise diff CI 含 0 = 不显著
395
+ --bootstrap-samples N bootstrap 重采样次数 (默认 1000)
396
+ --gold-dir <路径> 跑完自动对比 human gold 算 Krippendorff α / κ / Pearson,
397
+ 结果写入 report.meta.humanAgreement,HTML 报告显示「人工锚点」
398
+ --no-debias-length 退回 v2-cot 评委 prompt (不含"长度不是质量信号"段落),
399
+ 用于复现旧版本(v3-cot-length 之前)的报告 hash
400
+ --budget-usd <num> 总成本上限 (USD);超出中止评测,partial report 仍持久化
401
+ (`report.meta.budgetExhausted = true`)
402
+ --budget-per-sample-usd <num> 单样本成本上限;超出该样本失败但评测继续
403
+ --budget-per-sample-ms <num> 单样本耗时上限 (ms);超出该样本失败但评测继续
404
+ ```
405
+
406
+ **eval.yaml 预算字段**: `budget: { totalUSD?, perSampleUSD?, perSampleMs? }`,所有字段可选且必须 ≥ 0。CLI 同名 flag 覆盖配置值。
407
+
408
+ **和 `cost_max` / `latency_max` 断言的区别**: 断言是**单样本评分维度**(超出直接打 0 分,run 继续);budget 是**工作流级硬阈值**(`totalUSD` 超出整个 run abort 保留 partial report,per-sample 超出该样本失败但 run 继续)。一个回答"质量是否达标",一个回答"花钱/时间是否在预算内"。
409
+
410
+ ### `omk bench run --each`(批量评测)
411
+
412
+ 当 skills/ 下放了多个**独立的** artifact 时,使用 `--each` 逐个评测,每个 artifact 独立和 baseline 对比,生成一份合并报告。
413
+
414
+ ```
415
+ skills/
416
+ ├── asset.md ← artifact 文件
417
+ ├── asset.eval-samples.json ← 配对的测试集
418
+ ├── home.md
419
+ ├── home.eval-samples.json
420
+ └── product/ ← 目录格式也支持
421
+ ├── SKILL.md
422
+ └── eval-samples.json
423
+ ```
424
+
425
+ 配对规则:
426
+
427
+ - `{name}.md` → 查找同目录下的 `{name}.eval-samples.json`
428
+ - `{name}/SKILL.md` → 查找 `{name}/eval-samples.json`
429
+ - 没有配对 eval-samples 的 artifact 会被跳过并打印警告
430
+
431
+ ```bash
432
+ omk bench run --each
433
+ omk bench run --each --dry-run
434
+ ```
435
+
436
+ ### `omk bench gen-samples`(生成测评用例)
437
+
438
+ 读取 artifact 内容,通过 LLM 自动生成 eval-samples。生成后请审查编辑再跑评测。
439
+
440
+ ```bash
441
+ # 为指定 artifact 生成测试集(输出到 eval-samples.json)
442
+ omk bench gen-samples skills/my-skill.md
443
+
444
+ # 为 skills/ 下所有缺少测试集的 artifact 批量生成
445
+ omk bench gen-samples --each
446
+
447
+ # 指定生成数量
448
+ omk bench gen-samples skills/my-skill.md --count 10
449
+ ```
450
+
451
+ 选项:
452
+
453
+ ```
454
+ --each 为所有缺少 eval-samples 的 artifact 批量生成
455
+ --count <n> 每个 artifact 生成的样本数(默认:5)
456
+ --model <名称> 生成用的模型(默认:sonnet)
457
+ --skill-dir <路径> artifact 目录(默认:skills),配合 --each 使用
458
+ ```
459
+
460
+ ### `omk bench evolve`(自我循环改进)
461
+
462
+ 让 AI 自动迭代 artifact:评测 → 分析弱点 → LLM 改进 → 再评测 → 分数涨了留、没涨扔 → 重复。
463
+
464
+ ```bash
465
+ # 基本用法:迭代 5 轮
466
+ omk bench evolve skills/my-skill.md
467
+
468
+ # 指定轮数和目标分数
469
+ omk bench evolve skills/my-skill.md --rounds 10 --target 4.5
470
+ ```
471
+
472
+ 选项:
473
+
474
+ ```
475
+ --rounds <n> 最大迭代轮数(默认:5)
476
+ --target <分数> 目标分数,达到即停
477
+ --samples <路径> 样本文件(默认:eval-samples.json)
478
+ --improve-model <名称> 改进用模型(默认:sonnet)
479
+ ```
480
+
481
+ 每轮产出保存在 `skills/evolve/` 目录(`my-skill.r0.md`、`my-skill.r1.md`...),可以 diff 查看 AI 改了什么。最佳版本自动写回原始文件。
482
+
483
+ ### `omk bench ci`
484
+
485
+ 在自动化流水线中运行评测。评分达标则退出码为 0(通过),否则为 1(失败),可直接用于卡点判断。
486
+
487
+ 门禁是**三层 all-pass**:`avgFactScore >= threshold AND avgBehaviorScore >= threshold AND avgJudgeScore >= threshold`,任一层低于阈值即 FAIL,输出显示是哪一层破了 gate。这样能把"事实 4.5→2.5 但 judge 3→5"这种合成分均值不变但事实层崩盘的 case 暴露出来 — 任何一层退化都会被卡住。
488
+
489
+ ```bash
490
+ omk bench ci [选项]
491
+ --threshold <数值> 各层最低分数(默认:3.5);独立应用于
492
+ fact / behavior / judge 三层
493
+ ```
494
+
495
+ ### `omk bench report`
496
+
497
+ 启动报告服务,浏览历史报告、提交反馈、删除报告。
498
+
499
+ ```bash
500
+ omk bench report [选项]
501
+ --port <端口号> 服务端口(默认:7799)
502
+ ```
503
+
504
+ ### `omk bench init`
505
+
506
+ ```bash
507
+ omk bench init [目录] # 生成评测项目脚手架
508
+ ```
509
+
510
+ ### `omk bench gold`(人工锚点)
511
+
512
+ 人工标注(或更强模型代理)作为外部锚点,与 LLM 评委的分数对比 Krippendorff α / 加权 κ / Pearson。回答"评委对不对",与 Bootstrap CI 的"评委稳不稳"互补。
513
+
514
+ ```bash
515
+ omk bench gold init [--out <dir>] [--annotator <id>] # 生成数据集模板
516
+ omk bench gold validate <dir> # 校验 schema (annotator/时间/版本/score 范围)
517
+ omk bench gold compare <reportId> --gold-dir <dir> # 与已有 report 对比,输出 verdict + α/κ/r
518
+ ```
519
+
520
+ dataset 目录结构:
521
+
522
+ ```
523
+ gold-dir/
524
+ ├── metadata.yaml # annotator (注意不要与 omk judge 同模型,会触发污染警告) + 时间 + 版本
525
+ └── annotations.yaml # [{ sample_id, score, reason? }] 按 sample_id 拼接
526
+ ```
527
+
528
+ α 阈值参考 Krippendorff (2011):≥ 0.80 高度一致;[0.67, 0.80) 可接受;< 0.40 偏差大需排查 rubric / prompt。
529
+
530
+ 完整 demo: [examples/gold-dataset/](examples/gold-dataset/)
531
+
532
+ ### `omk bench debias-validate length`(评委长度偏差检测)
533
+
534
+ 重判已有 report 的所有 (sample × variant),用相反的 length-debias 设置(v3-cot-length ↔ v2-cot),bootstrap CI 算两次差值。差异显著 = 评委对长度敏感(length bias 间接证据)。
535
+
536
+ ```bash
537
+ omk bench debias-validate length <reportId> [选项]
538
+ --variant <name> 只测一个 variant
539
+ --judge-model <id> override report 的 judge model
540
+ --bootstrap-samples N bootstrap 迭代数 (默认 1000)
541
+ --seed N 确定性种子
542
+ ```
543
+
544
+ verdict 分四档:未检测 / 弱 / 中(差值 |0.2-0.5|)/ 强(差值 ≥ 0.5)。重判 cost 大致翻倍。
545
+
546
+ ### `omk bench saturation`(饱和曲线)
547
+
548
+ 回答"我跑够样本了吗"。从已有 report 读取 saturation trace 输出判定,无需重跑评测。需要原 run 跑了 `--repeat ≥ 5` 才会有 verdict(低 repeat 只画曲线)。
549
+
550
+ ```bash
551
+ omk bench saturation <reportId> [选项]
552
+ --variant <name> 只看一个 variant
553
+ --method <m> slope | bootstrap-ci-width (默认) | plateau-height
554
+ --threshold <num> 方法相关阈值 (默认随 method)
555
+ --window <num> 连续多少窗口满足才判饱和 (默认 3)
556
+ ```
557
+
558
+ HTML 报告会内联 SVG 饱和曲线(横 N,纵 mean ± 95% CI 阴影带,per-variant 一条),自动渲染。
559
+
560
+ ### `omk bench verdict`(一行 ship/no-ship 结论)
561
+
562
+ 聚合 bootstrap CI / 三层 ci-gate / saturation / human α 给一行结论。Verdict 六档:**PROGRESS**(显著改进 + 三层全过 → exit 0)/ **CAUTIOUS**(改进真实但有警告:gate 破/幅度太小/控制组本身崩 → exit 1)/ **REGRESS**(显著回退 → exit 1)/ **NOISE**(CI 跨 0,无法判定 → exit 1)/ **UNDERPOWERED**(样本不足 → exit 1)/ **SOLO**(单变体,仅自身三层 gate 过才 exit 0)。
563
+
564
+ ```bash
565
+ omk bench verdict <reportId> [选项]
566
+ --threshold <num> 三层 gate 阈值 (默认 3.5,匹配 omk bench ci)
567
+ --trivial-diff <num> "幅度太小"阈值 (默认 0.1)
568
+ --verbose 展开 per-pair 详情
569
+ ```
570
+
571
+ 与 HTML 报告顶部的 verdict pill 共享规则模块,CLI 与 UI 不会矛盾。
572
+
573
+ ### `omk bench diagnose`(样本质量诊断)
574
+
575
+ 回答"测评结论是否被坏样本污染"。诊断 7 类样本质量问题:`flat_scores`(区分度低)/ `all_pass`(太简单)/ `all_fail`(broken,error 级)/ `near_duplicate`(prompt ROUGE-1 ≥ 阈值)/ `ambiguous_rubric`(judge stddev 大,需要 `--judge-repeat ≥ 2`)/ `cost_outlier`(≥ k× median)/ `latency_outlier`(≥ k× median)/ `error_prone`(执行失败)。
576
+
577
+ ```bash
578
+ omk bench diagnose <reportId> [选项]
579
+ --top <n> 每类显示前 N 个 (默认 10,0=全部)
580
+ --duplicate-rouge <num> near-duplicate ROUGE-1 阈值 (默认 0.7)
581
+ --ambiguous-stddev <num> 歧义 judge stddev 阈值 (默认 1.0)
582
+ --cost-k <num> 成本异常倍数 vs median (默认 3)
583
+ --latency-k <num> 耗时异常倍数 vs median (默认 3)
584
+ --flat <num> flat_scores 分差阈值 (默认 0.5)
585
+ ```
586
+
587
+ 输出含 healthScore(0-100,公式 `100 - normalized × 20`,其中 `normalized = (errors×8 + warnings×3 + infos×1) / N`)。exit code 0 仅当 `healthScore ≥ 70` 且无 error 级 issue,适合 CI 链。
588
+
589
+ ### `omk bench failures`(失败 case LLM 聚类)
590
+
591
+ 跑完 14 条失败,逐个看太慢。本命令把失败样本喂给单次 LLM 调用,自动聚到 ≤ N 个 cluster,每个 cluster 给根因 + 修复建议。失败定义:`compositeScore < threshold` 或 `ok = false`。
592
+
593
+ ```bash
594
+ omk bench failures <reportId> [选项]
595
+ --judge-executor <name> 执行器 (默认 claude)
596
+ --judge-model <id> 聚类用 model (默认沿用 report.meta.judgeModel)
597
+ --max-clusters <n> 最多多少 cluster (默认 5)
598
+ --threshold <num> 算失败的分数阈值 (默认 3)
599
+ --max-feed <n> 最多喂给 LLM 多少条 (默认 50,超出取最差)
600
+ ```
601
+
602
+ 容错:tolerate ```json``` markdown fence、`"sample_id@variant"` 字符串成员形式、hallucinated 成员自动剔除、单条失败跳过 LLM 直接列出、executor 错误降级到 unclassified。
603
+
604
+ ### `omk bench diff`(报告对比 — 单参 / 双参双模式)
605
+
606
+ **单参模式**(within-report sample-level 钻取): `omk bench diff <reportId>` — 在同一份报告内对比两个 variant 的逐样本得分,默认对比 `variants[0]` vs `variants[1]`。
607
+
608
+ **双参模式**(cross-report variant-level): `omk bench diff <reportId1> <reportId2>` — 跨报告对比同一 variant 的整体均值漂移(向后兼容旧用法)。
609
+
610
+ ```bash
611
+ omk bench diff <reportId> [--variant <name>] [--regressions-only] [--threshold 0] [--top N]
612
+ omk bench diff <reportId1> <reportId2> [--regressions-only] [--threshold 0]
613
+ ```
614
+
615
+ 单参模式表格按 |Δ| 排序,Δ < threshold 高亮 regression。`--top N` 限制行数,`--regressions-only` 过滤到只看回退。
616
+
617
+ ## `omk analyze` — 生产观测
618
+
619
+ `omk bench run` 是**离线评测**(固定对照、可复现、可评分)。生产环境不一样 — 没对照组、没标准答案、没重复,所以评分在那里不成立。`omk analyze` 把已有的 Claude Code session trace 转成**skill 健康度报告**(按 skill 维度的覆盖率、缺口信号、执行稳定性、tokens/延迟)。它给的是"哪个 skill 值得拉回离线再测一遍"的线索,不是生产评分。
620
+
621
+ ```bash
622
+ # 分析当前项目的所有 cc session(kb 路径从 trace 里自动推断)
623
+ omk analyze ~/.claude/projects/-Users-you-Documents-my-project
624
+
625
+ # 限定时间窗:最近 7 天 / 24 小时 / 30 分钟
626
+ omk analyze ~/.claude/projects/my-project --last 7d
627
+
628
+ # 绝对时间窗
629
+ omk analyze ~/.claude/projects/my-project --from 2026-04-01T00:00:00Z --to 2026-04-15T23:59:59Z
630
+
631
+ # 白名单特定 skill
632
+ omk analyze ~/.claude/projects/my-project --skills audit,polish
633
+
634
+ # 显式指定知识库根目录(覆盖自动推断)
635
+ omk analyze ~/.claude/projects/my-project --kb /path/to/project
636
+ ```
637
+
638
+ 命令产出 `~/.oh-my-knowledge/analyses/<timestamp>-skill-health.json`。启 `omk bench report` 后,首页右上有"📊 Skill 健康度日报"入口;每张 skill card 上有"查看趋势 →"链接;`/analyses` 列表页顶部有 Compare 选择器,可以选两份报告生成 diff。
639
+
640
+ **每个 skill 你能看到:**
641
+
642
+ - **知识使用** — 这个 skill 实际读了哪些 KB 文件(coverage %)
643
+ - **知识盲区** — 四类加权信号(搜索未命中 / 模型标记缺口 / 表达不确定 / 反复未命中);hedging 经 LLM 二次判定过滤"业务可能性"和"知识不确定"
644
+ - **执行稳定性** — 工具失败率;失败率 > 20% 的 skill 会标警告,提示"gap 信号可能是环境问题而非真实知识缺口"
645
+ - **使用成本** — billable tokens(input+output)和 cached tokens 分列,总耗时
646
+
647
+ **这不是什么:**
648
+
649
+ - 不是通用 APM(请求级 latency/cost tracing 是 Langfuse / Datadog 的领域)
650
+ - 不是 streaming / alert(只做 batch — 想要周期快照用 cron)
651
+ - 不是生产评分(没对照组没标答 — 评分回到 `omk bench run`)
652
+
653
+ ## 执行器
654
+
655
+ ### 内置执行器
656
+
657
+ | 执行器 | 适用场景 | 说明 |
658
+ |--------|----------|------|
659
+ | `claude` | 默认 | 通过 `claude -p` 调用 Claude CLI |
660
+ | `claude-sdk` | 结构化输出 | 通过 Claude Agent SDK 调用,无 stdout 解析,避免 buffer 截断 |
661
+ | `openai` | 跨厂商对比 | 通过 `openai api` CLI 调用 |
662
+ | `gemini` | 跨厂商对比 | 通过 `gemini` CLI 调用 |
663
+ | `anthropic-api` | 无需 CLI | 直接调用 Anthropic HTTP API(需 `ANTHROPIC_API_KEY`) |
664
+ | `openai-api` | 无需 CLI | 直接调用 OpenAI HTTP API(需 `OPENAI_API_KEY`) |
665
+
666
+ API 直调执行器支持通过环境变量自定义 Base URL:`ANTHROPIC_BASE_URL`、`OPENAI_BASE_URL`。
667
+
668
+ ### 自定义执行器
669
+
670
+ 任何 shell 命令都可以作为执行器,通过 stdin/stdout JSON 协议通信:
671
+
672
+ ```bash
673
+ omk bench run --executor "python my_provider.py"
674
+ omk bench run --executor "./my-executor.sh"
675
+ ```
676
+
677
+ **协议约定:**
678
+
679
+ - **输入**(stdin):JSON `{"model":"...","system":"...","prompt":"..."}`
680
+ - **输出**(stdout):JSON `{"output":"模型回复","inputTokens":0,"outputTokens":0,"costUSD":0}`
681
+ - stdout 中只需返回有值的字段,其余默认为 0;也可以直接输出纯文本(不解析 token/成本)
682
+ - 非零退出码视为执行失败
683
+
684
+ ### Artifact 目录结构
685
+
686
+ 默认执行器(claude/openai/gemini)支持两种 artifact 布局,同一次评测中可混用:
687
+
688
+ ```
689
+ skills/
690
+ ├── v1.md # 方式一:直接放 .md 文件
691
+ └── my-skill/ # 方式二:完整 artifact 目录
692
+ ├── SKILL.md # 工具自动读取此文件作为 system prompt
693
+ ├── config.json # 其他文件不参与评测,仅保留完整性
694
+ └── scripts/
695
+ ```
696
+
697
+ **Variant 解析规则:**
698
+
699
+ `variant` 是实验分组表达式。解析之后,OMK 会得到一个 `artifact` 与可选的 `runtime context`(当前主要是 `cwd`)。
700
+
701
+ | 格式 | 含义 |
702
+ |------|------|
703
+ | `name` | 从 artifact 目录查找 `name.md` 或 `name/SKILL.md`,解析为一个 artifact |
704
+ | `baseline` | 空 artifact,不使用 system prompt;可直接理解为“什么都没有” |
705
+ | `project-env@/path/to/project` | 空 artifact,但在指定项目目录运行,用于单独观察项目级 runtime context |
706
+ | `git:name` | 从 git HEAD 读取一个 artifact 的上次提交版本 |
707
+ | `git:ref:name` | 从 git 指定 commit 读取一个 artifact |
708
+ | `./path/to/file.md` | 含 `/` 的路径,直接读取文件作为 artifact |
709
+ | `variant@/path/to/project` | 给任意变体附加运行目录,支持 `name@cwd`、`git:name@cwd`、`/file.md@cwd` |
710
+
711
+ `--control` 和 `--treatment` 都不传时,用 `--config eval.yaml` 或 `--each`。`--each` 模式下会自动用 `baseline` 作对照组,每个被发现的 artifact 作实验组。
712
+
713
+ ```bash
714
+ # 显式:一个 control,一个或多个 treatment
715
+ omk bench run --control v1 --treatment v2
716
+ omk bench run --control baseline --treatment v1,v2,v3
717
+
718
+ # 对比空 artifact 和显式 artifact 的效果差异
719
+ omk bench run --control baseline --treatment my-skill
720
+
721
+ # 单独观察项目级 runtime context 的影响(用自描述标签)
722
+ omk bench run --control baseline --treatment project-env@/path/to/target-project
723
+
724
+ # 对比"项目级 runtime context"与"显式 artifact 注入"
725
+ omk bench run \
726
+ --control project-env@/path/to/target-project \
727
+ --treatment /path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
728
+
729
+ # 对比修改前后(旧版本从 git 历史读取)
730
+ omk bench run --control git:my-skill --treatment my-skill
731
+
732
+ # 直接指定文件路径
733
+ omk bench run --control ./old-skill.md --treatment ./new-skill.md
734
+
735
+ # 配置文件驱动(evaluation-as-code)
736
+ omk bench run --config eval.yaml
737
+ ```
738
+
739
+ **前置要求:**
740
+
741
+ - **claude**:安装 [Claude Code](https://claude.ai/code) 并认证
742
+ - **claude-sdk**:安装 [Claude Code](https://claude.ai/code) 并认证(使用 Agent SDK,无需 CLI stdout 解析)
743
+ - **anthropic-api**:设置 `ANTHROPIC_API_KEY` 环境变量
744
+ - **openai**:`pip install openai` 并设置 `OPENAI_API_KEY`
745
+ - **openai-api**:设置 `OPENAI_API_KEY` 环境变量
746
+ - **gemini**:`npm i -g @google/gemini-cli` 并认证
747
+
748
+ ### Agent 评测与项目级 Runtime Context
749
+
750
+ 当执行器使用 `claude-sdk` 时,OMK 现在已经支持第一版 agent-aware evaluation。
751
+
752
+ 这里建议把几个概念分开理解:
753
+
754
+ - `artifact`:被评测对象,例如 baseline、skill、prompt、agent
755
+ - `variant`:CLI 里的实验分组表达式
756
+ - `runtime context`:运行时上下文,当前主要是 `cwd`;在项目型 agent 场景下,它就包含项目目录、`CLAUDE.md`、本地 skills 等会影响行为的环境因素
757
+
758
+ 在 OMK 里,`agent` 不是所有对象的总称,`skill` 也不是所有对象的总称。更稳妥的说法是:你在比较不同 artifact 在不同 runtime context 下的表现。
759
+
760
+ - 自动抽取 turns / toolCalls trace
761
+ - 支持基于工具调用行为的断言
762
+ - 支持在指定 `cwd` 下运行,让 Claude Code 自动加载项目内的 `CLAUDE.md`、skills 和本地 runtime context
763
+
764
+ #### 推荐执行器
765
+
766
+ ```bash
767
+ omk bench run --executor claude-sdk
768
+ ```
769
+
770
+ #### 支持的 agent 相关断言
771
+
772
+ | 断言 | 含义 |
773
+ |------|------|
774
+ | `tools_called` | 必须调用指定工具 |
775
+ | `tools_not_called` | 禁止调用指定工具 |
776
+ | `tools_count_min` / `tools_count_max` | 工具调用次数上下界 |
777
+ | `tool_output_contains` | 指定工具输出必须包含关键内容 |
778
+ | `turns_min` / `turns_max` | 交互轮次上下界 |
779
+
780
+ #### 三种常见对照组
781
+
782
+ **1. 裸模型 baseline**
783
+
784
+ 不注入 system prompt,也不进入带知识的项目目录。至少需要一个 treatment 做对比:
785
+
786
+ ```bash
787
+ omk bench run \
788
+ --executor claude-sdk \
789
+ --control baseline \
790
+ --treatment my-skill
791
+ ```
792
+
793
+ **2. 空 artifact + 项目级 runtime context**
794
+
795
+ 不注入 system prompt,但在项目目录运行。它不是严格意义上的"裸 baseline",而是"空 artifact + 项目级 runtime context"。
796
+
797
+ ```bash
798
+ omk bench run \
799
+ --executor claude-sdk \
800
+ --control baseline \
801
+ --treatment project-env@/path/to/target-project
802
+ ```
803
+
804
+ **3. 显式 artifact 注入**
805
+
806
+ 直接把某个外部 `SKILL.md` 作为 artifact 注入,同时保留项目目录上下文。适合对比"项目级 runtime context"与"显式单 artifact 注入"之间的差异。
807
+
808
+ ```bash
809
+ omk bench run \
810
+ --executor claude-sdk \
811
+ --control project-env@/path/to/target-project \
812
+ --treatment /path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
813
+ ```
814
+
815
+ #### 推荐的第一轮对照设计
816
+
817
+ 对于 PRD / 复杂业务知识场景,建议从下面开始:
818
+
819
+ ```bash
820
+ omk bench run \
821
+ --executor claude-sdk \
822
+ --samples skills/evaluate-review/eval-samples.yaml \
823
+ --control baseline \
824
+ --treatment /path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
825
+ ```
826
+
827
+ 如果你想证明"项目目录中的知识沉淀本身"是否有效,加第二个 treatment:
828
+
829
+ ```bash
830
+ omk bench run \
831
+ --executor claude-sdk \
832
+ --samples skills/evaluate-review/eval-samples.yaml \
833
+ --control baseline \
834
+ --treatment project-env@/path/to/target-project,/path/to/target-project/.claude/skills/prd/SKILL.md@/path/to/target-project
835
+ ```
836
+
837
+ #### 设计建议
838
+
839
+ - **先用 `--dry-run`**:确认样本、variant 和 `cwd` 被正确解析
840
+ - **项目级对照必须区分 `cwd`**:相同 prompt 在不同项目目录下会走不同 runtime context
841
+ - **优先先跑 PRD 场景**:相比 Coding,更容易验证知识完整性、影响面识别和业务正确性
842
+
843
+ ### 常见模型配置示例
844
+
845
+ **没有 Claude?** 大多数国产模型(GLM、通义千问、Moonshot、DeepSeek 等)都兼容 OpenAI API 格式,可以直接使用 `openai-api` 执行器:
846
+
847
+ ```bash
848
+ # GLM(智谱)
849
+ export OPENAI_API_KEY="你的智谱 API Key"
850
+ export OPENAI_BASE_URL="https://open.bigmodel.cn/api/paas/v4"
851
+ omk bench run --executor openai-api --model glm-4-plus \
852
+ --judge-model glm-4-plus --no-cache
853
+
854
+ # 通义千问
855
+ export OPENAI_API_KEY="你的通义 API Key"
856
+ export OPENAI_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
857
+ omk bench run --executor openai-api --model qwen-plus \
858
+ --judge-model qwen-plus
859
+
860
+ # DeepSeek
861
+ export OPENAI_API_KEY="你的 DeepSeek API Key"
862
+ export OPENAI_BASE_URL="https://api.deepseek.com"
863
+ omk bench run --executor openai-api --model deepseek-chat \
864
+ --judge-model deepseek-chat
865
+
866
+ # Moonshot(Kimi)
867
+ export OPENAI_API_KEY="你的 Moonshot API Key"
868
+ export OPENAI_BASE_URL="https://api.moonshot.cn/v1"
869
+ omk bench run --executor openai-api --model moonshot-v1-8k \
870
+ --judge-model moonshot-v1-8k
871
+ ```
872
+
873
+ **Ollama 本地模型:**
874
+
875
+ ```bash
876
+ omk bench run --executor "python examples/custom-executor/ollama-executor.py" \
877
+ --model llama3 --no-judge
878
+ ```
879
+
880
+ **关于评委模型:**
881
+
882
+ - `--judge-model` 指定 LLM 评委使用的模型,默认 `haiku`
883
+ - `--judge-executor` 指定评委使用的执行器(默认与 `--executor` 相同)
884
+ - 如果你没有 Claude,用 `--judge-executor` 和 `--judge-model` 指向你可用的模型
885
+ - 加 `--no-judge` 可跳过 LLM 评委,仅使用断言评分
886
+
887
+ ## 环境变量
888
+
889
+ | 变量 | 说明 |
890
+ |------|------|
891
+ | `CCV_PROXY_URL` | 将请求代理到 cc-viewer,实时可视化评测流量 |
892
+ | `OMK_BENCH_PORT` | 报告服务端口(默认:7799) |
893
+
894
+ ## 系统要求
895
+
896
+ - Node.js >= 20
897
+ - `claude` CLI(用于默认执行器和 LLM 评委,安装方式见 [Claude Code](https://claude.ai/code))
898
+ - 使用其他执行器(openai/gemini)且加 `--no-judge` 时可不装
899
+
900
+ ## 安全说明
901
+
902
+ 本工具设计用于**本地可信环境**(开发机、CI 流水线)。以下功能会执行本地代码,请确保输入来源可信:
903
+
904
+ | 功能 | 风险说明 | 适用范围 |
905
+ |------|----------|----------|
906
+ | **自定义断言** (`custom`) | 动态加载并执行用户指定的 `.mjs` 文件 | 仅使用自己编写或审查过的断言文件 |
907
+ | **eval-samples.json** | 断言配置中可引用外部文件路径 | 不要使用不可信来源的样本文件 |
908
+
909
+ **建议:**
910
+
911
+ - 不要在公网服务中暴露 `omk bench report` 服务(无认证)
912
+ - 不要用不可信的第三方 eval-samples 文件
913
+ - 自定义断言有 30 秒执行超时,但无沙箱隔离
914
+
915
+ ---
916
+
917
+ 版本变更记录见 [CHANGELOG](./CHANGELOG.md)。欢迎贡献 — 详见 [CONTRIBUTING](./CONTRIBUTING.md)。