oh-my-knowledge 0.40.0 → 0.41.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 +1 -2
- package/README.zh.md +1 -2
- package/dist/analysis/report-diagnostics.js +27 -0
- package/dist/assets/agent-skills/omk/references/commands.md +1 -2
- package/dist/authoring/generator.js +3 -2
- package/dist/cli/commands/eval/index.d.ts +0 -1
- package/dist/cli/commands/eval/index.js +10 -10
- package/dist/cli/lib/cmd-flags.d.ts +0 -1
- package/dist/cli/lib/i18n-dict/run.js +2 -2
- package/dist/cli/lib/parse-run-config.d.ts +0 -1
- package/dist/cli/lib/parse-run-config.js +0 -2
- package/dist/eval-core/evaluation-job.d.ts +1 -2
- package/dist/eval-core/evaluation-job.js +1 -2
- package/dist/eval-core/evaluation-reporting.d.ts +0 -1
- package/dist/eval-core/evaluation-reporting.js +2 -38
- package/dist/eval-core/execution-strategy.js +3 -2
- package/dist/eval-core/judge-independence.d.ts +28 -0
- package/dist/eval-core/judge-independence.js +29 -0
- package/dist/eval-core/verdict.d.ts +9 -1
- package/dist/eval-core/verdict.js +43 -5
- package/dist/eval-workflows/batch-evaluation-workflow.d.ts +1 -1
- package/dist/eval-workflows/batch-evaluation-workflow.js +1 -2
- package/dist/eval-workflows/evaluation-pipeline/report-finalize.d.ts +1 -5
- package/dist/eval-workflows/evaluation-pipeline/report-finalize.js +1 -8
- package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts +1 -2
- package/dist/eval-workflows/evaluation-pipeline/run-state.js +1 -2
- package/dist/eval-workflows/evaluation-pipeline.d.ts +1 -2
- package/dist/eval-workflows/evaluation-pipeline.js +1 -3
- package/dist/eval-workflows/run-evaluation.d.ts +2 -3
- package/dist/eval-workflows/run-evaluation.js +1 -2
- package/dist/executors/claude-cli.js +5 -6
- package/dist/executors/claude-sdk.d.ts +5 -2
- package/dist/executors/claude-sdk.js +13 -8
- package/dist/executors/codex-cli.js +3 -4
- package/dist/executors/shared.d.ts +2 -0
- package/dist/executors/shared.js +15 -0
- package/dist/grading/assertions.js +6 -122
- package/dist/grading/gold-cli.js +1 -1
- package/dist/grading/human-gold.d.ts +5 -3
- package/dist/grading/human-gold.js +5 -3
- package/dist/grading/index.d.ts +4 -4
- package/dist/grading/judge.d.ts +6 -14
- package/dist/grading/judge.js +5 -88
- package/dist/inputs/eval-config.js +6 -2
- package/dist/managed/evidence.js +1 -2
- package/dist/managed/version-scores.js +1 -1
- package/dist/renderer/html-renderer.js +0 -9
- package/dist/renderer/layout.js +4 -4
- package/dist/renderer/summary.js +23 -1
- package/dist/shared/llm-prompts/debias-instructions.d.ts +4 -0
- package/dist/shared/llm-prompts/debias-instructions.js +44 -0
- package/dist/shared/llm-prompts/judge-prompts.d.ts +30 -0
- package/dist/shared/llm-prompts/judge-prompts.js +205 -0
- package/dist/shared/llm-prompts/registry.d.ts +27 -0
- package/dist/shared/llm-prompts/registry.js +69 -0
- package/dist/types/eval.d.ts +8 -8
- package/dist/types/judge.d.ts +1 -1
- package/dist/types/report.d.ts +1 -3
- package/package.json +1 -1
- package/dist/grading/debias-validate.d.ts +0 -83
- package/dist/grading/debias-validate.js +0 -176
package/README.md
CHANGED
|
@@ -105,8 +105,7 @@ RAG-specific evals: see RAGAS (separate niche, complementary to omk). Full compa
|
|
|
105
105
|
| **Git & remote sources** | install / eval from a local git ref or a remote git URL (`--git-url`); directory-skills run in a content-addressed **isolated copy** so `references/` assets are real measured input, not just `SKILL.md` |
|
|
106
106
|
| **Evidence-gated management** | `omk install` registers a managed record; `omk eval` auto-writes evidence bound by content fingerprint, moving a skill `installed → measurable`; `omk list` surfaces each managed skill's status (installed / measurable / promoted / stale); `omk promote` accepts a version once its evidence passes the gate (default PROGRESS only); `omk rollback` revokes that acceptance, returning the skill to `measurable`. [spec →](docs/specs/evidence-gated-management.md) |
|
|
107
107
|
| **Sample design science** | sample schema with `capability` / `difficulty` / `construct` / `provenance` metadata (HF Dataset Cards style); studio surfaces coverage breakdown plus `rubric_clarity_low` / `capability_thin` flags. [docs/specs/sample-design-spec.md](docs/specs/sample-design-spec.md) |
|
|
108
|
-
| **Multi-judge ensemble** | `--judge-models claude:opus,openai:gpt-4o` cross-vendor scoring + agreement metrics |
|
|
109
|
-
| **Blind A/B** | `--blind` hides variant names; HTML report has a reveal button |
|
|
108
|
+
| **Multi-judge ensemble** | `--judge-models claude:opus,openai-api:gpt-4o` cross-vendor scoring + agreement metrics |
|
|
110
109
|
| **Multi-run variance** | `--repeat N` repeats the eval and computes mean / SD / CI / t-test |
|
|
111
110
|
| **MCP URL fetching** | pull content from private-doc URLs via an MCP server (SSO-protected knowledge bases, etc.) |
|
|
112
111
|
| **Auto analysis** | detects low-discrimination assertions, flat scores, all-pass / all-fail, expensive samples |
|
package/README.zh.md
CHANGED
|
@@ -105,8 +105,7 @@ RAG 专项评测请看 RAGAS(独立 niche,跟 omk 互补)。完整对比
|
|
|
105
105
|
| **Git / 远端源** | install / eval 支持本地 git ref 或远端 git URL(`--git-url`);目录-skill 在内容寻址**隔离副本**里执行,`references/` 资产是真实测量输入,不只是 `SKILL.md` |
|
|
106
106
|
| **证据门控管理** | `omk install` 登记受管记录;`omk eval` 按内容指纹自动写入证据,把 skill 从 `installed` 推到 `measurable`;`omk list` 查看各受管 skill 的状态(installed / measurable / promoted / stale);`omk promote` 在证据过门禁(默认仅 PROGRESS)后把该版本接受为当前版本;`omk rollback` 撤销这次接受,让 skill 回到 `measurable`。[规范 →](docs/zh/specs/evidence-gated-management.md) |
|
|
107
107
|
| **用例设计科学性** | Sample schema 加 `capability` / `difficulty` / `construct` / `provenance` 元数据字段(HF Dataset Cards 风),studio 输出 coverage 分桶 + `rubric_clarity_low` / `capability_thin` issue。[docs/zh/specs/sample-design-spec.md](docs/zh/specs/sample-design-spec.md) |
|
|
108
|
-
| **多评委 ensemble** | `--judge-models claude:opus,openai:gpt-4o` 跨厂商评分 + agreement 度量 |
|
|
109
|
-
| **盲测 A/B** | `--blind` 隐藏变体名称,HTML 报告有揭晓按钮 |
|
|
108
|
+
| **多评委 ensemble** | `--judge-models claude:opus,openai-api:gpt-4o` 跨厂商评分 + agreement 度量 |
|
|
110
109
|
| **多轮方差分析** | `--repeat N` 重复 N 次,计算均值/标准差/置信区间/t 检验 |
|
|
111
110
|
| **MCP URL 获取** | 通过 MCP Server 获取私有文档 URL 内容(SSO 保护的知识库等) |
|
|
112
111
|
| **自动分析** | 检测低区分度断言、均匀分数、全通过/全失败、高成本用例 |
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Auto-analysis: detect patterns and generate insights from evaluation results.
|
|
3
3
|
*/
|
|
4
4
|
import { normalizeCapability } from './sample-diagnostics.js';
|
|
5
|
+
import { analyzeJudgeIndependence } from '../eval-core/judge-independence.js';
|
|
5
6
|
/**
|
|
6
7
|
* Analyze an evaluation report and produce structured insights.
|
|
7
8
|
*/
|
|
@@ -15,6 +16,10 @@ export function analyzeResults(report, opts = {}) {
|
|
|
15
16
|
const sampleQuality = opts.samples
|
|
16
17
|
? buildSampleQualityAggregate(opts.samples)
|
|
17
18
|
: undefined;
|
|
19
|
+
// 评委独立性对**单变体**报告同样适用(SOLO 只有绝对分、没有 A/B 差值去抵消自我偏好,
|
|
20
|
+
// 反而更该报),故放在 variants<2 早退之前;仅 results 为空(dry-run)时不评估。
|
|
21
|
+
if (results.length > 0)
|
|
22
|
+
detectJudgeIndependence(report, insights);
|
|
18
23
|
if (results.length === 0 || variants.length < 2) {
|
|
19
24
|
return { insights, ...(sampleQuality && { sampleQuality }) };
|
|
20
25
|
}
|
|
@@ -38,11 +43,33 @@ export function analyzeResults(report, opts = {}) {
|
|
|
38
43
|
detectAgentAssertionDiscrimination(results, variants, insights);
|
|
39
44
|
// 10. Suggest --repeat when score variance is high and no repeat data
|
|
40
45
|
detectNeedRepeat(report, results, variants, insights);
|
|
46
|
+
// 11. Judge independence — 已在 variants<2 早退前调过(单变体也适用),此处不重复。
|
|
41
47
|
return {
|
|
42
48
|
insights,
|
|
43
49
|
...(sampleQuality && { sampleQuality }),
|
|
44
50
|
};
|
|
45
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* 评委独立性诊断:同厂商评委(自我偏好敞口)/ 单厂商 ensemble(一致性不反驳共有偏置)。
|
|
54
|
+
* 判定走单一来源 `analyzeJudgeIndependence`;挂了 gold 校准则软化为 info。
|
|
55
|
+
*/
|
|
56
|
+
function detectJudgeIndependence(report, insights) {
|
|
57
|
+
const ind = analyzeJudgeIndependence(report);
|
|
58
|
+
if (ind.sameVendorJudge) {
|
|
59
|
+
insights.push({
|
|
60
|
+
type: 'judge_self_preference',
|
|
61
|
+
severity: ind.goldCalibrated ? 'info' : 'warning',
|
|
62
|
+
details: { judgeVendors: ind.judgeVendors, outputVendors: ind.outputVendors, goldCalibrated: ind.goldCalibrated },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (ind.singleVendorEnsemble) {
|
|
66
|
+
insights.push({
|
|
67
|
+
type: 'single_vendor_ensemble',
|
|
68
|
+
severity: ind.goldCalibrated ? 'info' : 'warning',
|
|
69
|
+
details: { judgeVendors: ind.judgeVendors, goldCalibrated: ind.goldCalibrated },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
46
73
|
/**
|
|
47
74
|
* Build sample design science aggregate from sample metadata.
|
|
48
75
|
*
|
|
@@ -67,7 +67,6 @@ omk eval [flags]
|
|
|
67
67
|
**Flags:**
|
|
68
68
|
|
|
69
69
|
- `--batch` `boolean`:batch 模式:baseline vs 每个 skill
|
|
70
|
-
- `--blind` `boolean`:judge blind 模式
|
|
71
70
|
- `--bootstrap` `boolean`:加 bootstrap CI
|
|
72
71
|
- `--bootstrap-samples` `option`:bootstrap 重采样次数,默认 1000
|
|
73
72
|
- `--budget-per-sample-ms` `option`:单 sample 时长上限 ms(必须 > 0,不传则无上限)
|
|
@@ -82,7 +81,7 @@ omk eval [flags]
|
|
|
82
81
|
- `--executor` `option`:执行器:claude / claude-sdk / codex / codex-sdk / openai-api / gemini / 自定义命令(默认 claude)。
|
|
83
82
|
- `--global` `boolean`:报告写全局 ~/.oh-my-knowledge/reports,而非项目 .omk/
|
|
84
83
|
- `--gold-dir` `option`:gold dataset 目录
|
|
85
|
-
- `--judge-models` `option`:评委配置,格式 executor:model[,...],例 claude:haiku 或 claude:opus,openai:gpt-4o(≥ 2 个 = ensemble)。默认 <executor>:haiku。
|
|
84
|
+
- `--judge-models` `option`:评委配置,格式 executor:model[,...],例 claude:haiku 或 claude:opus,openai-api:gpt-4o(≥ 2 个 = ensemble)。默认 <executor>:haiku。
|
|
86
85
|
- `--judge-repeat` `option`:每个 dim 评 N 次
|
|
87
86
|
- `--lang` `option` (默认 `zh`):输出语言 zh|en,优先级 CLI > OMK_LANG env > zh。
|
|
88
87
|
- `--layered-stats` `boolean`:输出分层统计
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createExecutor } from '../executors/index.js';
|
|
2
|
+
import { DEFAULT_GATE_THRESHOLD } from '../eval-core/verdict.js';
|
|
2
3
|
/**
|
|
3
4
|
* Generator 默认模型 'opus' (跟 eval 默认对齐)。
|
|
4
5
|
* lean=true 路径会自动追加 `--effort low`,关掉 opus 默认的扩展思考,
|
|
@@ -76,7 +77,7 @@ const SYSTEM_PROMPT = `你是一个评测用例生成器。你的任务是根据
|
|
|
76
77
|
来涨数量)。omk 的评分体系是 layered scoring: **fact 层**(deterministic 字面/工具断言)
|
|
77
78
|
+ **behavior 层**(代价指标如 turn 数 / 工具失败率) + **judge 层**(主观语义评分,从
|
|
78
79
|
sample.rubric 派生维度,judge LLM 看 trace 评 1-5)三层独立计分,verdict 是三层
|
|
79
|
-
独立过 threshold(默认
|
|
80
|
+
独立过 threshold(默认 ${DEFAULT_GATE_THRESHOLD})。**fact 层的本职是测 deterministic 端点,不是测轨迹**。
|
|
80
81
|
|
|
81
82
|
**断言哲学(关键):fact 测结果+里程碑,过程质量交 judge**
|
|
82
83
|
─────────────────────────────────────────────────────────────
|
|
@@ -109,7 +110,7 @@ const SYSTEM_PROMPT = `你是一个评测用例生成器。你的任务是根据
|
|
|
109
110
|
- tools_not_called 反模式断言 0-1 条(禁止接触某禁忌工具,如 tripwire sample)
|
|
110
111
|
- rubric 3-5 个判分维度(细致写明 judge 该看什么),由 sample.rubric 字段承载
|
|
111
112
|
|
|
112
|
-
*测量学背景:* 当前 omk verdict 三层独立 threshold(默认
|
|
113
|
+
*测量学背景:* 当前 omk verdict 三层独立 threshold(默认 ${DEFAULT_GATE_THRESHOLD}),fact 条目少之后单条
|
|
113
114
|
权重大、单次评测方差大,**强烈建议** 评测时带 \`--repeat 2\` 或更大测稳定性(coefficient
|
|
114
115
|
of variation),并参考 bootstrap CI 而非点估计。这是 fact 层稀疏化的代价,换来的是
|
|
115
116
|
fact 信号干净(不被 trajectory 字面噪音污染)。
|
|
@@ -37,7 +37,6 @@ export default class Eval extends BaseCommand {
|
|
|
37
37
|
'no-strict-baseline': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
38
38
|
effort: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
39
39
|
'no-diagnostic': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
40
|
-
blind: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
41
40
|
repeat: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
42
41
|
'judge-repeat': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
43
42
|
bootstrap: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -8,6 +8,7 @@ import { parseRunConfig } from '../../lib/parse-run-config.js';
|
|
|
8
8
|
import { makeOnProgress } from '../../lib/progress.js';
|
|
9
9
|
import { computeRunTally } from '../../lib/run-tally.js';
|
|
10
10
|
import { DEFAULT_BOOTSTRAP_SAMPLES } from '../../../eval-core/bootstrap.js';
|
|
11
|
+
import { DEFAULT_GATE_THRESHOLD } from '../../../eval-core/verdict.js';
|
|
11
12
|
import { EVALUATION_REPORT_SCHEMA_VERSION } from '../../../eval-core/evaluation-reporting.js';
|
|
12
13
|
function isDryRunReport(report) {
|
|
13
14
|
return Boolean(report && typeof report === 'object' && report.dryRun === true);
|
|
@@ -17,9 +18,11 @@ function isDryRunBatchReport(report) {
|
|
|
17
18
|
}
|
|
18
19
|
function verdictOptions(values) {
|
|
19
20
|
const rawThreshold = values.threshold;
|
|
21
|
+
// 不传 --threshold 时返回 undefined,由 computeVerdict 应用 DEFAULT_GATE_THRESHOLD ——
|
|
22
|
+
// 避免在此再硬编码一份 3.5(单一来源在 verdict.ts)。
|
|
20
23
|
const gateThreshold = rawThreshold !== undefined && Number.isFinite(Number(rawThreshold))
|
|
21
24
|
? Number(rawThreshold)
|
|
22
|
-
:
|
|
25
|
+
: undefined;
|
|
23
26
|
const rawTrivial = values['trivial-diff'];
|
|
24
27
|
const triviallySmallDiff = rawTrivial !== undefined && Number.isFinite(Number(rawTrivial))
|
|
25
28
|
? Number(rawTrivial)
|
|
@@ -158,9 +161,6 @@ async function announceSavedReport({ report, filePath, reportsDir, values, lang,
|
|
|
158
161
|
async function runEval(_args, flags, lang) {
|
|
159
162
|
const { values, config, evalConfig } = parseRunConfig({ ...flags });
|
|
160
163
|
const { runEvaluation, runMultiple, runBatchEvaluation } = await import('../../../eval-workflows/run-evaluation.js');
|
|
161
|
-
if (values.blind !== undefined) {
|
|
162
|
-
config.blind = values.blind;
|
|
163
|
-
}
|
|
164
164
|
config.onProgress = makeOnProgress(lang);
|
|
165
165
|
const repeatRaw = values.repeat;
|
|
166
166
|
const parsedRepeat = repeatRaw !== undefined ? Number(repeatRaw) : (evalConfig?.repeat ?? 1);
|
|
@@ -368,8 +368,8 @@ export default class Eval extends BaseCommand {
|
|
|
368
368
|
}),
|
|
369
369
|
'judge-models': Flags.string({
|
|
370
370
|
description: bilingual({
|
|
371
|
-
zh: '评委配置,格式 executor:model[,...],例 claude:haiku 或 claude:opus,openai:gpt-4o(≥ 2 个 = ensemble)。默认 <executor>:haiku。',
|
|
372
|
-
en: 'Judge config: executor:model[,...]. e.g. claude:haiku or claude:opus,openai:gpt-4o (≥ 2 = ensemble). Default <executor>:haiku.',
|
|
371
|
+
zh: '评委配置,格式 executor:model[,...],例 claude:haiku 或 claude:opus,openai-api:gpt-4o(≥ 2 个 = ensemble)。默认 <executor>:haiku。',
|
|
372
|
+
en: 'Judge config: executor:model[,...]. e.g. claude:haiku or claude:opus,openai-api:gpt-4o (≥ 2 = ensemble). Default <executor>:haiku.',
|
|
373
373
|
}),
|
|
374
374
|
}),
|
|
375
375
|
'output-dir': Flags.string({
|
|
@@ -453,9 +453,6 @@ export default class Eval extends BaseCommand {
|
|
|
453
453
|
}),
|
|
454
454
|
}),
|
|
455
455
|
// ── eval-runner extra ──
|
|
456
|
-
blind: Flags.boolean({
|
|
457
|
-
description: bilingual({ zh: 'judge blind 模式', en: 'Blind judge mode' }),
|
|
458
|
-
}),
|
|
459
456
|
repeat: Flags.string({
|
|
460
457
|
description: bilingual({ zh: '每个 sample 重复跑 N 次', en: 'Repeat each sample N times' }),
|
|
461
458
|
parse: integerStringParser('--repeat', { min: 1 }),
|
|
@@ -490,7 +487,10 @@ export default class Eval extends BaseCommand {
|
|
|
490
487
|
parse: numberStringParser('--budget-per-sample-ms', { minExclusive: 0 }),
|
|
491
488
|
}),
|
|
492
489
|
threshold: Flags.string({
|
|
493
|
-
description: bilingual({
|
|
490
|
+
description: bilingual({
|
|
491
|
+
zh: `verdict 阈值,默认 ${DEFAULT_GATE_THRESHOLD}`,
|
|
492
|
+
en: `Verdict threshold, default ${DEFAULT_GATE_THRESHOLD}`,
|
|
493
|
+
}),
|
|
494
494
|
parse: numberStringParser('--threshold'),
|
|
495
495
|
}),
|
|
496
496
|
'trivial-diff': Flags.string({
|
|
@@ -52,8 +52,8 @@ export const runDict = {
|
|
|
52
52
|
en: '⚠ --judge-repeat "{value}" is invalid (expected an integer ≥ 1), falling back to 1 judge call\n',
|
|
53
53
|
},
|
|
54
54
|
'cli.run.no_debias_length_active': {
|
|
55
|
-
zh: 'ℹ --no-debias-length
|
|
56
|
-
en: 'ℹ --no-debias-length is active: judge prompt
|
|
55
|
+
zh: 'ℹ --no-debias-length 已生效:judge prompt 去掉长度去偏指令(debias-off 变体),hash 与默认开启时不同。\n',
|
|
56
|
+
en: 'ℹ --no-debias-length is active: the judge prompt drops the length-debias instruction (debias-off variant); its hash differs from the default.\n',
|
|
57
57
|
},
|
|
58
58
|
'cli.run.invalid_bootstrap_samples': {
|
|
59
59
|
zh: '⚠ --bootstrap-samples "{value}" 无效 (期望 ≥ 100 的整数), 已按 1000 执行\n',
|
|
@@ -94,7 +94,6 @@ export function parseRunConfig(values) {
|
|
|
94
94
|
const verbose = values.verbose ?? false;
|
|
95
95
|
const retry = Math.max(0, Number(values.retry ?? 0) || 0);
|
|
96
96
|
const resume = values.resume;
|
|
97
|
-
const blind = values.blind ?? evalConfig?.blind ?? false;
|
|
98
97
|
const layeredStats = values['layered-stats'] ?? false;
|
|
99
98
|
// strict-baseline default true. Reconcile both flag forms with eval.yaml fallback.
|
|
100
99
|
// Priority: --no-strict-baseline > --strict-baseline > eval.yaml strictBaseline > true。
|
|
@@ -144,7 +143,6 @@ export function parseRunConfig(values) {
|
|
|
144
143
|
verbose,
|
|
145
144
|
retry,
|
|
146
145
|
resume,
|
|
147
|
-
blind,
|
|
148
146
|
layeredStats,
|
|
149
147
|
budget: evalConfig?.budget,
|
|
150
148
|
strictBaseline,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Artifact, EvaluationErrorCategory, EvaluationJob, EvaluationRequest, EvaluationRun, JudgeConfig } from '../types/index.js';
|
|
2
|
-
export declare function buildEvaluationRequest({ samplesPath, skillDir, artifacts, model, executor, noJudge, concurrency, timeoutMs, noCache, dryRun,
|
|
2
|
+
export declare function buildEvaluationRequest({ samplesPath, skillDir, artifacts, model, executor, noJudge, concurrency, timeoutMs, noCache, dryRun, project, owner, tags, repeat, batch, judgeRepeat, judgeModels, bootstrap, bootstrapSamples, lengthDebias, budget, effort, }: {
|
|
3
3
|
samplesPath: string;
|
|
4
4
|
skillDir: string;
|
|
5
5
|
artifacts: Artifact[];
|
|
@@ -10,7 +10,6 @@ export declare function buildEvaluationRequest({ samplesPath, skillDir, artifact
|
|
|
10
10
|
timeoutMs?: number;
|
|
11
11
|
noCache: boolean;
|
|
12
12
|
dryRun: boolean;
|
|
13
|
-
blind: boolean;
|
|
14
13
|
project?: string;
|
|
15
14
|
owner?: string;
|
|
16
15
|
tags?: string[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
function nowIso() {
|
|
2
2
|
return new Date().toISOString();
|
|
3
3
|
}
|
|
4
|
-
export function buildEvaluationRequest({ samplesPath, skillDir, artifacts, model, executor, noJudge, concurrency, timeoutMs, noCache, dryRun,
|
|
4
|
+
export function buildEvaluationRequest({ samplesPath, skillDir, artifacts, model, executor, noJudge, concurrency, timeoutMs, noCache, dryRun, project, owner, tags, repeat, batch, judgeRepeat, judgeModels, bootstrap, bootstrapSamples, lengthDebias, budget, effort, }) {
|
|
5
5
|
return {
|
|
6
6
|
samplesPath,
|
|
7
7
|
skillDir,
|
|
@@ -13,7 +13,6 @@ export function buildEvaluationRequest({ samplesPath, skillDir, artifacts, model
|
|
|
13
13
|
timeoutMs,
|
|
14
14
|
noCache,
|
|
15
15
|
dryRun,
|
|
16
|
-
blind,
|
|
17
16
|
project,
|
|
18
17
|
owner,
|
|
19
18
|
tags,
|
|
@@ -28,7 +28,6 @@ interface AggregateReportOptions {
|
|
|
28
28
|
layeredStats?: boolean;
|
|
29
29
|
}
|
|
30
30
|
export declare function aggregateReport({ runId, variants, model, judgeModel, noJudge, executorName, samples, tasks, results, totalCostUSD, artifacts, request, run, job, layeredStats, }: AggregateReportOptions): Report;
|
|
31
|
-
export declare function applyBlindMode(report: Report, variants: string[], blindSeed: string): void;
|
|
32
31
|
export interface PersistableReport {
|
|
33
32
|
id: string;
|
|
34
33
|
}
|
|
@@ -199,8 +199,8 @@ export function aggregateReport({ runId, variants, model, judgeModel, noJudge, e
|
|
|
199
199
|
...(noJudge ? {} : { runtime: getExecutorRuntimeFingerprint(jc.executor, jc.model, runtimeOptions) }),
|
|
200
200
|
}));
|
|
201
201
|
// length-debias is on by default; the request only sets it
|
|
202
|
-
// false when the user passed --no-debias-length. The
|
|
203
|
-
//
|
|
202
|
+
// false when the user passed --no-debias-length. The judgePromptHash differs between
|
|
203
|
+
// the length-debias-on and -off prompt variants so readers can detect the divergence.
|
|
204
204
|
const lengthDebiasOn = request?.lengthDebias !== false;
|
|
205
205
|
const debiasModeList = [];
|
|
206
206
|
if (lengthDebiasOn)
|
|
@@ -271,42 +271,6 @@ export function aggregateReport({ runId, variants, model, judgeModel, noJudge, e
|
|
|
271
271
|
}])),
|
|
272
272
|
};
|
|
273
273
|
}
|
|
274
|
-
export function applyBlindMode(report, variants, blindSeed) {
|
|
275
|
-
const labels = variants.map((_, i) => String.fromCharCode(65 + i));
|
|
276
|
-
let seed = parseInt(hashString(blindSeed).slice(0, 8), 16) | 0;
|
|
277
|
-
const seededRandom = () => {
|
|
278
|
-
seed |= 0;
|
|
279
|
-
seed = seed + 0x6D2B79F5 | 0;
|
|
280
|
-
let value = Math.imul(seed ^ seed >>> 15, 1 | seed);
|
|
281
|
-
value ^= value + Math.imul(value ^ value >>> 7, 61 | value);
|
|
282
|
-
return ((value ^ value >>> 14) >>> 0) / 4294967296;
|
|
283
|
-
};
|
|
284
|
-
const shuffled = [...variants];
|
|
285
|
-
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
286
|
-
const j = Math.floor(seededRandom() * (i + 1));
|
|
287
|
-
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
288
|
-
}
|
|
289
|
-
const blindMap = Object.fromEntries(shuffled.map((variant, i) => [labels[i], variant]));
|
|
290
|
-
const reverseMap = Object.fromEntries(Object.entries(blindMap).map(([label, variant]) => [variant, label]));
|
|
291
|
-
report.meta.blind = true;
|
|
292
|
-
report.meta.blindMap = blindMap;
|
|
293
|
-
report.meta.variants = labels;
|
|
294
|
-
if (report.meta.executorRuntimes) {
|
|
295
|
-
report.meta.executorRuntimes = Object.fromEntries(Object.entries(report.meta.executorRuntimes).map(([variant, runtime]) => [reverseMap[variant] ?? variant, runtime]));
|
|
296
|
-
}
|
|
297
|
-
const newSummary = {};
|
|
298
|
-
for (const [variant, stats] of Object.entries(report.summary)) {
|
|
299
|
-
newSummary[reverseMap[variant]] = stats;
|
|
300
|
-
}
|
|
301
|
-
report.summary = newSummary;
|
|
302
|
-
for (const result of report.results) {
|
|
303
|
-
const newVariants = {};
|
|
304
|
-
for (const [variant, data] of Object.entries(result.variants)) {
|
|
305
|
-
newVariants[reverseMap[variant]] = data;
|
|
306
|
-
}
|
|
307
|
-
result.variants = newVariants;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
274
|
export function persistReport(report, outputDir) {
|
|
311
275
|
if (!outputDir)
|
|
312
276
|
return null;
|
|
@@ -101,8 +101,9 @@ export function resolveExecutionStrategy(task, model, timeoutMs, verbose, effort
|
|
|
101
101
|
verbose,
|
|
102
102
|
...(effort && { effort }),
|
|
103
103
|
// pass skill-isolation declaration to executors. undefined keeps
|
|
104
|
-
// SDK default; [] = strict isolation (skills:[] + disallowedTools:['Skill'])
|
|
105
|
-
//
|
|
104
|
+
// SDK default; [] = strict isolation (skills:[] + disallowedTools:['Skill']).
|
|
105
|
+
// non-empty allowedSkills is rejected upstream (validateEvalConfig) and by every
|
|
106
|
+
// executor — a skill whitelist could not be fully isolated, so it was removed.
|
|
106
107
|
...(task.artifact.allowedSkills !== undefined && { allowedSkills: task.artifact.allowedSkills }),
|
|
107
108
|
// Sample.mocks 透传到 executor。executor(claude-sdk / claude-cli)
|
|
108
109
|
// 自决定怎么落地(in-process hook vs 临时 CLAUDE_CONFIG_DIR + on-disk hook)。
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Report } from '../types/index.js';
|
|
2
|
+
import { type ExecutorVendor } from '../executors/shared.js';
|
|
3
|
+
/**
|
|
4
|
+
* 评委独立性分析(单一来源,verdict caveat 与 analysis 诊断共用)。
|
|
5
|
+
*
|
|
6
|
+
* LLM 评委有自我偏好偏置:偏爱与自己同模型家族产出的输出。omk 默认评委(claude:haiku)与默认
|
|
7
|
+
* 执行器(claude:*)同属一家,敞口默认就开着。本 helper 只算客观事实(评委/输出各属哪家、有没有
|
|
8
|
+
* 跨厂商评委、是不是单厂商 ensemble、有没有挂 gold 校准),严重度与文案交给消费方(report-diagnostics
|
|
9
|
+
* / verdict)决定。
|
|
10
|
+
*
|
|
11
|
+
* 缓解阶梯(行业共识):换跨厂商评委 > 跨厂商陪审团 > 人工金标校准 > 警告。omk 无法强制换 key,
|
|
12
|
+
* 故只检测 + 警告并指向 `--judge-models <跨厂商>` 和 `omk eval gold compare`。
|
|
13
|
+
*/
|
|
14
|
+
export interface JudgeIndependence {
|
|
15
|
+
/** 各评委的厂商家族(与 judgeModels 同序)。 */
|
|
16
|
+
judgeVendors: ExecutorVendor[];
|
|
17
|
+
/** 被测输出涉及的厂商家族(去重)。 */
|
|
18
|
+
outputVendors: ExecutorVendor[];
|
|
19
|
+
/** 至少有一个评委的厂商不在被测输出厂商集合里 —— 存在独立(跨厂商)评委。 */
|
|
20
|
+
crossVendorJudgePresent: boolean;
|
|
21
|
+
/** 有评委、厂商全可归类、且无任何跨厂商评委 —— 自我偏好敞口(J1)。 */
|
|
22
|
+
sameVendorJudge: boolean;
|
|
23
|
+
/** ≥ 2 评委且全部同一个(已归类)厂商 —— ensemble 一致性不反驳共有偏置(J2)。 */
|
|
24
|
+
singleVendorEnsemble: boolean;
|
|
25
|
+
/** 本次 run 挂了人工 gold 校准(report.meta.humanAgreement 存在)→ 敞口有背板。 */
|
|
26
|
+
goldCalibrated: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare function analyzeJudgeIndependence(report: Report): JudgeIndependence;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { executorVendor } from '../executors/shared.js';
|
|
2
|
+
export function analyzeJudgeIndependence(report) {
|
|
3
|
+
const judges = report.meta?.judgeModels ?? [];
|
|
4
|
+
const judgeVendors = judges.map((j) => executorVendor(j.executor));
|
|
5
|
+
const runtimes = report.meta?.executorRuntimes;
|
|
6
|
+
const outputExecutors = runtimes && Object.keys(runtimes).length > 0
|
|
7
|
+
? Object.values(runtimes).map((r) => r.executor).filter((e) => !!e)
|
|
8
|
+
: (report.meta?.executor ? [report.meta.executor] : []);
|
|
9
|
+
const outputVendors = [...new Set(outputExecutors.map(executorVendor))];
|
|
10
|
+
const goldCalibrated = report.meta?.humanAgreement != null;
|
|
11
|
+
const base = { judgeVendors, outputVendors, goldCalibrated };
|
|
12
|
+
// 不评估(不误报)的情形:
|
|
13
|
+
// - noJudge:judgeModels 为审计保留(非空),但评委根本没跑、composite 无评委层 → 自我偏好 moot;
|
|
14
|
+
// - 无评委 / 任一厂商无法归类(自定义 script)/ 拿不到被测厂商。
|
|
15
|
+
const noJudge = report.meta?.noJudge === true;
|
|
16
|
+
const anyUnknown = judgeVendors.includes('unknown') || outputVendors.includes('unknown');
|
|
17
|
+
if (judges.length === 0 || noJudge || anyUnknown || outputVendors.length === 0) {
|
|
18
|
+
return { ...base, crossVendorJudgePresent: false, sameVendorJudge: false, singleVendorEnsemble: false };
|
|
19
|
+
}
|
|
20
|
+
const outSet = new Set(outputVendors);
|
|
21
|
+
const crossVendorJudgePresent = judgeVendors.some((v) => !outSet.has(v));
|
|
22
|
+
const singleVendorEnsemble = judges.length >= 2 && new Set(judgeVendors).size === 1;
|
|
23
|
+
return {
|
|
24
|
+
...base,
|
|
25
|
+
crossVendorJudgePresent,
|
|
26
|
+
sameVendorJudge: !crossVendorJudgePresent,
|
|
27
|
+
singleVendorEnsemble,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -55,6 +55,14 @@ export declare const ENSEMBLE_DISSENT_PEARSON = 0.4;
|
|
|
55
55
|
* never gated by it — see `computeVerdict`.
|
|
56
56
|
*/
|
|
57
57
|
export declare const STABILITY_UNSTABLE_CV = 0.15;
|
|
58
|
+
/**
|
|
59
|
+
* Per-layer pass/fail line for the three-layer gate, on the 1-5 scale.
|
|
60
|
+
* **Pragmatic default, not derived from an external standard**: 3.5 is a clear margin
|
|
61
|
+
* above the 3.0 scale midpoint ("basically acceptable"), so a layer must land
|
|
62
|
+
* comfortably in the upper half to pass. Overridable via `omk eval --threshold`.
|
|
63
|
+
* doc ↔ code parity guarded by `test/scripts/doc-constants-drift.test.ts`.
|
|
64
|
+
*/
|
|
65
|
+
export declare const DEFAULT_GATE_THRESHOLD = 3.5;
|
|
58
66
|
export type VerdictLevel = 'PROGRESS' | 'CAUTIOUS' | 'REGRESS' | 'NOISE' | 'UNDERPOWERED' | 'SOLO';
|
|
59
67
|
export interface VerdictResult {
|
|
60
68
|
level: VerdictLevel;
|
|
@@ -82,7 +90,7 @@ export interface VerdictResult {
|
|
|
82
90
|
variants: string[];
|
|
83
91
|
}
|
|
84
92
|
export interface VerdictOptions {
|
|
85
|
-
/** Three-layer ci-gate threshold; defaults to
|
|
93
|
+
/** Three-layer ci-gate threshold; defaults to DEFAULT_GATE_THRESHOLD (matches `omk eval`). */
|
|
86
94
|
gateThreshold?: number;
|
|
87
95
|
/**
|
|
88
96
|
* Magnitude (in raw score points) below which a "significant" diff is treated
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { evaluateLayerGates } from './layer-gates.js';
|
|
31
31
|
import { ciLevelLabel } from './bootstrap.js';
|
|
32
|
+
import { analyzeJudgeIndependence } from './judge-independence.js';
|
|
32
33
|
/**
|
|
33
34
|
* Below this sample count a non-significant diff is read as UNDERPOWERED
|
|
34
35
|
* (only large effects are detectable) rather than NOISE. Matches the
|
|
@@ -56,11 +57,19 @@ export const ENSEMBLE_DISSENT_PEARSON = 0.4;
|
|
|
56
57
|
* never gated by it — see `computeVerdict`.
|
|
57
58
|
*/
|
|
58
59
|
export const STABILITY_UNSTABLE_CV = 0.15;
|
|
60
|
+
/**
|
|
61
|
+
* Per-layer pass/fail line for the three-layer gate, on the 1-5 scale.
|
|
62
|
+
* **Pragmatic default, not derived from an external standard**: 3.5 is a clear margin
|
|
63
|
+
* above the 3.0 scale midpoint ("basically acceptable"), so a layer must land
|
|
64
|
+
* comfortably in the upper half to pass. Overridable via `omk eval --threshold`.
|
|
65
|
+
* doc ↔ code parity guarded by `test/scripts/doc-constants-drift.test.ts`.
|
|
66
|
+
*/
|
|
67
|
+
export const DEFAULT_GATE_THRESHOLD = 3.5;
|
|
59
68
|
/**
|
|
60
69
|
* Compute a verdict for a finished report. Pure function — no I/O.
|
|
61
70
|
*/
|
|
62
71
|
export function computeVerdict(report, options = {}) {
|
|
63
|
-
const { gateThreshold =
|
|
72
|
+
const { gateThreshold = DEFAULT_GATE_THRESHOLD, triviallySmallDiff = 0.1 } = options;
|
|
64
73
|
const variants = report.meta?.variants ?? [];
|
|
65
74
|
const summary = report.summary ?? {};
|
|
66
75
|
const sampleCount = report.meta?.sampleCount ?? 0;
|
|
@@ -68,15 +77,18 @@ export function computeVerdict(report, options = {}) {
|
|
|
68
77
|
// Single-variant — no comparison possible. Just report whether the variant
|
|
69
78
|
// passes its own three-layer gate.
|
|
70
79
|
const gate = evaluateLayerGates(summary, gateThreshold);
|
|
80
|
+
// SOLO 只有绝对分、无 A/B 差值可抵消自我偏好,故同厂商评委的 caveat 更该出。
|
|
81
|
+
const judgeInd = judgeIndependenceCaveat(report);
|
|
71
82
|
return {
|
|
72
83
|
level: 'SOLO',
|
|
73
|
-
headline: gate.allPass
|
|
84
|
+
headline: (gate.allPass
|
|
74
85
|
? `SOLO · single variant, three-layer gate PASS @ threshold ${gateThreshold}`
|
|
75
|
-
: `SOLO · single variant, three-layer gate FAIL — see ci output
|
|
86
|
+
: `SOLO · single variant, three-layer gate FAIL — see ci output`) + judgeInd.note,
|
|
76
87
|
rationale: {
|
|
77
88
|
layerWinners: gate.lines.join('; '),
|
|
78
89
|
sampleSize: `N=${sampleCount}`,
|
|
79
90
|
stability: formatStability(report),
|
|
91
|
+
...(judgeInd.rationale ? { judgeAgreement: judgeInd.rationale } : {}),
|
|
80
92
|
},
|
|
81
93
|
variants,
|
|
82
94
|
};
|
|
@@ -110,18 +122,19 @@ export function computeVerdict(report, options = {}) {
|
|
|
110
122
|
const stabilityNote = stabilityGated && stab
|
|
111
123
|
? ` · 显著但 run-to-run 不稳(CV=${(stab.cv * 100).toFixed(1)}% > ${(STABILITY_UNSTABLE_CV * 100).toFixed(0)}%)`
|
|
112
124
|
: '';
|
|
125
|
+
const judgeInd = judgeIndependenceCaveat(report);
|
|
113
126
|
const significance = representative
|
|
114
127
|
? formatSignificance(representative)
|
|
115
128
|
: 'no pairwise comparison available — was --bootstrap used?';
|
|
116
129
|
const layerWinners = formatLayerWinners(summary, variants);
|
|
117
130
|
const sampleSize = formatSampleSize(report);
|
|
118
131
|
const stability = formatStability(report);
|
|
119
|
-
const judgeAgreement = formatJudgeAgreement(report);
|
|
132
|
+
const judgeAgreement = [formatJudgeAgreement(report), judgeInd.rationale].filter(Boolean).join(' · ') || undefined;
|
|
120
133
|
const shipRecommendation = recommendation(level, perPair);
|
|
121
134
|
return {
|
|
122
135
|
level,
|
|
123
136
|
headline: representative
|
|
124
|
-
? `${level} · ${representative.treatment} vs ${representative.control}: ${representative.headline}${stabilityNote}`
|
|
137
|
+
? `${level} · ${representative.treatment} vs ${representative.control}: ${representative.headline}${stabilityNote}${judgeInd.note}`
|
|
125
138
|
: `${level} · ${variants.length} variants`,
|
|
126
139
|
perPair,
|
|
127
140
|
rationale: {
|
|
@@ -376,6 +389,31 @@ function formatJudgeAgreement(report) {
|
|
|
376
389
|
: 'poor';
|
|
377
390
|
return `α=${Number.isNaN(a.alpha) ? 'NaN' : a.alpha.toFixed(2)} (${verdict}) vs gold ${a.goldAnnotator}`;
|
|
378
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* 评委独立性 caveat(自我偏好 J1 / 单厂商 ensemble J2)。返回 { note, rationale }:
|
|
394
|
+
* note —— 追到 headline 的短提示(仅未 gold 校准时出,提醒读者敞口);
|
|
395
|
+
* rationale —— 并进 rationale.judgeAgreement 的可执行说明(指向跨厂商评委 / gold)。
|
|
396
|
+
* **不改 verdict level**:omk 固定模型,自我偏好对 baseline / treatment 两臂同等加成、在 verdict
|
|
397
|
+
* 在意的 A/B 差值里大幅抵消,不该翻 ship/no-ship;真正受影响的是绝对分 / 版本曲线 / 跨模型比较。
|
|
398
|
+
*/
|
|
399
|
+
function judgeIndependenceCaveat(report) {
|
|
400
|
+
const ind = analyzeJudgeIndependence(report);
|
|
401
|
+
const reasons = [];
|
|
402
|
+
if (ind.sameVendorJudge)
|
|
403
|
+
reasons.push(`评委与被测输出同厂商(${ind.outputVendors.join('/')})`);
|
|
404
|
+
if (ind.singleVendorEnsemble)
|
|
405
|
+
reasons.push(`${ind.judgeVendors.length} 个评委同厂商,ensemble 一致性不反驳同模型偏置`);
|
|
406
|
+
if (reasons.length === 0)
|
|
407
|
+
return { note: '' };
|
|
408
|
+
if (ind.goldCalibrated) {
|
|
409
|
+
// 有 gold 校准背板 → 不进 headline,只软提示。
|
|
410
|
+
return { note: '', rationale: `自我偏好敞口(${reasons.join(';')})已有 gold 校准背板` };
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
note: ' · 评委自我偏好敞口未校准',
|
|
414
|
+
rationale: `${reasons.join(';')} —— 绝对分可能偏高;换跨厂商评委(--judge-models)或挂 gold(omk eval gold compare)校准`,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
379
417
|
function recommendation(level, _perPair) {
|
|
380
418
|
switch (level) {
|
|
381
419
|
case 'PROGRESS':
|
|
@@ -52,7 +52,7 @@ interface CompletedBatchSkillRun {
|
|
|
52
52
|
* 导致 #183 角色误绑各存一份)。
|
|
53
53
|
* 两个 variant 的 allowedSkills 都从 eval.yaml variants[].allowedSkills 取:treatment 按 skill
|
|
54
54
|
* 名 entry.name 查、baseline 按保留名 `baseline` 查,挂到对应 spec 上由 prepareEvaluationRun
|
|
55
|
-
* 统一绑定。baseline 的显式声明(
|
|
55
|
+
* 统一绑定。baseline 的显式声明(`[]`)必须保留——eval.yaml variant.allowedSkills
|
|
56
56
|
* 优先于 strictBaseline 默认,漏挂会让 `--batch --config` 的 baseline 隔离配置静默失效。 */
|
|
57
57
|
export declare function buildBatchVariantSpecs(entry: {
|
|
58
58
|
name: string;
|
|
@@ -10,7 +10,7 @@ import { DEFAULT_JOBS_DIR } from '../eval-core/default-dirs.js';
|
|
|
10
10
|
* 导致 #183 角色误绑各存一份)。
|
|
11
11
|
* 两个 variant 的 allowedSkills 都从 eval.yaml variants[].allowedSkills 取:treatment 按 skill
|
|
12
12
|
* 名 entry.name 查、baseline 按保留名 `baseline` 查,挂到对应 spec 上由 prepareEvaluationRun
|
|
13
|
-
* 统一绑定。baseline 的显式声明(
|
|
13
|
+
* 统一绑定。baseline 的显式声明(`[]`)必须保留——eval.yaml variant.allowedSkills
|
|
14
14
|
* 优先于 strictBaseline 默认,漏挂会让 `--batch --config` 的 baseline 隔离配置静默失效。 */
|
|
15
15
|
export function buildBatchVariantSpecs(entry, variantAllowedSkills) {
|
|
16
16
|
const baselineAllowed = variantAllowedSkills?.baseline;
|
|
@@ -98,7 +98,6 @@ export function buildBatchEvaluationReport({ batchRunId, skillDir, skillEntries,
|
|
|
98
98
|
timeoutMs,
|
|
99
99
|
noCache,
|
|
100
100
|
dryRun: false,
|
|
101
|
-
blind: false,
|
|
102
101
|
project,
|
|
103
102
|
owner,
|
|
104
103
|
tags,
|
|
@@ -9,20 +9,16 @@
|
|
|
9
9
|
* - 可选 gapReports: 文本信号(markers / hedging)始终适用,与 tool trace 无关;
|
|
10
10
|
* 带上 testSetHash 水印(spec §7.1 强制要求)
|
|
11
11
|
*
|
|
12
|
-
* 最后一步 `applyBlindMode` 是 blind 模式下的字段脱敏,放在所有 analysis 之后,
|
|
13
|
-
* 避免脱敏后字段被分析逻辑读取。
|
|
14
|
-
*
|
|
15
12
|
* 仅被 orchestrator 调用;独立拆出主要为让 orchestrator 的 try-finally 主干
|
|
16
13
|
* 看起来纯粹是「执行→收尾」时序。
|
|
17
14
|
*/
|
|
18
15
|
import type { Artifact, Report, Sample, VariantResult } from '../../types/index.js';
|
|
19
16
|
type EvaluationResults = Record<string, Record<string, VariantResult>>;
|
|
20
|
-
export declare function finalizeEvaluationReport({ report, results, artifacts, variantNames,
|
|
17
|
+
export declare function finalizeEvaluationReport({ report, results, artifacts, variantNames, samplesPath, samplesSourceFiles, samples, }: {
|
|
21
18
|
report: Report;
|
|
22
19
|
results: EvaluationResults;
|
|
23
20
|
artifacts: Artifact[];
|
|
24
21
|
variantNames: string[];
|
|
25
|
-
blind: boolean;
|
|
26
22
|
samplesPath: string;
|
|
27
23
|
/** 目录模式下,bundle 内所有源文件;单文件模式下 [samplesPath]。computeTestSetHash 用。 */
|
|
28
24
|
samplesSourceFiles?: string[];
|
|
@@ -9,18 +9,14 @@
|
|
|
9
9
|
* - 可选 gapReports: 文本信号(markers / hedging)始终适用,与 tool trace 无关;
|
|
10
10
|
* 带上 testSetHash 水印(spec §7.1 强制要求)
|
|
11
11
|
*
|
|
12
|
-
* 最后一步 `applyBlindMode` 是 blind 模式下的字段脱敏,放在所有 analysis 之后,
|
|
13
|
-
* 避免脱敏后字段被分析逻辑读取。
|
|
14
|
-
*
|
|
15
12
|
* 仅被 orchestrator 调用;独立拆出主要为让 orchestrator 的 try-finally 主干
|
|
16
13
|
* 看起来纯粹是「执行→收尾」时序。
|
|
17
14
|
*/
|
|
18
15
|
import { analyzeResults } from '../../analysis/report-diagnostics.js';
|
|
19
16
|
import { computeReportCoverage } from '../../analysis/coverage-analyzer.js';
|
|
20
17
|
import { computeReportGapRates } from '../../analysis/gap-analyzer.js';
|
|
21
|
-
import { applyBlindMode } from '../../eval-core/evaluation-reporting.js';
|
|
22
18
|
import { computeTestSetHash } from './test-set-hash.js';
|
|
23
|
-
export function finalizeEvaluationReport({ report, results, artifacts, variantNames,
|
|
19
|
+
export function finalizeEvaluationReport({ report, results, artifacts, variantNames, samplesPath, samplesSourceFiles, samples, }) {
|
|
24
20
|
// pass samples so analyzeResults can populate analysis.sampleQuality
|
|
25
21
|
// (capability/difficulty/construct/provenance coverage aggregate). Without
|
|
26
22
|
// samples, analysis.sampleQuality is omitted (老报告读取仍可工作).
|
|
@@ -49,8 +45,5 @@ export function finalizeEvaluationReport({ report, results, artifacts, variantNa
|
|
|
49
45
|
}
|
|
50
46
|
report.analysis.gapReports = gapReports;
|
|
51
47
|
}
|
|
52
|
-
if (blind) {
|
|
53
|
-
applyBlindMode(report, variantNames, `${variantNames.join(',')}:${samplesPath}`);
|
|
54
|
-
}
|
|
55
48
|
return report;
|
|
56
49
|
}
|