oh-my-knowledge 0.0.1 → 0.18.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 (215) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +537 -332
  3. package/dist/src/analysis/coverage-analyzer.d.ts +57 -0
  4. package/dist/src/analysis/coverage-analyzer.d.ts.map +1 -0
  5. package/dist/src/analysis/coverage-analyzer.js +262 -0
  6. package/dist/src/analysis/coverage-analyzer.js.map +1 -0
  7. package/dist/src/analysis/gap-analyzer.d.ts +114 -0
  8. package/dist/src/analysis/gap-analyzer.d.ts.map +1 -0
  9. package/dist/src/analysis/gap-analyzer.js +439 -0
  10. package/dist/src/analysis/gap-analyzer.js.map +1 -0
  11. package/dist/src/analysis/hedging-classifier.d.ts +34 -0
  12. package/dist/src/analysis/hedging-classifier.d.ts.map +1 -0
  13. package/dist/src/analysis/hedging-classifier.js +144 -0
  14. package/dist/src/analysis/hedging-classifier.js.map +1 -0
  15. package/dist/src/analysis/report-diagnostics.d.ts +9 -0
  16. package/dist/src/analysis/report-diagnostics.d.ts.map +1 -0
  17. package/dist/src/analysis/report-diagnostics.js +631 -0
  18. package/dist/src/analysis/report-diagnostics.js.map +1 -0
  19. package/dist/src/authoring/evolver.d.ts +64 -0
  20. package/dist/src/authoring/evolver.d.ts.map +1 -0
  21. package/dist/src/authoring/evolver.js +275 -0
  22. package/dist/src/authoring/evolver.js.map +1 -0
  23. package/dist/src/authoring/generator.d.ts +13 -0
  24. package/dist/src/authoring/generator.d.ts.map +1 -0
  25. package/dist/src/authoring/generator.js +60 -0
  26. package/dist/src/authoring/generator.js.map +1 -0
  27. package/dist/src/cli.d.ts +3 -0
  28. package/dist/src/cli.d.ts.map +1 -0
  29. package/dist/src/cli.js +1004 -0
  30. package/dist/src/cli.js.map +1 -0
  31. package/dist/src/eval-core/cache.d.ts +11 -0
  32. package/dist/src/eval-core/cache.d.ts.map +1 -0
  33. package/dist/src/eval-core/cache.js +53 -0
  34. package/dist/src/eval-core/cache.js.map +1 -0
  35. package/dist/src/eval-core/ci-gates.d.ts +17 -0
  36. package/dist/src/eval-core/ci-gates.d.ts.map +1 -0
  37. package/dist/src/eval-core/ci-gates.js +42 -0
  38. package/dist/src/eval-core/ci-gates.js.map +1 -0
  39. package/dist/src/eval-core/dependency-checker.d.ts +37 -0
  40. package/dist/src/eval-core/dependency-checker.d.ts.map +1 -0
  41. package/dist/src/eval-core/dependency-checker.js +280 -0
  42. package/dist/src/eval-core/dependency-checker.js.map +1 -0
  43. package/dist/src/eval-core/evaluation-execution.d.ts +26 -0
  44. package/dist/src/eval-core/evaluation-execution.d.ts.map +1 -0
  45. package/dist/src/eval-core/evaluation-execution.js +196 -0
  46. package/dist/src/eval-core/evaluation-execution.js.map +1 -0
  47. package/dist/src/eval-core/evaluation-job.d.ts +48 -0
  48. package/dist/src/eval-core/evaluation-job.d.ts.map +1 -0
  49. package/dist/src/eval-core/evaluation-job.js +112 -0
  50. package/dist/src/eval-core/evaluation-job.js.map +1 -0
  51. package/dist/src/eval-core/evaluation-reporting.d.ts +28 -0
  52. package/dist/src/eval-core/evaluation-reporting.d.ts.map +1 -0
  53. package/dist/src/eval-core/evaluation-reporting.js +123 -0
  54. package/dist/src/eval-core/evaluation-reporting.js.map +1 -0
  55. package/dist/src/eval-core/execution-strategy.d.ts +11 -0
  56. package/dist/src/eval-core/execution-strategy.d.ts.map +1 -0
  57. package/dist/src/eval-core/execution-strategy.js +121 -0
  58. package/dist/src/eval-core/execution-strategy.js.map +1 -0
  59. package/dist/src/eval-core/fact-checker.d.ts +25 -0
  60. package/dist/src/eval-core/fact-checker.d.ts.map +1 -0
  61. package/dist/src/eval-core/fact-checker.js +66 -0
  62. package/dist/src/eval-core/fact-checker.js.map +1 -0
  63. package/dist/src/eval-core/schema.d.ts +30 -0
  64. package/dist/src/eval-core/schema.d.ts.map +1 -0
  65. package/dist/src/eval-core/schema.js +195 -0
  66. package/dist/src/eval-core/schema.js.map +1 -0
  67. package/dist/src/eval-core/statistics.d.ts +86 -0
  68. package/dist/src/eval-core/statistics.d.ts.map +1 -0
  69. package/dist/src/eval-core/statistics.js +210 -0
  70. package/dist/src/eval-core/statistics.js.map +1 -0
  71. package/dist/src/eval-core/task-planner.d.ts +4 -0
  72. package/dist/src/eval-core/task-planner.d.ts.map +1 -0
  73. package/dist/src/eval-core/task-planner.js +33 -0
  74. package/dist/src/eval-core/task-planner.js.map +1 -0
  75. package/dist/src/eval-workflows/each-evaluation-workflow.d.ts +133 -0
  76. package/dist/src/eval-workflows/each-evaluation-workflow.d.ts.map +1 -0
  77. package/dist/src/eval-workflows/each-evaluation-workflow.js +169 -0
  78. package/dist/src/eval-workflows/each-evaluation-workflow.js.map +1 -0
  79. package/dist/src/eval-workflows/evaluation-pipeline.d.ts +38 -0
  80. package/dist/src/eval-workflows/evaluation-pipeline.d.ts.map +1 -0
  81. package/dist/src/eval-workflows/evaluation-pipeline.js +214 -0
  82. package/dist/src/eval-workflows/evaluation-pipeline.js.map +1 -0
  83. package/dist/src/eval-workflows/evaluation-preparation.d.ts +64 -0
  84. package/dist/src/eval-workflows/evaluation-preparation.d.ts.map +1 -0
  85. package/dist/src/eval-workflows/evaluation-preparation.js +90 -0
  86. package/dist/src/eval-workflows/evaluation-preparation.js.map +1 -0
  87. package/dist/src/eval-workflows/run-evaluation.d.ts +105 -0
  88. package/dist/src/eval-workflows/run-evaluation.d.ts.map +1 -0
  89. package/dist/src/eval-workflows/run-evaluation.js +264 -0
  90. package/dist/src/eval-workflows/run-evaluation.js.map +1 -0
  91. package/dist/src/executors/anthropic-api.d.ts +3 -0
  92. package/dist/src/executors/anthropic-api.d.ts.map +1 -0
  93. package/dist/src/executors/anthropic-api.js +43 -0
  94. package/dist/src/executors/anthropic-api.js.map +1 -0
  95. package/dist/src/executors/claude-cli.d.ts +3 -0
  96. package/dist/src/executors/claude-cli.d.ts.map +1 -0
  97. package/dist/src/executors/claude-cli.js +103 -0
  98. package/dist/src/executors/claude-cli.js.map +1 -0
  99. package/dist/src/executors/claude-sdk-trace.d.ts +10 -0
  100. package/dist/src/executors/claude-sdk-trace.d.ts.map +1 -0
  101. package/dist/src/executors/claude-sdk-trace.js +100 -0
  102. package/dist/src/executors/claude-sdk-trace.js.map +1 -0
  103. package/dist/src/executors/claude-sdk.d.ts +3 -0
  104. package/dist/src/executors/claude-sdk.d.ts.map +1 -0
  105. package/dist/src/executors/claude-sdk.js +160 -0
  106. package/dist/src/executors/claude-sdk.js.map +1 -0
  107. package/dist/src/executors/gemini.d.ts +3 -0
  108. package/dist/src/executors/gemini.d.ts.map +1 -0
  109. package/dist/src/executors/gemini.js +78 -0
  110. package/dist/src/executors/gemini.js.map +1 -0
  111. package/dist/src/executors/index.d.ts +7 -0
  112. package/dist/src/executors/index.d.ts.map +1 -0
  113. package/dist/src/executors/index.js +22 -0
  114. package/dist/src/executors/index.js.map +1 -0
  115. package/dist/src/executors/openai-api.d.ts +3 -0
  116. package/dist/src/executors/openai-api.d.ts.map +1 -0
  117. package/dist/src/executors/openai-api.js +40 -0
  118. package/dist/src/executors/openai-api.js.map +1 -0
  119. package/dist/src/executors/openai-cli.d.ts +3 -0
  120. package/dist/src/executors/openai-cli.d.ts.map +1 -0
  121. package/dist/src/executors/openai-cli.js +60 -0
  122. package/dist/src/executors/openai-cli.js.map +1 -0
  123. package/dist/src/executors/script.d.ts +3 -0
  124. package/dist/src/executors/script.d.ts.map +1 -0
  125. package/dist/src/executors/script.js +63 -0
  126. package/dist/src/executors/script.js.map +1 -0
  127. package/dist/src/executors/shared.d.ts +117 -0
  128. package/dist/src/executors/shared.d.ts.map +1 -0
  129. package/dist/src/executors/shared.js +49 -0
  130. package/dist/src/executors/shared.js.map +1 -0
  131. package/dist/src/grading/assertions.d.ts +18 -0
  132. package/dist/src/grading/assertions.d.ts.map +1 -0
  133. package/dist/src/grading/assertions.js +239 -0
  134. package/dist/src/grading/assertions.js.map +1 -0
  135. package/dist/src/grading/index.d.ts +26 -0
  136. package/dist/src/grading/index.d.ts.map +1 -0
  137. package/dist/src/grading/index.js +98 -0
  138. package/dist/src/grading/index.js.map +1 -0
  139. package/dist/src/grading/judge.d.ts +13 -0
  140. package/dist/src/grading/judge.d.ts.map +1 -0
  141. package/dist/src/grading/judge.js +98 -0
  142. package/dist/src/grading/judge.js.map +1 -0
  143. package/dist/src/grading/layered-scores.d.ts +13 -0
  144. package/dist/src/grading/layered-scores.d.ts.map +1 -0
  145. package/dist/src/grading/layered-scores.js +62 -0
  146. package/dist/src/grading/layered-scores.js.map +1 -0
  147. package/dist/src/inputs/eval-config.d.ts +13 -0
  148. package/dist/src/inputs/eval-config.d.ts.map +1 -0
  149. package/dist/src/inputs/eval-config.js +136 -0
  150. package/dist/src/inputs/eval-config.js.map +1 -0
  151. package/dist/src/inputs/load-samples.d.ts +16 -0
  152. package/dist/src/inputs/load-samples.d.ts.map +1 -0
  153. package/dist/src/inputs/load-samples.js +51 -0
  154. package/dist/src/inputs/load-samples.js.map +1 -0
  155. package/dist/src/inputs/mcp-resolver.d.ts +50 -0
  156. package/dist/src/inputs/mcp-resolver.d.ts.map +1 -0
  157. package/dist/src/inputs/mcp-resolver.js +307 -0
  158. package/dist/src/inputs/mcp-resolver.js.map +1 -0
  159. package/dist/src/inputs/skill-loader.d.ts +18 -0
  160. package/dist/src/inputs/skill-loader.d.ts.map +1 -0
  161. package/dist/src/inputs/skill-loader.js +222 -0
  162. package/dist/src/inputs/skill-loader.js.map +1 -0
  163. package/dist/src/inputs/url-fetcher.d.ts +19 -0
  164. package/dist/src/inputs/url-fetcher.d.ts.map +1 -0
  165. package/dist/src/inputs/url-fetcher.js +241 -0
  166. package/dist/src/inputs/url-fetcher.js.map +1 -0
  167. package/dist/src/observability/production-analyzer.d.ts +61 -0
  168. package/dist/src/observability/production-analyzer.d.ts.map +1 -0
  169. package/dist/src/observability/production-analyzer.js +183 -0
  170. package/dist/src/observability/production-analyzer.js.map +1 -0
  171. package/dist/src/observability/trace-adapter.d.ts +75 -0
  172. package/dist/src/observability/trace-adapter.d.ts.map +1 -0
  173. package/dist/src/observability/trace-adapter.js +341 -0
  174. package/dist/src/observability/trace-adapter.js.map +1 -0
  175. package/dist/src/renderer/html-renderer.d.ts +9 -0
  176. package/dist/src/renderer/html-renderer.d.ts.map +1 -0
  177. package/dist/src/renderer/html-renderer.js +338 -0
  178. package/dist/src/renderer/html-renderer.js.map +1 -0
  179. package/dist/src/renderer/layout.d.ts +13 -0
  180. package/dist/src/renderer/layout.d.ts.map +1 -0
  181. package/dist/src/renderer/layout.js +476 -0
  182. package/dist/src/renderer/layout.js.map +1 -0
  183. package/dist/src/renderer/skill-health-renderer.d.ts +20 -0
  184. package/dist/src/renderer/skill-health-renderer.d.ts.map +1 -0
  185. package/dist/src/renderer/skill-health-renderer.js +218 -0
  186. package/dist/src/renderer/skill-health-renderer.js.map +1 -0
  187. package/dist/src/renderer/summary.d.ts +21 -0
  188. package/dist/src/renderer/summary.d.ts.map +1 -0
  189. package/dist/src/renderer/summary.js +1072 -0
  190. package/dist/src/renderer/summary.js.map +1 -0
  191. package/dist/src/renderer/table.d.ts +3 -0
  192. package/dist/src/renderer/table.d.ts.map +1 -0
  193. package/dist/src/renderer/table.js +134 -0
  194. package/dist/src/renderer/table.js.map +1 -0
  195. package/dist/src/renderer/trends.d.ts +6 -0
  196. package/dist/src/renderer/trends.d.ts.map +1 -0
  197. package/dist/src/renderer/trends.js +127 -0
  198. package/dist/src/renderer/trends.js.map +1 -0
  199. package/dist/src/server/job-store.d.ts +4 -0
  200. package/dist/src/server/job-store.d.ts.map +1 -0
  201. package/dist/src/server/job-store.js +68 -0
  202. package/dist/src/server/job-store.js.map +1 -0
  203. package/dist/src/server/report-server.d.ts +16 -0
  204. package/dist/src/server/report-server.d.ts.map +1 -0
  205. package/dist/src/server/report-server.js +196 -0
  206. package/dist/src/server/report-server.js.map +1 -0
  207. package/dist/src/server/report-store.d.ts +44 -0
  208. package/dist/src/server/report-store.d.ts.map +1 -0
  209. package/dist/src/server/report-store.js +190 -0
  210. package/dist/src/server/report-store.js.map +1 -0
  211. package/dist/src/types.d.ts +553 -0
  212. package/dist/src/types.d.ts.map +1 -0
  213. package/dist/src/types.js +2 -0
  214. package/dist/src/types.js.map +1 -0
  215. package/package.json +44 -3
