oh-my-knowledge 0.28.0 → 0.30.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 +49 -3
- package/README.zh.md +49 -3
- package/dist/src/analysis/failure-clusterer.js +1 -1
- package/dist/src/analysis/failure-clusterer.js.map +1 -1
- package/dist/src/analysis/gap-analyzer.d.ts +8 -1
- package/dist/src/analysis/gap-analyzer.d.ts.map +1 -1
- package/dist/src/analysis/gap-analyzer.js +65 -33
- package/dist/src/analysis/gap-analyzer.js.map +1 -1
- package/dist/src/analysis/report-diagnostics.js +2 -0
- package/dist/src/analysis/report-diagnostics.js.map +1 -1
- package/dist/src/analysis/sample-diagnostics.js +14 -14
- package/dist/src/analysis/sample-diagnostics.js.map +1 -1
- package/dist/src/authoring/evolver.d.ts +7 -1
- package/dist/src/authoring/evolver.d.ts.map +1 -1
- package/dist/src/authoring/evolver.js +8 -5
- package/dist/src/authoring/evolver.js.map +1 -1
- package/dist/src/authoring/generator.d.ts +21 -23
- package/dist/src/authoring/generator.d.ts.map +1 -1
- package/dist/src/authoring/generator.js +621 -40
- package/dist/src/authoring/generator.js.map +1 -1
- package/dist/src/authoring/sample-fixer.d.ts +45 -0
- package/dist/src/authoring/sample-fixer.d.ts.map +1 -0
- package/dist/src/authoring/sample-fixer.js +186 -0
- package/dist/src/authoring/sample-fixer.js.map +1 -0
- package/dist/src/cli/commands/evolve.d.ts.map +1 -1
- package/dist/src/cli/commands/evolve.js +17 -0
- package/dist/src/cli/commands/evolve.js.map +1 -1
- package/dist/src/cli/commands/observe.d.ts.map +1 -1
- package/dist/src/cli/commands/observe.js +170 -0
- package/dist/src/cli/commands/observe.js.map +1 -1
- package/dist/src/cli/commands/registry.d.ts.map +1 -1
- package/dist/src/cli/commands/registry.js +9 -1
- package/dist/src/cli/commands/registry.js.map +1 -1
- package/dist/src/cli/commands/sample.d.ts +13 -0
- package/dist/src/cli/commands/sample.d.ts.map +1 -1
- package/dist/src/cli/commands/sample.js +307 -13
- package/dist/src/cli/commands/sample.js.map +1 -1
- package/dist/src/cli/commands/studio.d.ts.map +1 -1
- package/dist/src/cli/commands/studio.js +8 -0
- package/dist/src/cli/commands/studio.js.map +1 -1
- package/dist/src/cli/i18n-dict.d.ts +1 -1
- package/dist/src/cli/i18n-dict.d.ts.map +1 -1
- package/dist/src/cli/i18n-dict.js +189 -17
- package/dist/src/cli/i18n-dict.js.map +1 -1
- package/dist/src/cli/parse-run-config.d.ts +14 -2
- package/dist/src/cli/parse-run-config.d.ts.map +1 -1
- package/dist/src/cli/parse-run-config.js +62 -10
- package/dist/src/cli/parse-run-config.js.map +1 -1
- package/dist/src/doctor/health/builtin-dimensions.d.ts.map +1 -1
- package/dist/src/doctor/health/builtin-dimensions.js +20 -23
- package/dist/src/doctor/health/builtin-dimensions.js.map +1 -1
- package/dist/src/doctor/health/composer.d.ts.map +1 -1
- package/dist/src/doctor/health/composer.js +7 -3
- package/dist/src/doctor/health/composer.js.map +1 -1
- package/dist/src/doctor/health/dimension-spec.d.ts +1 -0
- package/dist/src/doctor/health/dimension-spec.d.ts.map +1 -1
- package/dist/src/doctor/health/parser.d.ts.map +1 -1
- package/dist/src/doctor/health/parser.js +1 -0
- package/dist/src/doctor/health/parser.js.map +1 -1
- package/dist/src/doctor/health/prompt-builder.js +25 -17
- package/dist/src/doctor/health/prompt-builder.js.map +1 -1
- package/dist/src/doctor/html-renderer.d.ts.map +1 -1
- package/dist/src/doctor/html-renderer.js +76 -66
- package/dist/src/doctor/html-renderer.js.map +1 -1
- package/dist/src/doctor/rules.d.ts.map +1 -1
- package/dist/src/doctor/rules.js +32 -0
- package/dist/src/doctor/rules.js.map +1 -1
- package/dist/src/eval-core/cache.d.ts +16 -2
- package/dist/src/eval-core/cache.d.ts.map +1 -1
- package/dist/src/eval-core/cache.js +86 -15
- package/dist/src/eval-core/cache.js.map +1 -1
- package/dist/src/eval-core/evaluation-execution.d.ts +11 -1
- package/dist/src/eval-core/evaluation-execution.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-execution.js +78 -6
- package/dist/src/eval-core/evaluation-execution.js.map +1 -1
- package/dist/src/eval-core/evaluation-job.d.ts +2 -1
- package/dist/src/eval-core/evaluation-job.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-job.js +2 -1
- package/dist/src/eval-core/evaluation-job.js.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.d.ts +7 -0
- package/dist/src/eval-core/evaluation-reporting.d.ts.map +1 -1
- package/dist/src/eval-core/evaluation-reporting.js +19 -2
- package/dist/src/eval-core/evaluation-reporting.js.map +1 -1
- package/dist/src/eval-core/execution-strategy.d.ts +1 -1
- package/dist/src/eval-core/execution-strategy.d.ts.map +1 -1
- package/dist/src/eval-core/execution-strategy.js +12 -1
- package/dist/src/eval-core/execution-strategy.js.map +1 -1
- package/dist/src/eval-core/mock-hook.cjs +203 -0
- package/dist/src/eval-core/mocks-runtime.d.ts +96 -0
- package/dist/src/eval-core/mocks-runtime.d.ts.map +1 -0
- package/dist/src/eval-core/mocks-runtime.js +323 -0
- package/dist/src/eval-core/mocks-runtime.js.map +1 -0
- package/dist/src/eval-core/schema.d.ts.map +1 -1
- package/dist/src/eval-core/schema.js +18 -5
- package/dist/src/eval-core/schema.js.map +1 -1
- package/dist/src/eval-core/task-planner.d.ts +9 -1
- package/dist/src/eval-core/task-planner.d.ts.map +1 -1
- package/dist/src/eval-core/task-planner.js +40 -1
- package/dist/src/eval-core/task-planner.js.map +1 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.d.ts +5 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.d.ts.map +1 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.js +2 -1
- package/dist/src/eval-workflows/batch-evaluation-workflow.js.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts +22 -1
- package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +1 -1
- package/dist/src/eval-workflows/evaluation-pipeline.js +39 -12
- package/dist/src/eval-workflows/evaluation-pipeline.js.map +1 -1
- package/dist/src/eval-workflows/evaluation-preparation.d.ts +5 -0
- package/dist/src/eval-workflows/evaluation-preparation.d.ts.map +1 -1
- package/dist/src/eval-workflows/evaluation-preparation.js +3 -1
- package/dist/src/eval-workflows/evaluation-preparation.js.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.d.ts +12 -2
- package/dist/src/eval-workflows/run-evaluation.d.ts.map +1 -1
- package/dist/src/eval-workflows/run-evaluation.js +17 -5
- package/dist/src/eval-workflows/run-evaluation.js.map +1 -1
- package/dist/src/executors/claude-cli.d.ts +1 -1
- package/dist/src/executors/claude-cli.d.ts.map +1 -1
- package/dist/src/executors/claude-cli.js +57 -6
- package/dist/src/executors/claude-cli.js.map +1 -1
- package/dist/src/executors/claude-sdk-trace.d.ts.map +1 -1
- package/dist/src/executors/claude-sdk-trace.js +10 -4
- package/dist/src/executors/claude-sdk-trace.js.map +1 -1
- package/dist/src/executors/claude-sdk.d.ts +1 -1
- package/dist/src/executors/claude-sdk.d.ts.map +1 -1
- package/dist/src/executors/claude-sdk.js +25 -2
- package/dist/src/executors/claude-sdk.js.map +1 -1
- package/dist/src/executors/shared.d.ts +1 -1
- package/dist/src/executors/shared.d.ts.map +1 -1
- package/dist/src/executors/shared.js +6 -1
- package/dist/src/executors/shared.js.map +1 -1
- package/dist/src/grading/assertions.d.ts +5 -0
- package/dist/src/grading/assertions.d.ts.map +1 -1
- package/dist/src/grading/assertions.js +27 -1
- package/dist/src/grading/assertions.js.map +1 -1
- package/dist/src/grading/debias-validate.js +4 -4
- package/dist/src/grading/debias-validate.js.map +1 -1
- package/dist/src/grading/diagnostic.d.ts +40 -0
- package/dist/src/grading/diagnostic.d.ts.map +1 -0
- package/dist/src/grading/diagnostic.js +450 -0
- package/dist/src/grading/diagnostic.js.map +1 -0
- package/dist/src/grading/gold-cli.js +2 -2
- package/dist/src/grading/gold-cli.js.map +1 -1
- package/dist/src/grading/index.d.ts +5 -0
- package/dist/src/grading/index.d.ts.map +1 -1
- package/dist/src/grading/judge.d.ts.map +1 -1
- package/dist/src/grading/judge.js +54 -2
- package/dist/src/grading/judge.js.map +1 -1
- package/dist/src/grading/layered-scores.d.ts.map +1 -1
- package/dist/src/grading/layered-scores.js +1 -0
- package/dist/src/grading/layered-scores.js.map +1 -1
- package/dist/src/inputs/eval-config.js +19 -0
- package/dist/src/inputs/eval-config.js.map +1 -1
- package/dist/src/inputs/load-samples.d.ts +22 -2
- package/dist/src/inputs/load-samples.d.ts.map +1 -1
- package/dist/src/inputs/load-samples.js +193 -22
- package/dist/src/inputs/load-samples.js.map +1 -1
- package/dist/src/inputs/skill-loader.d.ts.map +1 -1
- package/dist/src/inputs/skill-loader.js +11 -3
- package/dist/src/inputs/skill-loader.js.map +1 -1
- package/dist/src/observability/experience.d.ts +260 -0
- package/dist/src/observability/experience.d.ts.map +1 -0
- package/dist/src/observability/experience.js +1233 -0
- package/dist/src/observability/experience.js.map +1 -0
- package/dist/src/observability/inbox-view-model.d.ts +27 -0
- package/dist/src/observability/inbox-view-model.d.ts.map +1 -0
- package/dist/src/observability/inbox-view-model.js +135 -0
- package/dist/src/observability/inbox-view-model.js.map +1 -0
- package/dist/src/observability/inbox.d.ts +125 -0
- package/dist/src/observability/inbox.d.ts.map +1 -0
- package/dist/src/observability/inbox.js +741 -0
- package/dist/src/observability/inbox.js.map +1 -0
- package/dist/src/observability/problem-patterns.d.ts +51 -0
- package/dist/src/observability/problem-patterns.d.ts.map +1 -0
- package/dist/src/observability/problem-patterns.js +205 -0
- package/dist/src/observability/problem-patterns.js.map +1 -0
- package/dist/src/observability/review-state.d.ts +54 -0
- package/dist/src/observability/review-state.d.ts.map +1 -0
- package/dist/src/observability/review-state.js +131 -0
- package/dist/src/observability/review-state.js.map +1 -0
- package/dist/src/observability/skill-chain-advisories.d.ts +25 -0
- package/dist/src/observability/skill-chain-advisories.d.ts.map +1 -0
- package/dist/src/observability/skill-chain-advisories.js +69 -0
- package/dist/src/observability/skill-chain-advisories.js.map +1 -0
- package/dist/src/observability/skill-chain.d.ts +61 -0
- package/dist/src/observability/skill-chain.d.ts.map +1 -0
- package/dist/src/observability/skill-chain.js +233 -0
- package/dist/src/observability/skill-chain.js.map +1 -0
- package/dist/src/observability/text-signals.d.ts +7 -0
- package/dist/src/observability/text-signals.d.ts.map +1 -0
- package/dist/src/observability/text-signals.js +22 -0
- package/dist/src/observability/text-signals.js.map +1 -0
- package/dist/src/observability/trace-adapter.d.ts +12 -64
- package/dist/src/observability/trace-adapter.d.ts.map +1 -1
- package/dist/src/observability/trace-adapter.js +9 -355
- package/dist/src/observability/trace-adapter.js.map +1 -1
- package/dist/src/observability/trace-attribution.d.ts +56 -0
- package/dist/src/observability/trace-attribution.d.ts.map +1 -0
- package/dist/src/observability/trace-attribution.js +200 -0
- package/dist/src/observability/trace-attribution.js.map +1 -0
- package/dist/src/observability/trace-segmenter.d.ts +52 -0
- package/dist/src/observability/trace-segmenter.d.ts.map +1 -0
- package/dist/src/observability/trace-segmenter.js +320 -0
- package/dist/src/observability/trace-segmenter.js.map +1 -0
- package/dist/src/observability/trace-source.d.ts +99 -0
- package/dist/src/observability/trace-source.d.ts.map +1 -0
- package/dist/src/observability/trace-source.js +492 -0
- package/dist/src/observability/trace-source.js.map +1 -0
- package/dist/src/renderer/html-renderer.d.ts.map +1 -1
- package/dist/src/renderer/html-renderer.js +324 -218
- package/dist/src/renderer/html-renderer.js.map +1 -1
- package/dist/src/renderer/layout.d.ts.map +1 -1
- package/dist/src/renderer/layout.js +155 -71
- package/dist/src/renderer/layout.js.map +1 -1
- package/dist/src/renderer/observation-inbox-renderer.d.ts +4 -0
- package/dist/src/renderer/observation-inbox-renderer.d.ts.map +1 -0
- package/dist/src/renderer/observation-inbox-renderer.js +5376 -0
- package/dist/src/renderer/observation-inbox-renderer.js.map +1 -0
- package/dist/src/renderer/skill-detail-renderer.d.ts +15 -0
- package/dist/src/renderer/skill-detail-renderer.d.ts.map +1 -0
- package/dist/src/renderer/skill-detail-renderer.js +1234 -0
- package/dist/src/renderer/skill-detail-renderer.js.map +1 -0
- package/dist/src/renderer/skill-list-renderer.d.ts +4 -0
- package/dist/src/renderer/skill-list-renderer.d.ts.map +1 -0
- package/dist/src/renderer/skill-list-renderer.js +327 -0
- package/dist/src/renderer/skill-list-renderer.js.map +1 -0
- package/dist/src/renderer/summary.d.ts +17 -0
- package/dist/src/renderer/summary.d.ts.map +1 -1
- package/dist/src/renderer/summary.js +346 -74
- package/dist/src/renderer/summary.js.map +1 -1
- package/dist/src/renderer/table.d.ts.map +1 -1
- package/dist/src/renderer/table.js +213 -167
- package/dist/src/renderer/table.js.map +1 -1
- package/dist/src/renderer/test-view.d.ts +66 -0
- package/dist/src/renderer/test-view.d.ts.map +1 -0
- package/dist/src/renderer/test-view.js +905 -0
- package/dist/src/renderer/test-view.js.map +1 -0
- package/dist/src/renderer/trends.d.ts.map +1 -1
- package/dist/src/renderer/trends.js +3 -1
- package/dist/src/renderer/trends.js.map +1 -1
- package/dist/src/server/report-server.d.ts +6 -1
- package/dist/src/server/report-server.d.ts.map +1 -1
- package/dist/src/server/report-server.js +182 -6
- package/dist/src/server/report-server.js.map +1 -1
- package/dist/src/server/report-store.d.ts.map +1 -1
- package/dist/src/server/report-store.js +33 -1
- package/dist/src/server/report-store.js.map +1 -1
- package/dist/src/server/skill-index.d.ts +77 -0
- package/dist/src/server/skill-index.d.ts.map +1 -0
- package/dist/src/server/skill-index.js +331 -0
- package/dist/src/server/skill-index.js.map +1 -0
- package/dist/src/server/skill-insights.d.ts +92 -0
- package/dist/src/server/skill-insights.d.ts.map +1 -0
- package/dist/src/server/skill-insights.js +676 -0
- package/dist/src/server/skill-insights.js.map +1 -0
- package/dist/src/shared/hard-rules.d.ts +40 -0
- package/dist/src/shared/hard-rules.d.ts.map +1 -0
- package/dist/src/shared/hard-rules.js +200 -0
- package/dist/src/shared/hard-rules.js.map +1 -0
- package/dist/src/shared/time.d.ts +2 -0
- package/dist/src/shared/time.d.ts.map +1 -0
- package/dist/src/shared/time.js +10 -0
- package/dist/src/shared/time.js.map +1 -0
- package/dist/src/shared/tool-search.d.ts +9 -0
- package/dist/src/shared/tool-search.d.ts.map +1 -0
- package/dist/src/shared/tool-search.js +104 -0
- package/dist/src/shared/tool-search.js.map +1 -0
- package/dist/src/types/eval.d.ts +83 -0
- package/dist/src/types/eval.d.ts.map +1 -1
- package/dist/src/types/executor.d.ts +47 -0
- package/dist/src/types/executor.d.ts.map +1 -1
- package/dist/src/types/judge.d.ts +76 -0
- package/dist/src/types/judge.d.ts.map +1 -1
- package/dist/src/types/judge.js +3 -1
- package/dist/src/types/judge.js.map +1 -1
- package/dist/src/types/report.d.ts +63 -3
- package/dist/src/types/report.d.ts.map +1 -1
- package/dist/src/util/safe-slice.d.ts +23 -0
- package/dist/src/util/safe-slice.d.ts.map +1 -0
- package/dist/src/util/safe-slice.js +33 -0
- package/dist/src/util/safe-slice.js.map +1 -0
- package/package.json +5 -4
|
@@ -20,11 +20,11 @@ function verdictOneLine(level, lang, treatment, control) {
|
|
|
20
20
|
const c = control ?? (lang === 'zh' ? '对照组' : 'control');
|
|
21
21
|
if (lang === 'zh') {
|
|
22
22
|
switch (level) {
|
|
23
|
-
case 'PROGRESS': return `${t} 比 ${c}
|
|
24
|
-
case 'CAUTIOUS': return `${t} 比 ${c}
|
|
25
|
-
case 'REGRESS': return `${t} 比 ${c}
|
|
26
|
-
case 'NOISE': return `${t} 和 ${c}
|
|
27
|
-
case 'UNDERPOWERED': return
|
|
23
|
+
case 'PROGRESS': return `${t} 比 ${c} 明显更好——可以发布`;
|
|
24
|
+
case 'CAUTIOUS': return `${t} 比 ${c} 略好——但建议再仔细看,差距很小或某层未达标`;
|
|
25
|
+
case 'REGRESS': return `${t} 比 ${c} 明显更差——不要发布`;
|
|
26
|
+
case 'NOISE': return `${t} 和 ${c} 没看出明显差别——可以多加几条用例再试`;
|
|
27
|
+
case 'UNDERPOWERED': return `评测用例数太少,看不出 ${t} 和 ${c} 的差别——多跑几个再看`;
|
|
28
28
|
case 'SOLO': return `只跑了一组,需要加对照组才能对比`;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -76,17 +76,38 @@ export function levelTooltip(level, lang) {
|
|
|
76
76
|
case 'SOLO': return 'Single variant — no comparison';
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
//
|
|
80
|
-
// 1
|
|
81
|
-
// 2
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
// Verdict hero — verdict 是报告的「答案」,应在头部抢眼可读:
|
|
80
|
+
// 行 1: 状态 badge (明显进步 / PROGRESS) + 自然语言句子 (含 ship action)
|
|
81
|
+
// 行 2: 分差 + 评测规模 — 最直观的两个数字, "差多少 / 跑了多少"
|
|
82
|
+
// machine-readable enum 通过 data-verdict-level 属性挂在 section 上,给 CI/工具用;
|
|
83
|
+
// ship-action token 写进 aria-label 给 screen reader,但不放进 badge 显示文字 ——
|
|
84
|
+
// 因为 verdictOneLine 的中文句子已经包含 "可以发布 / 不要发布" 这类 action,
|
|
85
|
+
// badge 再放一遍是重复。CI / CV 这种统计学专名挪到 variance 表 + tooltip,hero
|
|
86
|
+
// 不堆 jargon. 想看精确边界值的工程师下滑到下方的配对对比 / 波动检验表里看。
|
|
87
|
+
// computeVerdict 在历史混合批量 report 上有 NPE 风险,try/catch 让 renderer 不 crash。
|
|
88
|
+
const SHIP_LABEL = {
|
|
89
|
+
PROGRESS: { zh: '可发布', en: 'SHIP' },
|
|
90
|
+
CAUTIOUS: { zh: '需排查', en: 'INVESTIGATE' },
|
|
91
|
+
REGRESS: { zh: '勿发布', en: 'DO NOT SHIP' },
|
|
92
|
+
NOISE: { zh: '不下结论', en: 'NO CALL' },
|
|
93
|
+
UNDERPOWERED: { zh: '数据不足', en: 'INSUFFICIENT DATA' },
|
|
94
|
+
SOLO: { zh: '缺对照', en: 'ADD CONTROL' },
|
|
95
|
+
};
|
|
96
|
+
function computeMedianCVPercent(report) {
|
|
97
|
+
const variance = report.variance;
|
|
98
|
+
if (!variance || (variance.runs ?? 0) < 2)
|
|
99
|
+
return null;
|
|
100
|
+
const cvs = [];
|
|
101
|
+
for (const v of Object.values(variance.perVariant ?? {})) {
|
|
102
|
+
if (typeof v.stddev === 'number' && typeof v.mean === 'number' && v.mean > 0) {
|
|
103
|
+
cvs.push((v.stddev / v.mean) * 100);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (cvs.length === 0)
|
|
107
|
+
return null;
|
|
108
|
+
cvs.sort((a, b) => a - b);
|
|
109
|
+
return cvs[Math.floor(cvs.length / 2)];
|
|
110
|
+
}
|
|
90
111
|
export function renderVerdictPill(report, lang) {
|
|
91
112
|
let result;
|
|
92
113
|
try {
|
|
@@ -100,13 +121,39 @@ export function renderVerdictPill(report, lang) {
|
|
|
100
121
|
const oneLine = verdictOneLine(level, lang, pair?.treatment, pair?.control);
|
|
101
122
|
const tooltip = levelTooltip(level, lang);
|
|
102
123
|
const prefix = lang === 'zh' ? '测评结论' : 'Verdict';
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
124
|
+
// 机器可读 enum 永远是 level token; 显示给用户的文字按 lang i18n.
|
|
125
|
+
const levelDisplay = lang === 'zh' ? levelLabel(level, lang) : level;
|
|
126
|
+
const shipAria = lang === 'zh' ? SHIP_LABEL[level].zh : SHIP_LABEL[level].en;
|
|
127
|
+
// hero 只放「答案」: 分差是 verdict 的核心证据数字, 单独一枚 chip。
|
|
128
|
+
// 评测规模 (用例数 × 轮次) 走「实验配置」section 的 subtitle 那条 canonical 路径,
|
|
129
|
+
// 不在 hero 里重复; CV / CI 走 chip tooltip + 方法学审计 / 波动表。
|
|
130
|
+
const ci = report.meta?.pairComparisons?.[0]?.diffBootstrapCI;
|
|
131
|
+
const cvPct = computeMedianCVPercent(report);
|
|
132
|
+
const metrics = [];
|
|
133
|
+
if (ci) {
|
|
134
|
+
const sign = ci.estimate >= 0 ? '+' : '';
|
|
135
|
+
const cvSuffix = cvPct != null
|
|
136
|
+
? (lang === 'zh' ? `;多轮稳定性 CV=${cvPct.toFixed(1)}% (${cvPct < 5 ? '稳' : cvPct < 15 ? '中' : '不稳'})` : `; CV=${cvPct.toFixed(1)}% (${cvPct < 5 ? 'stable' : cvPct < 15 ? 'moderate' : 'unstable'})`)
|
|
137
|
+
: '';
|
|
138
|
+
const ciTipBase = lang === 'zh'
|
|
139
|
+
? `实验组与对照组综合分均值差(Δ)。bootstrap 95% 可信区间 [${ci.low}, ${ci.high}],${ci.significant ? '不含 0 = 差异显著' : '跨过 0 = 差异不显著'}${cvSuffix}`
|
|
140
|
+
: `Treatment minus control mean composite score (Δ). Bootstrap 95% CI [${ci.low}, ${ci.high}], ${ci.significant ? 'excludes 0 ⇒ significant' : 'spans 0 ⇒ not significant'}${cvSuffix}`;
|
|
141
|
+
metrics.push({
|
|
142
|
+
label: lang === 'zh' ? '分差' : 'Δ',
|
|
143
|
+
value: `${sign}${ci.estimate}`,
|
|
144
|
+
tip: ciTipBase,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const metricChips = metrics.map((m) => `<span class="verdict-metric"${m.tip ? ` title="${e(m.tip)}"` : ''}>` +
|
|
148
|
+
`<span class="verdict-metric-label">${e(m.label)}</span>` +
|
|
149
|
+
`<span class="verdict-metric-value">${e(m.value)}</span></span>`).join('');
|
|
150
|
+
return `<section class="page-verdict verdict-${level}" role="status" data-verdict-level="${e(level)}" aria-label="${e(prefix)}: ${e(levelDisplay)} · ${e(shipAria)} · ${e(oneLine)}" title="${e(tooltip)}">
|
|
151
|
+
<div class="page-verdict-head">
|
|
152
|
+
<span class="page-verdict-badge"><span class="page-verdict-badge-dot" aria-hidden="true">●</span>${e(levelDisplay)}</span>
|
|
153
|
+
<span class="page-verdict-text">${e(oneLine)}</span>
|
|
154
|
+
</div>
|
|
155
|
+
${metricChips ? `<div class="page-verdict-metrics">${metricChips}</div>` : ''}
|
|
156
|
+
</section>`;
|
|
110
157
|
}
|
|
111
158
|
/**
|
|
112
159
|
* Pairwise diff (treatment vs control) bootstrap CI table — populated only when
|
|
@@ -170,11 +217,11 @@ export function renderHumanAgreement(agreement, lang) {
|
|
|
170
217
|
const interpret = lang === 'zh'
|
|
171
218
|
? (Number.isNaN(a.alpha)
|
|
172
219
|
? '评分多样性不足,α 未定义'
|
|
173
|
-
: a.alpha >= 0.8 ? '
|
|
174
|
-
: a.alpha >= 0.667 ? '
|
|
175
|
-
: a.alpha >= 0.4 ? '
|
|
176
|
-
: a.alpha >= 0 ? '
|
|
177
|
-
: '
|
|
220
|
+
: a.alpha >= 0.8 ? '高度一致——结论可放心使用'
|
|
221
|
+
: a.alpha >= 0.667 ? '可接受——谨慎结论'
|
|
222
|
+
: a.alpha >= 0.4 ? '较弱一致——结论需配合 CI 与人工抽检'
|
|
223
|
+
: a.alpha >= 0 ? '偏差较大——排查 rubric / prompt'
|
|
224
|
+
: '系统性反向——重新审视判分逻辑')
|
|
178
225
|
: (Number.isNaN(a.alpha)
|
|
179
226
|
? 'insufficient rating variance for α'
|
|
180
227
|
: a.alpha >= 0.8 ? 'high agreement — conclusions trustworthy'
|
|
@@ -323,7 +370,10 @@ export function renderSummaryCards(variants, summary, lang, variance) {
|
|
|
323
370
|
//
|
|
324
371
|
// composite 合成分 (= (fact + behavior + judge) / 3) 从 v0.16 起不再在此表主视觉呈现,
|
|
325
372
|
// 仅保留在 report JSON 数据层 + Variance & Significance 表顶层 flat 字段(legacy)。
|
|
326
|
-
//
|
|
373
|
+
// 综合分放在第一列, 紧贴 实验分组. 它是 fact/behavior/judge 三层的等权均值,
|
|
374
|
+
// 在 omk 内部承担「跨 run 排序 / bootstrap CI / verdict 比较信号」三个角色 ——
|
|
375
|
+
// 这里展示 + 表头 ? button 弹 modal 公开计算方式 + 局限, 让用户知情消费。
|
|
376
|
+
// 其余六列保持 layer-first (fact / behavior / judge / cost / efficiency / stability)。
|
|
327
377
|
const headerCols = [
|
|
328
378
|
{ key: 'dimFact', label: t('dimFact', lang) },
|
|
329
379
|
{ key: 'dimBehavior', label: t('dimBehavior', lang) },
|
|
@@ -332,18 +382,23 @@ export function renderSummaryCards(variants, summary, lang, variance) {
|
|
|
332
382
|
{ key: 'dimEfficiency', label: t('dimEfficiency', lang) },
|
|
333
383
|
{ key: 'dimStability', label: t('dimStability', lang) },
|
|
334
384
|
];
|
|
385
|
+
const scoringModalId = 'guide-scoring';
|
|
386
|
+
// v0.30 — 拆 hero(每变体综合分一行)+ heatmap 表(去 composite,加色块)。
|
|
387
|
+
// composite 从表里挪出来当 hero,大字号 + delta 跟 baseline 对比;
|
|
388
|
+
// 维度细节继续用表,但每格按 score 浅色填充,便于扫读弱维度。
|
|
335
389
|
const thead = `<tr><th data-i18n="variants">${t('variants', lang)}</th>${headerCols.map((c) => `<th data-i18n="${c.key}">${c.label}</th>`).join('')}</tr>`;
|
|
336
|
-
// 渲染单层分数 cell(事实/行为/LLM 评价通用)
|
|
337
|
-
// 优先读跨 run 均值(byLayer[key].mean),fallback 到 summary 的单 run avg。
|
|
338
|
-
// 缺数据时显示 "—" + 灰色,让读者明确看到"这一层这批用例/评委没测到",不假装有值。
|
|
390
|
+
// 渲染单层分数 cell(事实/行为/LLM 评价通用)— 加 heatmap 浅色背景
|
|
339
391
|
function renderLayerCell(varianceMean, summaryValue, detailHtml = '') {
|
|
340
392
|
const v = varianceMean ?? summaryValue;
|
|
341
393
|
const hasValue = typeof v === 'number' && v > 0;
|
|
394
|
+
const heatClass = hasValue
|
|
395
|
+
? (v >= 4 ? 'sm-heat-pass' : v >= 3 ? 'sm-heat-warn' : 'sm-heat-fail')
|
|
396
|
+
: '';
|
|
342
397
|
const color = hasValue
|
|
343
398
|
? (v >= 4 ? 'var(--green)' : v >= 3 ? 'var(--yellow)' : 'var(--red)')
|
|
344
399
|
: 'var(--text-muted)';
|
|
345
400
|
const display = hasValue ? v.toFixed(2) : '—';
|
|
346
|
-
return `<td class="summary-cell"><div class="summary-value summary-value-primary" style="color:${color}">${display}</div>${detailHtml}</td>`;
|
|
401
|
+
return `<td class="summary-cell ${heatClass}"><div class="summary-value summary-value-primary" style="color:${color}">${display}</div>${detailHtml}</td>`;
|
|
347
402
|
}
|
|
348
403
|
const rows = variants.map((v, i) => {
|
|
349
404
|
const s = summary[v] || {};
|
|
@@ -451,6 +506,41 @@ export function renderSummaryCards(variants, summary, lang, variance) {
|
|
|
451
506
|
const stabCell = `<td class="summary-cell"><div class="summary-value" style="color:${stabColor}">${stabValue}</div>${stabDetail}</td>`;
|
|
452
507
|
return `<tr><td style="border-left:3px solid ${color};padding-left:12px"><strong>${e(v)}</strong></td>${factCell}${behaviorCell}${judgeCell}${costCell}${effCell}${stabCell}</tr>`;
|
|
453
508
|
}).join('');
|
|
509
|
+
// ──────────── Hero 行:每 variant 一行,综合分大字号 + delta vs baseline ────────────
|
|
510
|
+
// 每变体一行,左 variant 名 + 颜色条;中 大字号 composite;右 delta(vs variants[0])
|
|
511
|
+
const baselineComposite = summary[variants[0]]?.avgCompositeScore;
|
|
512
|
+
const heroRows = variants.map((v, i) => {
|
|
513
|
+
const s = summary[v] || {};
|
|
514
|
+
const composite = s.avgCompositeScore;
|
|
515
|
+
const compositeHasValue = typeof composite === 'number' && composite > 0;
|
|
516
|
+
const compositeColor = compositeHasValue
|
|
517
|
+
? (composite >= 4 ? 'var(--green)' : composite >= 3 ? 'var(--yellow)' : 'var(--red)')
|
|
518
|
+
: 'var(--text-muted)';
|
|
519
|
+
const compositeDisplay = compositeHasValue ? composite.toFixed(2) : '—';
|
|
520
|
+
const color = COLORS[i % COLORS.length];
|
|
521
|
+
let deltaHtml = '';
|
|
522
|
+
if (i > 0 && compositeHasValue && typeof baselineComposite === 'number' && baselineComposite > 0) {
|
|
523
|
+
const diff = composite - baselineComposite;
|
|
524
|
+
if (Math.abs(diff) >= 0.01) {
|
|
525
|
+
const sign = diff > 0 ? '+' : '';
|
|
526
|
+
const dColor = diff > 0 ? 'var(--green)' : 'var(--red)';
|
|
527
|
+
const arrow = diff > 0 ? '↑' : '↓';
|
|
528
|
+
deltaHtml = `<span class="sm-hero-delta" style="color:${dColor}">${sign}${diff.toFixed(2)} ${arrow}</span>`;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return `<div class="sm-hero" style="border-left:3px solid ${color}">
|
|
532
|
+
<div class="sm-hero-name">${e(v)}</div>
|
|
533
|
+
<div class="sm-hero-score" style="color:${compositeColor}">${compositeDisplay}<span class="sm-hero-unit">/ 5</span></div>
|
|
534
|
+
${deltaHtml || '<span class="sm-hero-delta-placeholder"></span>'}
|
|
535
|
+
</div>`;
|
|
536
|
+
}).join('');
|
|
537
|
+
const heroBlock = `<div class="sm-hero-list">
|
|
538
|
+
<div class="sm-hero-h">
|
|
539
|
+
${lang === 'zh' ? '综合分' : 'Composite'}
|
|
540
|
+
<button type="button" class="hint-btn" onclick="openModal('${scoringModalId}')" aria-label="${e(lang === 'zh' ? '综合分怎么算的?' : 'How is composite computed?')}" aria-haspopup="dialog">?</button>
|
|
541
|
+
</div>
|
|
542
|
+
${heroRows}
|
|
543
|
+
</div>`;
|
|
454
544
|
const guideModalId = 'guide-six-dims';
|
|
455
545
|
const guideTitle = lang === 'zh' ? '如何阅读六维对比?' : 'How to read this six-dimension comparison?';
|
|
456
546
|
const guideIntro = lang === 'zh'
|
|
@@ -487,6 +577,7 @@ export function renderSummaryCards(variants, summary, lang, variance) {
|
|
|
487
577
|
<tr><td ${sub}>CV</td><td ${subDesc}>Score swing as a percentage of the mean (e.g., CV 2% = swings are about 2% of the mean)</td></tr>
|
|
488
578
|
<tr><td ${sub}>95% CI</td><td ${subDesc}>If you ran infinitely many times, the true mean has a 95% chance of falling in this range — narrower = you can trust the measured mean more</td></tr>
|
|
489
579
|
`;
|
|
580
|
+
const scoringModalHtml = renderScoringModal(scoringModalId, lang);
|
|
490
581
|
return `
|
|
491
582
|
<h2 style="display:flex;align-items:center;gap:4px">${lang === 'zh' ? '六维对比' : 'Six-Dimension Comparison'} <button type="button" class="hint-btn" onclick="openModal('${guideModalId}')" aria-label="${e(guideTitle)}" aria-haspopup="dialog">?</button></h2>
|
|
492
583
|
<div id="${guideModalId}" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="${guideModalId}-title" onclick="if(event.target===this)closeModal('${guideModalId}')">
|
|
@@ -499,13 +590,94 @@ export function renderSummaryCards(variants, summary, lang, variance) {
|
|
|
499
590
|
<table class="modal-table"><tbody>${guideRows}</tbody></table>
|
|
500
591
|
</div>
|
|
501
592
|
</div>
|
|
593
|
+
${scoringModalHtml}
|
|
594
|
+
${heroBlock}
|
|
502
595
|
<div class="table-wrap">
|
|
503
596
|
<table class="summary-table">
|
|
504
597
|
<thead>${thead}</thead>
|
|
505
598
|
<tbody>${rows}</tbody>
|
|
506
599
|
</table>
|
|
507
600
|
</div>
|
|
508
|
-
|
|
601
|
+
<style>
|
|
602
|
+
/* 六维对比 — Hero 区(每 variant 综合分 + delta) */
|
|
603
|
+
.sm-hero-list { display:flex;flex-direction:column;gap:8px;margin:14px 0 18px }
|
|
604
|
+
.sm-hero-h { font-size:13px;color:var(--text-muted);margin-bottom:4px;letter-spacing:0.02em }
|
|
605
|
+
.sm-hero { display:flex;align-items:baseline;gap:16px;padding:14px 18px;background:var(--bg-surface);border-radius:var(--radius);box-shadow:var(--shadow-sm) }
|
|
606
|
+
.sm-hero-name { flex:1;font-size:14.5px;font-weight:500;color:var(--text-primary) }
|
|
607
|
+
.sm-hero-score { font-size:28px;font-weight:600;font-variant-numeric:tabular-nums;line-height:1 }
|
|
608
|
+
.sm-hero-unit { font-size:13px;color:var(--text-muted);margin-left:4px;font-weight:400 }
|
|
609
|
+
.sm-hero-delta { font-size:14px;font-weight:600;font-variant-numeric:tabular-nums;font-family:"SF Mono",Menlo,monospace;min-width:60px;text-align:right }
|
|
610
|
+
.sm-hero-delta-placeholder { display:inline-block;min-width:60px }
|
|
611
|
+
/* 六维 heatmap — 每格按分数浅色填充,便于远看识别弱维度 */
|
|
612
|
+
.sm-heat-pass { background:linear-gradient(0deg,var(--green-bg),var(--green-bg)) }
|
|
613
|
+
.sm-heat-warn { background:linear-gradient(0deg,var(--yellow-bg),var(--yellow-bg)) }
|
|
614
|
+
.sm-heat-fail { background:linear-gradient(0deg,var(--red-bg),var(--red-bg)) }
|
|
615
|
+
</style>`;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* 评分说明 modal — 公开 composite 计算方式 + 局限,所有展示综合分的地方都通向同一份说明
|
|
619
|
+
* (六维对比表头 / 列表页 score 列 / 趋势页 chart caption)。
|
|
620
|
+
*
|
|
621
|
+
* 测量学诚实性 ≠ 藏起来怕用户误用,而是展示 + 说清楚边界。这个 modal 是简版 (~10 行),
|
|
622
|
+
* 完整推导 (五层评分管道架构 / ratioToScore 公式 / 多层 gate 与 composite 关系)
|
|
623
|
+
* 在 docs/zh/scoring.md。
|
|
624
|
+
*/
|
|
625
|
+
export function renderScoringModal(id, lang) {
|
|
626
|
+
const title = lang === 'zh' ? '综合分怎么算的?' : 'How is composite computed?';
|
|
627
|
+
const intro = lang === 'zh'
|
|
628
|
+
? '综合分(composite)= 等权均值({事实, 行为, LLM 评价})。1-5 制。'
|
|
629
|
+
: 'Composite = unweighted mean of {fact, behavior, LLM judge}. Scale 1-5.';
|
|
630
|
+
const sectionHead = (text) => `<tr><td colspan="2" style="padding:14px 0 6px;color:var(--text-primary);font-weight:600;font-size:13px;border-top:1px solid var(--border)">${e(text)}</td></tr>`;
|
|
631
|
+
const row = (label, body) => `<tr><td style="padding:6px 12px 6px 0;color:var(--text-secondary);font-size:12px;font-weight:500;white-space:nowrap;vertical-align:top;width:120px">${e(label)}</td><td style="padding:6px 0;color:var(--text-secondary);font-size:12px;line-height:1.6">${body}</td></tr>`;
|
|
632
|
+
const calcRows = lang === 'zh' ? [
|
|
633
|
+
row('事实 / 行为', '断言通过率经 <code>1 + 通过率 × 4</code> 线性映射到 1-5 制'),
|
|
634
|
+
row('LLM 评价', '评委模型直接给 1-5 分'),
|
|
635
|
+
row('缺失某层', '自动降维(参与计算的层取均值)'),
|
|
636
|
+
] : [
|
|
637
|
+
row('Fact / Behavior', 'Assertion pass-rate mapped to 1-5 via <code>1 + ratio × 4</code>'),
|
|
638
|
+
row('LLM judge', 'Judge model returns 1-5 score directly'),
|
|
639
|
+
row('Missing layer', 'Auto-collapse (mean over present layers)'),
|
|
640
|
+
];
|
|
641
|
+
const limitRows = lang === 'zh' ? [
|
|
642
|
+
row('① 等权聚合', '三层各 1/3 权重不是从需求 derive 的,无显式构造效度论证'),
|
|
643
|
+
row('② 量尺不一致', '事实是 binary 通过率 stretch 到 1-5;评委是真序数评分。直接均值违反量表理论(measurement scale homogeneity)'),
|
|
644
|
+
row('③ 缺失自动降维', '不同 variant / skill 配的层数不同时,综合分数字相同但 construct 不同,<strong>不可机械跨 variant / 跨 skill 比较</strong>'),
|
|
645
|
+
] : [
|
|
646
|
+
row('① Equal-weight aggregation', 'The 1/3 weight per layer is ad hoc, not derived from stakeholder needs; no explicit construct validity argument'),
|
|
647
|
+
row('② Mixed scales added directly', 'Fact is binary pass-rate stretched to 1-5; judge is true ordinal. Direct mean violates measurement scale homogeneity'),
|
|
648
|
+
row('③ Auto-collapse on missing', 'When variants/skills have different layer coverage, composite numbers look same but represent different constructs — <strong>not mechanically comparable across variants/skills</strong>'),
|
|
649
|
+
];
|
|
650
|
+
const usageRows = lang === 'zh' ? [
|
|
651
|
+
row('✓ 适用', '同一份 eval-samples 上 A/B 比较,看「分差 + bootstrap CI + 三层独立 gate」联合判断'),
|
|
652
|
+
row('✗ 不适用', '当作 absolute psychometric measure(说「这 skill 4.28/5 分」)'),
|
|
653
|
+
] : [
|
|
654
|
+
row('✓ Use for', 'A/B comparison on same eval-samples — read "diff + bootstrap CI + per-layer gate" jointly'),
|
|
655
|
+
row('✗ Don\'t use as', 'Absolute psychometric measure (claiming "this skill is 4.28/5")'),
|
|
656
|
+
];
|
|
657
|
+
const calcSection = lang === 'zh' ? '怎么算的' : 'How it\'s computed';
|
|
658
|
+
const limitSection = lang === 'zh' ? '局限(直白说)' : 'Limitations (frankly)';
|
|
659
|
+
const usageSection = lang === 'zh' ? '推荐用法' : 'Recommended usage';
|
|
660
|
+
const docsLink = lang === 'zh'
|
|
661
|
+
? '完整推导 / 五层评分管道架构 / 多层 gate 与综合分的关系:<a href="https://github.com/lizhiyao/oh-my-knowledge/blob/main/docs/zh/scoring.md" target="_blank" rel="noopener">docs/zh/scoring.md</a>'
|
|
662
|
+
: 'Full derivation / five-layer scoring pipeline / multi-layer gate & composite relationship: <a href="https://github.com/lizhiyao/oh-my-knowledge/blob/main/docs/zh/scoring.md" target="_blank" rel="noopener">docs/zh/scoring.md</a>';
|
|
663
|
+
return `<div id="${id}" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="${id}-title" onclick="if(event.target===this)closeModal('${id}')">
|
|
664
|
+
<div class="modal-content">
|
|
665
|
+
<div class="modal-header">
|
|
666
|
+
<strong id="${id}-title" style="font-size:1rem">${e(title)}</strong>
|
|
667
|
+
<button type="button" class="modal-close" onclick="closeModal('${id}')" aria-label="${lang === 'zh' ? '关闭' : 'Close'}">✕</button>
|
|
668
|
+
</div>
|
|
669
|
+
<p style="font-size:13px;color:var(--text-secondary);margin:4px 0 12px">${e(intro)}</p>
|
|
670
|
+
<table class="modal-table"><tbody>
|
|
671
|
+
${sectionHead(calcSection)}
|
|
672
|
+
${calcRows.join('')}
|
|
673
|
+
${sectionHead(limitSection)}
|
|
674
|
+
${limitRows.join('')}
|
|
675
|
+
${sectionHead(usageSection)}
|
|
676
|
+
${usageRows.join('')}
|
|
677
|
+
</tbody></table>
|
|
678
|
+
<p style="font-size:11px;color:var(--text-muted);margin:14px 0 0;line-height:1.6">${docsLink}</p>
|
|
679
|
+
</div>
|
|
680
|
+
</div>`;
|
|
509
681
|
}
|
|
510
682
|
/**
|
|
511
683
|
* Cross-sample inter-judge agreement table — only renders when at least one variant
|
|
@@ -513,7 +685,7 @@ export function renderSummaryCards(variants, summary, lang, variance) {
|
|
|
513
685
|
* Pearson + MAD across the whole sample set, the metric that refutes "Claude judge
|
|
514
686
|
* Claude same-modality bias".
|
|
515
687
|
*/
|
|
516
|
-
function renderJudgeAgreementBlock(variants, summary, lang) {
|
|
688
|
+
export function renderJudgeAgreementBlock(variants, summary, lang) {
|
|
517
689
|
const variantsWithEnsemble = variants.filter((v) => summary[v]?.judgeAgreement);
|
|
518
690
|
if (variantsWithEnsemble.length === 0)
|
|
519
691
|
return '';
|
|
@@ -551,6 +723,116 @@ function renderJudgeAgreementBlock(variants, summary, lang) {
|
|
|
551
723
|
</table>
|
|
552
724
|
</div>`;
|
|
553
725
|
}
|
|
726
|
+
function badgeIcon(status) {
|
|
727
|
+
switch (status) {
|
|
728
|
+
case 'pass': return '✓';
|
|
729
|
+
case 'warn': return '⚠';
|
|
730
|
+
case 'fail': return '✗';
|
|
731
|
+
case 'skip': return '—';
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function computeJudgeAgreementBadge(variants, summary, lang) {
|
|
735
|
+
const treatment = variants[1] ?? variants[0];
|
|
736
|
+
const ag = summary[treatment]?.judgeAgreement;
|
|
737
|
+
if (!ag || ag.pearson == null)
|
|
738
|
+
return null;
|
|
739
|
+
const p = ag.pearson;
|
|
740
|
+
const status = p >= 0.7 ? 'pass' : p >= 0.4 ? 'warn' : 'fail';
|
|
741
|
+
const verdict = status === 'pass'
|
|
742
|
+
? (lang === 'zh' ? '评委一致' : 'judges agree')
|
|
743
|
+
: status === 'warn'
|
|
744
|
+
? (lang === 'zh' ? '评委偏弱一致' : 'judges weakly agree')
|
|
745
|
+
: (lang === 'zh' ? '评委分歧大' : 'judges diverge');
|
|
746
|
+
return {
|
|
747
|
+
key: 'judge',
|
|
748
|
+
label: verdict,
|
|
749
|
+
status,
|
|
750
|
+
detail: lang === 'zh' ? `Pearson ${p}(≥0.7 一致 / 0.4-0.7 偏弱 / <0.4 分歧)` : `Pearson ${p} (≥0.7 agree / 0.4-0.7 weak / <0.4 diverge)`,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function computeSignificanceBadge(report, lang) {
|
|
754
|
+
const ci = report.meta?.pairComparisons?.[0]?.diffBootstrapCI;
|
|
755
|
+
if (!ci)
|
|
756
|
+
return null;
|
|
757
|
+
const status = ci.significant ? 'pass' : 'warn';
|
|
758
|
+
const label = ci.significant
|
|
759
|
+
? (lang === 'zh' ? '差异显著' : 'significant')
|
|
760
|
+
: (lang === 'zh' ? '差异不显著' : 'not significant');
|
|
761
|
+
return {
|
|
762
|
+
key: 'sig',
|
|
763
|
+
label,
|
|
764
|
+
status,
|
|
765
|
+
detail: lang === 'zh' ? `bootstrap 95% CI [${ci.low}, ${ci.high}]${ci.significant ? '(不含 0)' : '(跨过 0)'}` : `Bootstrap 95% CI [${ci.low}, ${ci.high}]${ci.significant ? ' (excludes 0)' : ' (spans 0)'}`,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
function computeSaturationBadge(report, variants, lang) {
|
|
769
|
+
const sat = report.variance?.saturation?.verdicts;
|
|
770
|
+
if (!sat)
|
|
771
|
+
return null;
|
|
772
|
+
const treatment = variants[1] ?? variants[0];
|
|
773
|
+
const v = sat[treatment];
|
|
774
|
+
if (!v)
|
|
775
|
+
return null;
|
|
776
|
+
const status = v.saturated
|
|
777
|
+
? (v.confidence === 'high' ? 'pass' : 'warn')
|
|
778
|
+
: 'warn';
|
|
779
|
+
const label = v.saturated
|
|
780
|
+
? (lang === 'zh' ? '已饱和' : 'saturated')
|
|
781
|
+
: (lang === 'zh' ? '未饱和' : 'not saturated');
|
|
782
|
+
return {
|
|
783
|
+
key: 'sat',
|
|
784
|
+
label,
|
|
785
|
+
status,
|
|
786
|
+
detail: lang === 'zh'
|
|
787
|
+
? `${v.saturated ? `于 N=${v.atN ?? '?'} 饱和` : '尚未饱和'}(${v.confidence} confidence)`
|
|
788
|
+
: `${v.saturated ? `saturates at N=${v.atN ?? '?'}` : 'not yet saturated'} (${v.confidence} confidence)`,
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
function computeHumanAlignmentBadge(report, lang) {
|
|
792
|
+
const a = report.meta?.humanAgreement;
|
|
793
|
+
if (!a || a.sampleCount === 0 || Number.isNaN(a.alpha))
|
|
794
|
+
return null;
|
|
795
|
+
const status = a.alpha >= 0.667 ? 'pass' : a.alpha >= 0.4 ? 'warn' : 'fail';
|
|
796
|
+
const label = status === 'pass'
|
|
797
|
+
? (lang === 'zh' ? '人工对齐' : 'aligns with humans')
|
|
798
|
+
: status === 'warn'
|
|
799
|
+
? (lang === 'zh' ? '人工对齐偏弱' : 'weakly aligns')
|
|
800
|
+
: (lang === 'zh' ? '与人工分歧' : 'diverges from humans');
|
|
801
|
+
return {
|
|
802
|
+
key: 'human',
|
|
803
|
+
label,
|
|
804
|
+
status,
|
|
805
|
+
detail: lang === 'zh' ? `Krippendorff α=${a.alpha.toFixed(3)} vs ${a.goldAnnotator}` : `Krippendorff α=${a.alpha.toFixed(3)} vs ${a.goldAnnotator}`,
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
export function renderMethodologyAudit(report, variants, summary, lang) {
|
|
809
|
+
const judgeBlock = renderJudgeAgreementBlock(variants, summary, lang);
|
|
810
|
+
const pairwiseBlock = renderPairwiseDiff(report.meta?.pairComparisons, lang);
|
|
811
|
+
const humanBlock = renderHumanAgreement(report.meta?.humanAgreement, lang);
|
|
812
|
+
const saturationBlock = renderSaturationCurve(report.variance?.saturation, variants, lang);
|
|
813
|
+
if (!judgeBlock && !pairwiseBlock && !humanBlock && !saturationBlock)
|
|
814
|
+
return '';
|
|
815
|
+
const badges = [
|
|
816
|
+
computeJudgeAgreementBadge(variants, summary, lang),
|
|
817
|
+
computeSignificanceBadge(report, lang),
|
|
818
|
+
computeSaturationBadge(report, variants, lang),
|
|
819
|
+
computeHumanAlignmentBadge(report, lang),
|
|
820
|
+
].filter((b) => b !== null);
|
|
821
|
+
// 默认折叠;只有 ✗ fail (红色 badge) 才强制展开 — summary 行已经显示
|
|
822
|
+
// ⚠ warn 状态, 用户能自决是否展开; 但 fail 是危险信号必须让用户看证据。
|
|
823
|
+
const hasFailure = badges.some((b) => b.status === 'fail');
|
|
824
|
+
const openAttr = hasFailure ? ' open' : '';
|
|
825
|
+
const summaryLabel = lang === 'zh' ? '测评可信度' : 'Reliability check';
|
|
826
|
+
const summaryHint = lang === 'zh' ? '点击展开看支撑证据(评委一致 / 差异显著 / 饱和度 / 人工对齐)' : 'click to expand evidence (judge agreement / significance / saturation / human alignment)';
|
|
827
|
+
const badgesHtml = badges.length > 0
|
|
828
|
+
? `<span class="methodology-badges">${badges.map((b) => `<span class="methodology-badge methodology-badge-${b.status}" title="${e(b.detail)}">${badgeIcon(b.status)} ${e(b.label)}</span>`).join('')}</span>`
|
|
829
|
+
: '';
|
|
830
|
+
const innerSections = [judgeBlock, pairwiseBlock, humanBlock, saturationBlock].filter(Boolean).join('');
|
|
831
|
+
return `<details class="methodology-audit"${openAttr}>
|
|
832
|
+
<summary><span class="methodology-summary-label">${e(summaryLabel)}</span>${badgesHtml}<span class="methodology-summary-hint">${e(summaryHint)}</span></summary>
|
|
833
|
+
<div class="methodology-body">${innerSections}</div>
|
|
834
|
+
</details>`;
|
|
835
|
+
}
|
|
554
836
|
function buildDiagnostic(metric, cfg, winner, lang) {
|
|
555
837
|
const es = metric.effectSize;
|
|
556
838
|
if (!es || es.primary === 'none') {
|
|
@@ -1203,56 +1485,39 @@ export function renderAgentOverview(variants, summary, lang) {
|
|
|
1203
1485
|
const hasAgentData = variants.some((variant) => summary[variant]?.avgToolCalls != null && summary[variant].avgToolCalls > 0);
|
|
1204
1486
|
if (!hasAgentData)
|
|
1205
1487
|
return '';
|
|
1206
|
-
|
|
1488
|
+
// v0.30 — Agent 执行概览弱化为 ctx-row(单行 inline)样式,跟实验配置并排放在 context-strip 里。
|
|
1489
|
+
// 主要数字:轮次 / 工具调用数 / 成功率,加 tooltip 显示工具分布。
|
|
1490
|
+
const variantRows = variants.map((variant, i) => {
|
|
1207
1491
|
const stats = summary[variant];
|
|
1208
1492
|
if (!stats)
|
|
1209
1493
|
return '';
|
|
1210
1494
|
const color = COLORS[i % COLORS.length];
|
|
1211
1495
|
const avgTools = stats.avgToolCalls ?? 0;
|
|
1212
|
-
const successRate = stats.toolSuccessRate != null ? `${(stats.toolSuccessRate * 100).toFixed(0)}%` : '
|
|
1496
|
+
const successRate = stats.toolSuccessRate != null ? `${(stats.toolSuccessRate * 100).toFixed(0)}%` : '—';
|
|
1213
1497
|
const successRateColor = (stats.toolSuccessRate ?? 1) >= 0.8 ? 'var(--green)' : 'var(--red)';
|
|
1214
1498
|
const turns = stats.avgFullNumTurns ?? stats.avgNumTurns ?? 0;
|
|
1215
1499
|
const distributionEntries = Object.entries(stats.toolDistribution || {}).sort((a, b) => b[1] - a[1]);
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
</
|
|
1224
|
-
<span
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px">
|
|
1230
|
-
<div>
|
|
1231
|
-
<div style="font-size:11px;color:var(--text-muted)">${t('agentAvgTurns', lang)}</div>
|
|
1232
|
-
<div style="font-size:20px;font-weight:600">${turns}</div>
|
|
1233
|
-
</div>
|
|
1234
|
-
<div>
|
|
1235
|
-
<div style="font-size:11px;color:var(--text-muted)">${t('agentAvgTools', lang)}</div>
|
|
1236
|
-
<div style="font-size:20px;font-weight:600">${avgTools}</div>
|
|
1237
|
-
</div>
|
|
1238
|
-
<div>
|
|
1239
|
-
<div style="font-size:11px;color:var(--text-muted)">${t('agentToolSuccess', lang)}</div>
|
|
1240
|
-
<div style="font-size:20px;font-weight:600;color:${successRateColor}">${successRate}</div>
|
|
1241
|
-
</div>
|
|
1242
|
-
</div>
|
|
1243
|
-
${distributionEntries.length > 0 ? `
|
|
1244
|
-
<div style="font-size:11px;color:var(--text-muted);margin-bottom:4px">${t('agentToolDist', lang)}</div>
|
|
1245
|
-
${distributionBars}
|
|
1246
|
-
` : ''}
|
|
1500
|
+
const distSummary = distributionEntries.length > 0
|
|
1501
|
+
? distributionEntries.slice(0, 5).map(([tool, count]) => `${tool}×${count}`).join(' · ')
|
|
1502
|
+
: '';
|
|
1503
|
+
const tooltip = distSummary ? `${t('agentToolDist', lang)}: ${distSummary}` : '';
|
|
1504
|
+
return `<div class="ctx-row" style="border-left:3px solid ${color}"${tooltip ? ` title="${e(tooltip)}"` : ''}>
|
|
1505
|
+
<span class="ctx-row-name">${e(variant)}</span>
|
|
1506
|
+
<span class="ctx-row-bits">
|
|
1507
|
+
<span>${turns} ${t('turnsPerReq', lang)}</span>
|
|
1508
|
+
<span class="ctx-sep">·</span>
|
|
1509
|
+
<span>${avgTools} ${lang === 'zh' ? '工具/次' : 'tools/req'}</span>
|
|
1510
|
+
<span class="ctx-sep">·</span>
|
|
1511
|
+
<span style="color:${successRateColor}">${successRate} OK</span>
|
|
1512
|
+
</span>
|
|
1247
1513
|
</div>`;
|
|
1248
1514
|
}).join('');
|
|
1249
1515
|
return `
|
|
1250
|
-
<
|
|
1251
|
-
<
|
|
1252
|
-
<div
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
</section>
|
|
1516
|
+
<div class="ctx-block">
|
|
1517
|
+
<div class="ctx-h">${t('agentOverview', lang)}</div>
|
|
1518
|
+
<div class="ctx-sub">${lang === 'zh' ? '工具调用统计 · 轮次 / 调用数 / 成功率' : 'Tool call stats · turns / calls / success rate'}</div>
|
|
1519
|
+
<div class="ctx-rows">${variantRows}</div>
|
|
1520
|
+
</div>
|
|
1256
1521
|
`;
|
|
1257
1522
|
}
|
|
1258
1523
|
export function renderCoverageSection(coverage, lang) {
|
|
@@ -1343,6 +1608,13 @@ export function renderKnowledgeInteractionSection(coverage, gapReports, lang) {
|
|
|
1343
1608
|
const hasGap = gapReports && Object.keys(gapReports).length > 0;
|
|
1344
1609
|
if (!hasCov && !hasGap)
|
|
1345
1610
|
return '';
|
|
1611
|
+
// 所有 variant 的 coverage 都不可用 (filesTotal === 0) 且所有 gap 都是 0%(无信号)
|
|
1612
|
+
// 时,整个 section 没有可读信息 — 渲染只会占大段纵向空间显示「数据不可用 / 0%」
|
|
1613
|
+
// 干扰用户阅读流。直接跳过比强行展示「显式无信号」更尊重读者。
|
|
1614
|
+
const usefulCoverage = hasCov && Object.values(coverage).some((c) => c.filesTotal > 0);
|
|
1615
|
+
const usefulGap = hasGap && Object.values(gapReports).some((g) => g.gapRate > 0 || g.signals.length > 0);
|
|
1616
|
+
if (!usefulCoverage && !usefulGap)
|
|
1617
|
+
return '';
|
|
1346
1618
|
const title = lang === 'zh' ? '本次测评:评测用例 × 知识库' : 'This Evaluation: Test Set × Knowledge Base';
|
|
1347
1619
|
const desc = lang === 'zh'
|
|
1348
1620
|
? '展示本次评测用例和知识库的交互画像——用到哪些知识(使用情况)· 哪些知识想找但没找到或任务执行模型表达不确定(盲区)。'
|