homegraph 1.1.1 → 1.1.3

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 (179) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +243 -243
  3. package/dist/bin/homegraph.js +371 -9
  4. package/dist/bin/homegraph.js.map +1 -1
  5. package/dist/db/migrations.js +18 -18
  6. package/dist/db/queries.js +140 -140
  7. package/dist/db/schema.sql +152 -152
  8. package/dist/directory.js +5 -5
  9. package/dist/extraction/wasm/tree-sitter-c_sharp.wasm +0 -0
  10. package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
  11. package/dist/installer/instructions-template.js +9 -9
  12. package/dist/installer/targets/deveco.d.ts +7 -2
  13. package/dist/installer/targets/deveco.d.ts.map +1 -1
  14. package/dist/installer/targets/deveco.js +62 -32
  15. package/dist/installer/targets/deveco.js.map +1 -1
  16. package/dist/installer/targets/shared.d.ts +5 -6
  17. package/dist/installer/targets/shared.d.ts.map +1 -1
  18. package/dist/installer/targets/shared.js +5 -6
  19. package/dist/installer/targets/shared.js.map +1 -1
  20. package/dist/mcp/liveness-watchdog.js +16 -16
  21. package/dist/mcp/server-instructions.js +69 -69
  22. package/dist/mcp/tools.d.ts +17 -4
  23. package/dist/mcp/tools.d.ts.map +1 -1
  24. package/dist/mcp/tools.js +192 -22
  25. package/dist/mcp/tools.js.map +1 -1
  26. package/dist/reasoning/reasoner.js +32 -32
  27. package/dist/spec/config.d.ts +43 -0
  28. package/dist/spec/config.d.ts.map +1 -0
  29. package/dist/spec/config.js +275 -0
  30. package/dist/spec/config.js.map +1 -0
  31. package/dist/spec/db/commit-node.d.ts +23 -0
  32. package/dist/spec/db/commit-node.d.ts.map +1 -0
  33. package/dist/spec/db/commit-node.js +62 -0
  34. package/dist/spec/db/commit-node.js.map +1 -0
  35. package/dist/spec/db/fragment-node.d.ts +23 -0
  36. package/dist/spec/db/fragment-node.d.ts.map +1 -0
  37. package/dist/spec/db/fragment-node.js +120 -0
  38. package/dist/spec/db/fragment-node.js.map +1 -0
  39. package/dist/spec/db/fts.d.ts +60 -0
  40. package/dist/spec/db/fts.d.ts.map +1 -0
  41. package/dist/spec/db/fts.js +285 -0
  42. package/dist/spec/db/fts.js.map +1 -0
  43. package/dist/spec/db/index.d.ts +13 -0
  44. package/dist/spec/db/index.d.ts.map +1 -0
  45. package/dist/spec/db/index.js +50 -0
  46. package/dist/spec/db/index.js.map +1 -0
  47. package/dist/spec/db/relations.d.ts +55 -0
  48. package/dist/spec/db/relations.d.ts.map +1 -0
  49. package/dist/spec/db/relations.js +158 -0
  50. package/dist/spec/db/relations.js.map +1 -0
  51. package/dist/spec/db/schema.d.ts +30 -0
  52. package/dist/spec/db/schema.d.ts.map +1 -0
  53. package/dist/spec/db/schema.js +87 -0
  54. package/dist/spec/db/schema.js.map +1 -0
  55. package/dist/spec/db/schema.sql +107 -0
  56. package/dist/spec/db/spec-node.d.ts +41 -0
  57. package/dist/spec/db/spec-node.d.ts.map +1 -0
  58. package/dist/spec/db/spec-node.js +114 -0
  59. package/dist/spec/db/spec-node.js.map +1 -0
  60. package/dist/spec/evolve/impact-locator.d.ts +13 -0
  61. package/dist/spec/evolve/impact-locator.d.ts.map +1 -0
  62. package/dist/spec/evolve/impact-locator.js +25 -0
  63. package/dist/spec/evolve/impact-locator.js.map +1 -0
  64. package/dist/spec/evolve/llm-client.d.ts +50 -0
  65. package/dist/spec/evolve/llm-client.d.ts.map +1 -0
  66. package/dist/spec/evolve/llm-client.js +176 -0
  67. package/dist/spec/evolve/llm-client.js.map +1 -0
  68. package/dist/spec/evolve/logic-checker.d.ts +12 -0
  69. package/dist/spec/evolve/logic-checker.d.ts.map +1 -0
  70. package/dist/spec/evolve/logic-checker.js +48 -0
  71. package/dist/spec/evolve/logic-checker.js.map +1 -0
  72. package/dist/spec/evolve/pipeline.d.ts +40 -0
  73. package/dist/spec/evolve/pipeline.d.ts.map +1 -0
  74. package/dist/spec/evolve/pipeline.js +314 -0
  75. package/dist/spec/evolve/pipeline.js.map +1 -0
  76. package/dist/spec/evolve/spec-rewriter.d.ts +42 -0
  77. package/dist/spec/evolve/spec-rewriter.d.ts.map +1 -0
  78. package/dist/spec/evolve/spec-rewriter.js +254 -0
  79. package/dist/spec/evolve/spec-rewriter.js.map +1 -0
  80. package/dist/spec/graph/queries.d.ts +58 -0
  81. package/dist/spec/graph/queries.d.ts.map +1 -0
  82. package/dist/spec/graph/queries.js +215 -0
  83. package/dist/spec/graph/queries.js.map +1 -0
  84. package/dist/spec/mining/diff-parser.d.ts +33 -0
  85. package/dist/spec/mining/diff-parser.d.ts.map +1 -0
  86. package/dist/spec/mining/diff-parser.js +166 -0
  87. package/dist/spec/mining/diff-parser.js.map +1 -0
  88. package/dist/spec/mining/git-scanner.d.ts +91 -0
  89. package/dist/spec/mining/git-scanner.d.ts.map +1 -0
  90. package/dist/spec/mining/git-scanner.js +266 -0
  91. package/dist/spec/mining/git-scanner.js.map +1 -0
  92. package/dist/spec/mining/pipeline.d.ts +53 -0
  93. package/dist/spec/mining/pipeline.d.ts.map +1 -0
  94. package/dist/spec/mining/pipeline.js +165 -0
  95. package/dist/spec/mining/pipeline.js.map +1 -0
  96. package/dist/spec/mining/scope-resolver.d.ts +45 -0
  97. package/dist/spec/mining/scope-resolver.d.ts.map +1 -0
  98. package/dist/spec/mining/scope-resolver.js +103 -0
  99. package/dist/spec/mining/scope-resolver.js.map +1 -0
  100. package/dist/spec/mining/spec-extractor.d.ts +69 -0
  101. package/dist/spec/mining/spec-extractor.d.ts.map +1 -0
  102. package/dist/spec/mining/spec-extractor.js +369 -0
  103. package/dist/spec/mining/spec-extractor.js.map +1 -0
  104. package/dist/spec/types.d.ts +149 -0
  105. package/dist/spec/types.d.ts.map +1 -0
  106. package/dist/spec/types.js +15 -0
  107. package/dist/spec/types.js.map +1 -0
  108. package/dist/spec/utils.d.ts +166 -0
  109. package/dist/spec/utils.d.ts.map +1 -0
  110. package/dist/spec/utils.js +461 -0
  111. package/dist/spec/utils.js.map +1 -0
  112. package/package.json +58 -57
  113. package/scripts/add-lang/bench.sh +60 -60
  114. package/scripts/add-lang/check-grammar.mjs +75 -75
  115. package/scripts/add-lang/dump-ast.mjs +103 -103
  116. package/scripts/add-lang/verify-extraction.mjs +70 -70
  117. package/scripts/agent-eval/ab-adoption.sh +91 -91
  118. package/scripts/agent-eval/ab-hook.sh +86 -86
  119. package/scripts/agent-eval/ab-impl.sh +78 -78
  120. package/scripts/agent-eval/ab-new-vs-baseline.sh +102 -102
  121. package/scripts/agent-eval/ab-sufficiency.sh +78 -78
  122. package/scripts/agent-eval/arms-F.sh +21 -21
  123. package/scripts/agent-eval/arms-matrix.sh +37 -37
  124. package/scripts/agent-eval/audit.sh +68 -68
  125. package/scripts/agent-eval/bench-readme.sh +28 -28
  126. package/scripts/agent-eval/bench-why-repo.sh +22 -22
  127. package/scripts/agent-eval/block-read-hook.sh +19 -19
  128. package/scripts/agent-eval/hook-settings.json +15 -15
  129. package/scripts/agent-eval/itrun.sh +120 -120
  130. package/scripts/agent-eval/offload-eval-3arm.sh +72 -72
  131. package/scripts/agent-eval/offload-eval-cost.mjs +133 -133
  132. package/scripts/agent-eval/offload-eval-effort.mjs +108 -108
  133. package/scripts/agent-eval/offload-eval-frontload-matrix.sh +25 -25
  134. package/scripts/agent-eval/offload-eval-frontload.sh +47 -47
  135. package/scripts/agent-eval/offload-eval-ground-truth.json +18 -18
  136. package/scripts/agent-eval/offload-eval-hook.mjs +84 -84
  137. package/scripts/agent-eval/offload-eval-judge.mjs +103 -103
  138. package/scripts/agent-eval/offload-eval-matrix.sh +20 -20
  139. package/scripts/agent-eval/offload-eval-metrics.mjs +94 -94
  140. package/scripts/agent-eval/offload-eval-refs1.sh +50 -50
  141. package/scripts/agent-eval/offload-eval-setup.sh +24 -24
  142. package/scripts/agent-eval/offload-eval-styles.sh +71 -71
  143. package/scripts/agent-eval/offload-eval-summarize.mjs +68 -68
  144. package/scripts/agent-eval/offload-eval.md +76 -76
  145. package/scripts/agent-eval/parse-arms.mjs +116 -116
  146. package/scripts/agent-eval/parse-bench-readme.mjs +84 -84
  147. package/scripts/agent-eval/parse-run.mjs +45 -45
  148. package/scripts/agent-eval/parse-session.mjs +93 -93
  149. package/scripts/agent-eval/probe-context.mjs +21 -21
  150. package/scripts/agent-eval/probe-explore.mjs +40 -40
  151. package/scripts/agent-eval/probe-node.mjs +20 -20
  152. package/scripts/agent-eval/probe-sweep.mjs +119 -119
  153. package/scripts/agent-eval/probe-trace.mjs +20 -20
  154. package/scripts/agent-eval/redirect-read-hook.sh +38 -38
  155. package/scripts/agent-eval/run-agent.sh +34 -34
  156. package/scripts/agent-eval/run-all.sh +69 -69
  157. package/scripts/agent-eval/run-arms.sh +56 -56
  158. package/scripts/agent-eval/seq-matrix.mjs +137 -137
  159. package/scripts/build-bundle.sh +118 -118
  160. package/scripts/extract-release-notes.mjs +130 -130
  161. package/scripts/local-install.sh +41 -41
  162. package/scripts/npm-sdk.js +75 -75
  163. package/scripts/npm-shim.js +246 -246
  164. package/scripts/pack-npm.sh +119 -119
  165. package/scripts/prepare-release.mjs +270 -270
  166. package/scripts/qa_eval/README.md +407 -400
  167. package/scripts/qa_eval/_test_deveco_probe.py +41 -0
  168. package/scripts/qa_eval/agent_runner.py +526 -526
  169. package/scripts/qa_eval/data/.gitignore +4 -4
  170. package/scripts/qa_eval/data/test-set.jsonl +2 -22
  171. package/scripts/qa_eval/eval_metrics.py +274 -233
  172. package/scripts/qa_eval/external_agent.py +976 -577
  173. package/scripts/qa_eval/llm_config.py +92 -92
  174. package/scripts/qa_eval/memory_monitor.py +132 -132
  175. package/scripts/qa_eval/my_answer_accuracy.py +187 -187
  176. package/scripts/qa_eval/requirements.txt +2 -2
  177. package/scripts/qa_eval/run_pipeline.py +804 -702
  178. package/scripts/qa_eval/stats_efficiency.py +279 -279
  179. package/scripts/qa_eval/stats_scores.py +207 -207