@@ -0,0 +1,631 @@
1
+ /**
2
+ * Auto-analysis: detect patterns and generate insights from evaluation results.
3
+ */
4
+ /**
5
+ * Analyze an evaluation report and produce insights + suggestions.
6
+ */
7
+ export function analyzeResults(report) {
8
+ const insights = [];
9
+ const suggestions = [];
10
+ const variants = report.meta?.variants || [];
11
+ const results = report.results || [];
12
+ if (results.length === 0 || variants.length < 2) {
13
+ return { insights, suggestions };
14
+ }
15
+ // 1. Low-discrimination assertions
16
+ detectLowDiscrimination(results, variants, insights, suggestions);
17
+ // 2. Uniform scores across variants
18
+ detectUniformScores(results, variants, insights, suggestions);
19
+ // 3. All-pass / all-fail assertions
20
+ detectAllPassFail(results, variants, insights, suggestions);
21
+ // 4. High-cost samples
22
+ detectHighCost(results, variants, insights);
23
+ // 5. Efficiency gap (turns & cost)
24
+ detectEfficiencyGap(report, variants, insights, suggestions);
25
+ // 6. Agent tool usage patterns
26
+ detectToolPatterns(report, variants, insights, suggestions);
27
+ // 7. Tooling / permission issues
28
+ detectToolPermissionIssues(results, variants, insights, suggestions);
29
+ // 8. Trace integrity
30
+ detectTraceIntegrity(report, variants, insights, suggestions);
31
+ // 9. Agent assertion discrimination
32
+ detectAgentAssertionDiscrimination(results, variants, insights, suggestions);
33
+ // 10. Suggest --repeat when score variance is high and no repeat data
34
+ detectNeedRepeat(report, results, variants, insights, suggestions);
35
+ const summary = generateSummary(report, variants);
36
+ return { summary, insights, suggestions };
37
+ }
38
+ function generateSummary(report, variants) {
39
+ if (variants.length < 2)
40
+ return undefined;
41
+ const stats = report.summary || {};
42
+ // Find control group. experimentRole 是 v0.16 起用户显式声明的 control/treatment
43
+ // 角色(见 docs/terminology-spec.md 三-4),是判定对照组的唯一来源。
44
+ // 老 report(v0.15 及更早)variantConfig 里可能缺 experimentRole 字段,
45
+ // 退化到从 artifactKind === 'baseline' / experimentType 反推——标注为 legacy 路径。
46
+ const configs = report.meta?.variantConfigs || [];
47
+ let controlVariants = configs
48
+ .filter((c) => c.experimentRole === 'control')
49
+ .map((c) => c.variant);
50
+ if (controlVariants.length === 0) {
51
+ // legacy fallback for old reports without experimentRole
52
+ controlVariants = configs
53
+ .filter((c) => c.artifactKind === 'baseline' || c.experimentType === 'runtime-context-only' || c.experimentType === 'baseline')
54
+ .map((c) => c.variant);
55
+ }
56
+ const control = controlVariants[0] || variants[0];
57
+ const test = variants.find((v) => v !== control);
58
+ if (!test || !control)
59
+ return undefined;
60
+ const cs = stats[control];
61
+ const ts = stats[test];
62
+ if (!cs || !ts)
63
+ return undefined;
64
+ const lines = [];
65
+ // ── Section 1: Core verdict ──
66
+ const cScore = cs.avgCompositeScore;
67
+ const tScore = ts.avgCompositeScore;
68
+ const scoreDiff = tScore != null && cScore != null ? tScore - cScore : null;
69
+ if (scoreDiff != null && tScore != null && cScore != null) {
70
+ const absDiff = Math.abs(scoreDiff);
71
+ if (absDiff < 0.1) {
72
+ lines.push(`【结论】${test} 与 ${control} 综合得分持平(${tScore.toFixed(2)} vs ${cScore.toFixed(2)}),质量无显著差异。`);
73
+ }
74
+ else if (scoreDiff > 0) {
75
+ const tag = absDiff > 0.3 ? '明显领先' : '略优';
76
+ lines.push(`【结论】${test} 综合得分 ${tag}(${tScore.toFixed(2)} vs ${cScore.toFixed(2)},+${scoreDiff.toFixed(2)})。`);
77
+ }
78
+ else {
79
+ const tag = absDiff > 0.3 ? '明显落后' : '略低';
80
+ lines.push(`【结论】${test} 综合得分 ${tag}(${tScore.toFixed(2)} vs ${cScore.toFixed(2)},${scoreDiff.toFixed(2)})。`);
81
+ }
82
+ }
83
+ // ── Section 2: Key differentiators with concrete numbers ──
84
+ const diffs = [];
85
+ // Quality sub-scores — only mention when there IS a difference
86
+ const tFact = ts.avgFactScore;
87
+ const cFact = cs.avgFactScore;
88
+ if (tFact != null && cFact != null) {
89
+ if (tFact === cFact) {
90
+ // Both same — don't mention (e.g. both 5/5 is not interesting)
91
+ }
92
+ else if (tFact > cFact) {
93
+ diffs.push(`事实性 ${tFact.toFixed(1)} vs ${cFact.toFixed(1)}(↑${(tFact - cFact).toFixed(1)})`);
94
+ }
95
+ else {
96
+ diffs.push(`事实性 ${tFact.toFixed(1)} vs ${cFact.toFixed(1)}(↓${(cFact - tFact).toFixed(1)})`);
97
+ }
98
+ }
99
+ const tBehavior = ts.avgBehaviorScore;
100
+ const cBehavior = cs.avgBehaviorScore;
101
+ if (tBehavior != null && cBehavior != null && Math.abs(tBehavior - cBehavior) > 0.3) {
102
+ const dir = tBehavior > cBehavior ? '↑' : '↓';
103
+ diffs.push(`行为合规 ${tBehavior.toFixed(1)} vs ${cBehavior.toFixed(1)}(${dir}${Math.abs(tBehavior - cBehavior).toFixed(1)})`);
104
+ }
105
+ const tJudge = ts.avgJudgeScore;
106
+ const cJudge = cs.avgJudgeScore;
107
+ if (tJudge != null && cJudge != null && Math.abs(tJudge - cJudge) >= 0.5) {
108
+ const dir = tJudge > cJudge ? '↑' : '↓';
109
+ diffs.push(`LLM 评价 ${tJudge.toFixed(1)} vs ${cJudge.toFixed(1)}(${dir}${Math.abs(tJudge - cJudge).toFixed(1)})`);
110
+ }
111
+ // Efficiency — with percentages
112
+ const cTurns = cs.avgNumTurns;
113
+ const tTurns = ts.avgNumTurns;
114
+ if (cTurns > 0 && tTurns > 0 && cTurns !== tTurns) {
115
+ const pct = Math.abs(((tTurns - cTurns) / cTurns) * 100).toFixed(0);
116
+ if (tTurns < cTurns) {
117
+ diffs.push(`轮次 ${tTurns.toFixed(1)} vs ${cTurns.toFixed(1)}(↓${pct}%,路径更高效)`);
118
+ }
119
+ else {
120
+ diffs.push(`轮次 ${tTurns.toFixed(1)} vs ${cTurns.toFixed(1)}(↑${pct}%)`);
121
+ }
122
+ }
123
+ // Cost — with percentages
124
+ const cCost = cs.avgCostPerSample;
125
+ const tCost = ts.avgCostPerSample;
126
+ if (cCost > 0 && tCost > 0 && Math.abs(tCost - cCost) / cCost > 0.05) {
127
+ const pct = Math.abs(((tCost - cCost) / cCost) * 100).toFixed(0);
128
+ if (tCost < cCost) {
129
+ diffs.push(`单样本成本 $${tCost.toFixed(4)} vs $${cCost.toFixed(4)}(↓${pct}%)`);
130
+ }
131
+ else {
132
+ diffs.push(`单样本成本 $${tCost.toFixed(4)} vs $${cCost.toFixed(4)}(↑${pct}%)`);
133
+ }
134
+ }
135
+ // Duration — with percentages
136
+ const cDur = cs.avgDurationMs;
137
+ const tDur = ts.avgDurationMs;
138
+ if (cDur > 0 && tDur > 0 && Math.abs(tDur - cDur) / cDur > 0.1) {
139
+ const pct = Math.abs(((tDur - cDur) / cDur) * 100).toFixed(0);
140
+ const tSec = (tDur / 1000).toFixed(1);
141
+ const cSec = (cDur / 1000).toFixed(1);
142
+ if (tDur < cDur) {
143
+ diffs.push(`耗时 ${tSec}s vs ${cSec}s(↓${pct}%)`);
144
+ }
145
+ else {
146
+ diffs.push(`耗时 ${tSec}s vs ${cSec}s(↑${pct}%)`);
147
+ }
148
+ }
149
+ // Tool usage difference
150
+ const tTools = ts.avgToolCalls;
151
+ const cTools = cs.avgToolCalls;
152
+ if (tTools != null && cTools != null && Math.abs(tTools - cTools) > 0.5) {
153
+ diffs.push(`工具调用 ${tTools.toFixed(1)} vs ${cTools.toFixed(1)} 次`);
154
+ }
155
+ if (diffs.length > 0) {
156
+ lines.push(`【关键差异】${diffs.join(';')}。`);
157
+ }
158
+ // ── Section 3: Synthesis — connect the dots ──
159
+ const synthesis = [];
160
+ // Quality-cost tradeoff insight
161
+ if (scoreDiff != null && cCost > 0 && tCost > 0) {
162
+ const costRatio = (tCost - cCost) / cCost;
163
+ if (Math.abs(scoreDiff) < 0.1 && costRatio < -0.15) {
164
+ synthesis.push(`质量相当但成本显著降低,${test} 是更经济的选择`);
165
+ }
166
+ else if (scoreDiff > 0.1 && costRatio > 0.15) {
167
+ synthesis.push(`质量提升伴随成本上涨,需权衡投入产出比`);
168
+ }
169
+ else if (scoreDiff > 0.1 && costRatio <= 0) {
170
+ synthesis.push(`质量与成本双优,${test} 全面领先`);
171
+ }
172
+ else if (scoreDiff < -0.1 && costRatio < -0.15) {
173
+ synthesis.push(`成本虽降但质量下滑,需评估质量底线是否可接受`);
174
+ }
175
+ }
176
+ // Tool success rate concern
177
+ const tToolSuccess = ts.toolSuccessRate;
178
+ if (tToolSuccess != null && tToolSuccess < 1 && tToolSuccess >= 0.5) {
179
+ synthesis.push(`${test} 存在工具调用失败(成功率 ${(tToolSuccess * 100).toFixed(0)}%),可能拉低了得分`);
180
+ }
181
+ // Variance / significance from --repeat
182
+ if (report.variance) {
183
+ const v = report.variance;
184
+ for (const comp of v.comparisons) {
185
+ if (comp.a === control && comp.b === test || comp.a === test && comp.b === control) {
186
+ const es = comp.effectSize;
187
+ let esText = '';
188
+ if (es && es.primary !== 'none') {
189
+ const primaryVal = es.primary === 'g' ? es.hedgesG : es.cohensD;
190
+ const secondaryLabel = es.primary === 'g' ? 'd' : 'g';
191
+ const secondaryVal = es.primary === 'g' ? es.cohensD : es.hedgesG;
192
+ esText = `,效应量 ${es.primary}=${primaryVal.toFixed(2)}(${es.magnitude},${secondaryLabel}=${secondaryVal.toFixed(2)})`;
193
+ }
194
+ if (comp.significant) {
195
+ synthesis.push(`${v.runs} 轮重复评测显示差异具有统计显著性(t=${comp.tStatistic.toFixed(2)}, df=${comp.df.toFixed(1)}, p<0.05${esText})`);
196
+ }
197
+ else {
198
+ synthesis.push(`${v.runs} 轮重复评测未达到统计显著性(t=${comp.tStatistic.toFixed(2)}, df=${comp.df.toFixed(1)}${esText}),差异可能源于随机波动`);
199
+ }
200
+ }
201
+ }
202
+ const testVd = v.perVariant[test];
203
+ if (testVd) {
204
+ synthesis.push(`${test} 跨轮 95% 置信区间 [${testVd.lower.toFixed(2)}, ${testVd.upper.toFixed(2)}]`);
205
+ }
206
+ }
207
+ if (synthesis.length > 0) {
208
+ lines.push(`【综合洞察】${synthesis.join(';')}。`);
209
+ }
210
+ // Caveats and recommendations are handled by the issues table below,
211
+ // so the summary focuses only on verdict + differentiators + synthesis.
212
+ if (lines.length === 0)
213
+ return undefined;
214
+ return lines.join('\n');
215
+ }
216
+ const AGENT_ASSERTION_TYPES = new Set([
217
+ 'tools_called',
218
+ 'tools_not_called',
219
+ 'tool_input_contains',
220
+ 'tool_output_contains',
221
+ 'tools_count_min',
222
+ 'tools_count_max',
223
+ 'turns_min',
224
+ 'turns_max',
225
+ ]);
226
+ const TRACE_HEAVY_AGENT_ASSERTION_TYPES = new Set([
227
+ 'tools_called',
228
+ 'tools_not_called',
229
+ 'tool_input_contains',
230
+ 'tool_output_contains',
231
+ 'tools_count_min',
232
+ 'tools_count_max',
233
+ ]);
234
+ function collectAgentAssertionTypes(results, variants) {
235
+ const types = new Set();
236
+ for (const result of results) {
237
+ const details = result.variants?.[variants[0]]?.assertions?.details || [];
238
+ for (const detail of details) {
239
+ if (AGENT_ASSERTION_TYPES.has(detail.type)) {
240
+ types.add(detail.type);
241
+ }
242
+ }
243
+ }
244
+ return types;
245
+ }
246
+ function detectLowDiscrimination(results, variants, insights, suggestions) {
247
+ // For each sample, check if all variants have the same assertion pass/fail pattern
248
+ const allPassedPatterns = [];
249
+ const allFailedPatterns = [];
250
+ for (const r of results) {
251
+ const firstVariantData = r.variants?.[variants[0]];
252
+ if (!firstVariantData?.assertions?.details)
253
+ continue;
254
+ for (let ai = 0; ai < firstVariantData.assertions.details.length; ai++) {
255
+ const assertion = firstVariantData.assertions.details[ai];
256
+ let allSame = true;
257
+ for (let vi = 1; vi < variants.length; vi++) {
258
+ const otherData = r.variants?.[variants[vi]];
259
+ const otherDetail = otherData?.assertions?.details?.[ai];
260
+ if (!otherDetail || otherDetail.passed !== assertion.passed) {
261
+ allSame = false;
262
+ break;
263
+ }
264
+ }
265
+ if (allSame && variants.length >= 2) {
266
+ const entry = {
267
+ sample_id: r.sample_id,
268
+ type: assertion.type,
269
+ value: assertion.value,
270
+ allPassed: assertion.passed,
271
+ };
272
+ if (assertion.passed) {
273
+ allPassedPatterns.push(entry);
274
+ }
275
+ else {
276
+ allFailedPatterns.push(entry);
277
+ }
278
+ }
279
+ }
280
+ }
281
+ if (allPassedPatterns.length > 0) {
282
+ insights.push({
283
+ type: 'low_discrimination_all_passed',
284
+ severity: 'info',
285
+ message: `${allPassedPatterns.length} 个断言所有变体均通过,baseline 也能答对,区分度低`,
286
+ details: allPassedPatterns,
287
+ });
288
+ suggestions.push('对于所有变体均通过的断言,考虑替换为检测 skill 文档中独有细节的断言(如特定参数名、配置值)');
289
+ }
290
+ if (allFailedPatterns.length > 0) {
291
+ insights.push({
292
+ type: 'low_discrimination_all_failed',
293
+ severity: 'warning',
294
+ message: `${allFailedPatterns.length} 个断言所有变体均失败,断言可能过于严格或存在配置错误`,
295
+ details: allFailedPatterns,
296
+ });
297
+ suggestions.push('对于所有变体均失败的断言,检查断言条件是否正确,或降低匹配要求');
298
+ }
299
+ }
300
+ function detectUniformScores(results, variants, insights, suggestions) {
301
+ let uniformCount = 0;
302
+ const uniformSamples = [];
303
+ for (const r of results) {
304
+ const scores = variants
305
+ .map((v) => r.variants?.[v]?.compositeScore)
306
+ .filter((s) => typeof s === 'number' && s > 0);
307
+ if (scores.length >= 2) {
308
+ const max = Math.max(...scores);
309
+ const min = Math.min(...scores);
310
+ if (max - min < 0.5) {
311
+ uniformCount++;
312
+ uniformSamples.push(r.sample_id);
313
+ }
314
+ }
315
+ }
316
+ if (uniformCount > 0) {
317
+ insights.push({
318
+ type: 'uniform_scores',
319
+ severity: uniformCount === results.length ? 'warning' : 'info',
320
+ message: `${uniformCount}/${results.length} 个样本在各变体间分差 < 0.5,区分度较低`,
321
+ details: uniformSamples,
322
+ });
323
+ if (uniformCount === results.length) {
324
+ suggestions.push('所有样本分数差异都很小,建议增加更有挑战性的测试用例或更严格的评分标准');
325
+ }
326
+ }
327
+ }
328
+ function detectAllPassFail(results, variants, insights, suggestions) {
329
+ let allPassCount = 0;
330
+ let allFailCount = 0;
331
+ for (const r of results) {
332
+ for (const v of variants) {
333
+ const assertions = r.variants?.[v]?.assertions;
334
+ if (!assertions || assertions.total === 0)
335
+ continue;
336
+ if (assertions.passed === assertions.total)
337
+ allPassCount++;
338
+ if (assertions.passed === 0)
339
+ allFailCount++;
340
+ }
341
+ }
342
+ const totalEntries = results.length * variants.length;
343
+ if (allPassCount === totalEntries && totalEntries > 0) {
344
+ insights.push({
345
+ type: 'all_pass',
346
+ severity: 'warning',
347
+ message: '所有断言在所有变体上全部通过,断言可能过于宽松',
348
+ details: { allPassCount },
349
+ });
350
+ suggestions.push('所有断言都通过了,考虑增加更严格的断言来更好地区分变体质量');
351
+ }
352
+ if (allFailCount === totalEntries && totalEntries > 0) {
353
+ insights.push({
354
+ type: 'all_fail',
355
+ severity: 'error',
356
+ message: '所有断言在所有变体上全部失败,请检查断言配置是否正确',
357
+ details: { allFailCount },
358
+ });
359
+ suggestions.push('所有断言都失败了,请检查评测配置是否有误');
360
+ }
361
+ }
362
+ function detectNeedRepeat(report, results, variants, insights, suggestions) {
363
+ // Skip if already has variance data (i.e. --repeat was used)
364
+ if (report.variance)
365
+ return;
366
+ // Check if any variant has high score spread (max - min >= 2)
367
+ for (const v of variants) {
368
+ const s = report.summary?.[v];
369
+ if (!s || s.minCompositeScore == null || s.maxCompositeScore == null)
370
+ continue;
371
+ const spread = s.maxCompositeScore - s.minCompositeScore;
372
+ if (spread >= 2) {
373
+ insights.push({
374
+ type: 'suggest_repeat',
375
+ severity: 'info',
376
+ message: `${v} 的分数跨度较大(${s.minCompositeScore}~${s.maxCompositeScore}),建议使用 --repeat 3 多轮评测以获取方差分析和统计显著性检验`,
377
+ details: { variant: v, min: s.minCompositeScore, max: s.maxCompositeScore, spread },
378
+ });
379
+ suggestions.push(`运行 omk bench run --repeat 3 获取置信区间和 t 检验结果,量化变体间差异的统计显著性`);
380
+ return; // Only suggest once
381
+ }
382
+ }
383
+ }
384
+ function detectEfficiencyGap(report, variants, insights, suggestions) {
385
+ if (variants.length < 2)
386
+ return;
387
+ const summary = report.summary || {};
388
+ // Compare first variant (usually baseline) with others
389
+ const base = summary[variants[0]];
390
+ if (!base)
391
+ return;
392
+ for (let i = 1; i < variants.length; i++) {
393
+ const other = summary[variants[i]];
394
+ if (!other)
395
+ continue;
396
+ const details = [];
397
+ // Turns comparison
398
+ const baseTurns = base.avgNumTurns;
399
+ const otherTurns = other.avgNumTurns;
400
+ if (baseTurns > 0 && otherTurns > 0 && baseTurns !== otherTurns) {
401
+ const turnsDiff = baseTurns - otherTurns;
402
+ const turnsPct = ((turnsDiff / baseTurns) * 100).toFixed(0);
403
+ if (Math.abs(turnsDiff / baseTurns) > 0.3) {
404
+ details.push(turnsDiff > 0
405
+ ? `${variants[i]} 平均减少 ${turnsDiff.toFixed(1)} 轮对话(↓${Math.abs(Number(turnsPct))}%)`
406
+ : `${variants[i]} 平均增加 ${Math.abs(turnsDiff).toFixed(1)} 轮对话(↑${Math.abs(Number(turnsPct))}%)`);
407
+ }
408
+ }
409
+ // Cost comparison
410
+ const baseCost = base.avgCostPerSample;
411
+ const otherCost = other.avgCostPerSample;
412
+ if (baseCost > 0 && otherCost > 0 && baseCost !== otherCost) {
413
+ const costDiff = baseCost - otherCost;
414
+ const costPct = ((costDiff / baseCost) * 100).toFixed(0);
415
+ if (Math.abs(costDiff / baseCost) > 0.2) {
416
+ details.push(costDiff > 0
417
+ ? `单样本成本降低 ${Math.abs(Number(costPct))}%($${otherCost.toFixed(4)} vs $${baseCost.toFixed(4)})`
418
+ : `单样本成本增加 ${Math.abs(Number(costPct))}%($${otherCost.toFixed(4)} vs $${baseCost.toFixed(4)})`);
419
+ }
420
+ }
421
+ if (details.length > 0) {
422
+ insights.push({
423
+ type: 'efficiency_gap',
424
+ severity: 'info',
425
+ message: details.join(';'),
426
+ details: { baseline: variants[0], variant: variants[i], baseTurns, otherTurns, baseCost, otherCost },
427
+ });
428
+ suggestions.push(`${variants[i]} 在效率维度与 ${variants[0]} 存在显著差异,这对导航型 Skill 是重要的价值体现`);
429
+ }
430
+ }
431
+ }
432
+ function detectToolPatterns(report, variants, insights, suggestions) {
433
+ const summary = report.summary || {};
434
+ const hasTools = variants.some((v) => summary[v]?.avgToolCalls != null && summary[v].avgToolCalls > 0);
435
+ if (!hasTools)
436
+ return;
437
+ for (let i = 0; i < variants.length; i++) {
438
+ const v = variants[i];
439
+ const s = summary[v];
440
+ if (!s)
441
+ continue;
442
+ // Low tool success rate
443
+ if (s.toolSuccessRate != null && s.toolSuccessRate < 0.8 && s.avgToolCalls != null && s.avgToolCalls > 0) {
444
+ insights.push({
445
+ type: 'low_tool_success_rate',
446
+ severity: 'warning',
447
+ message: `${v} 的工具调用成功率仅 ${(s.toolSuccessRate * 100).toFixed(0)}%,可能存在工具选择或参数问题`,
448
+ details: { variant: v, toolSuccessRate: s.toolSuccessRate, avgToolCalls: s.avgToolCalls },
449
+ });
450
+ suggestions.push(`检查 ${v} 的工具调用失败模式,考虑在 skill 中增加工具使用指导`);
451
+ }
452
+ }
453
+ // Compare tool counts between variants
454
+ if (variants.length >= 2) {
455
+ const base = summary[variants[0]];
456
+ for (let i = 1; i < variants.length; i++) {
457
+ const other = summary[variants[i]];
458
+ if (!base?.avgToolCalls || !other?.avgToolCalls)
459
+ continue;
460
+ const diff = other.avgToolCalls - base.avgToolCalls;
461
+ if (Math.abs(diff) > 2) {
462
+ insights.push({
463
+ type: 'tool_count_gap',
464
+ severity: 'info',
465
+ message: diff > 0
466
+ ? `${variants[i]} 平均多调用 ${diff.toFixed(1)} 次工具(${other.avgToolCalls} vs ${base.avgToolCalls})`
467
+ : `${variants[i]} 平均少调用 ${Math.abs(diff).toFixed(1)} 次工具(${other.avgToolCalls} vs ${base.avgToolCalls})`,
468
+ details: { baseline: variants[0], variant: variants[i], baseTools: base.avgToolCalls, otherTools: other.avgToolCalls },
469
+ });
470
+ }
471
+ }
472
+ }
473
+ }
474
+ function detectToolPermissionIssues(results, variants, insights, suggestions) {
475
+ const permissionErrors = [];
476
+ for (const result of results) {
477
+ for (const variant of variants) {
478
+ const calls = result.variants?.[variant]?.toolCalls || [];
479
+ for (const call of calls) {
480
+ if (call.success)
481
+ continue;
482
+ const output = String(call.output || '');
483
+ if (/EACCES|permission denied/i.test(output)) {
484
+ permissionErrors.push({
485
+ variant,
486
+ tool: call.tool,
487
+ sample_id: result.sample_id,
488
+ output: output.slice(0, 200),
489
+ });
490
+ }
491
+ }
492
+ }
493
+ }
494
+ if (permissionErrors.length === 0)
495
+ return;
496
+ insights.push({
497
+ type: 'tool_permission_error',
498
+ severity: 'warning',
499
+ message: `检测到 ${permissionErrors.length} 次工具权限错误,实验结论可能被环境问题污染`,
500
+ details: permissionErrors.slice(0, 10),
501
+ });
502
+ suggestions.push('先处理工具权限错误,再解读 agent 分数差异;若是 Glob/rg 权限问题,优先避免在控制实验中依赖该工具');
503
+ }
504
+ function detectTraceIntegrity(report, variants, insights, suggestions) {
505
+ const summary = report.summary || {};
506
+ const agentAssertionTypes = collectAgentAssertionTypes(report.results || [], variants);
507
+ const needsTraceHeavyCoverage = [...agentAssertionTypes].some((type) => TRACE_HEAVY_AGENT_ASSERTION_TYPES.has(type));
508
+ const hasAgentLikeData = variants.some((variant) => {
509
+ const stats = summary[variant];
510
+ return Boolean(stats?.avgToolCalls || stats?.avgNumTurns > 1);
511
+ });
512
+ if (!hasAgentLikeData || !needsTraceHeavyCoverage)
513
+ return;
514
+ const weakCoverage = variants
515
+ .map((variant) => ({
516
+ variant,
517
+ traceCoverageRate: summary[variant]?.traceCoverageRate ?? 0,
518
+ avgAssistantTurns: summary[variant]?.avgAssistantTurns ?? 0,
519
+ avgToolTurns: summary[variant]?.avgToolTurns ?? 0,
520
+ avgToolFailures: summary[variant]?.avgToolFailures ?? 0,
521
+ }))
522
+ .filter((item) => item.traceCoverageRate < 0.75);
523
+ if (weakCoverage.length > 0) {
524
+ insights.push({
525
+ type: 'trace_integrity_gap',
526
+ severity: 'warning',
527
+ message: `${weakCoverage.length} 个 variant 的 trace 覆盖率低于 75%,报告可能不足以解释 agent 行为差异`,
528
+ details: weakCoverage,
529
+ });
530
+ suggestions.push('优先补齐 turns、toolCalls、timing、full output 的采集与落盘,确保报告能解释工具路径和错误恢复过程');
531
+ }
532
+ }
533
+ function detectAgentAssertionDiscrimination(results, variants, insights, suggestions) {
534
+ const assertionTypes = collectAgentAssertionTypes(results, variants);
535
+ const hasTraceHeavyAssertions = [...assertionTypes].some((type) => TRACE_HEAVY_AGENT_ASSERTION_TYPES.has(type));
536
+ if (!hasTraceHeavyAssertions)
537
+ return;
538
+ const evaluated = [];
539
+ let discriminative = 0;
540
+ let allPass = 0;
541
+ let allFail = 0;
542
+ for (const result of results) {
543
+ const firstDetails = result.variants?.[variants[0]]?.assertions?.details;
544
+ if (!firstDetails)
545
+ continue;
546
+ for (let index = 0; index < firstDetails.length; index++) {
547
+ const first = firstDetails[index];
548
+ if (!AGENT_ASSERTION_TYPES.has(first.type))
549
+ continue;
550
+ const states = [];
551
+ for (const variant of variants) {
552
+ const detail = result.variants?.[variant]?.assertions?.details?.[index];
553
+ if (!detail)
554
+ continue;
555
+ states.push(detail.passed);
556
+ }
557
+ if (states.length < 2)
558
+ continue;
559
+ const pattern = states.map((state) => (state ? 'T' : 'F')).join('/');
560
+ evaluated.push({
561
+ sample_id: result.sample_id,
562
+ type: first.type,
563
+ value: first.value,
564
+ pattern,
565
+ });
566
+ const unique = new Set(states);
567
+ if (unique.size > 1)
568
+ discriminative++;
569
+ else if (states.every(Boolean))
570
+ allPass++;
571
+ else
572
+ allFail++;
573
+ }
574
+ }
575
+ if (evaluated.length < 4)
576
+ return;
577
+ const discriminationRate = Number((discriminative / evaluated.length).toFixed(2));
578
+ if (discriminationRate < 0.3) {
579
+ insights.push({
580
+ type: 'agent_assertion_discrimination_low',
581
+ severity: 'warning',
582
+ message: `agent 断言区分度偏低,只有 ${(discriminationRate * 100).toFixed(0)}% 的断言真正拉开了变体差异`,
583
+ details: {
584
+ total: evaluated.length,
585
+ discriminative,
586
+ allPass,
587
+ allFail,
588
+ examples: evaluated.slice(0, 10),
589
+ },
590
+ });
591
+ suggestions.push('重写 agent 断言时,优先约束工具路径、关键文件读取和 turns 上限,避免大量“全过”或“全挂”的弱断言');
592
+ }
593
+ else {
594
+ insights.push({
595
+ type: 'agent_assertion_discrimination_ok',
596
+ severity: 'info',
597
+ message: `agent 断言区分度达标,${(discriminationRate * 100).toFixed(0)}% 的断言能区分变体差异`,
598
+ details: {
599
+ total: evaluated.length,
600
+ discriminative,
601
+ allPass,
602
+ allFail,
603
+ },
604
+ });
605
+ }
606
+ }
607
+ function detectHighCost(results, variants, insights) {
608
+ const costs = [];
609
+ for (const r of results) {
610
+ let sampleCost = 0;
611
+ for (const v of variants) {
612
+ sampleCost += r.variants?.[v]?.costUSD || 0;
613
+ }
614
+ costs.push({ sample_id: r.sample_id, costUSD: sampleCost });
615
+ }
616
+ if (costs.length < 2)
617
+ return;
618
+ const avg = costs.reduce((s, c) => s + c.costUSD, 0) / costs.length;
619
+ if (avg === 0)
620
+ return;
621
+ const expensive = costs.filter((c) => c.costUSD > avg * 2);
622
+ if (expensive.length > 0) {
623
+ insights.push({
624
+ type: 'high_cost_sample',
625
+ severity: 'info',
626
+ message: `${expensive.length} 个样本成本显著高于平均值 (>${(avg * 2).toFixed(4)} USD)`,
627
+ details: expensive,
628
+ });
629
+ }
630
+ }
631
+ //# sourceMappingURL=report-diagnostics.js.map