@@ -1,207 +1,207 @@
1
- #!/usr/bin/env python3
2
- """
3
- Stage 3 — Answer Accuracy Score 统计。
4
-
5
- Usage:
6
- python scripts/qa_eval/stats_scores.py -i scripts/qa_eval/log/result-with-builtin-scored.jsonl
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import argparse
12
- import json
13
- import statistics
14
- import sys
15
- from pathlib import Path
16
-
17
- _SCRIPT_DIR = Path(__file__).resolve().parent
18
- DEFAULT_INPUT = _SCRIPT_DIR / "log" / "result-with-builtin-scored.jsonl"
19
-
20
-
21
- def calculate_answer_accuracy_statistics(file_path: Path) -> dict | None:
22
- scores: list[float] = []
23
- total_samples = 0
24
- success_samples = 0
25
- failed_samples = 0
26
-
27
- print(f"正在读取文件: {file_path}")
28
-
29
- try:
30
- with file_path.open("r", encoding="utf-8") as f:
31
- for line_num, line in enumerate(f, 1):
32
- if not line.strip():
33
- continue
34
- try:
35
- data = json.loads(line)
36
- total_samples += 1
37
-
38
- if "answer_accuracy_score" not in data:
39
- failed_samples += 1
40
- print(f" 第{line_num}行: 缺少 answer_accuracy_score 字段")
41
- continue
42
-
43
- score = data["answer_accuracy_score"]
44
- eval_status = data.get("evaluation_status", "unknown")
45
-
46
- if (
47
- eval_status == "success"
48
- and isinstance(score, (int, float))
49
- and not (isinstance(score, float) and str(score).lower() == "nan")
50
- ):
51
- scores.append(float(score))
52
- success_samples += 1
53
- else:
54
- failed_samples += 1
55
- print(f" 第{line_num}行: 评估失败或无效分数 - 状态: {eval_status}, 分数: {score}")
56
-
57
- except json.JSONDecodeError as e:
58
- print(f" 第{line_num}行: JSON 解析错误 - {e}")
59
- failed_samples += 1
60
-
61
- except FileNotFoundError:
62
- print(f"错误: 文件 {file_path} 不存在")
63
- return None
64
- except OSError as e:
65
- print(f"读取文件时出错: {e}")
66
- return None
67
-
68
- if not scores:
69
- print("警告: 没有找到有效的 answer_accuracy_score 数据")
70
- return {
71
- "total_samples": total_samples,
72
- "success_samples": success_samples,
73
- "failed_samples": failed_samples,
74
- "valid_scores": 0,
75
- }
76
-
77
- stats = {
78
- "total_samples": total_samples,
79
- "success_samples": success_samples,
80
- "failed_samples": failed_samples,
81
- "valid_scores": len(scores),
82
- "mean": statistics.mean(scores),
83
- "median": statistics.median(scores),
84
- "min": min(scores),
85
- "max": max(scores),
86
- "std_dev": statistics.stdev(scores) if len(scores) > 1 else 0.0,
87
- "variance": statistics.variance(scores) if len(scores) > 1 else 0.0,
88
- "_scores": scores,
89
- }
90
- return stats
91
-
92
-
93
- def compute_stats_from_rows(rows: list[dict]) -> dict | None:
94
- """Same stats as calculate_answer_accuracy_statistics, from in-memory rows."""
95
- scores: list[float] = []
96
- total_samples = len(rows)
97
- success_samples = 0
98
- failed_samples = 0
99
-
100
- for data in rows:
101
- if "answer_accuracy_score" not in data:
102
- failed_samples += 1
103
- continue
104
- score = data["answer_accuracy_score"]
105
- eval_status = data.get("evaluation_status", "unknown")
106
- if (
107
- eval_status == "success"
108
- and isinstance(score, (int, float))
109
- and not (isinstance(score, float) and str(score).lower() == "nan")
110
- ):
111
- scores.append(float(score))
112
- success_samples += 1
113
- else:
114
- failed_samples += 1
115
-
116
- if not scores:
117
- return {
118
- "total_samples": total_samples,
119
- "success_samples": success_samples,
120
- "failed_samples": failed_samples,
121
- "valid_scores": 0,
122
- }
123
-
124
- return {
125
- "total_samples": total_samples,
126
- "success_samples": success_samples,
127
- "failed_samples": failed_samples,
128
- "valid_scores": len(scores),
129
- "mean": statistics.mean(scores),
130
- "median": statistics.median(scores),
131
- "min": min(scores),
132
- "max": max(scores),
133
- "std_dev": statistics.stdev(scores) if len(scores) > 1 else 0.0,
134
- "variance": statistics.variance(scores) if len(scores) > 1 else 0.0,
135
- "_scores": scores,
136
- }
137
-
138
-
139
- def print_statistics(stats: dict | None, file_path: Path | None = None, *, title: str | None = None) -> None:
140
- if not stats:
141
- return
142
-
143
- print("\n" + "=" * 60)
144
- print(title or "Answer Accuracy Score 统计结果")
145
- print("=" * 60)
146
- if file_path:
147
- print(f"文件: {file_path}")
148
-
149
- print(f"总样本数: {stats['total_samples']}")
150
- print(f"成功评估样本数: {stats['success_samples']}")
151
- print(f"失败评估样本数: {stats['failed_samples']}")
152
- print(f"有效分数数量: {stats['valid_scores']}")
153
-
154
- if stats["valid_scores"] > 0:
155
- print("\n分数统计:")
156
- print(f" 平均值 (Mean): {stats['mean']:.4f}")
157
- print(f" 中位数 (Median): {stats['median']:.4f}")
158
- print(f" 最小值 (Min): {stats['min']:.4f}")
159
- print(f" 最大值 (Max): {stats['max']:.4f}")
160
- if stats["valid_scores"] > 1:
161
- print(f" 标准差 (Std Dev): {stats['std_dev']:.4f}")
162
- print(f" 方差 (Variance): {stats['variance']:.4f}")
163
-
164
- scores = stats.get("_scores") or []
165
- print("\n分数分布:")
166
- ranges = [
167
- (0.0, 0.2, "很低 (0.0-0.2)"),
168
- (0.2, 0.4, "低 (0.2-0.4)"),
169
- (0.4, 0.6, "中等 (0.4-0.6)"),
170
- (0.6, 0.8, "高 (0.6-0.8)"),
171
- (0.8, 1.0, "很高 (0.8-1.0)"),
172
- ]
173
- for min_val, max_val, label in ranges:
174
- count = sum(
175
- 1
176
- for score in scores
177
- if min_val <= score < max_val or (max_val == 1.0 and score == 1.0)
178
- )
179
- pct = (count / len(scores)) * 100 if scores else 0
180
- print(f" {label}: {count} ({pct:.1f}%)")
181
-
182
- print("=" * 60)
183
-
184
- if stats["valid_scores"] > 0:
185
- success_rate = (stats["success_samples"] / stats["total_samples"]) * 100 if stats["total_samples"] else 0
186
- print(f"\n简要报告: {stats['total_samples']} 个样本,{stats['success_samples']} 个成功评估")
187
- print(f"Answer Accuracy Score 平均值: {stats['mean']:.4f}")
188
- print(f"评估成功率: {success_rate:.1f}%")
189
-
190
-
191
- def main() -> int:
192
- parser = argparse.ArgumentParser(description="Answer Accuracy Score 统计")
193
- parser.add_argument("--input", "-i", type=str, default=str(DEFAULT_INPUT), help="打分后的 JSONL")
194
- args = parser.parse_args()
195
-
196
- file_path = Path(args.input)
197
- if not file_path.is_file():
198
- print(f"错误: 文件 {file_path} 不存在")
199
- return 1
200
-
201
- stats = calculate_answer_accuracy_statistics(file_path)
202
- print_statistics(stats, file_path)
203
- return 0 if stats else 1
204
-
205
-
206
- if __name__ == "__main__":
207
- raise SystemExit(main())
1
+ #!/usr/bin/env python3
2
+ """
3
+ Stage 3 — Answer Accuracy Score 统计。
4
+
5
+ Usage:
6
+ python scripts/qa_eval/stats_scores.py -i scripts/qa_eval/log/result-with-builtin-scored.jsonl
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import json
13
+ import statistics
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ _SCRIPT_DIR = Path(__file__).resolve().parent
18
+ DEFAULT_INPUT = _SCRIPT_DIR / "log" / "result-with-builtin-scored.jsonl"
19
+
20
+
21
+ def calculate_answer_accuracy_statistics(file_path: Path) -> dict | None:
22
+ scores: list[float] = []
23
+ total_samples = 0
24
+ success_samples = 0
25
+ failed_samples = 0
26
+
27
+ print(f"正在读取文件: {file_path}")
28
+
29
+ try:
30
+ with file_path.open("r", encoding="utf-8") as f:
31
+ for line_num, line in enumerate(f, 1):
32
+ if not line.strip():
33
+ continue
34
+ try:
35
+ data = json.loads(line)
36
+ total_samples += 1
37
+
38
+ if "answer_accuracy_score" not in data:
39
+ failed_samples += 1
40
+ print(f" 第{line_num}行: 缺少 answer_accuracy_score 字段")
41
+ continue
42
+
43
+ score = data["answer_accuracy_score"]
44
+ eval_status = data.get("evaluation_status", "unknown")
45
+
46
+ if (
47
+ eval_status == "success"
48
+ and isinstance(score, (int, float))
49
+ and not (isinstance(score, float) and str(score).lower() == "nan")
50
+ ):
51
+ scores.append(float(score))
52
+ success_samples += 1
53
+ else:
54
+ failed_samples += 1
55
+ print(f" 第{line_num}行: 评估失败或无效分数 - 状态: {eval_status}, 分数: {score}")
56
+
57
+ except json.JSONDecodeError as e:
58
+ print(f" 第{line_num}行: JSON 解析错误 - {e}")
59
+ failed_samples += 1
60
+
61
+ except FileNotFoundError:
62
+ print(f"错误: 文件 {file_path} 不存在")
63
+ return None
64
+ except OSError as e:
65
+ print(f"读取文件时出错: {e}")
66
+ return None
67
+
68
+ if not scores:
69
+ print("警告: 没有找到有效的 answer_accuracy_score 数据")
70
+ return {
71
+ "total_samples": total_samples,
72
+ "success_samples": success_samples,
73
+ "failed_samples": failed_samples,
74
+ "valid_scores": 0,
75
+ }
76
+
77
+ stats = {
78
+ "total_samples": total_samples,
79
+ "success_samples": success_samples,
80
+ "failed_samples": failed_samples,
81
+ "valid_scores": len(scores),
82
+ "mean": statistics.mean(scores),
83
+ "median": statistics.median(scores),
84
+ "min": min(scores),
85
+ "max": max(scores),
86
+ "std_dev": statistics.stdev(scores) if len(scores) > 1 else 0.0,
87
+ "variance": statistics.variance(scores) if len(scores) > 1 else 0.0,
88
+ "_scores": scores,
89
+ }
90
+ return stats
91
+
92
+
93
+ def compute_stats_from_rows(rows: list[dict]) -> dict | None:
94
+ """Same stats as calculate_answer_accuracy_statistics, from in-memory rows."""
95
+ scores: list[float] = []
96
+ total_samples = len(rows)
97
+ success_samples = 0
98
+ failed_samples = 0
99
+
100
+ for data in rows:
101
+ if "answer_accuracy_score" not in data:
102
+ failed_samples += 1
103
+ continue
104
+ score = data["answer_accuracy_score"]
105
+ eval_status = data.get("evaluation_status", "unknown")
106
+ if (
107
+ eval_status == "success"
108
+ and isinstance(score, (int, float))
109
+ and not (isinstance(score, float) and str(score).lower() == "nan")
110
+ ):
111
+ scores.append(float(score))
112
+ success_samples += 1
113
+ else:
114
+ failed_samples += 1
115
+
116
+ if not scores:
117
+ return {
118
+ "total_samples": total_samples,
119
+ "success_samples": success_samples,
120
+ "failed_samples": failed_samples,
121
+ "valid_scores": 0,
122
+ }
123
+
124
+ return {
125
+ "total_samples": total_samples,
126
+ "success_samples": success_samples,
127
+ "failed_samples": failed_samples,
128
+ "valid_scores": len(scores),
129
+ "mean": statistics.mean(scores),
130
+ "median": statistics.median(scores),
131
+ "min": min(scores),
132
+ "max": max(scores),
133
+ "std_dev": statistics.stdev(scores) if len(scores) > 1 else 0.0,
134
+ "variance": statistics.variance(scores) if len(scores) > 1 else 0.0,
135
+ "_scores": scores,
136
+ }
137
+
138
+
139
+ def print_statistics(stats: dict | None, file_path: Path | None = None, *, title: str | None = None) -> None:
140
+ if not stats:
141
+ return
142
+
143
+ print("\n" + "=" * 60)
144
+ print(title or "Answer Accuracy Score 统计结果")
145
+ print("=" * 60)
146
+ if file_path:
147
+ print(f"文件: {file_path}")
148
+
149
+ print(f"总样本数: {stats['total_samples']}")
150
+ print(f"成功评估样本数: {stats['success_samples']}")
151
+ print(f"失败评估样本数: {stats['failed_samples']}")
152
+ print(f"有效分数数量: {stats['valid_scores']}")
153
+
154
+ if stats["valid_scores"] > 0:
155
+ print("\n分数统计:")
156
+ print(f" 平均值 (Mean): {stats['mean']:.4f}")
157
+ print(f" 中位数 (Median): {stats['median']:.4f}")
158
+ print(f" 最小值 (Min): {stats['min']:.4f}")
159
+ print(f" 最大值 (Max): {stats['max']:.4f}")
160
+ if stats["valid_scores"] > 1:
161
+ print(f" 标准差 (Std Dev): {stats['std_dev']:.4f}")
162
+ print(f" 方差 (Variance): {stats['variance']:.4f}")
163
+
164
+ scores = stats.get("_scores") or []
165
+ print("\n分数分布:")
166
+ ranges = [
167
+ (0.0, 0.2, "很低 (0.0-0.2)"),
168
+ (0.2, 0.4, "低 (0.2-0.4)"),
169
+ (0.4, 0.6, "中等 (0.4-0.6)"),
170
+ (0.6, 0.8, "高 (0.6-0.8)"),
171
+ (0.8, 1.0, "很高 (0.8-1.0)"),
172
+ ]
173
+ for min_val, max_val, label in ranges:
174
+ count = sum(
175
+ 1
176
+ for score in scores
177
+ if min_val <= score < max_val or (max_val == 1.0 and score == 1.0)
178
+ )
179
+ pct = (count / len(scores)) * 100 if scores else 0
180
+ print(f" {label}: {count} ({pct:.1f}%)")
181
+
182
+ print("=" * 60)
183
+
184
+ if stats["valid_scores"] > 0:
185
+ success_rate = (stats["success_samples"] / stats["total_samples"]) * 100 if stats["total_samples"] else 0
186
+ print(f"\n简要报告: {stats['total_samples']} 个样本,{stats['success_samples']} 个成功评估")
187
+ print(f"Answer Accuracy Score 平均值: {stats['mean']:.4f}")
188
+ print(f"评估成功率: {success_rate:.1f}%")
189
+
190
+
191
+ def main() -> int:
192
+ parser = argparse.ArgumentParser(description="Answer Accuracy Score 统计")
193
+ parser.add_argument("--input", "-i", type=str, default=str(DEFAULT_INPUT), help="打分后的 JSONL")
194
+ args = parser.parse_args()
195
+
196
+ file_path = Path(args.input)
197
+ if not file_path.is_file():
198
+ print(f"错误: 文件 {file_path} 不存在")
199
+ return 1
200
+
201
+ stats = calculate_answer_accuracy_statistics(file_path)
202
+ print_statistics(stats, file_path)
203
+ return 0 if stats else 1
204
+
205
+
206
+ if __name__ == "__main__":
207
+ raise SystemExit(main())